--- gcc/config/arm/arm.h | 6 ++++ gcc/config/arm/sync.md | 63 +++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 68 insertions(+), 1 deletions(-)
diff --git a/gcc/config/arm/arm.h b/gcc/config/arm/arm.h index 31f4856..33e5b8e 100644 --- a/gcc/config/arm/arm.h +++ b/gcc/config/arm/arm.h @@ -276,6 +276,12 @@ extern void (*arm_lang_output_object_attributes_hook)(void); /* Nonzero if this chip implements a memory barrier instruction. */ #define TARGET_HAVE_MEMORY_BARRIER (TARGET_HAVE_DMB || TARGET_HAVE_DMB_MCR) +/* Nonzero if this chip supports swp and swpb. These are technically present + post-armv6, but deprecated. Never use it if we have OS support, as swp is + not well-defined on SMP systems. */ +#define TARGET_HAVE_SWP \ + (TARGET_ARM && arm_arch4 && !arm_arch6 && arm_abi != ARM_ABI_AAPCS_LINUX) + /* Nonzero if this chip supports ldrex and strex */ #define TARGET_HAVE_LDREX ((arm_arch6 && TARGET_ARM) || arm_arch7) diff --git a/gcc/config/arm/sync.md b/gcc/config/arm/sync.md index 124ebf0..72e7181 100644 --- a/gcc/config/arm/sync.md +++ b/gcc/config/arm/sync.md @@ -26,6 +26,10 @@ (DI "TARGET_HAVE_LDREXD && ARM_DOUBLEWORD_ALIGN && TARGET_HAVE_MEMORY_BARRIER")]) +(define_mode_attr swp_predtab + [(QI "TARGET_HAVE_SWP") (HI "false") + (SI "TARGET_HAVE_SWP") (DI "false")]) + (define_code_iterator syncop [plus minus ior xor and]) (define_code_attr sync_optab @@ -132,7 +136,41 @@ DONE; }) -(define_insn_and_split "atomic_exchange<mode>" +(define_expand "atomic_exchange<mode>" + [(match_operand:QHSD 0 "s_register_operand" "") + (match_operand:QHSD 1 "mem_noofs_operand" "") + (match_operand:QHSD 2 "s_register_operand" "r") + (match_operand:SI 3 "const_int_operand" "")] + "<sync_predtab> || <swp_predtab>" +{ + if (<sync_predtab>) + emit_insn (gen_atomic_exchange<mode>_rex (operands[0], operands[1], + operands[2], operands[3])); + else + { + /* Memory barriers are introduced in armv6, which also gains the + ldrex insns. Therefore we can ignore the memory model argument + when issuing a SWP instruction. */ + gcc_checking_assert (!TARGET_HAVE_MEMORY_BARRIER); + + if (<MODE>mode == QImode) + { + rtx x = gen_reg_rtx (SImode); + emit_insn (gen_atomic_exchangeqi_swp (x, operands[1], operands[2])); + emit_move_insn (operands[0], gen_lowpart (QImode, x)); + } + else if (<MODE>mode == SImode) + { + emit_insn (gen_atomic_exchangesi_swp + (operands[0], operands[1], operands[2])); + } + else + gcc_unreachable (); + } + DONE; +}) + +(define_insn_and_split "atomic_exchange<mode>_rex" [(set (match_operand:QHSD 0 "s_register_operand" "=&r") ;; output (match_operand:QHSD 1 "mem_noofs_operand" "+Ua")) ;; memory (set (match_dup 1) @@ -152,6 +190,29 @@ DONE; }) +(define_insn "atomic_exchangeqi_swp" + [(set (match_operand:SI 0 "s_register_operand" "=&r") ;; output + (zero_extend:SI + (match_operand:QI 1 "mem_noofs_operand" "+Ua"))) ;; memory + (set (match_dup 1) + (unspec_volatile:QI + [(match_operand:QI 2 "s_register_operand" "r")] ;; input + VUNSPEC_ATOMIC_XCHG))] + "TARGET_HAVE_SWP" + "swpb%?\t%0, %2, %C1" + [(set_attr "predicable" "yes")]) + +(define_insn "atomic_exchangesi_swp" + [(set (match_operand:SI 0 "s_register_operand" "=&r") ;; output + (match_operand:SI 1 "mem_noofs_operand" "+Ua")) ;; memory + (set (match_dup 1) + (unspec_volatile:SI + [(match_operand:SI 2 "s_register_operand" "r")] ;; input + VUNSPEC_ATOMIC_XCHG))] + "TARGET_HAVE_SWP" + "swp%?\t%0, %2, %C1" + [(set_attr "predicable" "yes")]) + (define_mode_attr atomic_op_operand [(QI "reg_or_int_operand") (HI "reg_or_int_operand") -- 1.7.6.4