;; 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")])