OSDN Git Service

* config/m68k/m68k.h: Remove comments copied over from tm.texi.
[pf3gnuchains/gcc-fork.git] / gcc / optabs.c
index 3733d63..562eb29 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 Free Software Foundation, Inc.
+   1999, 2000, 2001, 2002, 2003, 2004 Free Software Foundation, Inc.
 
 This file is part of GCC.
 
@@ -105,7 +105,6 @@ static void prepare_cmp_insn (rtx *, rtx *, enum rtx_code *, rtx,
 static enum insn_code can_fix_p (enum machine_mode, enum machine_mode, int,
                                 int *);
 static enum insn_code can_float_p (enum machine_mode, enum machine_mode, int);
-static rtx ftruncify (rtx);
 static optab new_optab (void);
 static convert_optab new_convert_optab (void);
 static inline optab init_optab (enum rtx_code);
@@ -154,8 +153,11 @@ add_equal_note (rtx insns, rtx target, enum rtx_code code, rtx op0, rtx op1)
       || NEXT_INSN (insns) == NULL_RTX)
     abort ();
 
-  if (GET_RTX_CLASS (code) != '1' && GET_RTX_CLASS (code) != '2'
-      && GET_RTX_CLASS (code) != 'c' && GET_RTX_CLASS (code) != '<')
+  if (GET_RTX_CLASS (code) != RTX_COMM_ARITH
+      && GET_RTX_CLASS (code) != RTX_BIN_ARITH
+      && GET_RTX_CLASS (code) != RTX_COMM_COMPARE
+      && GET_RTX_CLASS (code) != RTX_COMPARE
+      && GET_RTX_CLASS (code) != RTX_UNARY)
     return 1;
 
   if (GET_CODE (target) == ZERO_EXTRACT)
@@ -191,7 +193,7 @@ add_equal_note (rtx insns, rtx target, enum rtx_code code, rtx op0, rtx op1)
        }
     }
 
-  if (GET_RTX_CLASS (code) == '1')
+  if (GET_RTX_CLASS (code) == RTX_UNARY)
     note = gen_rtx_fmt_e (code, GET_MODE (target), copy_rtx (op0));
   else
     note = gen_rtx_fmt_ee (code, GET_MODE (target), copy_rtx (op0), copy_rtx (op1));
