OSDN Git Service

* config/rs6000/dfp.md: New file.
[pf3gnuchains/gcc-fork.git] / gcc / config / rs6000 / rs6000.c
index b9614b8..f21d5f1 100644 (file)
@@ -1131,11 +1131,11 @@ rs6000_hard_regno_mode_ok (int regno, enum machine_mode mode)
     return INT_REGNO_P (regno + HARD_REGNO_NREGS (regno, mode) - 1);
 
   /* The float registers can only hold floating modes and DImode.
-     This also excludes decimal float modes.  */
+     This excludes the 32-bit decimal float mode for now.  */
   if (FP_REGNO_P (regno))
     return
       (SCALAR_FLOAT_MODE_P (mode)
-       && !DECIMAL_FLOAT_MODE_P (mode)
+       && mode != SDmode
        && FP_REGNO_P (regno + HARD_REGNO_NREGS (regno, mode) - 1))
       || (GET_MODE_CLASS (mode) == MODE_INT
          && GET_MODE_SIZE (mode) == UNITS_PER_FP_WORD);
@@ -2314,7 +2314,10 @@ num_insns_constant (rtx op, enum machine_mode mode)
            REAL_VALUE_TYPE rv;
 
            REAL_VALUE_FROM_CONST_DOUBLE (rv, op);
-           REAL_VALUE_TO_TARGET_DOUBLE (rv, l);
+           if (DECIMAL_FLOAT_MODE_P (mode))
+             REAL_VALUE_TO_TARGET_DECIMAL64 (rv, l);
+           else
+             REAL_VALUE_TO_TARGET_DOUBLE (rv, l);
            high = l[WORDS_BIG_ENDIAN == 0];
            low  = l[WORDS_BIG_ENDIAN != 0];
          }
@@ -3054,6 +3057,7 @@ rs6000_legitimate_offset_address_p (enum machine_mode mode, rtx x, int strict)
       return SPE_CONST_OFFSET_OK (offset);
 
     case DFmode:
+    case DDmode:
       if (TARGET_E500_DOUBLE)
        return SPE_CONST_OFFSET_OK (offset);
 
@@ -3066,7 +3070,7 @@ rs6000_legitimate_offset_address_p (enum machine_mode mode, rtx x, int strict)
       if (TARGET_E500_DOUBLE)
        return SPE_CONST_OFFSET_OK (offset);
 
-      if (mode == DFmode || !TARGET_POWERPC64)
+      if (mode == DFmode || mode == DDmode || !TARGET_POWERPC64)
        extra = 4;
       else if (offset & 3)
        return false;
@@ -3078,7 +3082,8 @@ rs6000_legitimate_offset_address_p (enum machine_mode mode, rtx x, int strict)
                && SPE_CONST_OFFSET_OK (offset + 8));
 
     case TImode:
-      if (mode == TFmode || !TARGET_POWERPC64)
+    case TDmode:
+      if (mode == TFmode || mode == TDmode || !TARGET_POWERPC64)
        extra = 12;
       else if (offset & 3)
        return false;
