2004-04-14 Richard Henderson <rth@redhat.com>
+ * config/i386/i386.c (x86_cmpxchg, x86_xadd): New.
+ (ix86_compare_emitted): New.
+ (ix86_expand_compare): Use ix86_compare_emitted if set.
+ (ix86_expand_setcc): Only emit REG_EQUAL if both ix86_compare_op0
+ and ix86_compare_op0 are set.
+ * config/i386/i386.h (x86_cmpxchg, x86_xadd): Declare.
+ (TARGET_CMPXCHG, TARGET_XADD): New.
+ (ix86_compare_emitted): Declare.
+ * config/i386/i386.md: Include sync.md
+ (UNSPECV_CMPXCHG_1, UNSPECV_CMPXCHG_2): New.
+ (UNSPECV_XCHG, UNSPECV_LOCK): New.
+ * config/i386/sync.md: New file.
+
+2004-04-14 Richard Henderson <rth@redhat.com>
+
PR middle-end/14311
* builtin-types.def (BT_BOOL, BT_VOLATILE_PTR, BT_I1, BT_I2,
BT_I4, BT_I8, BT_FN_VOID_VPTR, BT_FN_I1_VPTR_I1, BT_FN_I2_VPTR_I2,
const int x86_four_jump_limit = m_PPRO | m_ATHLON_K8 | m_PENT4 | m_NOCONA;
const int x86_schedule = m_PPRO | m_ATHLON_K8 | m_K6 | m_PENT;
const int x86_use_bt = m_ATHLON_K8;
+/* Compare and exchange was added for 80486. */
+const int x86_cmpxchg = ~m_386;
+/* Exchange and add was added for 80486. */
+const int x86_xadd = ~m_386;
/* In case the average insn count for single function invocation is
lower than this constant, emit fast (but longer) prologue and
rtx ix86_compare_op0 = NULL_RTX;
rtx ix86_compare_op1 = NULL_RTX;
+rtx ix86_compare_emitted = NULL_RTX;
#define MAX_386_STACK_LOCALS 3
/* Size of the register save area. */
if (bypass_test)
*bypass_test = NULL_RTX;
- if (GET_MODE_CLASS (GET_MODE (op0)) == MODE_FLOAT)
+ if (ix86_compare_emitted)
+ {
+ ret = gen_rtx_fmt_ee (code, VOIDmode, ix86_compare_emitted, const0_rtx);
+ ix86_compare_emitted = NULL_RTX;
+ }
+ else if (GET_MODE_CLASS (GET_MODE (op0)) == MODE_FLOAT)
ret = ix86_expand_fp_compare (code, op0, op1, NULL_RTX,
second_test, bypass_test);
else
}
/* Attach a REG_EQUAL note describing the comparison result. */
- equiv = simplify_gen_relational (code, QImode,
- GET_MODE (ix86_compare_op0),
- ix86_compare_op0, ix86_compare_op1);
- set_unique_reg_note (get_last_insn (), REG_EQUAL, equiv);
+ if (ix86_compare_op0 && ix86_compare_op1)
+ {
+ equiv = simplify_gen_relational (code, QImode,
+ GET_MODE (ix86_compare_op0),
+ ix86_compare_op0, ix86_compare_op1);
+ set_unique_reg_note (get_last_insn (), REG_EQUAL, equiv);
+ }
return 1; /* DONE */
}
--- /dev/null
+;; GCC machine description for i386 synchronization instructions.
+;; Copyright (C) 2005
+;; Free Software Foundation, Inc.
+;;
+;; This file is part of GCC.
+;;
+;; GCC is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation; either version 2, or (at your option)
+;; any later version.
+;;
+;; GCC is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;; GNU General Public License for more details.
+;;
+;; You should have received a copy of the GNU General Public License
+;; along with GCC; see the file COPYING. If not, write to
+;; the Free Software Foundation, 59 Temple Place - Suite 330,
+;; Boston, MA 02111-1307, USA.
+
+(define_mode_macro IMODE [QI HI SI (DI "TARGET_64BIT")])
+(define_mode_attr modesuffix [(QI "b") (HI "w") (SI "l") (DI "q")])
+(define_mode_attr modeconstraint [(QI "q") (HI "r") (SI "r") (DI "r")])
+(define_mode_attr immconstraint [(QI "i") (HI "i") (SI "i") (DI "e")])
+
+;; ??? It would be possible to use cmpxchg8b on pentium for DImode
+;; changes. It's complicated because the insn uses ecx:ebx as the
+;; new value; note that the registers are reversed from the order
+;; that they'd be in with (reg:DI 2 ecx). Similarly for TImode
+;; data in 64-bit mode.
+
+(define_insn "sync_compare_and_swap<mode>"
+ [(set (match_operand:IMODE 0 "register_operand" "=a")
+ (unspec_volatile:IMODE
+ [(match_operand:IMODE 1 "memory_operand" "+m")
+ (match_operand:IMODE 2 "register_operand" "a")
+ (match_operand:IMODE 3 "register_operand" "<modeconstraint>")]
+ UNSPECV_CMPXCHG_1))
+ (set (match_dup 1)
+ (unspec_volatile:IMODE
+ [(match_dup 1) (match_dup 2) (match_dup 3)] UNSPECV_CMPXCHG_2))
+ (clobber (reg:CC FLAGS_REG))]
+ "TARGET_CMPXCHG"
+ "lock\;cmpxchg{<modesuffix>}\t{%3, %1|%1, %3}")
+
+(define_expand "sync_compare_and_swap_cc<mode>"
+ [(parallel
+ [(set (match_operand:IMODE 0 "register_operand" "")
+ (unspec_volatile:IMODE
+ [(match_operand:IMODE 1 "memory_operand" "")
+ (match_operand:IMODE 2 "register_operand" "")
+ (match_operand:IMODE 3 "register_operand" "")]
+ UNSPECV_CMPXCHG_1))
+ (set (match_dup 1)
+ (unspec_volatile:IMODE
+ [(match_dup 1) (match_dup 2) (match_dup 3)] UNSPECV_CMPXCHG_2))
+ (set (match_dup 4)
+ (compare:CCZ
+ (unspec_volatile:IMODE
+ [(match_dup 1) (match_dup 2) (match_dup 3)] UNSPECV_CMPXCHG_1)
+ (match_dup 3)))])]
+ "TARGET_CMPXCHG"
+{
+ operands[4] = gen_rtx_REG (CCZmode, FLAGS_REG);
+ ix86_compare_op0 = operands[3];
+ ix86_compare_op1 = NULL;
+ ix86_compare_emitted = operands[4];
+})
+
+(define_insn "*sync_compare_and_swap_cc<mode>"
+ [(set (match_operand:IMODE 0 "register_operand" "=a")
+ (unspec_volatile:IMODE
+ [(match_operand:IMODE 1 "memory_operand" "+m")
+ (match_operand:IMODE 2 "register_operand" "a")
+ (match_operand:IMODE 3 "register_operand" "<modeconstraint>")]
+ UNSPECV_CMPXCHG_1))
+ (set (match_dup 1)
+ (unspec_volatile:IMODE
+ [(match_dup 1) (match_dup 2) (match_dup 3)] UNSPECV_CMPXCHG_2))
+ (set (reg:CCZ FLAGS_REG)
+ (compare:CCZ
+ (unspec_volatile:IMODE
+ [(match_dup 1) (match_dup 2) (match_dup 3)] UNSPECV_CMPXCHG_1)
+ (match_dup 3)))]
+ "TARGET_CMPXCHG"
+ "lock\;cmpxchg{<modesuffix>}\t{%3, %1|%1, %3}")
+
+(define_insn "sync_old_add<mode>"
+ [(set (match_operand:IMODE 0 "register_operand" "=<modeconstraint>")
+ (unspec_volatile:IMODE
+ [(match_operand:IMODE 1 "memory_operand" "+m")] UNSPECV_XCHG))
+ (set (match_dup 1)
+ (plus:IMODE (match_dup 1)
+ (match_operand:IMODE 2 "register_operand" "0")))
+ (clobber (reg:CC FLAGS_REG))]
+ "TARGET_XADD"
+ "lock\;xadd{<modesuffix>}\t{%0, %1|%1, %0}")
+
+;; Recall that xchg implicitly sets LOCK#, so adding it again wastes space.
+(define_insn "sync_lock_test_and_set<mode>"
+ [(set (match_operand:IMODE 0 "register_operand" "=<modeconstraint>")
+ (unspec_volatile:IMODE
+ [(match_operand:IMODE 1 "memory_operand" "+m")] UNSPECV_XCHG))
+ (set (match_dup 1)
+ (match_operand:IMODE 2 "register_operand" "0"))]
+ ""
+ "xchg{<modesuffix>}\t{%1, %0|%0, %1}")
+
+(define_insn "sync_add<mode>"
+ [(set (match_operand:IMODE 0 "memory_operand" "=m")
+ (unspec_volatile:IMODE
+ [(plus:IMODE (match_dup 0)
+ (match_operand:IMODE 1 "nonmemory_operand" "r<immconstraint>"))]
+ UNSPECV_LOCK))
+ (clobber (reg:CC FLAGS_REG))]
+ ""
+ "lock\;add{<modesuffix>}\t{%1, %0|%0, %1}")
+
+(define_insn "sync_sub<mode>"
+ [(set (match_operand:IMODE 0 "memory_operand" "=m")
+ (unspec_volatile:IMODE
+ [(minus:IMODE (match_dup 0)
+ (match_operand:IMODE 1 "nonmemory_operand" "r<immconstraint>"))]
+ UNSPECV_LOCK))
+ (clobber (reg:CC FLAGS_REG))]
+ ""
+ "lock\;sub{<modesuffix>}\t{%1, %0|%0, %1}")
+
+(define_insn "sync_ior<mode>"
+ [(set (match_operand:IMODE 0 "memory_operand" "=m")
+ (unspec_volatile:IMODE
+ [(ior:IMODE (match_dup 0)
+ (match_operand:IMODE 1 "nonmemory_operand" "r<immconstraint>"))]
+ UNSPECV_LOCK))
+ (clobber (reg:CC FLAGS_REG))]
+ ""
+ "lock\;or{<modesuffix>}\t{%1, %0|%0, %1}")
+
+(define_insn "sync_and<mode>"
+ [(set (match_operand:IMODE 0 "memory_operand" "=m")
+ (unspec_volatile:IMODE
+ [(and:IMODE (match_dup 0)
+ (match_operand:IMODE 1 "nonmemory_operand" "r<immconstraint>"))]
+ UNSPECV_LOCK))
+ (clobber (reg:CC FLAGS_REG))]
+ ""
+ "lock\;and{<modesuffix>}\t{%1, %0|%0, %1}")
+
+(define_insn "sync_xor<mode>"
+ [(set (match_operand:IMODE 0 "memory_operand" "=m")
+ (unspec_volatile:IMODE
+ [(xor:IMODE (match_dup 0)
+ (match_operand:IMODE 1 "nonmemory_operand" "r<immconstraint>"))]
+ UNSPECV_LOCK))
+ (clobber (reg:CC FLAGS_REG))]
+ ""
+ "lock\;xor{<modesuffix>}\t{%1, %0|%0, %1}")