summaryrefslogtreecommitdiff
path: root/gcc/config/i386/i386-expand.c
diff options
context:
space:
mode:
Diffstat (limited to 'gcc/config/i386/i386-expand.c')
-rw-r--r--gcc/config/i386/i386-expand.c76
1 files changed, 76 insertions, 0 deletions
diff --git a/gcc/config/i386/i386-expand.c b/gcc/config/i386/i386-expand.c
index 088e6af2258..3e4de64ec24 100644
--- a/gcc/config/i386/i386-expand.c
+++ b/gcc/config/i386/i386-expand.c
@@ -23138,4 +23138,80 @@ ix86_expand_divmod_libfunc (rtx libfunc, machine_mode mode,
*rem_p = rem;
}
+void ix86_expand_atomic_fetch_op_loop (rtx target, rtx mem, rtx val,
+ enum rtx_code code, bool after,
+ bool doubleword)
+{
+ rtx old_reg, new_reg, old_mem, success, oldval, new_mem;
+ rtx_code_label *loop_label, *pause_label;
+ machine_mode mode = GET_MODE (target);
+
+ old_reg = gen_reg_rtx (mode);
+ new_reg = old_reg;
+ loop_label = gen_label_rtx ();
+ pause_label = gen_label_rtx ();
+ old_mem = copy_to_reg (mem);
+ emit_label (loop_label);
+ emit_move_insn (old_reg, old_mem);
+
+ /* return value for atomic_fetch_op. */
+ if (!after)
+ emit_move_insn (target, old_reg);
+
+ if (code == NOT)
+ {
+ new_reg = expand_simple_binop (mode, AND, new_reg, val, NULL_RTX,
+ true, OPTAB_LIB_WIDEN);
+ new_reg = expand_simple_unop (mode, code, new_reg, NULL_RTX, true);
+ }
+ else
+ new_reg = expand_simple_binop (mode, code, new_reg, val, NULL_RTX,
+ true, OPTAB_LIB_WIDEN);
+
+ /* return value for atomic_op_fetch. */
+ if (after)
+ emit_move_insn (target, new_reg);
+
+ /* Load memory again inside loop. */
+ new_mem = copy_to_reg (mem);
+ /* Compare mem value with expected value. */
+
+ if (doubleword)
+ {
+ machine_mode half_mode = (mode == DImode)? SImode : DImode;
+ rtx low_new_mem = gen_lowpart (half_mode, new_mem);
+ rtx low_old_mem = gen_lowpart (half_mode, old_mem);
+ rtx high_new_mem = gen_highpart (half_mode, new_mem);
+ rtx high_old_mem = gen_highpart (half_mode, old_mem);
+ emit_cmp_and_jump_insns (low_new_mem, low_old_mem, NE, NULL_RTX,
+ half_mode, 1, pause_label,
+ profile_probability::guessed_never ());
+ emit_cmp_and_jump_insns (high_new_mem, high_old_mem, NE, NULL_RTX,
+ half_mode, 1, pause_label,
+ profile_probability::guessed_never ());
+ }
+ else
+ emit_cmp_and_jump_insns (new_mem, old_mem, NE, NULL_RTX,
+ GET_MODE (old_mem), 1, pause_label,
+ profile_probability::guessed_never ());
+
+ success = NULL_RTX;
+ oldval = old_mem;
+ expand_atomic_compare_and_swap (&success, &oldval, mem, old_reg,
+ new_reg, false, MEMMODEL_SYNC_SEQ_CST,
+ MEMMODEL_RELAXED);
+ if (oldval != old_mem)
+ emit_move_insn (old_mem, oldval);
+
+ emit_cmp_and_jump_insns (success, const0_rtx, EQ, const0_rtx,
+ GET_MODE (success), 1, loop_label,
+ profile_probability::guessed_never ());
+
+ /* If mem is not expected, pause and loop back. */
+ emit_label (pause_label);
+ emit_insn (gen_pause ());
+ emit_jump_insn (gen_jump (loop_label));
+ emit_barrier ();
+}
+
#include "gt-i386-expand.h"