diff --git a/gcc/ChangeLog b/gcc/ChangeLog index d2f05662068..fb839f5a709 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,14 @@ +2007-03-19 Michael Matz + + * builtins.c (expand_builtin_sync_operation, + expand_builtin_compare_and_swap, + expand_builtin_lock_test_and_set): Care for extending CONST_INTs + correctly. + + * config/i386/sync.md (sync_double_compare_and_swapdi_pic, + sync_double_compare_and_swap_ccdi_pic): Use "SD" as constraint + for operand 3. + 2007-03-19 Andreas Krebbel * doc/tm.texi: Add brackets around the return type of diff --git a/gcc/builtins.c b/gcc/builtins.c index 0bb148907cb..989b8d740b2 100644 --- a/gcc/builtins.c +++ b/gcc/builtins.c @@ -5771,13 +5771,18 @@ expand_builtin_sync_operation (enum machine_mode mode, tree exp, 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); @@ -5795,18 +5800,27 @@ expand_builtin_compare_and_swap (enum machine_mode mode, tree exp, 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); @@ -5825,12 +5839,17 @@ expand_builtin_lock_test_and_set (enum machine_mode mode, tree exp, 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); } diff --git a/gcc/config/i386/sync.md b/gcc/config/i386/sync.md index 58e047bc234..9a0ae3f7512 100644 --- a/gcc/config/i386/sync.md +++ b/gcc/config/i386/sync.md @@ -98,6 +98,15 @@ "" "lock{\;| }cmpxchgb{\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")) @@ -105,7 +114,7 @@ (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))] @@ -189,6 +198,8 @@ "" "lock{\;| }cmpxchgb{\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")) @@ -196,7 +207,7 @@ (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)