;; Machine description for eBPF. ;; Copyright (C) 2019-2022 Free Software Foundation, Inc. ;; This file is part of GCC. ;; GCC is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation; either version 3, or (at your option) ;; any later version. ;; GCC is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with GCC; see the file COPYING3. If not see ;; . (include "predicates.md") (include "constraints.md") ;;;; Unspecs (define_c_enum "unspec" [ UNSPEC_LDINDABS UNSPEC_XADD ]) ;;;; Constants (define_constants [(R0_REGNUM 0) (R1_REGNUM 1) (R2_REGNUM 2) (R3_REGNUM 3) (R4_REGNUM 4) (R5_REGNUM 5) (R6_REGNUM 6) (R7_REGNUM 7) (R8_REGNUM 8) (R9_REGNUM 9) (R10_REGNUM 10) (R11_REGNUM 11) ]) ;;;; Attributes ;; Instruction classes. ;; alu 64-bit arithmetic. ;; alu32 32-bit arithmetic. ;; end endianness conversion instructions. ;; ld load instructions. ;; lddx load 64-bit immediate instruction. ;; ldx generic load instructions. ;; st generic store instructions for immediates. ;; stx generic store instructions. ;; jmp jump instructions. ;; xadd atomic exchange-and-add instructions. ;; multi multiword sequence (or user asm statements). (define_attr "type" "unknown,alu,alu32,end,ld,lddw,ldx,st,stx,jmp,xadd,multi" (const_string "unknown")) ;; Length of instruction in bytes. (define_attr "length" "" (cond [ (eq_attr "type" "lddw") (const_int 16) ] (const_int 8))) ;; Describe a user's asm statement. (define_asm_attributes [(set_attr "type" "multi")]) ;;;; Mode attributes and iterators (define_mode_attr mop [(QI "b") (HI "h") (SI "w") (DI "dw") (SF "w") (DF "dw")]) (define_mode_attr mtype [(SI "alu32") (DI "alu")]) (define_mode_attr msuffix [(SI "32") (DI "")]) ;;;; NOPs ;; The Linux kernel verifier performs some optimizations that rely on ;; nop instructions to be encoded as `ja 0', i.e. a jump to offset 0, ;; which actually means to jump to the next instruction, since in BPF ;; offsets are expressed in 64-bit words _minus one_. (define_insn "nop" [(const_int 0)] "" "ja\t0" [(set_attr "type" "alu")]) ;;;; Arithmetic/Logical ;; The arithmetic and logic operations below are defined for SI and DI ;; modes. The mode iterator AM is used in order to expand to two ;; insns, with the proper modes. ;; ;; 32-bit arithmetic (for SI modes) is implemented using the alu32 ;; instructions, if available. (define_mode_iterator AM [(SI "bpf_has_alu32") DI]) ;;; Addition (define_insn "add3" [(set (match_operand:AM 0 "register_operand" "=r,r") (plus:AM (match_operand:AM 1 "register_operand" " 0,0") (match_operand:AM 2 "reg_or_imm_operand" " r,I")))] "1" "add\t%0,%2" [(set_attr "type" "")]) ;;; Subtraction ;; Note that subtractions of constants become additions, so there is ;; no need to handle immediate operands in the subMODE3 insns. (define_insn "sub3" [(set (match_operand:AM 0 "register_operand" "=r") (minus:AM (match_operand:AM 1 "register_operand" " 0") (match_operand:AM 2 "register_operand" " r")))] "" "sub\t%0,%2" [(set_attr "type" "")]) ;;; Negation (define_insn "neg2" [(set (match_operand:AM 0 "register_operand" "=r") (neg:AM (match_operand:AM 1 "register_operand" " 0")))] "" "neg\t%0" [(set_attr "type" "")]) ;;; Multiplication (define_insn "mul3" [(set (match_operand:AM 0 "register_operand" "=r,r") (mult:AM (match_operand:AM 1 "register_operand" " 0,0") (match_operand:AM 2 "reg_or_imm_operand" " r,I")))] "" "mul\t%0,%2" [(set_attr "type" "")]) (define_insn "*mulsidi3_zeroextend" [(set (match_operand:DI 0 "register_operand" "=r,r") (zero_extend:DI (mult:SI (match_operand:SI 1 "register_operand" "0,0") (match_operand:SI 2 "reg_or_imm_operand" "r,I"))))] "" "mul32\t%0,%2" [(set_attr "type" "alu32")]) ;;; Division ;; Note that eBPF doesn't provide instructions for signed integer ;; division. (define_insn "udiv3" [(set (match_operand:AM 0 "register_operand" "=r,r") (udiv:AM (match_operand:AM 1 "register_operand" " 0,0") (match_operand:AM 2 "reg_or_imm_operand" "r,I")))] "" "div\t%0,%2" [(set_attr "type" "")]) ;; However, xBPF does provide a signed division operator, sdiv. (define_insn "div3" [(set (match_operand:AM 0 "register_operand" "=r,r") (div:AM (match_operand:AM 1 "register_operand" " 0,0") (match_operand:AM 2 "reg_or_imm_operand" "r,I")))] "TARGET_XBPF" "sdiv\t%0,%2" [(set_attr "type" "")]) ;;; Modulus ;; Note that eBPF doesn't provide instructions for signed integer ;; remainder. (define_insn "umod3" [(set (match_operand:AM 0 "register_operand" "=r,r") (umod:AM (match_operand:AM 1 "register_operand" " 0,0") (match_operand:AM 2 "reg_or_imm_operand" "r,I")))] "" "mod\t%0,%2" [(set_attr "type" "")]) ;; Again, xBPF provides a signed version, smod. (define_insn "mod3" [(set (match_operand:AM 0 "register_operand" "=r,r") (mod:AM (match_operand:AM 1 "register_operand" " 0,0") (match_operand:AM 2 "reg_or_imm_operand" "r,I")))] "TARGET_XBPF" "smod\t%0,%2" [(set_attr "type" "")]) ;;; Logical AND (define_insn "and3" [(set (match_operand:AM 0 "register_operand" "=r,r") (and:AM (match_operand:AM 1 "register_operand" " 0,0") (match_operand:AM 2 "reg_or_imm_operand" "r,I")))] "" "and\t%0,%2" [(set_attr "type" "")]) ;;; Logical inclusive-OR (define_insn "ior3" [(set (match_operand:AM 0 "register_operand" "=r,r") (ior:AM (match_operand:AM 1 "register_operand" " 0,0") (match_operand:AM 2 "reg_or_imm_operand" "r,I")))] "" "or\t%0,%2" [(set_attr "type" "")]) ;;; Logical exclusive-OR (define_insn "xor3" [(set (match_operand:AM 0 "register_operand" "=r,r") (xor:AM (match_operand:AM 1 "register_operand" " 0,0") (match_operand:AM 2 "reg_or_imm_operand" "r,I")))] "" "xor\t%0,%2" [(set_attr "type" "")]) ;;;; Conversions ;;; Zero-extensions ;; For register operands smaller than 32-bit zero-extending is ;; achieved ANDing the value in the source register to a suitable ;; mask. ;; ;; For register operands bigger or equal than 32-bit, we generate a ;; mov32 instruction to zero the high 32-bits of the destination ;; register. ;; ;; For memory operands, of any width, zero-extending is achieved using ;; the ldx{bhwdw} instructions to load the values in registers. (define_insn "zero_extendhidi2" [(set (match_operand:DI 0 "register_operand" "=r,r,r") (zero_extend:DI (match_operand:HI 1 "nonimmediate_operand" "0,r,m")))] "" "@ and\t%0,0xffff mov\t%0,%1\;and\t%0,0xffff ldxh\t%0,%1" [(set_attr "type" "alu,alu,ldx")]) (define_insn "zero_extendqidi2" [(set (match_operand:DI 0 "register_operand" "=r,r,r") (zero_extend:DI (match_operand:QI 1 "nonimmediate_operand" "0,r,m")))] "" "@ and\t%0,0xff mov\t%0,%1\;and\t%0,0xff ldxb\t%0,%1" [(set_attr "type" "alu,alu,ldx")]) (define_insn "zero_extendsidi2" [(set (match_operand:DI 0 "register_operand" "=r,r") (zero_extend:DI (match_operand:SI 1 "nonimmediate_operand" "r,m")))] "" "@ * return bpf_has_alu32 ? \"mov32\t%0,%1\" : \"mov\t%0,%1\;and\t%0,0xffffffff\"; ldxw\t%0,%1" [(set_attr "type" "alu,ldx")]) ;;; Sign-extension ;; Sign-extending a 32-bit value into a 64-bit value is achieved using ;; shifting, with instructions generated by the expand below. (define_expand "extendsidi2" [(set (match_operand:DI 0 "register_operand") (sign_extend:DI (match_operand:SI 1 "register_operand")))] "" { operands[1] = gen_lowpart (DImode, operands[1]); emit_insn (gen_ashldi3 (operands[0], operands[1], GEN_INT (32))); emit_insn (gen_ashrdi3 (operands[0], operands[0], GEN_INT (32))); DONE; }) ;;;; Data movement (define_mode_iterator MM [QI HI SI DI SF DF]) (define_expand "mov" [(set (match_operand:MM 0 "general_operand") (match_operand:MM 1 "general_operand"))] "" " { if (!register_operand(operands[0], mode) && !register_operand(operands[1], mode)) operands[1] = force_reg (mode, operands[1]); }") (define_insn "*mov" [(set (match_operand:MM 0 "nonimmediate_operand" "=r, r,r,m,m") (match_operand:MM 1 "mov_src_operand" " m,rI,B,r,I"))] "" "@ ldx\t%0,%1 mov\t%0,%1 lddw\t%0,%1 stx\t%0,%1 st\t%0,%1" [(set_attr "type" "ldx,alu,alu,stx,st")]) ;;;; Shifts (define_mode_iterator SIM [(SI "bpf_has_alu32") DI]) (define_insn "ashr3" [(set (match_operand:SIM 0 "register_operand" "=r,r") (ashiftrt:SIM (match_operand:SIM 1 "register_operand" " 0,0") (match_operand:SIM 2 "reg_or_imm_operand" " r,I")))] "" "arsh\t%0,%2" [(set_attr "type" "")]) (define_insn "ashl3" [(set (match_operand:SIM 0 "register_operand" "=r,r") (ashift:SIM (match_operand:SIM 1 "register_operand" " 0,0") (match_operand:SIM 2 "reg_or_imm_operand" " r,I")))] "" "lsh\t%0,%2" [(set_attr "type" "")]) (define_insn "lshr3" [(set (match_operand:SIM 0 "register_operand" "=r,r") (lshiftrt:SIM (match_operand:SIM 1 "register_operand" " 0,0") (match_operand:SIM 2 "reg_or_imm_operand" " r,I")))] "" "rsh\t%0,%2" [(set_attr "type" "")]) ;;;; Conditional branches ;; The eBPF jump instructions use 64-bit arithmetic when evaluating ;; the jump conditions. Therefore we use DI modes below. (define_mode_iterator JM [(SI "bpf_has_jmp32") DI]) (define_expand "cbranch4" [(set (pc) (if_then_else (match_operator 0 "comparison_operator" [(match_operand:JM 1 "register_operand") (match_operand:JM 2 "reg_or_imm_operand")]) (label_ref (match_operand 3 "" "")) (pc)))] "" { if (!ordered_comparison_operator (operands[0], VOIDmode)) FAIL; bpf_expand_cbranch (mode, operands); }) (define_insn "*branch_on_" [(set (pc) (if_then_else (match_operator 3 "ordered_comparison_operator" [(match_operand:JM 0 "register_operand" "r") (match_operand:JM 1 "reg_or_imm_operand" "rI")]) (label_ref (match_operand 2 "" "")) (pc)))] "" { int code = GET_CODE (operands[3]); switch (code) { case EQ: return "jeq\t%0,%1,%2"; break; case NE: return "jne\t%0,%1,%2"; break; case LT: return "jslt\t%0,%1,%2"; break; case LE: return "jsle\t%0,%1,%2"; break; case GT: return "jsgt\t%0,%1,%2"; break; case GE: return "jsge\t%0,%1,%2"; break; case LTU: return "jlt\t%0,%1,%2"; break; case LEU: return "jle\t%0,%1,%2"; break; case GTU: return "jgt\t%0,%1,%2"; break; case GEU: return "jge\t%0,%1,%2"; break; default: gcc_unreachable (); return ""; } } [(set_attr "type" "jmp")]) ;;;; Unconditional branches (define_insn "jump" [(set (pc) (label_ref (match_operand 0 "" "")))] "" "ja\t%0" [(set_attr "type" "jmp")]) ;;;; Function prologue/epilogue (define_insn "exit" [(simple_return)] "" "exit" [(set_attr "type" "jmp")]) (define_expand "prologue" [(const_int 0)] "" { bpf_expand_prologue (); DONE; }) (define_expand "epilogue" [(const_int 0)] "" { bpf_expand_epilogue (); DONE; }) ;;;; Function calls (define_expand "call" [(parallel [(call (match_operand 0 "") (match_operand 1 "")) (use (match_operand 2 "")) ;; next_arg_reg (use (match_operand 3 ""))])] ;; struct_value_size_rtx "" { rtx target = XEXP (operands[0], 0); emit_call_insn (gen_call_internal (target, operands[1])); DONE; }) (define_insn "call_internal" [(call (mem:DI (match_operand:DI 0 "call_operand" "Sr")) (match_operand:SI 1 "general_operand" ""))] ;; operands[2] is next_arg_register ;; operands[3] is struct_value_size_rtx. "" { return bpf_output_call (operands[0]); } [(set_attr "type" "jmp")]) (define_expand "call_value" [(parallel [(set (match_operand 0 "") (call (match_operand 1 "") (match_operand 2 ""))) (use (match_operand 3 ""))])] ;; next_arg_reg "" { rtx target = XEXP (operands[1], 0); emit_call_insn (gen_call_value_internal (operands[0], target, operands[2])); DONE; }) (define_insn "call_value_internal" [(set (match_operand 0 "register_operand" "") (call (mem:DI (match_operand:DI 1 "call_operand" "Sr")) (match_operand:SI 2 "general_operand" "")))] ;; operands[3] is next_arg_register ;; operands[4] is struct_value_size_rtx. "" { return bpf_output_call (operands[1]); } [(set_attr "type" "jmp")]) (define_insn "sibcall" [(call (label_ref (match_operand 0 "" "")) (match_operand:SI 1 "general_operand" ""))] ;; operands[2] is next_arg_register ;; operands[3] is struct_value_size_rtx. "" "ja\t%0" [(set_attr "type" "jmp")]) ;;;; Non-generic load instructions (define_mode_iterator LDM [QI HI SI DI]) (define_mode_attr ldop [(QI "b") (HI "h") (SI "w") (DI "dw")]) (define_insn "ldind" [(set (reg:LDM R0_REGNUM) (unspec:LDM [(match_operand:DI 0 "register_operand" "r") (match_operand:SI 1 "imm32_operand" "I")] UNSPEC_LDINDABS)) (clobber (reg:DI R1_REGNUM)) (clobber (reg:DI R2_REGNUM)) (clobber (reg:DI R3_REGNUM)) (clobber (reg:DI R4_REGNUM))] "" "ldind\t%0,%1" [(set_attr "type" "ld")]) (define_insn "ldabs" [(set (reg:LDM R0_REGNUM) (unspec:LDM [(match_operand:SI 0 "imm32_operand" "I") (match_operand:SI 1 "imm32_operand" "I")] UNSPEC_LDINDABS)) (clobber (reg:DI R1_REGNUM)) (clobber (reg:DI R2_REGNUM)) (clobber (reg:DI R3_REGNUM)) (clobber (reg:DI R4_REGNUM))] "" "ldabs\t%0" [(set_attr "type" "ld")]) ;;;; Atomic increments (define_mode_iterator AMO [SI DI]) (define_insn "atomic_add" [(set (match_operand:AMO 0 "memory_operand" "+m") (unspec_volatile:AMO [(plus:AMO (match_dup 0) (match_operand:AMO 1 "register_operand" "r")) (match_operand:SI 2 "const_int_operand")] ;; Memory model. UNSPEC_XADD))] "" "xadd\t%0,%1" [(set_attr "type" "xadd")])