OSDN Git Service

PR target/39590
[pf3gnuchains/gcc-fork.git] / gcc / optabs.c
index 32a6396..8bf8fa7 100644 (file)
@@ -1,6 +1,6 @@
 /* Expand the basic unary and binary arithmetic operations, for GNU compiler.
    Copyright (C) 1987, 1988, 1992, 1993, 1994, 1995, 1996, 1997, 1998,
-   1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008
+   1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009
    Free Software Foundation, Inc.
 
 This file is part of GCC.
@@ -132,8 +132,7 @@ void debug_optab_libfuncs (void);
 
 /* Info about libfunc.  We use same hashtable for normal optabs and conversion
    optab.  In the first case mode2 is unused.  */
-struct libfunc_entry GTY(())
-{
+struct GTY(()) libfunc_entry {
   size_t optab;
   enum machine_mode mode1, mode2;
   rtx libfunc;
@@ -823,7 +822,7 @@ expand_vec_shift_expr (tree vec_shift_expr, rtx target)
        gcc_unreachable ();
     }
 
-  icode = (int) optab_handler (shift_optab, mode)->insn_code;
+  icode = optab_handler (shift_optab, mode)->insn_code;
   gcc_assert (icode != CODE_FOR_nothing);
 
   mode1 = insn_data[icode].operand[1].mode;
@@ -1414,7 +1413,8 @@ avoid_expensive_constant (enum machine_mode mode, optab binoptab,
   if (mode != VOIDmode
       && optimize
       && CONSTANT_P (x)
-      && rtx_cost (x, binoptab->code) > COSTS_N_INSNS (1))
+      && rtx_cost (x, binoptab->code, optimize_insn_for_speed_p ())
+                   > COSTS_N_INSNS (1))
     {
       if (GET_CODE (x) == CONST_INT)
        {
@@ -1571,14 +1571,14 @@ expand_binop (enum machine_mode mode, optab binoptab, rtx op0, rtx op1,
   enum optab_methods next_methods
     = (methods == OPTAB_LIB || methods == OPTAB_LIB_WIDEN
        ? OPTAB_WIDEN : methods);
-  enum mode_class class;
+  enum mode_class mclass;
   enum machine_mode wider_mode;
   rtx libfunc;
   rtx temp;
   rtx entry_last = get_last_insn ();
   rtx last;
 
-  class = GET_MODE_CLASS (mode);
+  mclass = GET_MODE_CLASS (mode);
 
   /* If subtracting an integer constant, convert this into an addition of
      the negated constant.  */
@@ -1609,7 +1609,7 @@ expand_binop (enum machine_mode mode, optab binoptab, rtx op0, rtx op1,
        && optab_handler (rotr_optab, mode)->insn_code != CODE_FOR_nothing)
        || (binoptab == rotr_optab
           && optab_handler (rotl_optab, mode)->insn_code != CODE_FOR_nothing))
-      && class == MODE_INT)
+      && mclass == MODE_INT)
     {
       optab otheroptab = (binoptab == rotl_optab ? rotr_optab : rotl_optab);
       rtx newop1;
@@ -1658,7 +1658,7 @@ expand_binop (enum machine_mode mode, optab binoptab, rtx op0, rtx op1,
      can open-code the operation.  Check for a widening multiply at the
      wider mode as well.  */
 
-  if (CLASS_HAS_WIDER_MODES_P (class)
+  if (CLASS_HAS_WIDER_MODES_P (mclass)
       && methods != OPTAB_DIRECT && methods != OPTAB_LIB)
     for (wider_mode = GET_MODE_WIDER_MODE (mode);
         wider_mode != VOIDmode;
@@ -1683,7 +1683,7 @@ expand_binop (enum machine_mode mode, optab binoptab, rtx op0, rtx op1,
                 || binoptab == xor_optab
                 || binoptab == add_optab || binoptab == sub_optab
                 || binoptab == smul_optab || binoptab == ashl_optab)
-               && class == MODE_INT)
+               && mclass == MODE_INT)
              {
                no_extend = 1;
                xop0 = avoid_expensive_constant (mode, binoptab,
@@ -1703,7 +1703,7 @@ expand_binop (enum machine_mode mode, optab binoptab, rtx op0, rtx op1,
                                 unsignedp, OPTAB_DIRECT);
            if (temp)
              {
-               if (class != MODE_INT
+               if (mclass != MODE_INT
                     || !TRULY_NOOP_TRUNCATION (GET_MODE_BITSIZE (mode),
                                                GET_MODE_BITSIZE (wider_mode)))
                  {
@@ -1734,7 +1734,7 @@ expand_binop (enum machine_mode mode, optab binoptab, rtx op0, rtx op1,
 
   /* These can be done a word at a time.  */
   if ((binoptab == and_optab || binoptab == ior_optab || binoptab == xor_optab)
-      && class == MODE_INT
+      && mclass == MODE_INT
       && GET_MODE_SIZE (mode) > UNITS_PER_WORD
       && optab_handler (binoptab, word_mode)->insn_code != CODE_FOR_nothing)
     {
@@ -1785,8 +1785,8 @@ expand_binop (enum machine_mode mode, optab binoptab, rtx op0, rtx op1,
   /* Synthesize double word shifts from single word shifts.  */
   if ((binoptab == lshr_optab || binoptab == ashl_optab
        || binoptab == ashr_optab)
-      && class == MODE_INT
-      && (GET_CODE (op1) == CONST_INT || !optimize_size)
+      && mclass == MODE_INT
+      && (GET_CODE (op1) == CONST_INT || optimize_insn_for_speed_p ())
       && GET_MODE_SIZE (mode) == 2 * UNITS_PER_WORD
       && optab_handler (binoptab, word_mode)->insn_code != CODE_FOR_nothing
       && optab_handler (ashl_optab, word_mode)->insn_code != CODE_FOR_nothing
@@ -1855,7 +1855,7 @@ expand_binop (enum machine_mode mode, optab binoptab, rtx op0, rtx op1,
 
   /* Synthesize double word rotates from single word shifts.  */
   if ((binoptab == rotl_optab || binoptab == rotr_optab)
-      && class == MODE_INT
+      && mclass == MODE_INT
       && GET_CODE (op1) == CONST_INT
       && GET_MODE_SIZE (mode) == 2 * UNITS_PER_WORD
       && optab_handler (ashl_optab, word_mode)->insn_code != CODE_FOR_nothing
@@ -1968,7 +1968,7 @@ expand_binop (enum machine_mode mode, optab binoptab, rtx op0, rtx op1,
 
   /* These can be done a word at a time by propagating carries.  */
   if ((binoptab == add_optab || binoptab == sub_optab)
-      && class == MODE_INT
+      && mclass == MODE_INT
       && GET_MODE_SIZE (mode) >= 2 * UNITS_PER_WORD
       && optab_handler (binoptab, word_mode)->insn_code != CODE_FOR_nothing)
     {
@@ -2094,7 +2094,7 @@ expand_binop (enum machine_mode mode, optab binoptab, rtx op0, rtx op1,
      try using a signed widening multiply.  */
 
   if (binoptab == smul_optab
-      && class == MODE_INT
+      && mclass == MODE_INT
       && GET_MODE_SIZE (mode) == 2 * UNITS_PER_WORD
       && optab_handler (smul_optab, word_mode)->insn_code != CODE_FOR_nothing
       && optab_handler (add_optab, word_mode)->insn_code != CODE_FOR_nothing)
@@ -2197,7 +2197,7 @@ expand_binop (enum machine_mode mode, optab binoptab, rtx op0, rtx op1,
   /* Look for a wider mode of the same class for which it appears we can do
      the operation.  */
 
-  if (CLASS_HAS_WIDER_MODES_P (class))
+  if (CLASS_HAS_WIDER_MODES_P (mclass))
     {
       for (wider_mode = GET_MODE_WIDER_MODE (mode);
           wider_mode != VOIDmode;
@@ -2219,7 +2219,7 @@ expand_binop (enum machine_mode mode, optab binoptab, rtx op0, rtx op1,
                   || binoptab == xor_optab
                   || binoptab == add_optab || binoptab == sub_optab
                   || binoptab == smul_optab || binoptab == ashl_optab)
-                 && class == MODE_INT)
+                 && mclass == MODE_INT)
                no_extend = 1;
 
              xop0 = widen_operand (xop0, wider_mode, mode,
@@ -2233,7 +2233,7 @@ expand_binop (enum machine_mode mode, optab binoptab, rtx op0, rtx op1,
                                   unsignedp, methods);
              if (temp)
                {
-                 if (class != MODE_INT
+                 if (mclass != MODE_INT
                      || !TRULY_NOOP_TRUNCATION (GET_MODE_BITSIZE (mode),
                                                 GET_MODE_BITSIZE (wider_mode)))
                    {
@@ -2327,12 +2327,12 @@ expand_twoval_unop (optab unoptab, rtx op0, rtx targ0, rtx targ1,
                    int unsignedp)
 {
   enum machine_mode mode = GET_MODE (targ0 ? targ0 : targ1);
-  enum mode_class class;
+  enum mode_class mclass;
   enum machine_mode wider_mode;
   rtx entry_last = get_last_insn ();
   rtx last;
 
-  class = GET_MODE_CLASS (mode);
+  mclass = GET_MODE_CLASS (mode);
 
   if (!targ0)
     targ0 = gen_reg_rtx (mode);
@@ -2374,7 +2374,7 @@ expand_twoval_unop (optab unoptab, rtx op0, rtx targ0, rtx targ1,
 
   /* It can't be done in this mode.  Can we do it in a wider mode?  */
 
-  if (CLASS_HAS_WIDER_MODES_P (class))
+  if (CLASS_HAS_WIDER_MODES_P (mclass))
     {
       for (wider_mode = GET_MODE_WIDER_MODE (mode);
           wider_mode != VOIDmode;
@@ -2420,12 +2420,12 @@ expand_twoval_binop (optab binoptab, rtx op0, rtx op1, rtx targ0, rtx targ1,
                     int unsignedp)
 {
   enum machine_mode mode = GET_MODE (targ0 ? targ0 : targ1);
-  enum mode_class class;
+  enum mode_class mclass;
   enum machine_mode wider_mode;
   rtx entry_last = get_last_insn ();
   rtx last;
 
-  class = GET_MODE_CLASS (mode);
+  mclass = GET_MODE_CLASS (mode);
 
   if (!targ0)
     targ0 = gen_reg_rtx (mode);
@@ -2491,7 +2491,7 @@ expand_twoval_binop (optab binoptab, rtx op0, rtx op1, rtx targ0, rtx targ1,
 
   /* It can't be done in this mode.  Can we do it in a wider mode?  */
 
-  if (CLASS_HAS_WIDER_MODES_P (class))
+  if (CLASS_HAS_WIDER_MODES_P (mclass))
     {
       for (wider_mode = GET_MODE_WIDER_MODE (mode);
           wider_mode != VOIDmode;
@@ -2591,8 +2591,8 @@ expand_simple_unop (enum machine_mode mode, enum rtx_code code, rtx op0,
 static rtx
 widen_clz (enum machine_mode mode, rtx op0, rtx target)
 {
-  enum mode_class class = GET_MODE_CLASS (mode);
-  if (CLASS_HAS_WIDER_MODES_P (class))
+  enum mode_class mclass = GET_MODE_CLASS (mode);
+  if (CLASS_HAS_WIDER_MODES_P (mclass))
     {
       enum machine_mode wider_mode;
       for (wider_mode = GET_MODE_WIDER_MODE (mode);
@@ -2702,11 +2702,11 @@ expand_doubleword_clz (enum machine_mode mode, rtx op0, rtx target)
 static rtx
 widen_bswap (enum machine_mode mode, rtx op0, rtx target)
 {
-  enum mode_class class = GET_MODE_CLASS (mode);
+  enum mode_class mclass = GET_MODE_CLASS (mode);
   enum machine_mode wider_mode;
   rtx x, last;
 
-  if (!CLASS_HAS_WIDER_MODES_P (class))
+  if (!CLASS_HAS_WIDER_MODES_P (mclass))
     return NULL_RTX;
 
   for (wider_mode = GET_MODE_WIDER_MODE (mode);
@@ -2767,8 +2767,8 @@ expand_doubleword_bswap (enum machine_mode mode, rtx op, rtx target)
 static rtx
 expand_parity (enum machine_mode mode, rtx op0, rtx target)
 {
-  enum mode_class class = GET_MODE_CLASS (mode);
-  if (CLASS_HAS_WIDER_MODES_P (class))
+  enum mode_class mclass = GET_MODE_CLASS (mode);
+  if (CLASS_HAS_WIDER_MODES_P (mclass))
     {
       enum machine_mode wider_mode;
       for (wider_mode = mode; wider_mode != VOIDmode;
@@ -3116,7 +3116,7 @@ rtx
 expand_unop (enum machine_mode mode, optab unoptab, rtx op0, rtx target,
             int unsignedp)
 {
-  enum mode_class class = GET_MODE_CLASS (mode);
+  enum mode_class mclass = GET_MODE_CLASS (mode);
   enum machine_mode wider_mode;
   rtx temp;
   rtx libfunc;
@@ -3163,7 +3163,7 @@ expand_unop (enum machine_mode mode, optab unoptab, rtx op0, rtx target,
       goto try_libcall;
     }
 
-  if (CLASS_HAS_WIDER_MODES_P (class))
+  if (CLASS_HAS_WIDER_MODES_P (mclass))
     for (wider_mode = GET_MODE_WIDER_MODE (mode);
         wider_mode != VOIDmode;
         wider_mode = GET_MODE_WIDER_MODE (wider_mode))
@@ -3180,14 +3180,14 @@ expand_unop (enum machine_mode mode, optab unoptab, rtx op0, rtx target,
            xop0 = widen_operand (xop0, wider_mode, mode, unsignedp,
                                  (unoptab == neg_optab
                                   || unoptab == one_cmpl_optab)
-                                 && class == MODE_INT);
+                                 && mclass == MODE_INT);
 
            temp = expand_unop (wider_mode, unoptab, xop0, NULL_RTX,
                                unsignedp);
 
            if (temp)
              {
-               if (class != MODE_INT
+               if (mclass != MODE_INT
                    || !TRULY_NOOP_TRUNCATION (GET_MODE_BITSIZE (mode),
                                               GET_MODE_BITSIZE (wider_mode)))
                  {
@@ -3206,7 +3206,7 @@ expand_unop (enum machine_mode mode, optab unoptab, rtx op0, rtx target,
 
   /* These can be done a word at a time.  */
   if (unoptab == one_cmpl_optab
-      && class == MODE_INT
+      && mclass == MODE_INT
       && GET_MODE_SIZE (mode) > UNITS_PER_WORD
       && optab_handler (unoptab, word_mode)->insn_code != CODE_FOR_nothing)
     {
@@ -3323,7 +3323,7 @@ expand_unop (enum machine_mode mode, optab unoptab, rtx op0, rtx target,
 
   /* It can't be done in this mode.  Can we do it in a wider mode?  */
 
-  if (CLASS_HAS_WIDER_MODES_P (class))
+  if (CLASS_HAS_WIDER_MODES_P (mclass))
     {
       for (wider_mode = GET_MODE_WIDER_MODE (mode);
           wider_mode != VOIDmode;
@@ -3343,7 +3343,7 @@ expand_unop (enum machine_mode mode, optab unoptab, rtx op0, rtx target,
              xop0 = widen_operand (xop0, wider_mode, mode, unsignedp,
                                    (unoptab == neg_optab
                                     || unoptab == one_cmpl_optab)
-                                   && class == MODE_INT);
+                                   && mclass == MODE_INT);
 
              temp = expand_unop (wider_mode, unoptab, xop0, NULL_RTX,
                                  unsignedp);
@@ -3358,7 +3358,7 @@ expand_unop (enum machine_mode mode, optab unoptab, rtx op0, rtx target,
 
              if (temp)
                {
-                 if (class != MODE_INT)
+                 if (mclass != MODE_INT)
                    {
                      if (target == 0)
                        target = gen_reg_rtx (mode);
@@ -3443,7 +3443,9 @@ expand_abs_nojump (enum machine_mode mode, rtx op0, rtx target,
      value of X as (((signed) x >> (W-1)) ^ x) - ((signed) x >> (W-1)),
      where W is the width of MODE.  */
 
-  if (GET_MODE_CLASS (mode) == MODE_INT && BRANCH_COST >= 2)
+  if (GET_MODE_CLASS (mode) == MODE_INT
+      && BRANCH_COST (optimize_insn_for_speed_p (),
+                     false) >= 2)
     {
       rtx extended = expand_shift (RSHIFT_EXPR, mode, op0,
                                   size_int (GET_MODE_BITSIZE (mode) - 1),
@@ -3759,14 +3761,17 @@ expand_copysign (rtx op0, rtx op1, rtx target)
    with two operands: an output TARGET and an input OP0.
    TARGET *must* be nonzero, and the output is always stored there.
    CODE is an rtx code such that (CODE OP0) is an rtx that describes
-   the value that is stored into TARGET.  */
+   the value that is stored into TARGET. 
 
-void
-emit_unop_insn (int icode, rtx target, rtx op0, enum rtx_code code)
+   Return false if expansion failed.  */
+
+bool
+maybe_emit_unop_insn (int icode, rtx target, rtx op0, enum rtx_code code)
 {
   rtx temp;
   enum machine_mode mode0 = insn_data[icode].operand[1].mode;
   rtx pat;
+  rtx last = get_last_insn ();
 
   temp = target;
 
@@ -3779,6 +3784,11 @@ emit_unop_insn (int icode, rtx target, rtx op0, enum rtx_code code)
     temp = gen_reg_rtx (GET_MODE (temp));
 
   pat = GEN_FCN (icode) (temp, op0);
+  if (!pat)
+    {
+      delete_insns_since (last);
+      return false;
+    }
 
   if (INSN_P (pat) && NEXT_INSN (pat) != NULL_RTX && code != UNKNOWN)
     add_equal_note (pat, temp, code, op0, NULL_RTX);
@@ -3787,6 +3797,19 @@ emit_unop_insn (int icode, rtx target, rtx op0, enum rtx_code code)
 
   if (temp != target)
     emit_move_insn (target, temp);
+  return true;
+}
+/* Generate an instruction whose insn-code is INSN_CODE,
+   with two operands: an output TARGET and an input OP0.
+   TARGET *must* be nonzero, and the output is always stored there.
+   CODE is an rtx code such that (CODE OP0) is an rtx that describes
+   the value that is stored into TARGET.  */
+
+void
+emit_unop_insn (int icode, rtx target, rtx op0, enum rtx_code code)
+{
+  bool ok = maybe_emit_unop_insn (icode, target, op0, code);
+  gcc_assert (ok);
 }
 \f
 struct no_conflict_data
@@ -3879,8 +3902,7 @@ emit_libcall_block (rtx insns, rtx target, rtx result, rtx equiv)
          if (note != 0)
            XEXP (note, 0) = constm1_rtx;
          else
-           REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_EH_REGION, constm1_rtx,
-                                                 REG_NOTES (insn));
+           add_reg_note (insn, REG_EH_REGION, constm1_rtx);
        }
 
   /* First emit all insns that set pseudos.  Remove them from the list as
@@ -3957,8 +3979,12 @@ int
 can_compare_p (enum rtx_code code, enum machine_mode mode,
               enum can_compare_purpose purpose)
 {
+  rtx test;
+  test = gen_rtx_fmt_ee (code, mode, const0_rtx, const0_rtx);
   do
     {
+      int icode;
+
       if (optab_handler (cmp_optab, mode)->insn_code != CODE_FOR_nothing)
        {
          if (purpose == ccp_jump)
@@ -3970,15 +3996,19 @@ can_compare_p (enum rtx_code code, enum machine_mode mode,
            return 1;
        }
       if (purpose == ccp_jump
-         && optab_handler (cbranch_optab, mode)->insn_code != CODE_FOR_nothing)
-       return 1;
+          && (icode = optab_handler (cbranch_optab, mode)->insn_code) != CODE_FOR_nothing
+          && insn_data[icode].operand[0].predicate (test, mode))
+        return 1;
+      if (purpose == ccp_store_flag
+          && (icode = optab_handler (cstore_optab, mode)->insn_code) != CODE_FOR_nothing
+          && insn_data[icode].operand[1].predicate (test, mode))
+        return 1;
       if (purpose == ccp_cmov
          && optab_handler (cmov_optab, mode)->insn_code != CODE_FOR_nothing)
        return 1;
-      if (purpose == ccp_store_flag
-         && optab_handler (cstore_optab, mode)->insn_code != CODE_FOR_nothing)
-       return 1;
+
       mode = GET_MODE_WIDER_MODE (mode);
+      PUT_MODE (test, mode);
     }
   while (mode != VOIDmode);
 
@@ -4015,11 +4045,13 @@ prepare_cmp_insn (rtx *px, rtx *py, enum rtx_code *pcomparison, rtx size,
   /* If we are inside an appropriately-short loop and we are optimizing,
      force expensive constants into a register.  */
   if (CONSTANT_P (x) && optimize
-      && rtx_cost (x, COMPARE) > COSTS_N_INSNS (1))
+      && (rtx_cost (x, COMPARE, optimize_insn_for_speed_p ())
+          > COSTS_N_INSNS (1)))
     x = force_reg (mode, x);
 
   if (CONSTANT_P (y) && optimize
-      && rtx_cost (y, COMPARE) > COSTS_N_INSNS (1))
+      && (rtx_cost (y, COMPARE, optimize_insn_for_speed_p ())
+          > COSTS_N_INSNS (1)))
     y = force_reg (mode, y);
 
 #ifdef HAVE_cc0
@@ -4110,11 +4142,15 @@ prepare_cmp_insn (rtx *px, rtx *py, enum rtx_code *pcomparison, rtx size,
 
   *px = x;
   *py = y;
-  if (can_compare_p (*pcomparison, mode, purpose))
+  if (GET_MODE_CLASS (mode) == MODE_CC)
+    {
+      gcc_assert (can_compare_p (*pcomparison, CCmode, purpose));
+      return;
+    }
+  else if (can_compare_p (*pcomparison, mode, purpose))
     return;
 
   /* Handle a lib call just for the mode we are using.  */
-
   libfunc = optab_libfunc (cmp_optab, mode);
   if (libfunc && !SCALAR_FLOAT_MODE_P (mode))
     {
@@ -4192,18 +4228,19 @@ emit_cmp_and_jump_insn_1 (rtx x, rtx y, enum machine_mode mode,
                          enum rtx_code comparison, int unsignedp, rtx label)
 {
   rtx test = gen_rtx_fmt_ee (comparison, mode, x, y);
-  enum mode_class class = GET_MODE_CLASS (mode);
+  enum mode_class mclass = GET_MODE_CLASS (mode);
   enum machine_mode wider_mode = mode;
 
   /* Try combined insns first.  */
   do
     {
+      enum machine_mode optab_mode = mclass == MODE_CC ? CCmode : wider_mode;
       enum insn_code icode;
       PUT_MODE (test, wider_mode);
 
       if (label)
        {
-         icode = optab_handler (cbranch_optab, wider_mode)->insn_code;
+         icode = optab_handler (cbranch_optab, optab_mode)->insn_code;
 
          if (icode != CODE_FOR_nothing
              && insn_data[icode].operand[0].predicate (test, wider_mode))
@@ -4216,7 +4253,7 @@ emit_cmp_and_jump_insn_1 (rtx x, rtx y, enum machine_mode mode,
        }
 
       /* Handle some compares against zero.  */
-      icode = (int) optab_handler (tst_optab, wider_mode)->insn_code;
+      icode = optab_handler (tst_optab, optab_mode)->insn_code;
       if (y == CONST0_RTX (mode) && icode != CODE_FOR_nothing)
        {
          x = prepare_operand (icode, x, 0, mode, wider_mode, unsignedp);
@@ -4228,7 +4265,7 @@ emit_cmp_and_jump_insn_1 (rtx x, rtx y, enum machine_mode mode,
 
       /* Handle compares for which there is a directly suitable insn.  */
 
-      icode = (int) optab_handler (cmp_optab, wider_mode)->insn_code;
+      icode = optab_handler (cmp_optab, optab_mode)->insn_code;
       if (icode != CODE_FOR_nothing)
        {
          x = prepare_operand (icode, x, 0, mode, wider_mode, unsignedp);
@@ -4239,7 +4276,7 @@ emit_cmp_and_jump_insn_1 (rtx x, rtx y, enum machine_mode mode,
          return;
        }
 
-      if (!CLASS_HAS_WIDER_MODES_P (class))
+      if (!CLASS_HAS_WIDER_MODES_P (mclass))
        break;
 
       wider_mode = GET_MODE_WIDER_MODE (wider_mode);
@@ -5141,6 +5178,7 @@ expand_fix (rtx to, rtx from, int unsignedp)
 
        if (icode != CODE_FOR_nothing)
          {
+           rtx last = get_last_insn ();
            if (fmode != GET_MODE (from))
              from = convert_to_mode (fmode, from, 0);
 
@@ -5154,11 +5192,14 @@ expand_fix (rtx to, rtx from, int unsignedp)
            if (imode != GET_MODE (to))
              target = gen_reg_rtx (imode);
 
-           emit_unop_insn (icode, target, from,
-                           doing_unsigned ? UNSIGNED_FIX : FIX);
-           if (target != to)
-             convert_move (to, target, unsignedp);
-           return;
+           if (maybe_emit_unop_insn (icode, target, from,
+                                     doing_unsigned ? UNSIGNED_FIX : FIX))
+             {
+               if (target != to)
+                 convert_move (to, target, unsignedp);
+               return;
+             }
+           delete_insns_since (last);
          }
       }
 
@@ -5366,13 +5407,18 @@ expand_sfix_optab (rtx to, rtx from, convert_optab tab)
        icode = convert_optab_handler (tab, imode, fmode)->insn_code;
        if (icode != CODE_FOR_nothing)
          {
+           rtx last = get_last_insn ();
            if (fmode != GET_MODE (from))
              from = convert_to_mode (fmode, from, 0);
 
            if (imode != GET_MODE (to))
              target = gen_reg_rtx (imode);
 
-           emit_unop_insn (icode, target, from, UNKNOWN);
+           if (!maybe_emit_unop_insn (icode, target, from, UNKNOWN))
+             {
+               delete_insns_since (last);
+               continue;
+             }
            if (target != to)
              convert_move (to, target, 0);
            return true;
@@ -6038,6 +6084,24 @@ init_one_libfunc (const char *name)
   return XEXP (DECL_RTL (decl), 0);
 }
 
+/* Adjust the assembler name of libfunc NAME to ASMSPEC.  */
+
+rtx
+set_user_assembler_libfunc (const char *name, const char *asmspec)
+{
+  tree id, decl;
+  void **slot;
+  hashval_t hash;
+
+  id = get_identifier (name);
+  hash = htab_hash_string (name);
+  slot = htab_find_slot_with_hash (libfunc_decls, id, hash, NO_INSERT);
+  gcc_assert (slot);
+  decl = (tree) *slot;
+  set_user_assembler_name (decl, asmspec);
+  return XEXP (DECL_RTL (decl), 0);
+}
+
 /* Call this to reset the function entry for one optab (OPTABLE) in mode
    MODE to NAME, which should be either 0 or a string constant.  */
 void
@@ -6339,7 +6403,6 @@ init_optabs (void)
       sync_new_xor_optab[i] = CODE_FOR_nothing;
       sync_new_nand_optab[i] = CODE_FOR_nothing;
       sync_compare_and_swap[i] = CODE_FOR_nothing;
-      sync_compare_and_swap_cc[i] = CODE_FOR_nothing;
       sync_lock_test_and_set[i] = CODE_FOR_nothing;
       sync_lock_release[i] = CODE_FOR_nothing;
 
@@ -6624,7 +6687,7 @@ debug_optab_libfuncs (void)
        rtx l;
 
        o = &optab_table[i];
-       l = optab_libfunc (o, j);
+       l = optab_libfunc (o, (enum machine_mode) j);
        if (l)
          {
            gcc_assert (GET_CODE (l) == SYMBOL_REF);
@@ -6644,7 +6707,8 @@ debug_optab_libfuncs (void)
          rtx l;
 
          o = &convert_optab_table[i];
-         l = convert_optab_libfunc (o, j, k);
+         l = convert_optab_libfunc (o, (enum machine_mode) j,
+                                    (enum machine_mode) k);
          if (l)
            {
              gcc_assert (GET_CODE (l) == SYMBOL_REF);
@@ -6910,6 +6974,21 @@ expand_val_compare_and_swap (rtx mem, rtx old_val, rtx new_val, rtx target)
   return expand_val_compare_and_swap_1 (mem, old_val, new_val, target, icode);
 }
 
+/* Helper function to find the MODE_CC set in a sync_compare_and_swap
+   pattern.  */
+
+static void
+find_cc_set (rtx x, const_rtx pat, void *data)
+{
+  if (REG_P (x) && GET_MODE_CLASS (GET_MODE (x)) == MODE_CC
+      && GET_CODE (pat) == SET)
+    {
+      rtx *p_cc_reg = (rtx *) data;
+      gcc_assert (!*p_cc_reg);
+      *p_cc_reg = x;
+    }
+}
+
 /* Expand a compare-and-swap operation and store true into the result if
    the operation was successful and false otherwise.  Return the result.
    Unlike other routines, TARGET is not optional.  */
@@ -6919,84 +6998,46 @@ expand_bool_compare_and_swap (rtx mem, rtx old_val, rtx new_val, rtx target)
 {
   enum machine_mode mode = GET_MODE (mem);
   enum insn_code icode;
-  rtx subtarget, label0, label1;
+  rtx subtarget, seq, cc_reg;
 
   /* If the target supports a compare-and-swap pattern that simultaneously
      sets some flag for success, then use it.  Otherwise use the regular
      compare-and-swap and follow that immediately with a compare insn.  */
-  icode = sync_compare_and_swap_cc[mode];
-  switch (icode)
-    {
-    default:
-      subtarget = expand_val_compare_and_swap_1 (mem, old_val, new_val,
-                                                NULL_RTX, icode);
-      if (subtarget != NULL_RTX)
-       break;
-
-      /* FALLTHRU */
-    case CODE_FOR_nothing:
-      icode = sync_compare_and_swap[mode];
-      if (icode == CODE_FOR_nothing)
-       return NULL_RTX;
-
-      /* Ensure that if old_val == mem, that we're not comparing
-        against an old value.  */
-      if (MEM_P (old_val))
-       old_val = force_reg (mode, old_val);
+  icode = sync_compare_and_swap[mode];
+  if (icode == CODE_FOR_nothing)
+    return NULL_RTX;
 
+  do
+    {
+      start_sequence ();
       subtarget = expand_val_compare_and_swap_1 (mem, old_val, new_val,
-                                                NULL_RTX, icode);
+                                                NULL_RTX, icode);
+      cc_reg = NULL_RTX;
       if (subtarget == NULL_RTX)
-       return NULL_RTX;
-
-      emit_cmp_insn (subtarget, old_val, EQ, const0_rtx, mode, true);
-    }
-
-  /* If the target has a sane STORE_FLAG_VALUE, then go ahead and use a
-     setcc instruction from the beginning.  We don't work too hard here,
-     but it's nice to not be stupid about initial code gen either.  */
-  if (STORE_FLAG_VALUE == 1)
-    {
-      icode = setcc_gen_code[EQ];
-      if (icode != CODE_FOR_nothing)
        {
-         enum machine_mode cmode = insn_data[icode].operand[0].mode;
-         rtx insn;
-
-         subtarget = target;
-         if (!insn_data[icode].operand[0].predicate (target, cmode))
-           subtarget = gen_reg_rtx (cmode);
-
-         insn = GEN_FCN (icode) (subtarget);
-         if (insn)
-           {
-             emit_insn (insn);
-             if (GET_MODE (target) != GET_MODE (subtarget))
-               {
-                 convert_move (target, subtarget, 1);
-                 subtarget = target;
-               }
-             return subtarget;
-           }
+         end_sequence ();
+         return NULL_RTX;
        }
-    }
 
-  /* Without an appropriate setcc instruction, use a set of branches to
-     get 1 and 0 stored into target.  Presumably if the target has a
-     STORE_FLAG_VALUE that isn't 1, then this will get cleaned up by ifcvt.  */
-
-  label0 = gen_label_rtx ();
-  label1 = gen_label_rtx ();
+      if (have_insn_for (COMPARE, CCmode))
+       note_stores (PATTERN (get_last_insn ()), find_cc_set, &cc_reg);
+      seq = get_insns ();
+      end_sequence ();
 
-  emit_jump_insn (bcc_gen_fctn[EQ] (label0));
-  emit_move_insn (target, const0_rtx);
-  emit_jump_insn (gen_jump (label1));
-  emit_barrier ();
-  emit_label (label0);
-  emit_move_insn (target, const1_rtx);
-  emit_label (label1);
+      /* We might be comparing against an old value.  Try again. :-(  */
+      if (!cc_reg && MEM_P (old_val))
+       {
+         seq = NULL_RTX;
+         old_val = force_reg (mode, old_val);
+        }
+    }
+  while (!seq);
 
-  return target;
+  emit_insn (seq);
+  if (cc_reg)
+    return emit_store_flag (target, EQ, cc_reg, const0_rtx, VOIDmode, 0, 1);
+  else
+    return emit_store_flag (target, EQ, subtarget, old_val, VOIDmode, 1, 1);
 }
 
 /* This is a helper function for the other atomic operations.  This function
@@ -7013,7 +7054,7 @@ expand_compare_and_swap_loop (rtx mem, rtx old_reg, rtx new_reg, rtx seq)
 {
   enum machine_mode mode = GET_MODE (mem);
   enum insn_code icode;
-  rtx label, cmp_reg, subtarget;
+  rtx label, cmp_reg, subtarget, cc_reg;
 
   /* The loop we want to generate looks like
 
@@ -7040,37 +7081,32 @@ expand_compare_and_swap_loop (rtx mem, rtx old_reg, rtx new_reg, rtx seq)
   /* If the target supports a compare-and-swap pattern that simultaneously
      sets some flag for success, then use it.  Otherwise use the regular
      compare-and-swap and follow that immediately with a compare insn.  */
-  icode = sync_compare_and_swap_cc[mode];
-  switch (icode)
-    {
-    default:
-      subtarget = expand_val_compare_and_swap_1 (mem, old_reg, new_reg,
-                                                cmp_reg, icode);
-      if (subtarget != NULL_RTX)
-       {
-         gcc_assert (subtarget == cmp_reg);
-         break;
-       }
+  icode = sync_compare_and_swap[mode];
+  if (icode == CODE_FOR_nothing)
+    return false;
 
-      /* FALLTHRU */
-    case CODE_FOR_nothing:
-      icode = sync_compare_and_swap[mode];
-      if (icode == CODE_FOR_nothing)
-       return false;
+  subtarget = expand_val_compare_and_swap_1 (mem, old_reg, new_reg,
+                                            cmp_reg, icode);
+  if (subtarget == NULL_RTX)
+    return false;
 
-      subtarget = expand_val_compare_and_swap_1 (mem, old_reg, new_reg,
-                                                cmp_reg, icode);
-      if (subtarget == NULL_RTX)
-       return false;
+  cc_reg = NULL_RTX;
+  if (have_insn_for (COMPARE, CCmode))
+    note_stores (PATTERN (get_last_insn ()), find_cc_set, &cc_reg);
+  if (cc_reg)
+    {
+      cmp_reg = cc_reg;
+      old_reg = const0_rtx;
+    }
+  else
+    {
       if (subtarget != cmp_reg)
        emit_move_insn (cmp_reg, subtarget);
-
-      emit_cmp_insn (cmp_reg, old_reg, EQ, const0_rtx, mode, true);
     }
 
   /* ??? Mark this jump predicted not taken?  */
-  emit_jump_insn (bcc_gen_fctn[NE] (label));
-
+  emit_cmp_and_jump_insns (cmp_reg, old_reg, NE, const0_rtx, GET_MODE (cmp_reg), 1,
+                          label);
   return true;
 }
 
@@ -7148,12 +7184,13 @@ expand_sync_operation (rtx mem, rtx val, enum rtx_code code)
       t1 = t0;
       if (code == NOT)
        {
-         t1 = expand_simple_unop (mode, NOT, t1, NULL_RTX, true);
-         code = AND;
+         t1 = expand_simple_binop (mode, AND, t1, val, NULL_RTX,
+                                   true, OPTAB_LIB_WIDEN);
+         t1 = expand_simple_unop (mode, code, t1, NULL_RTX, true);
        }
-      t1 = expand_simple_binop (mode, code, t1, val, NULL_RTX,
-                               true, OPTAB_LIB_WIDEN);
-
+      else
+       t1 = expand_simple_binop (mode, code, t1, val, NULL_RTX,
+                                 true, OPTAB_LIB_WIDEN);
       insn = get_insns ();
       end_sequence ();
 
@@ -7281,9 +7318,17 @@ expand_sync_fetch_operation (rtx mem, rtx val, enum rtx_code code,
                }
 
              if (code == NOT)
-               target = expand_simple_unop (mode, NOT, target, NULL_RTX, true);
-             target = expand_simple_binop (mode, code, target, val, NULL_RTX,
-                                           true, OPTAB_LIB_WIDEN);
+               {
+                 target = expand_simple_binop (mode, AND, target, val,
+                                               NULL_RTX, true,
+                                               OPTAB_LIB_WIDEN);
+                 target = expand_simple_unop (mode, code, target,
+                                              NULL_RTX, true);
+               }
+             else
+               target = expand_simple_binop (mode, code, target, val,
+                                             NULL_RTX, true,
+                                             OPTAB_LIB_WIDEN);
            }
 
          return target;
@@ -7306,11 +7351,13 @@ expand_sync_fetch_operation (rtx mem, rtx val, enum rtx_code code,
       t1 = t0;
       if (code == NOT)
        {
-         t1 = expand_simple_unop (mode, NOT, t1, NULL_RTX, true);
-         code = AND;
+         t1 = expand_simple_binop (mode, AND, t1, val, NULL_RTX,
+                                   true, OPTAB_LIB_WIDEN);
+         t1 = expand_simple_unop (mode, code, t1, NULL_RTX, true);
        }
-      t1 = expand_simple_binop (mode, code, t1, val, NULL_RTX,
-                               true, OPTAB_LIB_WIDEN);
+      else
+       t1 = expand_simple_binop (mode, code, t1, val, NULL_RTX,
+                                 true, OPTAB_LIB_WIDEN);
       if (after)
        emit_move_insn (target, t1);