@@ -3233,8 +3238,9 @@ rs6000_legitimize_address (rtx x, rtx oldx ATTRIBUTE_UNUSED,
           && GET_MODE_NUNITS (mode) == 1
           && ((TARGET_HARD_FLOAT && TARGET_FPRS)
               || TARGET_POWERPC64
-              || (((mode != DImode && mode != DFmode) || TARGET_E500_DOUBLE)
-                  && mode != TFmode))
+              || (((mode != DImode && mode != DFmode && mode != DDmode)
+                   || TARGET_E500_DOUBLE)
+                  && mode != TFmode && mode != TDmode))
           && (TARGET_POWERPC64 || mode != DImode)
           && mode != TImode)
     {
@@ -3255,6 +3261,7 @@ rs6000_legitimize_address (rtx x, rtx oldx ATTRIBUTE_UNUSED,
     }
   else if (SPE_VECTOR_MODE (mode)
           || (TARGET_E500_DOUBLE && (mode == DFmode || mode == TFmode
+                                     || mode == DDmode || mode == TDmode
                                      || mode == DImode)))
     {
       if (mode == DImode)
@@ -3700,10 +3707,11 @@ rs6000_legitimize_reload_address (rtx x, enum machine_mode mode,
       && DEFAULT_ABI == ABI_V4
       && !flag_pic
 #endif
-      /* Don't do this for TFmode, since the result isn't offsettable.
+      /* Don't do this for TFmode or TDmode, since the result isn't offsettable.
         The same goes for DImode without 64-bit gprs and DFmode
         without fprs.  */
       && mode != TFmode
+      && mode != TDmode
       && (mode != DImode || TARGET_POWERPC64)
       && (mode != DFmode || TARGET_POWERPC64
          || (TARGET_FPRS && TARGET_HARD_FLOAT)))
@@ -3773,8 +3781,8 @@ rs6000_legitimize_reload_address (rtx x, enum machine_mode mode,
    word aligned.
 
    For modes spanning multiple registers (DFmode in 32-bit GPRs,
-   32-bit DImode, TImode, TFmode), indexed addressing cannot be used because
-   adjacent memory cells are accessed by adding word-sized offsets
+   32-bit DImode, TImode, TFmode, TDmode), indexed addressing cannot be used
+   because adjacent memory cells are accessed by adding word-sized offsets
    during assembly output.  */
 int
 rs6000_legitimate_address (enum machine_mode mode, rtx x, int reg_ok_strict)
@@ -3795,6 +3803,7 @@ rs6000_legitimate_address (enum machine_mode mode, rtx x, int reg_ok_strict)
       && !ALTIVEC_VECTOR_MODE (mode)
       && !SPE_VECTOR_MODE (mode)
       && mode != TFmode
+      && mode != TDmode
       /* Restrict addressing for DI because of our SUBREG hackery.  */
       && !(TARGET_E500_DOUBLE && (mode == DFmode || mode == TFmode
                                  || mode == DImode))
@@ -3817,6 +3826,7 @@ rs6000_legitimate_address (enum machine_mode mode, rtx x, int reg_ok_strict)
     return 1;
   if (mode != TImode
       && mode != TFmode
+      && mode != TDmode
       && ((TARGET_HARD_FLOAT && TARGET_FPRS)
          || TARGET_POWERPC64
          || ((mode != DFmode || TARGET_E500_DOUBLE) && mode != TFmode))
@@ -4173,7 +4183,7 @@ rs6000_emit_set_long_const (rtx dest, HOST_WIDE_INT c1, HOST_WIDE_INT c2)
 }
 
 /* Helper for the following.  Get rid of [r+r] memory refs
-   in cases where it won't work (TImode, TFmode).  */
+   in cases where it won't work (TImode, TFmode, TDmode).  */
 
 static void
 rs6000_eliminate_indexed_memrefs (rtx operands[2])
@@ -4337,10 +4347,12 @@ rs6000_emit_move (rtx dest, rtx source, enum machine_mode mode)
       break;
 
     case TFmode:
+    case TDmode:
       rs6000_eliminate_indexed_memrefs (operands);
       /* fall through */
 
     case DFmode:
+    case DDmode:
     case SFmode:
       if (CONSTANT_P (operands[1])
          && ! easy_fp_constant (operands[1], mode))
@@ -4552,7 +4564,7 @@ rs6000_emit_move (rtx dest, rtx source, enum machine_mode mode)
 /* Nonzero if we can use a floating-point register to pass this arg.  */
 #define USE_FP_FOR_ARG_P(CUM,MODE,TYPE)                \
   (SCALAR_FLOAT_MODE_P (MODE)                  \
-   && !DECIMAL_FLOAT_MODE_P (MODE)             \
+   && (MODE) != SDmode                         \
    && (CUM)->fregno <= FP_ARG_MAX_REG          \
    && TARGET_HARD_FLOAT && TARGET_FPRS)
 
@@ -4794,7 +4806,7 @@ function_arg_boundary (enum machine_mode mode, tree type)
       && (GET_MODE_SIZE (mode) == 8
          || (TARGET_HARD_FLOAT
              && TARGET_FPRS
-             && mode == TFmode)))
+             && (mode == TFmode || mode == TDmode))))
     return 64;
   else if (SPE_VECTOR_MODE (mode)
           || (type && TREE_CODE (type) == VECTOR_TYPE
@@ -5032,14 +5044,20 @@ function_arg_advance (CUMULATIVE_ARGS *cum, enum machine_mode mode,
     {
       if (TARGET_HARD_FLOAT && TARGET_FPRS
          && (mode == SFmode || mode == DFmode
+             || mode == DDmode || mode == TDmode
              || (mode == TFmode && !TARGET_IEEEQUAD)))
        {
-         if (cum->fregno + (mode == TFmode ? 1 : 0) <= FP_ARG_V4_MAX_REG)
+         /* _Decimal128 must use an even/odd register pair.  */
+         if (mode == TDmode && cum->fregno % 2)
+           cum->fregno++;
+
+         if (cum->fregno + (mode == TFmode || mode == TDmode ? 1 : 0)
+             <= FP_ARG_V4_MAX_REG)
            cum->fregno += (GET_MODE_SIZE (mode) + 7) >> 3;
          else
            {
              cum->fregno = FP_ARG_V4_MAX_REG + 1;
-             if (mode == DFmode || mode == TFmode)
+             if (mode == DFmode || mode == TFmode || mode == DDmode || mode == TDmode)
                cum->words += cum->words & 1;
              cum->words += rs6000_arg_size (mode, type);
            }
@@ -5091,7 +5109,7 @@ function_arg_advance (CUMULATIVE_ARGS *cum, enum machine_mode mode,
       cum->words = align_words + n_words;
 
       if (SCALAR_FLOAT_MODE_P (mode)
-         && !DECIMAL_FLOAT_MODE_P (mode)
+         && mode != SDmode
          && TARGET_HARD_FLOAT && TARGET_FPRS)
        cum->fregno += (GET_MODE_SIZE (mode) + 7) >> 3;
 
@@ -5310,7 +5328,7 @@ rs6000_darwin64_record_arg_recurse (CUMULATIVE_ARGS *cum, tree type,
              = gen_rtx_EXPR_LIST (VOIDmode,
                                   gen_rtx_REG (mode, cum->fregno++),
                                   GEN_INT (bitpos / BITS_PER_UNIT));
-           if (mode == TFmode)
+           if (mode == TFmode || mode == TDmode)
              cum->fregno++;
          }
        else if (cum->named && USE_ALTIVEC_FOR_ARG_P (cum, mode, ftype, 1))
@@ -5571,8 +5589,10 @@ function_arg (CUMULATIVE_ARGS *cum, enum machine_mode mode,
   else if (TARGET_SPE_ABI && TARGET_SPE
           && (SPE_VECTOR_MODE (mode)
               || (TARGET_E500_DOUBLE && (mode == DFmode
+                                         || mode == DDmode
                                          || mode == DCmode
                                          || mode == TFmode
+                                         || mode == TDmode
                                          || mode == TCmode))))
     return rs6000_spe_function_arg (cum, mode, type);
 
@@ -5580,9 +5600,15 @@ function_arg (CUMULATIVE_ARGS *cum, enum machine_mode mode,
     {
       if (TARGET_HARD_FLOAT && TARGET_FPRS
          && (mode == SFmode || mode == DFmode
-             || (mode == TFmode && !TARGET_IEEEQUAD)))
+             || (mode == TFmode && !TARGET_IEEEQUAD)
+             || mode == DDmode || mode == TDmode))
        {
-         if (cum->fregno + (mode == TFmode ? 1 : 0) <= FP_ARG_V4_MAX_REG)
+         /* _Decimal128 must use an even/odd register pair.  */
+         if (mode == TDmode && cum->fregno % 2)
+           cum->fregno++;
+
+         if (cum->fregno + (mode == TFmode || mode == TDmode ? 1 : 0)
+             <= FP_ARG_V4_MAX_REG)
            return gen_rtx_REG (mode, cum->fregno);
          else
            return NULL_RTX;
@@ -5625,10 +5651,11 @@ function_arg (CUMULATIVE_ARGS *cum, enum machine_mode mode,
            {
              /* Currently, we only ever need one reg here because complex
                 doubles are split.  */
-             gcc_assert (cum->fregno == FP_ARG_MAX_REG && fmode == TFmode);
+             gcc_assert (cum->fregno == FP_ARG_MAX_REG
+                         && (fmode == TFmode || fmode == TDmode));
 
-             /* Long double split over regs and memory.  */
-             fmode = DFmode;
+             /* Long double or _Decimal128 split over regs and memory.  */
+             fmode = DECIMAL_FLOAT_MODE_P (fmode) ? DDmode : DFmode;
            }
 
          /* Do we also need to pass this arg in the parameter save
@@ -6183,6 +6210,7 @@ rs6000_gimplify_va_arg (tree valist, tree type, tree *pre_p, tree *post_p)
   tree lab_false, lab_over, addr;
   int align;
   tree ptrtype = build_pointer_type (type);
+  int regalign = 0;
 
   if (pass_by_reference (NULL, TYPE_MODE (type), type, false))
     {
@@ -6239,7 +6267,9 @@ rs6000_gimplify_va_arg (tree valist, tree type, tree *pre_p, tree *post_p)
   if (TARGET_HARD_FLOAT && TARGET_FPRS
       && (TYPE_MODE (type) == SFmode
          || TYPE_MODE (type) == DFmode
-         || TYPE_MODE (type) == TFmode))
+         || TYPE_MODE (type) == TFmode
+         || TYPE_MODE (type) == DDmode
+         || TYPE_MODE (type) == TDmode))
     {
       /* FP args go in FP registers, if present.  */
       reg = fpr;
@@ -6280,10 +6310,19 @@ rs6000_gimplify_va_arg (tree valist, tree type, tree *pre_p, tree *post_p)
       u = reg;
       if (n_reg == 2 && reg == gpr)
        {
+         regalign = 1;
          u = build2 (BIT_AND_EXPR, TREE_TYPE (reg), reg,
                     size_int (n_reg - 1));
          u = build2 (POSTINCREMENT_EXPR, TREE_TYPE (reg), reg, u);
        }
+      /* _Decimal128 is passed in even/odd fpr pairs; the stored
+        reg number is 0 for f1, so we want to make it odd.  */
+      else if (reg == fpr && TYPE_MODE (type) == TDmode)
+       {
+         regalign = 1;
+         t = build2 (BIT_IOR_EXPR, TREE_TYPE (reg), reg, size_int (1));
+         u = build2 (MODIFY_EXPR, void_type_node, reg, t);
+       }
 
       t = fold_convert (TREE_TYPE (reg), size_int (8 - n_reg + 1));
       t = build2 (GE_EXPR, boolean_type_node, u, t);
@@ -6309,10 +6348,10 @@ rs6000_gimplify_va_arg (tree valist, tree type, tree *pre_p, tree *post_p)
       t = build1 (LABEL_EXPR, void_type_node, lab_false);
       append_to_statement_list (t, pre_p);
 
-      if ((n_reg == 2 && reg != gpr) || n_reg > 2)
+      if ((n_reg == 2 && !regalign) || n_reg > 2)
        {
          /* Ensure that we don't find any more args in regs.
-            Alignment has taken care of the n_reg == 2 gpr case.  */
+            Alignment has taken care of for special cases.  */
          t = build2 (GIMPLE_MODIFY_STMT, TREE_TYPE (reg), reg, size_int (8));
          gimplify_and_add (t, pre_p);
        }
@@ -12923,7 +12962,7 @@ rs6000_split_multireg_move (rtx dst, rtx src)
   mode = GET_MODE (dst);
   nregs = hard_regno_nregs[reg][mode];
   if (FP_REGNO_P (reg))
-    reg_mode = DFmode;
+    reg_mode = DECIMAL_FLOAT_MODE_P (mode) ? DDmode : DFmode;
   else if (ALTIVEC_REGNO_P (reg))
     reg_mode = V16QImode;
   else if (TARGET_E500_DOUBLE && mode == TFmode)
@@ -15857,7 +15896,9 @@ rs6000_output_function_epilogue (FILE *file,
                          break;
 
                        case DFmode:
+                       case DDmode:
                        case TFmode:
+                       case TDmode:
                          bits = 0x3;
                          break;
 
@@ -20196,7 +20237,7 @@ rs6000_register_move_cost (enum machine_mode mode,
 
   /* Moving between two similar registers is just one instruction.  */
   else if (reg_classes_intersect_p (to, from))
-    return mode == TFmode ? 4 : 2;
+    return (mode == TFmode || mode == TDmode) ? 4 : 2;
 
   /* Everything else has to go through GENERAL_REGS.  */
   else
@@ -20529,7 +20570,28 @@ rs6000_function_value (tree valtype, tree func ATTRIBUTE_UNUSED)
     mode = TYPE_MODE (valtype);
 
   if (DECIMAL_FLOAT_MODE_P (mode))
-    regno = GP_ARG_RETURN;
+    {
+      if (TARGET_HARD_FLOAT && TARGET_FPRS)
+       {
+         switch (mode)
+           {
+           default:
+             gcc_unreachable ();
+           case SDmode:
+             regno = GP_ARG_RETURN;
+             break;
+           case DDmode:
+             regno = FP_ARG_RETURN;
+             break;
+           case TDmode:
+             /* Use f2:f3 specified by the ABI.  */
+             regno = FP_ARG_RETURN + 1;
+             break;
+           }
+       }
+      else
+       regno = GP_ARG_RETURN;
+    }
   else if (SCALAR_FLOAT_TYPE_P (valtype) && TARGET_HARD_FLOAT && TARGET_FPRS)
     regno = FP_ARG_RETURN;
   else if (TREE_CODE (valtype) == COMPLEX_TYPE
@@ -20571,7 +20633,28 @@ rs6000_libcall_value (enum machine_mode mode)
     }
 
   if (DECIMAL_FLOAT_MODE_P (mode))
-    regno = GP_ARG_RETURN;
+    {
+      if (TARGET_HARD_FLOAT && TARGET_FPRS)
+       {
+         switch (mode)
+           {
+           default:
+             gcc_unreachable ();
+           case SDmode:
+             regno = GP_ARG_RETURN;
+             break;
+           case DDmode:
+             regno = FP_ARG_RETURN;
+             break;
+           case TDmode:
+             /* Use f2:f3 specified by the ABI.  */
+             regno = FP_ARG_RETURN + 1;
+             break;
+           }
+       }
+      else
+       regno = GP_ARG_RETURN;
+    }
   else if (SCALAR_FLOAT_MODE_P (mode)
           && TARGET_HARD_FLOAT && TARGET_FPRS)
     regno = FP_ARG_RETURN;