OSDN Git Service

PR c++/20175
[pf3gnuchains/gcc-fork.git] / gcc / optabs.c
index a4b85b1..9525620 100644 (file)
@@ -2657,45 +2657,100 @@ expand_abs (enum machine_mode mode, rtx op0, rtx target,
   return target;
 }
 
-/* Expand the C99 copysign operation.  OP0 and OP1 must be the same 
-   scalar floating point mode.  Return NULL if we do not know how to
-   expand the operation inline.  */
+/* A subroutine of expand_copysign, perform the copysign operation using the
+   abs and neg primitives advertised to exist on the target.  The assumption
+   is that we have a split register file, and leaving op0 in fp registers,
+   and not playing with subregs so much, will help the register allocator.  */
 
 rtx
-expand_copysign (rtx op0, rtx op1, rtx target)
+expand_copysign_absneg (enum machine_mode mode, rtx op0, rtx op1, rtx target,
+                       int bitpos, bool op0_is_abs)
 {
-  enum machine_mode mode = GET_MODE (op0);
-  const struct real_format *fmt;
   enum machine_mode imode;
-  int bitpos, word, nwords, i, have_abs;
   HOST_WIDE_INT hi, lo;
-  rtx temp, insns;
+  int word;
+  rtx label;
 
-  gcc_assert (SCALAR_FLOAT_MODE_P (mode));
-  gcc_assert (GET_MODE (op1) == mode);
-
-  /* First try to do it with a special instruction.  */
-  temp = expand_binop (mode, copysign_optab, op0, op1,
-                      target, 0, OPTAB_DIRECT);
-  if (temp)
-    return temp;
+  if (target == op1)
+    target = NULL_RTX;
 
-  fmt = REAL_MODE_FORMAT (mode);
-  if (fmt == NULL || !fmt->has_signed_zero)
-    return NULL_RTX;
+  if (!op0_is_abs)
+    {
+      op0 = expand_unop (mode, abs_optab, op0, target, 0);
+      if (op0 == NULL)
+       return NULL_RTX;
+      target = op0;
+    }
+  else
+    {
+      if (target == NULL_RTX)
+        target = copy_to_reg (op0);
+      else
+       emit_move_insn (target, op0);
+    }
 
-  bitpos = fmt->signbit;
-  if (bitpos < 0)
-    return NULL_RTX;
+  if (GET_MODE_SIZE (mode) <= UNITS_PER_WORD)
+    {
+      imode = int_mode_for_mode (mode);
+      if (imode == BLKmode)
+       return NULL_RTX;
+      op1 = gen_lowpart (imode, op1);
+    }
+  else
+    {
+      imode = word_mode;
+      if (FLOAT_WORDS_BIG_ENDIAN)
+       word = (GET_MODE_BITSIZE (mode) - bitpos) / BITS_PER_WORD;
+      else
+       word = bitpos / BITS_PER_WORD;
+      bitpos = bitpos % BITS_PER_WORD;
+      op1 = operand_subword_force (op1, word, mode);
+    }
 
-  have_abs = false;
-  if (GET_CODE (op0) == CONST_DOUBLE)
+  if (bitpos < HOST_BITS_PER_WIDE_INT)
     {
-      if (real_isneg (CONST_DOUBLE_REAL_VALUE (op0)))
-       op0 = simplify_unary_operation (ABS, mode, op0, mode);
-      have_abs = true;
+      hi = 0;
+      lo = (HOST_WIDE_INT) 1 << bitpos;
+    }
+  else
+    {
+      hi = (HOST_WIDE_INT) 1 << (bitpos - HOST_BITS_PER_WIDE_INT);
+      lo = 0;
     }
 
+  op1 = expand_binop (imode, and_optab, op1,
+                     immed_double_const (lo, hi, imode),
+                     NULL_RTX, 1, OPTAB_LIB_WIDEN);
+
+  label = gen_label_rtx ();
+  emit_cmp_and_jump_insns (op1, const0_rtx, EQ, NULL_RTX, imode, 1, label);
+
+  if (GET_CODE (op0) == CONST_DOUBLE)
+    op0 = simplify_unary_operation (NEG, mode, op0, mode);
+  else
+    op0 = expand_unop (mode, neg_optab, op0, target, 0);
+  if (op0 != target)
+    emit_move_insn (target, op0);
+
+  emit_label (label);
+
+  return target;
+}
+
+
+/* A subroutine of expand_copysign, perform the entire copysign operation
+   with integer bitmasks.  BITPOS is the position of the sign bit; OP0_IS_ABS
+   is true if op0 is known to have its sign bit clear.  */
+
+static rtx
+expand_copysign_bit (enum machine_mode mode, rtx op0, rtx op1, rtx target,
+                    int bitpos, bool op0_is_abs)
+{
+  enum machine_mode imode;
+  HOST_WIDE_INT hi, lo;
+  int word, nwords, i;
+  rtx temp, insns;
+
   if (GET_MODE_SIZE (mode) <= UNITS_PER_WORD)
     {
       imode = int_mode_for_mode (mode);
@@ -2741,7 +2796,7 @@ expand_copysign (rtx op0, rtx op1, rtx target)
        
          if (i == word)
            {
-             if (!have_abs)
+             if (!op0_is_abs)
                op0_piece = expand_binop (imode, and_optab, op0_piece,
                                          immed_double_const (~lo, ~hi, imode),
                                          NULL_RTX, 1, OPTAB_LIB_WIDEN);
@@ -2772,7 +2827,7 @@ expand_copysign (rtx op0, rtx op1, rtx target)
                          NULL_RTX, 1, OPTAB_LIB_WIDEN);
 
       op0 = gen_lowpart (imode, op0);
-      if (!have_abs)
+      if (!op0_is_abs)
        op0 = expand_binop (imode, and_optab, op0,
                            immed_double_const (~lo, ~hi, imode),
                            NULL_RTX, 1, OPTAB_LIB_WIDEN);
@@ -2784,6 +2839,57 @@ expand_copysign (rtx op0, rtx op1, rtx target)
 
   return target;
 }
+
+/* Expand the C99 copysign operation.  OP0 and OP1 must be the same 
+   scalar floating point mode.  Return NULL if we do not know how to
+   expand the operation inline.  */
+
+rtx
+expand_copysign (rtx op0, rtx op1, rtx target)
+{
+  enum machine_mode mode = GET_MODE (op0);
+  const struct real_format *fmt;
+  int bitpos;
+  bool op0_is_abs;
+  rtx temp;
+
+  gcc_assert (SCALAR_FLOAT_MODE_P (mode));
+  gcc_assert (GET_MODE (op1) == mode);
+
+  /* First try to do it with a special instruction.  */
+  temp = expand_binop (mode, copysign_optab, op0, op1,
+                      target, 0, OPTAB_DIRECT);
+  if (temp)
+    return temp;
+
+  fmt = REAL_MODE_FORMAT (mode);
+  if (fmt == NULL || !fmt->has_signed_zero)
+    return NULL_RTX;
+
+  bitpos = fmt->signbit;
+  if (bitpos < 0)
+    return NULL_RTX;
+
+  op0_is_abs = false;
+  if (GET_CODE (op0) == CONST_DOUBLE)
+    {
+      if (real_isneg (CONST_DOUBLE_REAL_VALUE (op0)))
+       op0 = simplify_unary_operation (ABS, mode, op0, mode);
+      op0_is_abs = true;
+    }
+
+  if (GET_CODE (op0) == CONST_DOUBLE
+      || (neg_optab->handlers[mode].insn_code != CODE_FOR_nothing
+          && abs_optab->handlers[mode].insn_code != CODE_FOR_nothing))
+    {
+      temp = expand_copysign_absneg (mode, op0, op1, target,
+                                    bitpos, op0_is_abs);
+      if (temp)
+       return temp;
+    }
+
+  return expand_copysign_bit (mode, op0, op1, target, bitpos, op0_is_abs);
+}
 \f
 /* Generate an instruction whose insn-code is INSN_CODE,
    with two operands: an output TARGET and an input OP0.
@@ -4921,6 +5027,7 @@ init_optabs (void)
   exp10_optab = init_optab (UNKNOWN);
   exp2_optab = init_optab (UNKNOWN);
   expm1_optab = init_optab (UNKNOWN);
+  ldexp_optab = init_optab (UNKNOWN);
   logb_optab = init_optab (UNKNOWN);
   ilogb_optab = init_optab (UNKNOWN);
   log_optab = init_optab (UNKNOWN);
@@ -4943,6 +5050,8 @@ init_optabs (void)
   vec_realign_load_optab = init_optab (UNKNOWN);
   movmisalign_optab = init_optab (UNKNOWN);
 
+  powi_optab = init_optab (UNKNOWN);
+
   /* Conversions.  */
   sext_optab = init_convert_optab (SIGN_EXTEND);
   zext_optab = init_convert_optab (ZERO_EXTEND);
@@ -5029,6 +5138,8 @@ init_optabs (void)
   init_floating_libfuncs (le_optab, "le", '2');
   init_floating_libfuncs (unord_optab, "unord", '2');
 
+  init_floating_libfuncs (powi_optab, "powi", '2');
+
   /* Conversions.  */
   init_interclass_conv_libfuncs (sfloat_optab, "float",
                                 MODE_INT, MODE_FLOAT);