* config/rs6000/dfp.md: New file.
authorbergner <bergner@138bc75d-0d04-0410-961f-82ee72b054a4>
Fri, 2 Mar 2007 15:57:08 +0000 (15:57 +0000)
committerbergner <bergner@138bc75d-0d04-0410-961f-82ee72b054a4>
Fri, 2 Mar 2007 15:57:08 +0000 (15:57 +0000)
* config/rs6000/rs6000.md: Include dfp.md.
(add<mode>3_internal1): Disable for DECIMAL_FLOAT_MODE_P operands.
* config/rs6000/rs6000.c (rs6000_hard_regno_mode_ok): Handle DDmode
and TDmode decimal float modes in FP registers.
(num_insns_constant): Likewise.
(rs6000_legitimate_offset_address_p): Likewise.
(rs6000_legitimize_address): Likewise.
(rs6000_legitimize_reload_address): Likewise.
(rs6000_legitimate_address): Likewise.
(rs6000_emit_move): Likewise.
(function_arg_boundary): Likewise.
(function_arg_advance): Likewise.
(rs6000_darwin64_record_arg_recurse): Likewise.
(function_arg): Likewise.
(rs6000_gimplify_va_arg): Likewise.
(rs6000_split_multireg_move): Likewise.
(rs6000_output_function_epilogue): Likewise.
(rs6000_output_function_epilogue): Likewise.
(rs6000_register_move_cost): Likewise.
(rs6000_function_value): Likewise.
(rs6000_libcall_value): Likewise.

git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@122477 138bc75d-0d04-0410-961f-82ee72b054a4

gcc/ChangeLog
gcc/config/rs6000/dfp.md [new file with mode: 0644]
gcc/config/rs6000/rs6000.c
gcc/config/rs6000/rs6000.md

index cd50a3b..29df5e2 100644 (file)
@@ -1,3 +1,30 @@
+2007-03-02  Ben Elliston  <bje@au.ibm.com>
+           Peter Bergner  <bergner@vnet.ibm.com>
+           Janis Johnson  <janis187@us.ibm.com>
+
+       * config/rs6000/dfp.md: New file.
+       * config/rs6000/rs6000.md: Include dfp.md.
+       (add<mode>3_internal1): Disable for DECIMAL_FLOAT_MODE_P operands.
+       * config/rs6000/rs6000.c (rs6000_hard_regno_mode_ok): Handle DDmode
+       and TDmode decimal float modes in FP registers.
+       (num_insns_constant): Likewise.
+       (rs6000_legitimate_offset_address_p): Likewise.
+       (rs6000_legitimize_address): Likewise.
+       (rs6000_legitimize_reload_address): Likewise.
+       (rs6000_legitimate_address): Likewise.
+       (rs6000_emit_move): Likewise.
+       (function_arg_boundary): Likewise.
+       (function_arg_advance): Likewise.
+       (rs6000_darwin64_record_arg_recurse): Likewise.
+       (function_arg): Likewise.
+       (rs6000_gimplify_va_arg): Likewise.
+       (rs6000_split_multireg_move): Likewise.
+       (rs6000_output_function_epilogue): Likewise.
+       (rs6000_output_function_epilogue): Likewise.
+       (rs6000_register_move_cost): Likewise.
+       (rs6000_function_value): Likewise.
+       (rs6000_libcall_value): Likewise.
+
 2007-03-02  Richard Sandiford  <richard@codesourcery.com>
 
        * config/t-vxworks (LIMITS_H_TEST): Define to true for VxWorks.
