diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 88c3a037497..2e0aa6cd749 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,18 @@ +2004-04-14 Richard Henderson + + * 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 PR middle-end/14311 diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c index 2c680756723..b543a7afe32 100644 --- a/gcc/config/i386/i386.c +++ b/gcc/config/i386/i386.c @@ -584,6 +584,10 @@ const int x86_ext_80387_constants = m_K6 | m_ATHLON | m_PENT4 | m_NOCONA | m_PPR 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 @@ -727,6 +731,7 @@ int const svr4_dbx_register_map[FIRST_PSEUDO_REGISTER] = 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. */ @@ -9049,7 +9054,12 @@ ix86_expand_compare (enum rtx_code code, rtx *second_test, rtx *bypass_test) 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 @@ -9378,10 +9388,13 @@ ix86_expand_setcc (enum rtx_code code, rtx dest) } /* 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 */ } diff --git a/gcc/config/i386/i386.h b/gcc/config/i386/i386.h index 20bd099423f..5854944c8ec 100644 --- a/gcc/config/i386/i386.h +++ b/gcc/config/i386/i386.h @@ -253,6 +253,7 @@ extern const int x86_sse_typeless_stores, x86_sse_load0_by_pxor; extern const int x86_use_ffreep; extern const int x86_inter_unit_moves, x86_schedule; extern const int x86_use_bt; +extern const int x86_cmpxchg, x86_xadd; extern int x86_prefetch_sse; #define TARGET_USE_LEAVE (x86_use_leave & TUNEMASK) @@ -333,6 +334,9 @@ extern int x86_prefetch_sse; #define TARGET_GNU_TLS (ix86_tls_dialect == TLS_DIALECT_GNU) #define TARGET_SUN_TLS (ix86_tls_dialect == TLS_DIALECT_SUN) +#define TARGET_CMPXCHG (x86_cmpxchg & (1 << ix86_arch)) +#define TARGET_XADD (x86_xadd & (1 << ix86_arch)) + /* WARNING: Do not mark empty strings for translation, as calling gettext on an empty string does NOT return an empty string. */ @@ -2463,6 +2467,7 @@ extern enum reg_class const regclass_map[FIRST_PSEUDO_REGISTER]; extern rtx ix86_compare_op0; /* operand 0 for comparisons */ extern rtx ix86_compare_op1; /* operand 1 for comparisons */ +extern rtx ix86_compare_emitted; /* To properly truncate FP values into integers, we need to set i387 control word. We can't emit proper mode switching code before reload, as spills diff --git a/gcc/config/i386/i386.md b/gcc/config/i386/i386.md index e65d9c790af..f60d3d2be25 100644 --- a/gcc/config/i386/i386.md +++ b/gcc/config/i386/i386.md @@ -151,6 +151,10 @@ (UNSPECV_ALIGN 7) (UNSPECV_MONITOR 8) (UNSPECV_MWAIT 9) + (UNSPECV_CMPXCHG_1 10) + (UNSPECV_CMPXCHG_2 11) + (UNSPECV_XCHG 12) + (UNSPECV_LOCK 13) ]) ;; Registers by name. @@ -19757,3 +19761,4 @@ (include "sse.md") (include "mmx.md") +(include "sync.md") diff --git a/gcc/config/i386/sync.md b/gcc/config/i386/sync.md new file mode 100644 index 00000000000..477b398185b --- /dev/null +++ b/gcc/config/i386/sync.md @@ -0,0 +1,158 @@ +;; 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" + [(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" "")] + 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{}\t{%3, %1|%1, %3}") + +(define_expand "sync_compare_and_swap_cc" + [(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" + [(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" "")] + 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{}\t{%3, %1|%1, %3}") + +(define_insn "sync_old_add" + [(set (match_operand:IMODE 0 "register_operand" "=") + (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{}\t{%0, %1|%1, %0}") + +;; Recall that xchg implicitly sets LOCK#, so adding it again wastes space. +(define_insn "sync_lock_test_and_set" + [(set (match_operand:IMODE 0 "register_operand" "=") + (unspec_volatile:IMODE + [(match_operand:IMODE 1 "memory_operand" "+m")] UNSPECV_XCHG)) + (set (match_dup 1) + (match_operand:IMODE 2 "register_operand" "0"))] + "" + "xchg{}\t{%1, %0|%0, %1}") + +(define_insn "sync_add" + [(set (match_operand:IMODE 0 "memory_operand" "=m") + (unspec_volatile:IMODE + [(plus:IMODE (match_dup 0) + (match_operand:IMODE 1 "nonmemory_operand" "r"))] + UNSPECV_LOCK)) + (clobber (reg:CC FLAGS_REG))] + "" + "lock\;add{}\t{%1, %0|%0, %1}") + +(define_insn "sync_sub" + [(set (match_operand:IMODE 0 "memory_operand" "=m") + (unspec_volatile:IMODE + [(minus:IMODE (match_dup 0) + (match_operand:IMODE 1 "nonmemory_operand" "r"))] + UNSPECV_LOCK)) + (clobber (reg:CC FLAGS_REG))] + "" + "lock\;sub{}\t{%1, %0|%0, %1}") + +(define_insn "sync_ior" + [(set (match_operand:IMODE 0 "memory_operand" "=m") + (unspec_volatile:IMODE + [(ior:IMODE (match_dup 0) + (match_operand:IMODE 1 "nonmemory_operand" "r"))] + UNSPECV_LOCK)) + (clobber (reg:CC FLAGS_REG))] + "" + "lock\;or{}\t{%1, %0|%0, %1}") + +(define_insn "sync_and" + [(set (match_operand:IMODE 0 "memory_operand" "=m") + (unspec_volatile:IMODE + [(and:IMODE (match_dup 0) + (match_operand:IMODE 1 "nonmemory_operand" "r"))] + UNSPECV_LOCK)) + (clobber (reg:CC FLAGS_REG))] + "" + "lock\;and{}\t{%1, %0|%0, %1}") + +(define_insn "sync_xor" + [(set (match_operand:IMODE 0 "memory_operand" "=m") + (unspec_volatile:IMODE + [(xor:IMODE (match_dup 0) + (match_operand:IMODE 1 "nonmemory_operand" "r"))] + UNSPECV_LOCK)) + (clobber (reg:CC FLAGS_REG))] + "" + "lock\;xor{}\t{%1, %0|%0, %1}")