rtx target, bool ignore)
{
rtx val, mem;
+ enum machine_mode old_mode;
/* Expand the operands. */
mem = get_builtin_sync_mem (CALL_EXPR_ARG (exp, 0), mode);
val = expand_expr (CALL_EXPR_ARG (exp, 1), NULL, mode, EXPAND_NORMAL);
- /* If VAL is promoted to a wider mode, convert it back to MODE. */
- val = convert_to_mode (mode, val, 1);
+ /* If VAL is promoted to a wider mode, convert it back to MODE. Take care
+ of CONST_INTs, where we know the old_mode only from the call argument. */
+ old_mode = GET_MODE (val);
+ if (old_mode == VOIDmode)
+ old_mode = TYPE_MODE (TREE_TYPE (CALL_EXPR_ARG (exp, 1)));
+ val = convert_modes (mode, old_mode, val, 1);
if (ignore)
return expand_sync_operation (mem, val, code);
bool is_bool, rtx target)
{
rtx old_val, new_val, mem;
+ enum machine_mode old_mode;
/* Expand the operands. */
mem = get_builtin_sync_mem (CALL_EXPR_ARG (exp, 0), mode);
old_val = expand_expr (CALL_EXPR_ARG (exp, 1), NULL, mode, EXPAND_NORMAL);
- /* If OLD_VAL is promoted to a wider mode, convert it back to MODE. */
- old_val = convert_to_mode (mode, old_val, 1);
+ /* If VAL is promoted to a wider mode, convert it back to MODE. Take care
+ of CONST_INTs, where we know the old_mode only from the call argument. */
+ old_mode = GET_MODE (old_val);
+ if (old_mode == VOIDmode)
+ old_mode = TYPE_MODE (TREE_TYPE (CALL_EXPR_ARG (exp, 1)));
+ old_val = convert_modes (mode, old_mode, old_val, 1);
new_val = expand_expr (CALL_EXPR_ARG (exp, 2), NULL, mode, EXPAND_NORMAL);
- /* If NEW_VAL is promoted to a wider mode, convert it back to MODE. */
- new_val = convert_to_mode (mode, new_val, 1);
+ /* If VAL is promoted to a wider mode, convert it back to MODE. Take care
+ of CONST_INTs, where we know the old_mode only from the call argument. */
+ old_mode = GET_MODE (new_val);
+ if (old_mode == VOIDmode)
+ old_mode = TYPE_MODE (TREE_TYPE (CALL_EXPR_ARG (exp, 2)));
+ new_val = convert_modes (mode, old_mode, new_val, 1);
if (is_bool)
return expand_bool_compare_and_swap (mem, old_val, new_val, target);
rtx target)
{
rtx val, mem;
+ enum machine_mode old_mode;
/* Expand the operands. */
mem = get_builtin_sync_mem (CALL_EXPR_ARG (exp, 0), mode);
val = expand_expr (CALL_EXPR_ARG (exp, 1), NULL, mode, EXPAND_NORMAL);
- /* If VAL is promoted to a wider mode, convert it back to MODE. */
- val = convert_to_mode (mode, val, 1);
+ /* If VAL is promoted to a wider mode, convert it back to MODE. Take care
+ of CONST_INTs, where we know the old_mode only from the call argument. */
+ old_mode = GET_MODE (val);
+ if (old_mode == VOIDmode)
+ old_mode = TYPE_MODE (TREE_TYPE (CALL_EXPR_ARG (exp, 1)));
+ val = convert_modes (mode, old_mode, val, 1);
return expand_sync_lock_test_and_set (mem, val, target);
}
""
"lock{\;| }cmpxchg<doublemodesuffix>b{\t| }%1")
+;; Theoretically we'd like to use constraint "r" (any reg) for operand
+;; 3, but that includes ecx. If operand 3 and 4 are the same (like when
+;; the input is -1LL) GCC might chose to allocate operand 3 to ecx, like
+;; operand 4. This breaks, as the xchg will move the PIC register contents
+;; to %ecx then --> boom. Operands 3 and 4 really need to be different
+;; registers, which in this case means operand 3 must not be ecx.
+;; Instead of playing tricks with fake early clobbers or the like we
+;; just enumerate all regs possible here, which (as this is !TARGET_64BIT)
+;; are just esi and edi.
(define_insn "*sync_double_compare_and_swapdi_pic"
[(set (match_operand:DI 0 "register_operand" "=A")
(match_operand:DI 1 "memory_operand" "+m"))
(unspec_volatile:DI
[(match_dup 1)
(match_operand:DI 2 "register_operand" "A")
- (match_operand:SI 3 "register_operand" "r")
+ (match_operand:SI 3 "register_operand" "SD")
(match_operand:SI 4 "register_operand" "c")]
UNSPECV_CMPXCHG_1))
(clobber (reg:CC FLAGS_REG))]
""
"lock{\;| }cmpxchg<doublemodesuffix>b{\t| }%1")
+;; See above for the explanation of using the constraint "SD" for
+;; operand 3.
(define_insn "*sync_double_compare_and_swap_ccdi_pic"
[(set (match_operand:DI 0 "register_operand" "=A")
(match_operand:DI 1 "memory_operand" "+m"))
(unspec_volatile:DI
[(match_dup 1)
(match_operand:DI 2 "register_operand" "A")
- (match_operand:SI 3 "register_operand" "r")
+ (match_operand:SI 3 "register_operand" "SD")
(match_operand:SI 4 "register_operand" "c")]
UNSPECV_CMPXCHG_1))
(set (reg:CCZ FLAGS_REG)