;; Machine description for eBPF.
;; Copyright (C) 2023-2024 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
;; .
(define_mode_iterator AMO [SI DI])
;;; Plain atomic modify operations.
;; Non-fetching atomic add predates all other BPF atomic insns.
;; Use xadd{w,dw} for compatibility with older GAS without support
;; for v3 atomics. Newer GAS supports "aadd[32]" in line with the
;; other atomic operations.
(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_AADD))]
""
"{xadd\t%0,%1|lock *( *)(%w0) += %w1}"
[(set_attr "type" "atomic")])
(define_insn "atomic_and"
[(set (match_operand:AMO 0 "memory_operand" "+m")
(unspec_volatile:AMO
[(and:AMO (match_dup 0)
(match_operand:AMO 1 "register_operand" "r"))
(match_operand:SI 2 "const_int_operand")] ;; Memory model.
UNSPEC_AAND))]
"bpf_has_v3_atomics"
"{aand\t%0,%1|lock *( *)(%w0) &= %w1}")
(define_insn "atomic_or"
[(set (match_operand:AMO 0 "memory_operand" "+m")
(unspec_volatile:AMO
[(ior:AMO (match_dup 0)
(match_operand:AMO 1 "register_operand" "r"))
(match_operand:SI 2 "const_int_operand")] ;; Memory model.
UNSPEC_AOR))]
"bpf_has_v3_atomics"
"{aor\t%0,%1|lock *( *)(%w0) %|= %w1}")
(define_insn "atomic_xor"
[(set (match_operand:AMO 0 "memory_operand" "+m")
(unspec_volatile:AMO
[(xor:AMO (match_dup 0)
(match_operand:AMO 1 "register_operand" "r"))
(match_operand:SI 2 "const_int_operand")] ;; Memory model.
UNSPEC_AXOR))]
"bpf_has_v3_atomics"
"{axor\t%0,%1|lock *( *)(%w0) ^= %w1}")
;;; Feching (read-modify-store) versions of atomic operations.
(define_insn "atomic_fetch_add"
[(set (match_operand:AMO 0 "register_operand" "=r") ; output
(match_operand:AMO 1 "memory_operand" "+m"))
(set (match_dup 1)
(unspec_volatile:AMO
[(plus:AMO (match_dup 1)
(match_operand:AMO 2 "nonmemory_operand" "0")) ; second operand to op
(match_operand:AMO 3 "const_int_operand")] ;; Memory model
UNSPEC_AFADD))]
"bpf_has_v3_atomics"
"{afadd\t%1,%0|%w0 = atomic_fetch_add(( *)(%1), %w0)}")
(define_insn "atomic_fetch_and"
[(set (match_operand:AMO 0 "register_operand" "=r")
(match_operand:AMO 1 "memory_operand" "+m"))
(set (match_dup 1)
(unspec_volatile:AMO
[(and:AMO (match_dup 1)
(match_operand:AMO 2 "nonmemory_operand" "0"))
(match_operand:AMO 3 "const_int_operand")]
UNSPEC_AFAND))]
"bpf_has_v3_atomics"
"{afand\t%1,%0|%w0 = atomic_fetch_and(( *)(%1), %w0)}")
(define_insn "atomic_fetch_or"
[(set (match_operand:AMO 0 "register_operand" "=r")
(match_operand:AMO 1 "memory_operand" "+m"))
(set (match_dup 1)
(unspec_volatile:AMO
[(ior:AMO (match_dup 1)
(match_operand:AMO 2 "nonmemory_operand" "0"))
(match_operand:AMO 3 "const_int_operand")]
UNSPEC_AFOR))]
"bpf_has_v3_atomics"
"{afor\t%1,%0|%w0 = atomic_fetch_or(( *)(%1), %w0)}")
(define_insn "atomic_fetch_xor"
[(set (match_operand:AMO 0 "register_operand" "=r")
(match_operand:AMO 1 "memory_operand" "+m"))
(set (match_dup 1)
(unspec_volatile:AMO
[(xor:AMO (match_dup 1)
(match_operand:AMO 2 "nonmemory_operand" "0"))
(match_operand:AMO 3 "const_int_operand")]
UNSPEC_AFXOR))]
"bpf_has_v3_atomics"
"{afxor\t%1,%0|%w0 = atomic_fetch_xor(( *)(%1), %w0)}")
;; Weird suffixes used in pseudo-c atomic compare-exchange insns.
(define_mode_attr pcaxsuffix [(SI "32_32") (DI "_64")])
(define_insn "atomic_exchange"
[(set (match_operand:AMO 0 "register_operand" "=r")
(unspec_volatile:AMO
[(match_operand:AMO 1 "memory_operand" "+m")
(match_operand:AMO 3 "const_int_operand")]
UNSPEC_AXCHG))
(set (match_dup 1)
(match_operand:AMO 2 "nonmemory_operand" "0"))]
"bpf_has_v3_atomics"
"{axchg\t%1,%0|%w0 = xchg(%1, %w0)}")
;; The eBPF atomic-compare-and-exchange instruction has the form
;; acmp [%dst+offset], %src
;; The instruction atomically compares the value addressed by %dst+offset
;; with register R0. If they match, the value at %dst+offset is overwritten
;; with the value of %src. Otherwise, no write occurs. In either case, the
;; original value of %dst+offset is zero-extended and loaded back into R0.
(define_expand "atomic_compare_and_swap"
[(match_operand:SI 0 "register_operand" "=r") ;; bool success
(match_operand:AMO 1 "register_operand" "=r") ;; old value
(match_operand:AMO 2 "memory_operand" "+m") ;; memory
(match_operand:AMO 3 "register_operand") ;; expected
(match_operand:AMO 4 "register_operand") ;; desired
(match_operand:SI 5 "const_int_operand") ;; is_weak (unused)
(match_operand:SI 6 "const_int_operand") ;; success model (unused)
(match_operand:SI 7 "const_int_operand")] ;; failure model (unused)
"bpf_has_v3_atomics"
{
/* Load the expected value (into R0 by constraint of below). */
emit_move_insn (operands[1], operands[3]);
/* Emit the acmp. */
emit_insn (gen_atomic_compare_and_swap_1 (operands[1], operands[2], operands[3], operands[4]));
/* Assume that the operation was successful. */
emit_move_insn (operands[0], const1_rtx);
rtx_code_label *success_label = gen_label_rtx ();
/* Compare value that was in memory (now in R0/op[1]) to expected value.
If they are equal, then the write occurred. Otherwise, indicate fail in output. */
emit_cmp_and_jump_insns (operands[1], operands[3], EQ, 0,
GET_MODE (operands[1]), 1, success_label);
emit_move_insn (operands[0], const0_rtx);
if (success_label)
{
emit_label (success_label);
LABEL_NUSES (success_label) = 1;
}
DONE;
})
(define_insn "atomic_compare_and_swap_1"
[(set (match_operand:AMO 0 "register_operand" "+t") ;; R0 is both input (expected value)
(unspec_volatile:AMO ;; and output (original value)
[(match_dup 0) ;; result depends on R0
(match_operand:AMO 1 "memory_operand") ;; memory
(match_operand:AMO 2 "register_operand") ;; expected
(match_operand:AMO 3 "register_operand")] ;; desired
UNSPEC_ACMP))]
"bpf_has_v3_atomics"
"{acmp\t%1,%3|%w0 = cmpxchg(%1, %w0, %w3)}")