diff --git a/gcc/config/rs6000/dfp.md b/gcc/config/rs6000/dfp.md
new file mode 100644 (file)
index 0000000..28f7b93
--- /dev/null
@@ -0,0 +1,316 @@
+;; Decimal Floating Point (DFP) patterns.
+;; Copyright (C) 2007
+;; Free Software Foundation, Inc.
+;; Contributed by Ben Elliston (bje@au.ibm.com) and Peter Bergner
+;; (bergner@vnet.ibm.com).
+
+;; 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, 51 Franklin Street, Fifth Floor, Boston,
+;; MA 02110-1301, USA.
+
+(define_expand "movdd"
+  [(set (match_operand:DD 0 "nonimmediate_operand" "")
+       (match_operand:DD 1 "any_operand" ""))]
+  ""
+  "{ rs6000_emit_move (operands[0], operands[1], DDmode); DONE; }")
+
+(define_split
+  [(set (match_operand:DD 0 "gpc_reg_operand" "")
+       (match_operand:DD 1 "const_int_operand" ""))]
+  "! TARGET_POWERPC64 && reload_completed
+   && ((GET_CODE (operands[0]) == REG && REGNO (operands[0]) <= 31)
+       || (GET_CODE (operands[0]) == SUBREG
+          && GET_CODE (SUBREG_REG (operands[0])) == REG
+          && REGNO (SUBREG_REG (operands[0])) <= 31))"
+  [(set (match_dup 2) (match_dup 4))
+   (set (match_dup 3) (match_dup 1))]
+  "
+{
+  int endian = (WORDS_BIG_ENDIAN == 0);
+  HOST_WIDE_INT value = INTVAL (operands[1]);
+
+  operands[2] = operand_subword (operands[0], endian, 0, DDmode);
+  operands[3] = operand_subword (operands[0], 1 - endian, 0, DDmode);
+#if HOST_BITS_PER_WIDE_INT == 32
+  operands[4] = (value & 0x80000000) ? constm1_rtx : const0_rtx;
+#else
+  operands[4] = GEN_INT (value >> 32);
+  operands[1] = GEN_INT (((value & 0xffffffff) ^ 0x80000000) - 0x80000000);
+#endif
+}")
+
+(define_split
+  [(set (match_operand:DD 0 "gpc_reg_operand" "")
+       (match_operand:DD 1 "const_double_operand" ""))]
+  "! TARGET_POWERPC64 && reload_completed
+   && ((GET_CODE (operands[0]) == REG && REGNO (operands[0]) <= 31)
+       || (GET_CODE (operands[0]) == SUBREG
+          && GET_CODE (SUBREG_REG (operands[0])) == REG
+          && REGNO (SUBREG_REG (operands[0])) <= 31))"
+  [(set (match_dup 2) (match_dup 4))
+   (set (match_dup 3) (match_dup 5))]
+  "
+{
+  int endian = (WORDS_BIG_ENDIAN == 0);
+  long l[2];
+  REAL_VALUE_TYPE rv;
+
+  REAL_VALUE_FROM_CONST_DOUBLE (rv, operands[1]);
+  REAL_VALUE_TO_TARGET_DECIMAL64 (rv, l);
+
+  operands[2] = operand_subword (operands[0], endian, 0, DDmode);
+  operands[3] = operand_subword (operands[0], 1 - endian, 0, DDmode);
+  operands[4] = gen_int_mode (l[endian], SImode);
+  operands[5] = gen_int_mode (l[1 - endian], SImode);
+}")
+
+(define_split
+  [(set (match_operand:DD 0 "gpc_reg_operand" "")
+       (match_operand:DD 1 "const_double_operand" ""))]
+  "TARGET_POWERPC64 && reload_completed
+   && ((GET_CODE (operands[0]) == REG && REGNO (operands[0]) <= 31)
+       || (GET_CODE (operands[0]) == SUBREG
+          && GET_CODE (SUBREG_REG (operands[0])) == REG
+          && REGNO (SUBREG_REG (operands[0])) <= 31))"
+  [(set (match_dup 2) (match_dup 3))]
+  "
+{
+  int endian = (WORDS_BIG_ENDIAN == 0);
+  long l[2];
+  REAL_VALUE_TYPE rv;
+#if HOST_BITS_PER_WIDE_INT >= 64
+  HOST_WIDE_INT val;
+#endif
+
+  REAL_VALUE_FROM_CONST_DOUBLE (rv, operands[1]);
+  REAL_VALUE_TO_TARGET_DECIMAL64 (rv, l);
+
+  operands[2] = gen_lowpart (DImode, operands[0]);
+  /* HIGHPART is lower memory address when WORDS_BIG_ENDIAN.  */
+#if HOST_BITS_PER_WIDE_INT >= 64
+  val = ((HOST_WIDE_INT)(unsigned long)l[endian] << 32
+        | ((HOST_WIDE_INT)(unsigned long)l[1 - endian]));
+
+  operands[3] = gen_int_mode (val, DImode);
+#else
+  operands[3] = immed_double_const (l[1 - endian], l[endian], DImode);
+#endif
+}")
+
+;; Don't have reload use general registers to load a constant.  First,
+;; it might not work if the output operand is the equivalent of
+;; a non-offsettable memref, but also it is less efficient than loading
+;; the constant into an FP register, since it will probably be used there.
+;; The "??" is a kludge until we can figure out a more reasonable way
+;; of handling these non-offsettable values.
+(define_insn "*movdd_hardfloat32"
+  [(set (match_operand:DD 0 "nonimmediate_operand" "=!r,??r,m,f,f,m,!r,!r,!r")
+       (match_operand:DD 1 "input_operand" "r,m,r,f,m,f,G,H,F"))]
+  "! TARGET_POWERPC64 && TARGET_HARD_FLOAT && TARGET_FPRS
+   && (gpc_reg_operand (operands[0], DDmode)
+       || gpc_reg_operand (operands[1], DDmode))"
+  "*
+{
+  switch (which_alternative)
+    {
+    default:
+      gcc_unreachable ();
+    case 0:
+      /* We normally copy the low-numbered register first.  However, if
+        the first register operand 0 is the same as the second register
+        of operand 1, we must copy in the opposite order.  */
+      if (REGNO (operands[0]) == REGNO (operands[1]) + 1)
+       return \"mr %L0,%L1\;mr %0,%1\";
+      else
+       return \"mr %0,%1\;mr %L0,%L1\";
+    case 1:
+      if (rs6000_offsettable_memref_p (operands[1])
+         || (GET_CODE (operands[1]) == MEM
+             && (GET_CODE (XEXP (operands[1], 0)) == LO_SUM
+                 || GET_CODE (XEXP (operands[1], 0)) == PRE_INC
+                 || GET_CODE (XEXP (operands[1], 0)) == PRE_DEC)))
+       {
+         /* If the low-address word is used in the address, we must load
+            it last.  Otherwise, load it first.  Note that we cannot have
+            auto-increment in that case since the address register is
+            known to be dead.  */
+         if (refers_to_regno_p (REGNO (operands[0]), REGNO (operands[0]) + 1,
+                                operands[1], 0))
+           return \"{l|lwz} %L0,%L1\;{l|lwz} %0,%1\";
+         else
+           return \"{l%U1|lwz%U1} %0,%1\;{l|lwz} %L0,%L1\";
+       }
+      else
+       {
+         rtx addreg;
+
+         addreg = find_addr_reg (XEXP (operands[1], 0));
+         if (refers_to_regno_p (REGNO (operands[0]),
+                                REGNO (operands[0]) + 1,
+                                operands[1], 0))
+           {
+             output_asm_insn (\"{cal|la} %0,4(%0)\", &addreg);
+             output_asm_insn (\"{lx|lwzx} %L0,%1\", operands);
+             output_asm_insn (\"{cal|la} %0,-4(%0)\", &addreg);
+             return \"{lx|lwzx} %0,%1\";
+           }
+         else
+           {
+             output_asm_insn (\"{lx|lwzx} %0,%1\", operands);
+             output_asm_insn (\"{cal|la} %0,4(%0)\", &addreg);
+             output_asm_insn (\"{lx|lwzx} %L0,%1\", operands);
+             output_asm_insn (\"{cal|la} %0,-4(%0)\", &addreg);
+             return \"\";
+           }
+       }
+    case 2:
+      if (rs6000_offsettable_memref_p (operands[0])
+         || (GET_CODE (operands[0]) == MEM
+             && (GET_CODE (XEXP (operands[0], 0)) == LO_SUM
+                 || GET_CODE (XEXP (operands[0], 0)) == PRE_INC
+                 || GET_CODE (XEXP (operands[0], 0)) == PRE_DEC)))
+       return \"{st%U0|stw%U0} %1,%0\;{st|stw} %L1,%L0\";
+      else
+       {
+         rtx addreg;
+
+         addreg = find_addr_reg (XEXP (operands[0], 0));
+         output_asm_insn (\"{stx|stwx} %1,%0\", operands);
+         output_asm_insn (\"{cal|la} %0,4(%0)\", &addreg);
+         output_asm_insn (\"{stx|stwx} %L1,%0\", operands);
+         output_asm_insn (\"{cal|la} %0,-4(%0)\", &addreg);
+         return \"\";
+       }
+    case 3:
+      return \"fmr %0,%1\";
+    case 4:
+      return \"lfd%U1%X1 %0,%1\";
+    case 5:
+      return \"stfd%U0%X0 %1,%0\";
+    case 6:
+    case 7:
+    case 8:
+      return \"#\";
+    }
+}"
+  [(set_attr "type" "two,load,store,fp,fpload,fpstore,*,*,*")
+   (set_attr "length" "8,16,16,4,4,4,8,12,16")])
+
+(define_insn "*movdd_softfloat32"
+  [(set (match_operand:DD 0 "nonimmediate_operand" "=r,r,m,r,r,r")
+       (match_operand:DD 1 "input_operand" "r,m,r,G,H,F"))]
+  "! TARGET_POWERPC64 && TARGET_SOFT_FLOAT
+   && (gpc_reg_operand (operands[0], DDmode)
+       || gpc_reg_operand (operands[1], DDmode))"
+  "*
+{
+  switch (which_alternative)
+    {
+    default:
+      gcc_unreachable ();
+    case 0:
+      /* We normally copy the low-numbered register first.  However, if
+        the first register operand 0 is the same as the second register of
+        operand 1, we must copy in the opposite order.  */
+      if (REGNO (operands[0]) == REGNO (operands[1]) + 1)
+       return \"mr %L0,%L1\;mr %0,%1\";
+      else
+       return \"mr %0,%1\;mr %L0,%L1\";
+    case 1:
+      /* If the low-address word is used in the address, we must load
+        it last.  Otherwise, load it first.  Note that we cannot have
+        auto-increment in that case since the address register is
+        known to be dead.  */
+      if (refers_to_regno_p (REGNO (operands[0]), REGNO (operands[0]) + 1,
+                            operands[1], 0))
+       return \"{l|lwz} %L0,%L1\;{l|lwz} %0,%1\";
+      else
+       return \"{l%U1|lwz%U1} %0,%1\;{l|lwz} %L0,%L1\";
+    case 2:
+      return \"{st%U0|stw%U0} %1,%0\;{st|stw} %L1,%L0\";
+    case 3:
+    case 4:
+    case 5:
+      return \"#\";
+    }
+}"
+  [(set_attr "type" "two,load,store,*,*,*")
+   (set_attr "length" "8,8,8,8,12,16")])
+
+; ld/std require word-aligned displacements -> 'Y' constraint.
+; List Y->r and r->Y before r->r for reload.
+(define_insn "*movdd_hardfloat64"
+  [(set (match_operand:DD 0 "nonimmediate_operand" "=Y,r,!r,f,f,m,*c*l,!r,*h,!r,!r,!r")
+       (match_operand:DD 1 "input_operand" "r,Y,r,f,m,f,r,h,0,G,H,F"))]
+  "TARGET_POWERPC64 && TARGET_HARD_FLOAT && TARGET_FPRS
+   && (gpc_reg_operand (operands[0], DDmode)
+       || gpc_reg_operand (operands[1], DDmode))"
+  "@
+   std%U0%X0 %1,%0
+   ld%U1%X1 %0,%1
+   mr %0,%1
+   fmr %0,%1
+   lfd%U1%X1 %0,%1
+   stfd%U0%X0 %1,%0
+   mt%0 %1
+   mf%1 %0
+   {cror 0,0,0|nop}
+   #
+   #
+   #"
+  [(set_attr "type" "store,load,*,fp,fpload,fpstore,mtjmpr,mfjmpr,*,*,*,*")
+   (set_attr "length" "4,4,4,4,4,4,4,4,4,8,12,16")])
+
+(define_insn "*movdd_softfloat64"
+  [(set (match_operand:DD 0 "nonimmediate_operand" "=r,Y,r,cl,r,r,r,r,*h")
+       (match_operand:DD 1 "input_operand" "Y,r,r,r,h,G,H,F,0"))]
+  "TARGET_POWERPC64 && (TARGET_SOFT_FLOAT || !TARGET_FPRS)
+   && (gpc_reg_operand (operands[0], DDmode)
+       || gpc_reg_operand (operands[1], DDmode))"
+  "@
+   ld%U1%X1 %0,%1
+   std%U0%X0 %1,%0
+   mr %0,%1
+   mt%0 %1
+   mf%1 %0
+   #
+   #
+   #
+   {cror 0,0,0|nop}"
+  [(set_attr "type" "load,store,*,mtjmpr,mfjmpr,*,*,*,*")
+   (set_attr "length" "4,4,4,4,4,8,12,16,4")])
+
+(define_expand "movtd"
+  [(set (match_operand:TD 0 "general_operand" "")
+       (match_operand:TD 1 "any_operand" ""))]
+  "TARGET_HARD_FLOAT && TARGET_FPRS"
+  "{ rs6000_emit_move (operands[0], operands[1], TDmode); DONE; }")
+
+; It's important to list the o->f and f->o moves before f->f because
+; otherwise reload, given m->f, will try to pick f->f and reload it,
+; which doesn't make progress.  Likewise r->Y must be before r->r.
+(define_insn_and_split "*movtd_internal"
+  [(set (match_operand:TD 0 "nonimmediate_operand" "=o,f,f,r,Y,r")
+       (match_operand:TD 1 "input_operand"         "f,o,f,YGHF,r,r"))]
+  "TARGET_HARD_FLOAT && TARGET_FPRS
+   && (gpc_reg_operand (operands[0], TDmode)
+       || gpc_reg_operand (operands[1], TDmode))"
+  "#"
+  "&& reload_completed"
+  [(pc)]
+{ rs6000_split_multireg_move (operands[0], operands[1]); DONE; }
+  [(set_attr "length" "8,8,8,20,20,16")])
+
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;
index 54202e4..75f064a 100644 (file)
   [(set (match_operand:GPR 0 "gpc_reg_operand" "=r,r,?r,r")
        (plus:GPR (match_operand:GPR 1 "gpc_reg_operand" "%r,b,r,b")
                  (match_operand:GPR 2 "add_operand" "r,I,I,L")))]
-  ""
+  "!DECIMAL_FLOAT_MODE_P (GET_MODE (operands[0])) && !DECIMAL_FLOAT_MODE_P (GET_MODE (operands[1]))"
   "@
    {cax|add} %0,%1,%2
    {cal %0,%2(%1)|addi %0,%1,%2}
 (include "sync.md")
 (include "altivec.md")
 (include "spe.md")
+(include "dfp.md")