@@ -59,6 +59,7 @@ extern void riscv_expand_int_scc (rtx, enum rtx_code, rtx, rtx);
extern void riscv_expand_float_scc (rtx, enum rtx_code, rtx, rtx);
extern void riscv_expand_conditional_branch (rtx, enum rtx_code, rtx, rtx);
extern void riscv_expand_conditional_move (rtx, rtx, rtx, rtx_code, rtx, rtx);
+extern void riscv_expand_compare_and_swap (rtx[]);
#endif
extern rtx riscv_legitimize_call_address (rtx);
extern void riscv_set_return_address (rtx, rtx);
@@ -2496,6 +2496,81 @@ riscv_expand_conditional_move (rtx dest, rtx cons, rtx alt, rtx_code code,
cons, alt)));
}
+/* Mark the previous jump instruction as unlikely. */
+
+static void
+riscv_emit_unlikely_jump (rtx insn)
+{
+ rtx_insn *jump = emit_jump_insn (insn);
+ add_reg_br_prob_note (jump, profile_probability::very_unlikely ());
+}
+
+/* Expand code to perform a compare-and-swap. */
+
+extern void
+riscv_expand_compare_and_swap (rtx operands[])
+{
+ rtx bval, oldval, mem, expval, newval, mod_s, mod_f, scratch, cond1, cond2;
+ machine_mode mode;
+ rtx_code_label *begin_label, *end_label;
+
+ bval = operands[0];
+ oldval = operands[1];
+ mem = operands[2];
+ expval = operands[3];
+ newval = operands[4];
+ mod_s = operands[6];
+ mod_f = operands[7];
+ mode = GET_MODE (mem);
+ scratch = gen_reg_rtx (mode);
+ begin_label = gen_label_rtx ();
+ end_label = gen_label_rtx ();
+
+ /* No support for sub-word CAS. */
+ if (mode == QImode || mode == HImode)
+ gcc_unreachable ();
+
+ /* We use mod_f for LR and mod_s for SC below, but
+ RV does not have any guarantees for LR.rl and SC.aq. */
+ if (is_mm_acquire (memmodel_base (INTVAL (mod_s)))
+ && is_mm_relaxed (memmodel_base (INTVAL (mod_f))))
+ {
+ mod_f = GEN_INT (MEMMODEL_ACQUIRE);
+ mod_s = GEN_INT (MEMMODEL_RELAXED);
+ }
+
+ /* Since we want to maintain a branch-free good-case, but also want
+ to not have two branches in the bad-case, we set bval to FALSE
+ on top of the sequence. In the bad case, we simply jump over
+ the assignment of bval to TRUE at the end of the sequence. */
+
+ emit_insn (gen_rtx_SET (bval, gen_rtx_CONST_INT (SImode, FALSE)));
+
+ /* Make sure the scheduler does not move INSNs beyond here. */
+ emit_insn (gen_blockage ());
+
+ emit_label (begin_label);
+
+ emit_insn (gen_riscv_load_reserved (mode, oldval, mem, mod_f));
+
+ cond1 = gen_rtx_NE (mode, oldval, expval);
+ riscv_emit_unlikely_jump (gen_cbranch4 (Pmode, cond1, oldval, expval,
+ end_label));
+
+ emit_insn (gen_riscv_store_conditional (mode, scratch, mem, newval, mod_s));
+
+ /* Make sure the scheduler does not move INSNs beyond here. */
+ emit_insn (gen_blockage ());
+
+ cond2 = gen_rtx_NE (mode, scratch, const0_rtx);
+ riscv_emit_unlikely_jump (gen_cbranch4 (Pmode, cond2, scratch, const0_rtx,
+ begin_label));
+
+ emit_insn (gen_rtx_SET (bval, gen_rtx_CONST_INT (SImode, TRUE)));
+
+ emit_label (end_label);
+}
+
/* Implement TARGET_FUNCTION_ARG_BOUNDARY. Every parameter gets at
least PARM_BOUNDARY bits of alignment, but will be given anything up
to PREFERRED_STACK_BOUNDARY bits if the type requires it. */
@@ -207,20 +207,6 @@
"amoswap.<amo>%A3 %0,%z2,%1"
)
-(define_insn "atomic_cas_value_strong<mode>"
- [(set (match_operand:GPR 0 "register_operand" "=&r")
- (match_operand:GPR 1 "memory_operand" "+A"))
- (set (match_dup 1)
- (unspec_volatile:GPR [(match_operand:GPR 2 "reg_or_0_operand" "rJ")
- (match_operand:GPR 3 "reg_or_0_operand" "rJ")
- (match_operand:SI 4 "const_int_operand") ;; mod_s
- (match_operand:SI 5 "const_int_operand")] ;; mod_f
- UNSPEC_COMPARE_AND_SWAP))
- (clobber (match_scratch:GPR 6 "=&r"))]
- "TARGET_ATOMIC"
- "1: lr.<amo>%A5 %0,%1; bne %0,%z2,1f; sc.<amo>%A4 %6,%z3,%1; bnez %6,1b; 1:"
- [(set (attr "length") (const_int 20))])
-
(define_expand "atomic_compare_and_swap<mode>"
[(match_operand:SI 0 "register_operand" "") ;; bool output
(match_operand:GPR 1 "register_operand" "") ;; val output
@@ -232,26 +218,7 @@
(match_operand:SI 7 "const_int_operand" "")] ;; mod_f
"TARGET_ATOMIC"
{
- emit_insn (gen_atomic_cas_value_strong<mode> (operands[1], operands[2],
- operands[3], operands[4],
- operands[6], operands[7]));
-
- rtx compare = operands[1];
- if (operands[3] != const0_rtx)
- {
- rtx difference = gen_rtx_MINUS (<MODE>mode, operands[1], operands[3]);
- compare = gen_reg_rtx (<MODE>mode);
- emit_insn (gen_rtx_SET (compare, difference));
- }
-
- if (word_mode != <MODE>mode)
- {
- rtx reg = gen_reg_rtx (word_mode);
- emit_insn (gen_rtx_SET (reg, gen_rtx_SIGN_EXTEND (word_mode, compare)));
- compare = reg;
- }
-
- emit_insn (gen_rtx_SET (operands[0], gen_rtx_EQ (SImode, compare, const0_rtx)));
+ riscv_expand_compare_and_swap (operands);
DONE;
})