From 01c196ea91cce2665bc7e8df7edfd7dca0352d86 Mon Sep 17 00:00:00 2001 From: Tom de Vries Date: Wed, 20 Jun 2012 00:57:23 +0000 Subject: [PATCH] 2012-06-19 Tom de Vries Maxim Kuvyrkov * config/mips/mips.c (mips_emit_pre_atomic_barrier_p,) (mips_emit_post_atomic_barrier_p): New static functions. (mips_process_sync_loop): Use them. Emit sync memory barriers in accordance with memory model semantics. Add return of CMP result for compare_and_swap. * config/mips/mips.md: Update comment. (sync_cmp): New attribute. (sync_memmodel): New attribute replacing sync_release_barrier. * config/mips/sync.md (UNSPEC_ATOMIC_COMPARE_AND_SWAP,) (UNSPEC_ATOMIC_EXCHANGE, UNSPEC_ATOMIC_FETCH_OP): New constants. (sync_lock_test_and_set, test_and_set_12): Update. (atomic_compare_and_swap, atomic_exchange, atomic_exchange_llsc,) (atomic_fetch_add, atomic_fetch_add_llsc): New patterns. Co-Authored-By: Maxim Kuvyrkov From-SVN: r188803 --- gcc/ChangeLog | 17 +++++++ gcc/config/mips/mips.c | 71 ++++++++++++++++++++++++-- gcc/config/mips/mips.md | 13 +++-- gcc/config/mips/sync.md | 107 +++++++++++++++++++++++++++++++++++++++- 4 files changed, 199 insertions(+), 9 deletions(-) diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 77e029e1414..069b61c9535 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,20 @@ +2012-06-19 Tom de Vries + Maxim Kuvyrkov + + * config/mips/mips.c (mips_emit_pre_atomic_barrier_p,) + (mips_emit_post_atomic_barrier_p): New static functions. + (mips_process_sync_loop): Use them. Emit sync memory barriers in + accordance with memory model semantics. Add return of CMP result for + compare_and_swap. + * config/mips/mips.md: Update comment. + (sync_cmp): New attribute. + (sync_memmodel): New attribute replacing sync_release_barrier. + * config/mips/sync.md (UNSPEC_ATOMIC_COMPARE_AND_SWAP,) + (UNSPEC_ATOMIC_EXCHANGE, UNSPEC_ATOMIC_FETCH_OP): New constants. + (sync_lock_test_and_set, test_and_set_12): Update. + (atomic_compare_and_swap, atomic_exchange, atomic_exchange_llsc,) + (atomic_fetch_add, atomic_fetch_add_llsc): New patterns. + 2012-06-19 Joseph Myers * config/rs6000/spe.md (*mov_si_e500_subreg0): Rename to diff --git a/gcc/config/mips/mips.c b/gcc/config/mips/mips.c index 6e0f39c1d1c..f37c19499d7 100644 --- a/gcc/config/mips/mips.c +++ b/gcc/config/mips/mips.c @@ -11976,6 +11976,45 @@ mips_sync_insn2_template (enum attr_sync_insn2 type) gcc_unreachable (); } +/* Subroutines of the mips_process_sync_loop. + Emit barriers as needed for the memory MODEL. */ + +static bool +mips_emit_pre_atomic_barrier_p (enum memmodel model) +{ + switch (model) + { + case MEMMODEL_RELAXED: + case MEMMODEL_CONSUME: + case MEMMODEL_ACQUIRE: + return false; + case MEMMODEL_RELEASE: + case MEMMODEL_ACQ_REL: + case MEMMODEL_SEQ_CST: + return true; + default: + gcc_unreachable (); + } +} + +static bool +mips_emit_post_atomic_barrier_p (enum memmodel model) +{ + switch (model) + { + case MEMMODEL_RELAXED: + case MEMMODEL_CONSUME: + case MEMMODEL_RELEASE: + return false; + case MEMMODEL_ACQUIRE: + case MEMMODEL_ACQ_REL: + case MEMMODEL_SEQ_CST: + return true; + default: + gcc_unreachable (); + } +} + /* OPERANDS are the operands to a sync loop instruction and INDEX is the value of the one of the sync_* attributes. Return the operand referred to by the attribute, or DEFAULT_VALUE if the insn doesn't @@ -11996,11 +12035,13 @@ static void mips_process_sync_loop (rtx insn, rtx *operands) { rtx at, mem, oldval, newval, inclusive_mask, exclusive_mask; - rtx required_oldval, insn1_op2, tmp1, tmp2, tmp3; + rtx required_oldval, insn1_op2, tmp1, tmp2, tmp3, cmp; unsigned int tmp3_insn; enum attr_sync_insn1 insn1; enum attr_sync_insn2 insn2; bool is_64bit_p; + int memmodel_attr; + enum memmodel model; /* Read an operand from the sync_WHAT attribute and store it in variable WHAT. DEFAULT is the default value if no attribute @@ -12017,6 +12058,7 @@ mips_process_sync_loop (rtx insn, rtx *operands) /* Read the other attributes. */ at = gen_rtx_REG (GET_MODE (mem), AT_REGNUM); READ_OPERAND (oldval, at); + READ_OPERAND (cmp, 0); READ_OPERAND (newval, at); READ_OPERAND (inclusive_mask, 0); READ_OPERAND (exclusive_mask, 0); @@ -12025,10 +12067,23 @@ mips_process_sync_loop (rtx insn, rtx *operands) insn1 = get_attr_sync_insn1 (insn); insn2 = get_attr_sync_insn2 (insn); + memmodel_attr = get_attr_sync_memmodel (insn); + switch (memmodel_attr) + { + case 10: + model = MEMMODEL_ACQ_REL; + break; + case 11: + model = MEMMODEL_ACQUIRE; + break; + default: + model = INTVAL (operands[memmodel_attr]); + } + mips_multi_start (); /* Output the release side of the memory barrier. */ - if (get_attr_sync_release_barrier (insn) == SYNC_RELEASE_BARRIER_YES) + if (mips_emit_pre_atomic_barrier_p (model)) { if (required_oldval == 0 && TARGET_OCTEON) { @@ -12066,6 +12121,10 @@ mips_process_sync_loop (rtx insn, rtx *operands) tmp1 = at; } mips_multi_add_insn ("bne\t%0,%z1,2f", tmp1, required_oldval, NULL); + + /* CMP = 0 [delay slot]. */ + if (cmp) + mips_multi_add_insn ("li\t%0,0", cmp, NULL); } /* $TMP1 = OLDVAL & EXCLUSIVE_MASK. */ @@ -12129,11 +12188,15 @@ mips_process_sync_loop (rtx insn, rtx *operands) mips_multi_copy_insn (tmp3_insn); mips_multi_set_operand (mips_multi_last_index (), 0, newval); } - else + else if (!(required_oldval && cmp)) mips_multi_add_insn ("nop", NULL); + /* CMP = 1 -- either standalone or in a delay slot. */ + if (required_oldval && cmp) + mips_multi_add_insn ("li\t%0,1", cmp, NULL); + /* Output the acquire side of the memory barrier. */ - if (TARGET_SYNC_AFTER_SC) + if (TARGET_SYNC_AFTER_SC && mips_emit_post_atomic_barrier_p (model)) mips_multi_add_insn ("sync", NULL); /* Output the exit label, if needed. */ diff --git a/gcc/config/mips/mips.md b/gcc/config/mips/mips.md index 0d853401555..5b1735fe7f5 100644 --- a/gcc/config/mips/mips.md +++ b/gcc/config/mips/mips.md @@ -349,13 +349,15 @@ ;; if (RELEASE_BARRIER == YES) sync ;; 1: OLDVAL = *MEM ;; if ((OLDVAL & INCLUSIVE_MASK) != REQUIRED_OLDVAL) goto 2 +;; CMP = 0 [delay slot] ;; $TMP1 = OLDVAL & EXCLUSIVE_MASK ;; $TMP2 = INSN1 (OLDVAL, INSN1_OP2) ;; $TMP3 = INSN2 ($TMP2, INCLUSIVE_MASK) ;; $AT |= $TMP1 | $TMP3 ;; if (!commit (*MEM = $AT)) goto 1. ;; if (INSN1 != MOVE && INSN1 != LI) NEWVAL = $TMP3 [delay slot] -;; sync +;; CMP = 1 +;; if (ACQUIRE_BARRIER == YES) sync ;; 2: ;; ;; where "$" values are temporaries and where the other values are @@ -364,6 +366,7 @@ ;; specified, the following values are used instead: ;; ;; - OLDVAL: $AT +;; - CMP: NONE ;; - NEWVAL: $AT ;; - INCLUSIVE_MASK: -1 ;; - REQUIRED_OLDVAL: OLDVAL & INCLUSIVE_MASK @@ -375,6 +378,7 @@ ;; but the gen* programs don't yet support that. (define_attr "sync_mem" "none,0,1,2,3,4,5" (const_string "none")) (define_attr "sync_oldval" "none,0,1,2,3,4,5" (const_string "none")) +(define_attr "sync_cmp" "none,0,1,2,3,4,5" (const_string "none")) (define_attr "sync_newval" "none,0,1,2,3,4,5" (const_string "none")) (define_attr "sync_inclusive_mask" "none,0,1,2,3,4,5" (const_string "none")) (define_attr "sync_exclusive_mask" "none,0,1,2,3,4,5" (const_string "none")) @@ -384,8 +388,11 @@ (const_string "move")) (define_attr "sync_insn2" "nop,and,xor,not" (const_string "nop")) -(define_attr "sync_release_barrier" "yes,no" - (const_string "yes")) +;; Memory model specifier. +;; "0"-"9" values specify the operand that stores the memory model value. +;; "10" specifies MEMMODEL_ACQ_REL, +;; "11" specifies MEMMODEL_ACQUIRE. +(define_attr "sync_memmodel" "" (const_int 10)) ;; Length of instruction in bytes. (define_attr "length" "" diff --git a/gcc/config/mips/sync.md b/gcc/config/mips/sync.md index 1b4097ec225..604aefa3d0e 100644 --- a/gcc/config/mips/sync.md +++ b/gcc/config/mips/sync.md @@ -29,6 +29,9 @@ UNSPEC_SYNC_EXCHANGE UNSPEC_SYNC_EXCHANGE_12 UNSPEC_MEMORY_BARRIER + UNSPEC_ATOMIC_COMPARE_AND_SWAP + UNSPEC_ATOMIC_EXCHANGE + UNSPEC_ATOMIC_FETCH_OP ]) ;; Atomic fetch bitwise operations. @@ -54,6 +57,7 @@ "GENERATE_SYNC" { return mips_output_sync (); }) +;; Can be removed in favor of atomic_compare_and_swap below. (define_insn "sync_compare_and_swap" [(set (match_operand:GPR 0 "register_operand" "=&d,&d") (match_operand:GPR 1 "memory_operand" "+R,R")) @@ -368,6 +372,7 @@ (set_attr "sync_mem" "0") (set_attr "sync_insn1_op2" "1")]) +;; Can be removed in favor of atomic_fetch_add below. (define_insn "sync_old_add" [(set (match_operand:GPR 0 "register_operand" "=&d,&d") (match_operand:GPR 1 "memory_operand" "+R,R")) @@ -521,7 +526,7 @@ UNSPEC_SYNC_EXCHANGE))] "GENERATE_LL_SC" { return mips_output_sync_loop (insn, operands); } - [(set_attr "sync_release_barrier" "no") + [(set_attr "sync_memmodel" "11") (set_attr "sync_insn1" "li,move") (set_attr "sync_oldval" "0") (set_attr "sync_mem" "1") @@ -550,7 +555,7 @@ UNSPEC_SYNC_EXCHANGE_12))] "GENERATE_LL_SC" { return mips_output_sync_loop (insn, operands); } - [(set_attr "sync_release_barrier" "no") + [(set_attr "sync_memmodel" "11") (set_attr "sync_oldval" "0") (set_attr "sync_mem" "1") ;; Unused, but needed to give the number of operands expected by @@ -558,3 +563,101 @@ (set_attr "sync_inclusive_mask" "2") (set_attr "sync_exclusive_mask" "3") (set_attr "sync_insn1_op2" "4")]) + +(define_insn "atomic_compare_and_swap" + [(set (match_operand:GPR 0 "register_operand" "=&d,&d") + ;; Logically this unspec is an "eq" operator, but we need to obscure + ;; reads and writes from/to memory with an unspec to prevent + ;; optimizations on shared memory locations. Otherwise, comparison in + ;; { mem = 2; if (atomic_cmp_swap(mem,...) == 2) ...; } + ;; would be optimized away. In addition to that we need to use + ;; unspec_volatile, not just plain unspec -- for the sake of other + ;; threads -- to make sure we don't remove the entirety of the pattern + ;; just because current thread doesn't observe any effect from it. + ;; TODO: the obscuring unspec can be relaxed for permissive memory + ;; models. + ;; Same applies to other atomic_* patterns. + (unspec_volatile:GPR [(match_operand:GPR 2 "memory_operand" "+R,R") + (match_operand:GPR 3 "reg_or_0_operand" "dJ,dJ")] + UNSPEC_ATOMIC_COMPARE_AND_SWAP)) + (set (match_operand:GPR 1 "register_operand" "=&d,&d") + (unspec_volatile:GPR [(match_dup 2)] + UNSPEC_ATOMIC_COMPARE_AND_SWAP)) + (set (match_dup 2) + (unspec_volatile:GPR [(match_dup 2) + (match_dup 3) + (match_operand:GPR 4 "arith_operand" "I,d")] + UNSPEC_ATOMIC_COMPARE_AND_SWAP)) + (unspec_volatile:GPR [(match_operand:SI 5 "const_int_operand") + (match_operand:SI 6 "const_int_operand") + (match_operand:SI 7 "const_int_operand")] + UNSPEC_ATOMIC_COMPARE_AND_SWAP)] + "GENERATE_LL_SC" + { return mips_output_sync_loop (insn, operands); } + [(set_attr "sync_insn1" "li,move") + (set_attr "sync_oldval" "1") + (set_attr "sync_cmp" "0") + (set_attr "sync_mem" "2") + (set_attr "sync_required_oldval" "3") + (set_attr "sync_insn1_op2" "4") + (set_attr "sync_memmodel" "6")]) + +(define_expand "atomic_exchange" + [(match_operand:GPR 0 "register_operand") + (match_operand:GPR 1 "memory_operand") + (match_operand:GPR 2 "arith_operand") + (match_operand:SI 3 "const_int_operand")] + "GENERATE_LL_SC" +{ + emit_insn (gen_atomic_exchange_llsc (operands[0], operands[1], + operands[2], operands[3])); + DONE; +}) + +(define_insn "atomic_exchange_llsc" + [(set (match_operand:GPR 0 "register_operand" "=&d,&d") + (unspec_volatile:GPR [(match_operand:GPR 1 "memory_operand" "+R,R")] + UNSPEC_ATOMIC_EXCHANGE)) + (set (match_dup 1) + (unspec_volatile:GPR [(match_operand:GPR 2 "arith_operand" "I,d")] + UNSPEC_ATOMIC_EXCHANGE)) + (unspec_volatile:GPR [(match_operand:SI 3 "const_int_operand")] + UNSPEC_ATOMIC_EXCHANGE)] + "GENERATE_LL_SC" + { return mips_output_sync_loop (insn, operands); } + [(set_attr "sync_insn1" "li,move") + (set_attr "sync_oldval" "0") + (set_attr "sync_mem" "1") + (set_attr "sync_insn1_op2" "2") + (set_attr "sync_memmodel" "3")]) + +(define_expand "atomic_fetch_add" + [(match_operand:GPR 0 "register_operand") + (match_operand:GPR 1 "memory_operand") + (match_operand:GPR 2 "arith_operand") + (match_operand:SI 3 "const_int_operand")] + "GENERATE_LL_SC" +{ + emit_insn (gen_atomic_fetch_add_llsc (operands[0], operands[1], + operands[2], operands[3])); + DONE; +}) + +(define_insn "atomic_fetch_add_llsc" + [(set (match_operand:GPR 0 "register_operand" "=&d,&d") + (unspec_volatile:GPR [(match_operand:GPR 1 "memory_operand" "+R,R")] + UNSPEC_ATOMIC_FETCH_OP)) + (set (match_dup 1) + (unspec_volatile:GPR + [(plus:GPR (match_dup 1) + (match_operand:GPR 2 "arith_operand" "I,d"))] + UNSPEC_ATOMIC_FETCH_OP)) + (unspec_volatile:GPR [(match_operand:SI 3 "const_int_operand")] + UNSPEC_ATOMIC_FETCH_OP)] + "GENERATE_LL_SC" + { return mips_output_sync_loop (insn, operands); } + [(set_attr "sync_insn1" "addiu,addu") + (set_attr "sync_oldval" "0") + (set_attr "sync_mem" "1") + (set_attr "sync_insn1_op2" "2") + (set_attr "sync_memmodel" "3")]) -- 2.30.2