| ;; Machine description for ARM processor synchronization primitives. |
| ;; Copyright (C) 2010-2026 Free Software Foundation, Inc. |
| ;; Written by Marcus Shawcroft (marcus.shawcroft@arm.com) |
| ;; 64bit Atomics by Dave Gilbert (david.gilbert@linaro.org) |
| ;; |
| ;; 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 |
| ;; <http://www.gnu.org/licenses/>. */ |
| |
| (define_mode_attr sync_predtab |
| [(QI "TARGET_HAVE_LDREXBH && TARGET_HAVE_MEMORY_BARRIER") |
| (HI "TARGET_HAVE_LDREXBH && TARGET_HAVE_MEMORY_BARRIER") |
| (SI "TARGET_HAVE_LDREX && TARGET_HAVE_MEMORY_BARRIER") |
| (DI "TARGET_HAVE_LDREXD && ARM_DOUBLEWORD_ALIGN |
| && TARGET_HAVE_MEMORY_BARRIER")]) |
| |
| (define_code_iterator syncop [plus minus ior xor and]) |
| |
| (define_code_attr sync_optab |
| [(ior "or") (xor "xor") (and "and") (plus "add") (minus "sub")]) |
| |
| (define_mode_attr sync_sfx |
| [(QI "b") (HI "h") (SI "") (DI "d")]) |
| |
| (define_expand "memory_barrier" |
| [(set (match_dup 0) |
| (unspec:BLK [(match_dup 0)] UNSPEC_MEMORY_BARRIER))] |
| "TARGET_HAVE_MEMORY_BARRIER" |
| { |
| operands[0] = gen_rtx_MEM (BLKmode, gen_rtx_SCRATCH (Pmode)); |
| MEM_VOLATILE_P (operands[0]) = 1; |
| }) |
| |
| (define_insn "*memory_barrier" |
| [(set (match_operand:BLK 0 "" "") |
| (unspec:BLK [(match_dup 0)] UNSPEC_MEMORY_BARRIER))] |
| "TARGET_HAVE_MEMORY_BARRIER" |
| { |
| if (TARGET_HAVE_DMB) |
| { |
| return "dmb\\tish"; |
| } |
| |
| if (TARGET_HAVE_DMB_MCR) |
| return "mcr\\tp15, 0, r0, c7, c10, 5"; |
| |
| gcc_unreachable (); |
| } |
| [(set_attr "length" "4") |
| (set_attr "conds" "unconditional") |
| (set_attr "predicable" "no")]) |
| |
| (define_insn "arm_atomic_load<mode>" |
| [(set (match_operand:QHSI 0 "register_operand" "=r,l") |
| (unspec_volatile:QHSI |
| [(match_operand:QHSI 1 "mem_and_no_t1_wback_op" "m,Uw")] |
| VUNSPEC_LDR))] |
| "" |
| "ldr<sync_sfx>\t%0, %1" |
| [(set_attr "arch" "32,any")]) |
| |
| (define_insn "arm_atomic_load_acquire<mode>" |
| [(set (match_operand:QHSI 0 "register_operand" "=r") |
| (unspec_volatile:QHSI |
| [(match_operand:QHSI 1 "arm_sync_memory_operand" "Q")] |
| VUNSPEC_LDA))] |
| "TARGET_HAVE_LDACQ" |
| "lda<sync_sfx>\t%0, %C1" |
| ) |
| |
| (define_insn "arm_atomic_store<mode>" |
| [(set (match_operand:QHSI 0 "mem_and_no_t1_wback_op" "=m,Uw") |
| (unspec_volatile:QHSI |
| [(match_operand:QHSI 1 "register_operand" "r,l")] |
| VUNSPEC_STR))] |
| "" |
| "str<sync_sfx>\t%1, %0"; |
| [(set_attr "arch" "32,any")]) |
| |
| (define_insn "arm_atomic_store_release<mode>" |
| [(set (match_operand:QHSI 0 "arm_sync_memory_operand" "=Q") |
| (unspec_volatile:QHSI |
| [(match_operand:QHSI 1 "register_operand" "r")] |
| VUNSPEC_STL))] |
| "TARGET_HAVE_LDACQ" |
| "stl<sync_sfx>\t%1, %C0") |
| |
| |
| (define_expand "atomic_load<mode>" |
| [(match_operand:QHSI 0 "register_operand") ;; val out |
| (match_operand:QHSI 1 "arm_sync_memory_operand") ;; memory |
| (match_operand:SI 2 "const_int_operand")] ;; model |
| "" |
| { |
| memmodel model = memmodel_from_int (INTVAL (operands[2])); |
| |
| if (TARGET_HAVE_LDACQ && !is_mm_relaxed (model)) |
| { |
| emit_insn (gen_arm_atomic_load_acquire<mode> (operands[0], operands[1])); |
| DONE; |
| } |
| |
| /* The seq_cst model needs a barrier before the load to block reordering with |
| earlier accesses. */ |
| if (is_mm_seq_cst (model)) |
| expand_mem_thread_fence (model); |
| |
| emit_insn (gen_arm_atomic_load<mode> (operands[0], operands[1])); |
| |
| /* All non-relaxed models need a barrier after the load when load-acquire |
| instructions are not available. */ |
| if (!is_mm_relaxed (model)) |
| expand_mem_thread_fence (model); |
| |
| DONE; |
| }) |
| |
| (define_expand "atomic_store<mode>" |
| [(match_operand:QHSI 0 "arm_sync_memory_operand") ;; memory |
| (match_operand:QHSI 1 "register_operand") ;; store value |
| (match_operand:SI 2 "const_int_operand")] ;; model |
| "" |
| { |
| memmodel model = memmodel_from_int (INTVAL (operands[2])); |
| |
| if (TARGET_HAVE_LDACQ && !is_mm_relaxed (model)) |
| { |
| emit_insn (gen_arm_atomic_store_release<mode> (operands[0], operands[1])); |
| DONE; |
| } |
| |
| /* All non-relaxed models need a barrier after the load when load-acquire |
| instructions are not available. */ |
| if (!is_mm_relaxed (model)) |
| expand_mem_thread_fence (model); |
| |
| emit_insn (gen_arm_atomic_store<mode> (operands[0], operands[1])); |
| |
| /* The seq_cst model needs a barrier after the store to block reordering with |
| later accesses. */ |
| if (is_mm_seq_cst (model)) |
| expand_mem_thread_fence (model); |
| |
| DONE; |
| }) |
| |
| ;; An LDRD instruction usable by the atomic_loaddi expander on LPAE targets |
| |
| (define_insn "arm_atomic_loaddi2_ldrd" |
| [(set (match_operand:DI 0 "register_operand" "=r") |
| (unspec_volatile:DI |
| [(match_operand:DI 1 "arm_ldrd_memory_operand" "Uo")] |
| VUNSPEC_LDRD_ATOMIC))] |
| "ARM_DOUBLEWORD_ALIGN && TARGET_HAVE_LPAE" |
| "ldrd\t%0, %H0, %1" |
| ) |
| |
| ;; There are three ways to expand this depending on the architecture |
| ;; features available. As for the barriers, a load needs a barrier |
| ;; after it on all non-relaxed memory models except when the load |
| ;; has acquire semantics (for ARMv8-A). |
| |
| (define_expand "atomic_loaddi" |
| [(match_operand:DI 0 "s_register_operand") ;; val out |
| (match_operand:DI 1 "mem_noofs_operand") ;; memory |
| (match_operand:SI 2 "const_int_operand")] ;; model |
| "(TARGET_HAVE_LDREXD || TARGET_HAVE_LPAE || TARGET_HAVE_LDACQEXD) |
| && ARM_DOUBLEWORD_ALIGN" |
| { |
| memmodel model = memmodel_from_int (INTVAL (operands[2])); |
| |
| /* For ARMv8-A we can use an LDAEXD to atomically load two 32-bit registers |
| when acquire or stronger semantics are needed. When the relaxed model is |
| used this can be relaxed to a normal LDRD. */ |
| if (TARGET_HAVE_LDACQEXD) |
| { |
| if (is_mm_relaxed (model)) |
| emit_insn (gen_arm_atomic_loaddi2_ldrd (operands[0], operands[1])); |
| else |
| emit_insn (gen_arm_load_acquire_exclusivedi (operands[0], operands[1])); |
| |
| DONE; |
| } |
| |
| /* The seq_cst model needs a barrier before the load to block reordering with |
| earlier accesses. */ |
| if (is_mm_seq_cst (model)) |
| expand_mem_thread_fence (model); |
| |
| /* On LPAE targets LDRD and STRD accesses to 64-bit aligned |
| locations are 64-bit single-copy atomic. We still need barriers in the |
| appropriate places to implement the ordering constraints. */ |
| if (TARGET_HAVE_LPAE) |
| emit_insn (gen_arm_atomic_loaddi2_ldrd (operands[0], operands[1])); |
| else |
| emit_insn (gen_arm_load_exclusivedi (operands[0], operands[1])); |
| |
| /* All non-relaxed models need a barrier after the load when load-acquire |
| instructions are not available. */ |
| if (!is_mm_relaxed (model)) |
| expand_mem_thread_fence (model); |
| |
| DONE; |
| }) |
| |
| (define_expand "atomic_compare_and_swap<mode>" |
| [(match_operand:SI 0 "s_register_operand") ;; bool out |
| (match_operand:QHSD 1 "s_register_operand") ;; val out |
| (match_operand:QHSD 2 "mem_noofs_operand") ;; memory |
| (match_operand:QHSD 3 "general_operand") ;; expected |
| (match_operand:QHSD 4 "s_register_operand") ;; desired |
| (match_operand:SI 5 "const_int_operand") ;; is_weak |
| (match_operand:SI 6 "const_int_operand") ;; mod_s |
| (match_operand:SI 7 "const_int_operand")] ;; mod_f |
| "<sync_predtab>" |
| { |
| arm_expand_compare_and_swap (operands); |
| DONE; |
| }) |
| |
| ;; Constraints of this pattern must be at least as strict as those of the |
| ;; cbranchsi operations in thumb1.md and aim to be as permissive. |
| (define_insn_and_split "@atomic_compare_and_swap<CCSI:arch><NARROW:mode>_1" |
| [(set (match_operand:CCSI 0 "cc_register_operand" "=&c,&l,&l") ;; bool out |
| (unspec_volatile:CCSI [(const_int 0)] VUNSPEC_ATOMIC_CAS)) |
| (set (match_operand:SI 1 "s_register_operand" "=&r,&l,&l*h") ;; val out |
| (zero_extend:SI |
| (match_operand:NARROW 2 "mem_noofs_operand" "+Ua,Ua,Ua"))) ;; memory |
| (set (match_dup 2) |
| (unspec_volatile:NARROW |
| [(match_operand:SI 3 "arm_add_operand" "rIL,lILJ*h,*r") ;; expected |
| (match_operand:NARROW 4 "s_register_operand" "r,r,r") ;; desired |
| (match_operand:SI 5 "const_int_operand") ;; is_weak |
| (match_operand:SI 6 "const_int_operand") ;; mod_s |
| (match_operand:SI 7 "const_int_operand")] ;; mod_f |
| VUNSPEC_ATOMIC_CAS)) |
| (clobber (match_scratch:SI 8 "=&r,X,X"))] |
| "<NARROW:sync_predtab>" |
| "#" |
| "&& reload_completed" |
| [(const_int 0)] |
| { |
| arm_split_compare_and_swap (operands); |
| DONE; |
| } |
| [(set_attr "arch" "32,v8mb,v8mb")]) |
| |
| (define_mode_attr cas_cmp_operand |
| [(SI "arm_add_operand") (DI "cmpdi_operand")]) |
| (define_mode_attr cas_cmp_str |
| [(SI "rIL") (DI "rDi")]) |
| |
| ;; Constraints of this pattern must be at least as strict as those of the |
| ;; cbranchsi operations in thumb1.md and aim to be as permissive. |
| (define_insn_and_split "@atomic_compare_and_swap<CCSI:arch><SIDI:mode>_1" |
| [(set (match_operand:CCSI 0 "cc_register_operand" "=&c,&l,&l") ;; bool out |
| (unspec_volatile:CCSI [(const_int 0)] VUNSPEC_ATOMIC_CAS)) |
| (set (match_operand:SIDI 1 "s_register_operand" "=&r,&l,&l*h") ;; val out |
| (match_operand:SIDI 2 "mem_noofs_operand" "+Ua,Ua,Ua")) ;; memory |
| (set (match_dup 2) |
| (unspec_volatile:SIDI |
| [(match_operand:SIDI 3 "<SIDI:cas_cmp_operand>" "<SIDI:cas_cmp_str>,lILJ*h,*r") ;; expect |
| (match_operand:SIDI 4 "s_register_operand" "r,r,r") ;; desired |
| (match_operand:SI 5 "const_int_operand") ;; is_weak |
| (match_operand:SI 6 "const_int_operand") ;; mod_s |
| (match_operand:SI 7 "const_int_operand")] ;; mod_f |
| VUNSPEC_ATOMIC_CAS)) |
| (clobber (match_scratch:SI 8 "=&r,X,X"))] |
| "<SIDI:sync_predtab>" |
| "#" |
| "&& reload_completed" |
| [(const_int 0)] |
| { |
| arm_split_compare_and_swap (operands); |
| DONE; |
| } |
| [(set_attr "arch" "32,v8mb,v8mb")]) |
| |
| (define_insn_and_split "atomic_exchange<mode>" |
| [(set (match_operand:QHSD 0 "s_register_operand" "=&r,&r") ;; output |
| (match_operand:QHSD 1 "mem_noofs_operand" "+Ua,Ua")) ;; memory |
| (set (match_dup 1) |
| (unspec_volatile:QHSD |
| [(match_operand:QHSD 2 "s_register_operand" "r,r") ;; input |
| (match_operand:SI 3 "const_int_operand" "")] ;; model |
| VUNSPEC_ATOMIC_XCHG)) |
| (clobber (reg:CC CC_REGNUM)) |
| (clobber (match_scratch:SI 4 "=&r,&l"))] |
| "<sync_predtab>" |
| "#" |
| "&& reload_completed" |
| [(const_int 0)] |
| { |
| arm_split_atomic_op (SET, operands[0], NULL, operands[1], |
| operands[2], operands[3], operands[4]); |
| DONE; |
| } |
| [(set_attr "arch" "32,v8mb")]) |
| |
| ;; The following mode and code attribute are defined here because they are |
| ;; specific to atomics and are not needed anywhere else. |
| |
| (define_mode_attr atomic_op_operand |
| [(QI "reg_or_int_operand") |
| (HI "reg_or_int_operand") |
| (SI "reg_or_int_operand") |
| (DI "s_register_operand")]) |
| |
| (define_mode_attr atomic_op_str |
| [(QI "rn") (HI "rn") (SI "rn") (DI "r")]) |
| |
| (define_code_attr thumb1_atomic_op_str |
| [(ior "l,l") (xor "l,l") (and "l,l") (plus "lIJL,r") (minus "lPd,lPd")]) |
| |
| (define_code_attr thumb1_atomic_newop_str |
| [(ior "&l,&l") (xor "&l,&l") (and "&l,&l") (plus "&l,&r") (minus "&l,&l")]) |
| |
| ;; Constraints of this pattern must be at least as strict as those of the non |
| ;; atomic operations in thumb1.md and aim to be as permissive. |
| (define_insn_and_split "atomic_<sync_optab><mode>" |
| [(set (match_operand:QHSD 0 "mem_noofs_operand" "+Ua,Ua,Ua") |
| (unspec_volatile:QHSD |
| [(syncop:QHSD (match_dup 0) |
| (match_operand:QHSD 1 "<atomic_op_operand>" "<atomic_op_str>,<thumb1_atomic_op_str>")) |
| (match_operand:SI 2 "const_int_operand")] ;; model |
| VUNSPEC_ATOMIC_OP)) |
| (clobber (reg:CC CC_REGNUM)) |
| (clobber (match_scratch:QHSD 3 "=&r,<thumb1_atomic_newop_str>")) |
| (clobber (match_scratch:SI 4 "=&r,&l,&l"))] |
| "<sync_predtab>" |
| "#" |
| "&& reload_completed" |
| [(const_int 0)] |
| { |
| arm_split_atomic_op (<CODE>, NULL, operands[3], operands[0], |
| operands[1], operands[2], operands[4]); |
| DONE; |
| } |
| [(set_attr "arch" "32,v8mb,v8mb")]) |
| |
| ;; Constraints of this pattern must be at least as strict as those of the non |
| ;; atomic NANDs in thumb1.md and aim to be as permissive. |
| (define_insn_and_split "atomic_nand<mode>" |
| [(set (match_operand:QHSD 0 "mem_noofs_operand" "+Ua,Ua") |
| (unspec_volatile:QHSD |
| [(not:QHSD |
| (and:QHSD (match_dup 0) |
| (match_operand:QHSD 1 "<atomic_op_operand>" "<atomic_op_str>,l"))) |
| (match_operand:SI 2 "const_int_operand")] ;; model |
| VUNSPEC_ATOMIC_OP)) |
| (clobber (reg:CC CC_REGNUM)) |
| (clobber (match_scratch:QHSD 3 "=&r,&l")) |
| (clobber (match_scratch:SI 4 "=&r,&l"))] |
| "<sync_predtab>" |
| "#" |
| "&& reload_completed" |
| [(const_int 0)] |
| { |
| arm_split_atomic_op (NOT, NULL, operands[3], operands[0], |
| operands[1], operands[2], operands[4]); |
| DONE; |
| } |
| [(set_attr "arch" "32,v8mb")]) |
| |
| ;; 3 alternatives are needed to represent constraints after split from |
| ;; thumb1_addsi3: (i) case where operand1 and destination can be in different |
| ;; registers, (ii) case where they are in the same low register and (iii) case |
| ;; when they are in the same register without restriction on the register. We |
| ;; disparage slightly alternatives that require copying the old value into the |
| ;; register for the new value (see bind_old_new in arm_split_atomic_op). |
| (define_code_attr thumb1_atomic_fetch_op_str |
| [(ior "l,l,l") (xor "l,l,l") (and "l,l,l") (plus "lL,?IJ,?r") (minus "lPd,lPd,lPd")]) |
| |
| (define_code_attr thumb1_atomic_fetch_newop_str |
| [(ior "&l,&l,&l") (xor "&l,&l,&l") (and "&l,&l,&l") (plus "&l,&l,&r") (minus "&l,&l,&l")]) |
| |
| (define_code_attr thumb1_atomic_fetch_oldop_str |
| [(ior "&r,&r,&r") (xor "&r,&r,&r") (and "&r,&r,&r") (plus "&l,&r,&r") (minus "&l,&l,&l")]) |
| |
| ;; Constraints of this pattern must be at least as strict as those of the non |
| ;; atomic operations in thumb1.md and aim to be as permissive. |
| (define_insn_and_split "atomic_fetch_<sync_optab><mode>" |
| [(set (match_operand:QHSD 0 "s_register_operand" "=&r,<thumb1_atomic_fetch_oldop_str>") |
| (match_operand:QHSD 1 "mem_noofs_operand" "+Ua,Ua,Ua,Ua")) |
| (set (match_dup 1) |
| (unspec_volatile:QHSD |
| [(syncop:QHSD (match_dup 1) |
| (match_operand:QHSD 2 "<atomic_op_operand>" "<atomic_op_str>,<thumb1_atomic_fetch_op_str>")) |
| (match_operand:SI 3 "const_int_operand")] ;; model |
| VUNSPEC_ATOMIC_OP)) |
| (clobber (reg:CC CC_REGNUM)) |
| (clobber (match_scratch:QHSD 4 "=&r,<thumb1_atomic_fetch_newop_str>")) |
| (clobber (match_scratch:SI 5 "=&r,&l,&l,&l"))] |
| "<sync_predtab>" |
| "#" |
| "&& reload_completed" |
| [(const_int 0)] |
| { |
| arm_split_atomic_op (<CODE>, operands[0], operands[4], operands[1], |
| operands[2], operands[3], operands[5]); |
| DONE; |
| } |
| [(set_attr "arch" "32,v8mb,v8mb,v8mb")]) |
| |
| ;; Constraints of this pattern must be at least as strict as those of the non |
| ;; atomic NANDs in thumb1.md and aim to be as permissive. |
| (define_insn_and_split "atomic_fetch_nand<mode>" |
| [(set (match_operand:QHSD 0 "s_register_operand" "=&r,&r") |
| (match_operand:QHSD 1 "mem_noofs_operand" "+Ua,Ua")) |
| (set (match_dup 1) |
| (unspec_volatile:QHSD |
| [(not:QHSD |
| (and:QHSD (match_dup 1) |
| (match_operand:QHSD 2 "<atomic_op_operand>" "<atomic_op_str>,l"))) |
| (match_operand:SI 3 "const_int_operand")] ;; model |
| VUNSPEC_ATOMIC_OP)) |
| (clobber (reg:CC CC_REGNUM)) |
| (clobber (match_scratch:QHSD 4 "=&r,&l")) |
| (clobber (match_scratch:SI 5 "=&r,&l"))] |
| "<sync_predtab>" |
| "#" |
| "&& reload_completed" |
| [(const_int 0)] |
| { |
| arm_split_atomic_op (NOT, operands[0], operands[4], operands[1], |
| operands[2], operands[3], operands[5]); |
| DONE; |
| } |
| [(set_attr "arch" "32,v8mb")]) |
| |
| ;; Constraints of this pattern must be at least as strict as those of the non |
| ;; atomic operations in thumb1.md and aim to be as permissive. |
| (define_insn_and_split "atomic_<sync_optab>_fetch<mode>" |
| [(set (match_operand:QHSD 0 "s_register_operand" "=&r,<thumb1_atomic_newop_str>") |
| (syncop:QHSD |
| (match_operand:QHSD 1 "mem_noofs_operand" "+Ua,Ua,Ua") |
| (match_operand:QHSD 2 "<atomic_op_operand>" "<atomic_op_str>,<thumb1_atomic_op_str>"))) |
| (set (match_dup 1) |
| (unspec_volatile:QHSD |
| [(match_dup 1) (match_dup 2) |
| (match_operand:SI 3 "const_int_operand")] ;; model |
| VUNSPEC_ATOMIC_OP)) |
| (clobber (reg:CC CC_REGNUM)) |
| (clobber (match_scratch:SI 4 "=&r,&l,&l"))] |
| "<sync_predtab>" |
| "#" |
| "&& reload_completed" |
| [(const_int 0)] |
| { |
| arm_split_atomic_op (<CODE>, NULL, operands[0], operands[1], |
| operands[2], operands[3], operands[4]); |
| DONE; |
| } |
| [(set_attr "arch" "32,v8mb,v8mb")]) |
| |
| ;; Constraints of this pattern must be at least as strict as those of the non |
| ;; atomic NANDs in thumb1.md and aim to be as permissive. |
| (define_insn_and_split "atomic_nand_fetch<mode>" |
| [(set (match_operand:QHSD 0 "s_register_operand" "=&r,&l") |
| (not:QHSD |
| (and:QHSD |
| (match_operand:QHSD 1 "mem_noofs_operand" "+Ua,Ua") |
| (match_operand:QHSD 2 "<atomic_op_operand>" "<atomic_op_str>,l")))) |
| (set (match_dup 1) |
| (unspec_volatile:QHSD |
| [(match_dup 1) (match_dup 2) |
| (match_operand:SI 3 "const_int_operand")] ;; model |
| VUNSPEC_ATOMIC_OP)) |
| (clobber (reg:CC CC_REGNUM)) |
| (clobber (match_scratch:SI 4 "=&r,&l"))] |
| "<sync_predtab>" |
| "#" |
| "&& reload_completed" |
| [(const_int 0)] |
| { |
| arm_split_atomic_op (NOT, NULL, operands[0], operands[1], |
| operands[2], operands[3], operands[4]); |
| DONE; |
| } |
| [(set_attr "arch" "32,v8mb")]) |
| |
| (define_insn "arm_load_exclusive<mode>" |
| [(set (match_operand:SI 0 "s_register_operand" "=r") |
| (zero_extend:SI |
| (unspec_volatile:NARROW |
| [(match_operand:NARROW 1 "mem_noofs_operand" "Ua")] |
| VUNSPEC_LL)))] |
| "TARGET_HAVE_LDREXBH" |
| "ldrex<sync_sfx>\t%0, %C1" |
| ) |
| |
| (define_insn "arm_load_acquire_exclusive<mode>" |
| [(set (match_operand:SI 0 "s_register_operand" "=r") |
| (zero_extend:SI |
| (unspec_volatile:NARROW |
| [(match_operand:NARROW 1 "mem_noofs_operand" "Ua")] |
| VUNSPEC_LAX)))] |
| "TARGET_HAVE_LDACQ" |
| "ldaex<sync_sfx>\\t%0, %C1" |
| ) |
| |
| (define_insn "arm_load_exclusivesi" |
| [(set (match_operand:SI 0 "s_register_operand" "=r") |
| (unspec_volatile:SI |
| [(match_operand:SI 1 "mem_noofs_operand" "Ua")] |
| VUNSPEC_LL))] |
| "TARGET_HAVE_LDREX" |
| "ldrex\t%0, %C1" |
| ) |
| |
| (define_insn "arm_load_acquire_exclusivesi" |
| [(set (match_operand:SI 0 "s_register_operand" "=r") |
| (unspec_volatile:SI |
| [(match_operand:SI 1 "mem_noofs_operand" "Ua")] |
| VUNSPEC_LAX))] |
| "TARGET_HAVE_LDACQ" |
| "ldaex\t%0, %C1" |
| ) |
| |
| (define_insn "arm_load_exclusivedi" |
| [(set (match_operand:DI 0 "s_register_operand" "=r") |
| (unspec_volatile:DI |
| [(match_operand:DI 1 "mem_noofs_operand" "Ua")] |
| VUNSPEC_LL))] |
| "TARGET_HAVE_LDREXD" |
| "ldrexd\t%0, %H0, %C1" |
| ) |
| |
| (define_insn "arm_load_acquire_exclusivedi" |
| [(set (match_operand:DI 0 "s_register_operand" "=r") |
| (unspec_volatile:DI |
| [(match_operand:DI 1 "mem_noofs_operand" "Ua")] |
| VUNSPEC_LAX))] |
| "TARGET_HAVE_LDACQEXD && ARM_DOUBLEWORD_ALIGN" |
| "ldaexd\t%0, %H0, %C1" |
| ) |
| |
| (define_insn "arm_store_exclusive<mode>" |
| [(set (match_operand:SI 0 "s_register_operand" "=&r") |
| (unspec_volatile:SI [(const_int 0)] VUNSPEC_SC)) |
| (set (match_operand:QHSD 1 "mem_noofs_operand" "=Ua") |
| (unspec_volatile:QHSD |
| [(match_operand:QHSD 2 "s_register_operand" "r")] |
| VUNSPEC_SC))] |
| "<sync_predtab>" |
| { |
| if (<MODE>mode == DImode) |
| { |
| /* The restrictions on target registers in ARM mode are that the two |
| registers are consecutive and the first one is even; Thumb is |
| actually more flexible, but DI should give us this anyway. |
| Note that the 1st register always gets the |
| lowest word in memory. */ |
| gcc_assert ((REGNO (operands[2]) & 1) == 0 || TARGET_THUMB2); |
| return "strexd\t%0, %2, %H2, %C1"; |
| } |
| return "strex<sync_sfx>\t%0, %2, %C1"; |
| } |
| ) |
| |
| (define_insn "arm_store_release_exclusivedi" |
| [(set (match_operand:SI 0 "s_register_operand" "=&r") |
| (unspec_volatile:SI [(const_int 0)] VUNSPEC_SLX)) |
| (set (match_operand:DI 1 "mem_noofs_operand" "=Ua") |
| (unspec_volatile:DI |
| [(match_operand:DI 2 "s_register_operand" "r")] |
| VUNSPEC_SLX))] |
| "TARGET_HAVE_LDACQEXD && ARM_DOUBLEWORD_ALIGN" |
| { |
| /* See comment in arm_store_exclusive<mode> above. */ |
| gcc_assert ((REGNO (operands[2]) & 1) == 0 || TARGET_THUMB2); |
| return "stlexd\t%0, %2, %H2, %C1"; |
| } |
| ) |
| |
| (define_insn "arm_store_release_exclusive<mode>" |
| [(set (match_operand:SI 0 "s_register_operand" "=&r") |
| (unspec_volatile:SI [(const_int 0)] VUNSPEC_SLX)) |
| (set (match_operand:QHSI 1 "mem_noofs_operand" "=Ua") |
| (unspec_volatile:QHSI |
| [(match_operand:QHSI 2 "s_register_operand" "r")] |
| VUNSPEC_SLX))] |
| "TARGET_HAVE_LDACQ" |
| "stlex<sync_sfx>\t%0, %2, %C1") |