@@ -719,7 +721,7 @@ expand_binop (enum machine_mode mode, optab binoptab, rtx op0, rtx op1,
      try to make the first operand a register.
      Even better, try to make it the same as the target.
      Also try to make the last operand a constant.  */
-  if (GET_RTX_CLASS (binoptab->code) == 'c'
+  if (GET_RTX_CLASS (binoptab->code) == RTX_COMM_ARITH
       || binoptab == smul_widen_optab
       || binoptab == umul_widen_optab
       || binoptab == smul_highpart_optab
@@ -1085,8 +1087,12 @@ expand_binop (enum machine_mode mode, optab binoptab, rtx op0, rtx op1,
       int shift_count, left_shift, outof_word;
 
       /* If TARGET is the same as one of the operands, the REG_EQUAL note
-        won't be accurate, so use a new target.  */
-      if (target == 0 || target == op0 || target == op1)
+        won't be accurate, so use a new target. Do this also if target is not
+        a REG, first because having a register instead may open optimization
+        opportunities, and second because if target and op0 happen to be MEMs
+        designating the same location, we would risk clobbering it too early
+        in the code sequence we generate below.  */
+      if (target == 0 || target == op0 || target == op1 || ! REG_P (target))
        target = gen_reg_rtx (mode);
 
       start_sequence ();
@@ -1521,24 +1527,17 @@ expand_binop (enum machine_mode mode, optab binoptab, rtx op0, rtx op1,
       rtx real0 = 0, imag0 = 0;
       rtx real1 = 0, imag1 = 0;
       rtx realr, imagr, res;
-      rtx seq;
-      rtx equiv_value;
+      rtx seq, result;
       int ok = 0;
 
       /* Find the correct mode for the real and imaginary parts.  */
-      enum machine_mode submode = GET_MODE_INNER(mode);
+      enum machine_mode submode = GET_MODE_INNER (mode);
 
       if (submode == BLKmode)
        abort ();
 
-      if (! target)
-       target = gen_reg_rtx (mode);
-
       start_sequence ();
 
-      realr = gen_realpart (submode, target);
-      imagr = gen_imagpart (submode, target);
-
       if (GET_MODE (op0) == mode)
        {
          real0 = gen_realpart (submode, op0);
@@ -1558,6 +1557,10 @@ expand_binop (enum machine_mode mode, optab binoptab, rtx op0, rtx op1,
       if (real0 == 0 || real1 == 0 || ! (imag0 != 0 || imag1 != 0))
        abort ();
 
+      result = gen_reg_rtx (mode);
+      realr = gen_realpart (submode, result);
+      imagr = gen_imagpart (submode, result);
+
       switch (binoptab->code)
        {
        case PLUS:
@@ -1749,16 +1752,10 @@ expand_binop (enum machine_mode mode, optab binoptab, rtx op0, rtx op1,
 
       if (ok)
        {
-         if (binoptab->code != UNKNOWN)
-           equiv_value
-             = gen_rtx_fmt_ee (binoptab->code, mode,
-                               copy_rtx (op0), copy_rtx (op1));
-         else
-           equiv_value = 0;
-
-         emit_no_conflict_block (seq, target, op0, op1, equiv_value);
-
-         return target;
+         rtx equiv = gen_rtx_fmt_ee (binoptab->code, mode,
+                                     copy_rtx (op0), copy_rtx (op1));
+         emit_no_conflict_block (seq, result, op0, op1, equiv);
+         return result;
        }
     }
 
@@ -2150,6 +2147,109 @@ sign_expand_binop (enum machine_mode mode, optab uoptab, optab soptab,
   return 0;
 }
 \f
+/* Generate code to perform an operation specified by UNOPPTAB
+   on operand OP0, with two results to TARG0 and TARG1.
+   We assume that the order of the operands for the instruction
+   is TARG0, TARG1, OP0.
+
+   Either TARG0 or TARG1 may be zero, but what that means is that
+   the result is not actually wanted.  We will generate it into
+   a dummy pseudo-reg and discard it.  They may not both be zero.
+
+   Returns 1 if this operation can be performed; 0 if not.  */
+
+int
+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 machine_mode wider_mode;
+  rtx entry_last = get_last_insn ();
+  rtx last;
+
+  class = GET_MODE_CLASS (mode);
+
+  op0 = protect_from_queue (op0, 0);
+
+  if (flag_force_mem)
+    {
+      op0 = force_not_mem (op0);
+    }
+
+  if (targ0)
+    targ0 = protect_from_queue (targ0, 1);
+  else
+    targ0 = gen_reg_rtx (mode);
+  if (targ1)
+    targ1 = protect_from_queue (targ1, 1);
+  else
+    targ1 = gen_reg_rtx (mode);
+
+  /* Record where to go back to if we fail.  */
+  last = get_last_insn ();
+
+  if (unoptab->handlers[(int) mode].insn_code != CODE_FOR_nothing)
+    {
+      int icode = (int) unoptab->handlers[(int) mode].insn_code;
+      enum machine_mode mode0 = insn_data[icode].operand[2].mode;
+      rtx pat;
+      rtx xop0 = op0;
+
+      if (GET_MODE (xop0) != VOIDmode
+         && GET_MODE (xop0) != mode0)
+       xop0 = convert_to_mode (mode0, xop0, unsignedp);
+
+      /* Now, if insn doesn't accept these operands, put them into pseudos.  */
+      if (! (*insn_data[icode].operand[2].predicate) (xop0, mode0))
+       xop0 = copy_to_mode_reg (mode0, xop0);
+
+      /* We could handle this, but we should always be called with a pseudo
+        for our targets and all insns should take them as outputs.  */
+      if (! (*insn_data[icode].operand[0].predicate) (targ0, mode)
+         || ! (*insn_data[icode].operand[1].predicate) (targ1, mode))
+       abort ();
+
+      pat = GEN_FCN (icode) (targ0, targ1, xop0);
+      if (pat)
+       {
+         emit_insn (pat);
+         return 1;
+       }
+      else
+       delete_insns_since (last);
+    }
+
+  /* It can't be done in this mode.  Can we do it in a wider mode?  */
+
+  if (class == MODE_INT || class == MODE_FLOAT || class == MODE_COMPLEX_FLOAT)
+    {
+      for (wider_mode = GET_MODE_WIDER_MODE (mode); wider_mode != VOIDmode;
+          wider_mode = GET_MODE_WIDER_MODE (wider_mode))
+       {
+         if (unoptab->handlers[(int) wider_mode].insn_code
+             != CODE_FOR_nothing)
+           {
+             rtx t0 = gen_reg_rtx (wider_mode);
+             rtx t1 = gen_reg_rtx (wider_mode);
+             rtx cop0 = convert_modes (wider_mode, mode, op0, unsignedp);
+
+             if (expand_twoval_unop (unoptab, cop0, t0, t1, unsignedp))
+               {
+                 convert_move (targ0, t0, unsignedp);
+                 convert_move (targ1, t1, unsignedp);
+                 return 1;
+               }
+             else
+               delete_insns_since (last);
+           }
+       }
+    }
+
+  delete_insns_since (entry_last);
+  return 0;
+}
+\f
 /* Generate code to perform an operation specified by BINOPTAB
    on operands OP0 and OP1, with two results to TARG1 and TARG2.
    We assume that the order of the operands for the instruction
@@ -2367,7 +2467,7 @@ expand_parity (enum machine_mode mode, rtx op0, rtx target)
              temp = expand_unop (wider_mode, popcount_optab, xop0, NULL_RTX,
                                  true);
              if (temp != 0)
-               temp = expand_binop (wider_mode, and_optab, temp, GEN_INT (1),
+               temp = expand_binop (wider_mode, and_optab, temp, const1_rtx,
                                     target, true, OPTAB_DIRECT);
              if (temp == 0)
                delete_insns_since (last);
@@ -2615,7 +2715,16 @@ expand_unop (enum machine_mode mode, optab unoptab, rtx op0, rtx target,
                               immed_double_const (lo, hi, imode),
                               NULL_RTX, 1, OPTAB_LIB_WIDEN);
          if (temp != 0)
-           return gen_lowpart (mode, temp);
+           {
+             rtx insn;
+             if (target == 0)
+               target = gen_reg_rtx (mode);
+             insn = emit_move_insn (target, gen_lowpart (mode, temp));
+             set_unique_reg_note (insn, REG_EQUAL,
+                                  gen_rtx_fmt_e (NEG, mode,
+                                                 copy_rtx (op0)));
+             return target;
+           }
          delete_insns_since (last);
         }
     }
@@ -2628,6 +2737,15 @@ expand_unop (enum machine_mode mode, optab unoptab, rtx op0, rtx target,
        return temp;
     }
 
+  /* If there is no negation pattern, try subtracting from zero.  */
+  if (unoptab == neg_optab && class == MODE_INT)
+    {
+      temp = expand_binop (mode, sub_optab, CONST0_RTX (mode), op0,
+                           target, unsignedp, OPTAB_DIRECT);
+      if (temp)
+       return temp;
+    }
+
  try_libcall:
   /* Now try a library call in this mode.  */
   if (unoptab->handlers[(int) mode].libfunc)
@@ -2790,7 +2908,16 @@ expand_abs_nojump (enum machine_mode mode, rtx op0, rtx target,
                               immed_double_const (~lo, ~hi, imode),
                               NULL_RTX, 1, OPTAB_LIB_WIDEN);
          if (temp != 0)
-           return gen_lowpart (mode, temp);
+           {
+             rtx insn;
+             if (target == 0)
+               target = gen_reg_rtx (mode);
+             insn = emit_move_insn (target, gen_lowpart (mode, temp));
+             set_unique_reg_note (insn, REG_EQUAL,
+                                  gen_rtx_fmt_e (ABS, mode,
+                                                 copy_rtx (op0)));
+             return target;
+           }
          delete_insns_since (last);
        }
     }
@@ -3328,9 +3455,9 @@ emit_libcall_block (rtx insns, rtx target, rtx result, rtx equiv)
          rtx note = find_reg_note (insn, REG_EH_REGION, NULL_RTX);
 
          if (note != 0)
-           XEXP (note, 0) = GEN_INT (-1);
+           XEXP (note, 0) = constm1_rtx;
          else
-           REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_EH_REGION, GEN_INT (-1),
+           REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_EH_REGION, constm1_rtx,
                                                  REG_NOTES (insn));
        }
 
@@ -3629,7 +3756,7 @@ prepare_cmp_insn (rtx *px, rtx *py, enum rtx_code *pcomparison, rtx size,
       result_mode = TYPE_MODE (integer_type_node);
       cmp_mode = TYPE_MODE (length_type);
       size = convert_to_mode (TYPE_MODE (length_type), size,
-                             TREE_UNSIGNED (length_type));
+                             TYPE_UNSIGNED (length_type));
 
       result = emit_library_call_value (libfunc, 0, LCT_PURE_MAKE_BLOCK,
                                        result_mode, 3,
@@ -3642,6 +3769,16 @@ prepare_cmp_insn (rtx *px, rtx *py, enum rtx_code *pcomparison, rtx size,
       return;
     }
 
+  /* Don't allow operands to the compare to trap, as that can put the
+     compare and branch in different basic blocks.  */
+  if (flag_non_call_exceptions)
+    {
+      if (may_trap_p (x))
+       x = force_reg (mode, x);
+      if (may_trap_p (y))
+       y = force_reg (mode, y);
+    }
+
   *px = x;
   *py = y;
   if (can_compare_p (*pcomparison, mode, purpose))
@@ -3871,70 +4008,62 @@ prepare_float_lib_cmp (rtx *px, rtx *py, enum rtx_code *pcomparison,
       y = convert_to_mode (mode, y, 0);
     }
 
-  /* If we're optimizing attach a REG_EQUAL note describing the semantics
-     of the libcall to the RTL.  The allows the RTL optimizers to delete
-     the libcall if the condition can be determined at compile-time.  */
-  if (optimize
-      && ! side_effects_p (x)
-      && ! side_effects_p (y))
+  /* Attach a REG_EQUAL note describing the semantics of the libcall to
+     the RTL.  The allows the RTL optimizers to delete the libcall if the
+     condition can be determined at compile-time.  */
+  if (comparison == UNORDERED)
     {
-      if (comparison == UNORDERED)
-       {
-         rtx temp = simplify_gen_relational (NE, word_mode, mode, x, x);
-         equiv = simplify_gen_relational (NE, word_mode, mode, y, y);
-         equiv = simplify_gen_ternary (IF_THEN_ELSE, word_mode, word_mode,
-                                       temp, const_true_rtx, equiv);
-       }
-      else
+      rtx temp = simplify_gen_relational (NE, word_mode, mode, x, x);
+      equiv = simplify_gen_relational (NE, word_mode, mode, y, y);
+      equiv = simplify_gen_ternary (IF_THEN_ELSE, word_mode, word_mode,
+                                   temp, const_true_rtx, equiv);
+    }
+  else
+    {
+      equiv = simplify_gen_relational (comparison, word_mode, mode, x, y);
+      if (! FLOAT_LIB_COMPARE_RETURNS_BOOL (mode, comparison))
        {
-         equiv = simplify_gen_relational (comparison, word_mode, mode, x, y);
-         if (! FLOAT_LIB_COMPARE_RETURNS_BOOL (mode, comparison))
-           {
-             rtx true_rtx, false_rtx;
-
-             switch (comparison)
-               {
-               case EQ:
-                 true_rtx = const0_rtx;
-                 false_rtx = const_true_rtx;
-                 break;
-
-               case NE:
-                 true_rtx = const_true_rtx;
-                 false_rtx = const0_rtx;
-                 break;
+         rtx true_rtx, false_rtx;
 
-               case GT:
-                 true_rtx = const1_rtx;
-                 false_rtx = const0_rtx;
-                 break;
-
-               case GE:
-                 true_rtx = const0_rtx;
-                 false_rtx = constm1_rtx;
-                 break;
-
-               case LT:
-                 true_rtx = constm1_rtx;
-                 false_rtx = const0_rtx;
-                 break;
-
-               case LE:
-                 true_rtx = const0_rtx;
-                 false_rtx = const1_rtx;
-                 break;
-
-               default:
-                 abort ();
-               }
-             equiv = simplify_gen_ternary (IF_THEN_ELSE, word_mode,
-                                           word_mode, equiv,
-                                           true_rtx, false_rtx);
+         switch (comparison)
+           {
+           case EQ:
+             true_rtx = const0_rtx;
+             false_rtx = const_true_rtx;
+             break;
+
+           case NE:
+             true_rtx = const_true_rtx;
+             false_rtx = const0_rtx;
+             break;
+
+           case GT:
+             true_rtx = const1_rtx;
+             false_rtx = const0_rtx;
+             break;
+
+           case GE:
+             true_rtx = const0_rtx;
+             false_rtx = constm1_rtx;
+             break;
+
+           case LT:
+             true_rtx = constm1_rtx;
+             false_rtx = const0_rtx;
+             break;
+
+           case LE:
+             true_rtx = const0_rtx;
+             false_rtx = const1_rtx;
+             break;
+
+           default:
+             abort ();
            }
+         equiv = simplify_gen_ternary (IF_THEN_ELSE, word_mode, word_mode,
+                                       equiv, true_rtx, false_rtx);
        }
     }
-  else
-    equiv = NULL_RTX;
 
   start_sequence ();
   value = emit_library_call_value (libfunc, NULL_RTX, LCT_CONST,
@@ -4433,6 +4562,9 @@ can_fix_p (enum machine_mode fixmode, enum machine_mode fltmode,
       return icode;
     }
 
+  /* FIXME: This requires a port to define both FIX and FTRUNC pattern
+     for this to work. We need to rework the fix* and ftrunc* patterns
+     and documentation.  */
   tab = unsignedp ? ufix_optab : sfix_optab;
   icode = tab->handlers[fixmode][fltmode].insn_code;
   if (icode != CODE_FOR_nothing
@@ -4672,15 +4804,8 @@ expand_float (rtx to, rtx from, int unsignedp)
     }
 }
 \f
-/* expand_fix: generate code to convert FROM to fixed point
-   and store in TO.  FROM must be floating point.  */
-
-static rtx
-ftruncify (rtx x)
-{
-  rtx temp = gen_reg_rtx (GET_MODE (x));
-  return expand_unop (GET_MODE (x), ftrunc_optab, x, temp, 0);
-}
+/* Generate code to convert FROM to fixed point and store in TO.  FROM
+   must be floating point.  */
 
 void
 expand_fix (rtx to, rtx from, int unsignedp)
@@ -4715,7 +4840,11 @@ expand_fix (rtx to, rtx from, int unsignedp)
              from = convert_to_mode (fmode, from, 0);
 
            if (must_trunc)
-             from = ftruncify (from);
+             {
+               rtx temp = gen_reg_rtx (GET_MODE (from));
+               from = expand_unop (GET_MODE (from), ftrunc_optab, from,
+                                   temp, 0);
+             }
 
            if (imode != GET_MODE (to))
              target = gen_reg_rtx (imode);
@@ -4940,7 +5069,7 @@ init_convert_optab (enum rtx_code code)
 /* Initialize the libfunc fields of an entire group of entries in some
    optab.  Each entry is set equal to a string consisting of a leading
    pair of underscores followed by a generic operation name followed by
-   a mode name (downshifted to lower case) followed by a single character
+   a mode name (downshifted to lowercase) followed by a single character
    representing the number of operands for the given operation (which is
    usually one of the characters '2', '3', or '4').
 
@@ -5009,18 +5138,7 @@ init_integral_libfuncs (optab optable, const char *opname, int suffix)
 static void
 init_floating_libfuncs (optab optable, const char *opname, int suffix)
 {
-  enum machine_mode fmode, dmode, lmode;
-
-  fmode = float_type_node ? TYPE_MODE (float_type_node) : VOIDmode;
-  dmode = double_type_node ? TYPE_MODE (double_type_node) : VOIDmode;
-  lmode = long_double_type_node ? TYPE_MODE (long_double_type_node) : VOIDmode;
-
-  if (fmode != VOIDmode)
-    init_libfuncs (optable, fmode, fmode, opname, suffix);
-  if (dmode != fmode && dmode != VOIDmode)
-    init_libfuncs (optable, dmode, dmode, opname, suffix);
-  if (lmode != dmode && lmode != VOIDmode)
-    init_libfuncs (optable, lmode, lmode, opname, suffix);
+  init_libfuncs (optable, MIN_MODE_FLOAT, MAX_MODE_FLOAT, opname, suffix);
 }
 
 /* Initialize the libfunc fields of an entire group of entries of an
@@ -5128,8 +5246,8 @@ init_intraclass_conv_libfuncs (convert_optab tab, const char *opname,
        *p++ = '2';
        *p = '\0';
 
-       tab->handlers[widening ? nmode : wmode]
-                    [widening ? wmode : nmode].libfunc
+       tab->handlers[widening ? wmode : nmode]
+                    [widening ? nmode : wmode].libfunc
          = init_one_libfunc (ggc_alloc_string (libfunc_name,
                                                p - libfunc_name));
       }
@@ -5219,6 +5337,8 @@ init_optabs (void)
   udivmod_optab = init_optab (UNKNOWN);
   smod_optab = init_optab (MOD);
   umod_optab = init_optab (UMOD);
+  fmod_optab = init_optab (UNKNOWN);
+  drem_optab = init_optab (UNKNOWN);
   ftrunc_optab = init_optab (UNKNOWN);
   and_optab = init_optab (AND);
   ior_optab = init_optab (IOR);
@@ -5269,10 +5389,21 @@ init_optabs (void)
   round_optab = init_optab (UNKNOWN);
   btrunc_optab = init_optab (UNKNOWN);
   nearbyint_optab = init_optab (UNKNOWN);
+  sincos_optab = init_optab (UNKNOWN);
   sin_optab = init_optab (UNKNOWN);
+  asin_optab = init_optab (UNKNOWN);
   cos_optab = init_optab (UNKNOWN);
+  acos_optab = init_optab (UNKNOWN);
   exp_optab = init_optab (UNKNOWN);
+  exp10_optab = init_optab (UNKNOWN);
+  exp2_optab = init_optab (UNKNOWN);
+  expm1_optab = init_optab (UNKNOWN);
+  logb_optab = init_optab (UNKNOWN);
+  ilogb_optab = init_optab (UNKNOWN);
   log_optab = init_optab (UNKNOWN);
+  log10_optab = init_optab (UNKNOWN);
+  log2_optab = init_optab (UNKNOWN);
+  log1p_optab = init_optab (UNKNOWN);
   tan_optab = init_optab (UNKNOWN);
   atan_optab = init_optab (UNKNOWN);
   strlen_optab = init_optab (UNKNOWN);
@@ -5281,6 +5412,9 @@ init_optabs (void)
   cstore_optab = init_optab (UNKNOWN);
   push_optab = init_optab (UNKNOWN);
 
+  vec_extract_optab = init_optab (UNKNOWN);
+  vec_set_optab = init_optab (UNKNOWN);
+  vec_init_optab = init_optab (UNKNOWN);
   /* Conversions.  */
   sext_optab = init_convert_optab (SIGN_EXTEND);
   zext_optab = init_convert_optab (ZERO_EXTEND);
@@ -5381,7 +5515,7 @@ init_optabs (void)
     abs_optab->handlers[TYPE_MODE (complex_double_type_node)].libfunc
       = init_one_libfunc ("cabs");
 
-  /* The ffs function op[1erates on `int'.  */
+  /* The ffs function operates on `int'.  */
   ffs_optab->handlers[(int) mode_for_size (INT_TYPE_SIZE, MODE_INT, 0)].libfunc
     = init_one_libfunc ("ffs");