From 7fefc6e7a0f36666c94f24e90aa84d2c1051059a Mon Sep 17 00:00:00 2001 From: bergner Date: Fri, 2 Mar 2007 15:57:08 +0000 Subject: [PATCH] * config/rs6000/dfp.md: New file. * config/rs6000/rs6000.md: Include dfp.md. (add3_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 | 27 ++++ gcc/config/rs6000/dfp.md | 316 ++++++++++++++++++++++++++++++++++++++++++++ gcc/config/rs6000/rs6000.c | 141 ++++++++++++++++---- gcc/config/rs6000/rs6000.md | 3 +- 4 files changed, 457 insertions(+), 30 deletions(-) create mode 100644 gcc/config/rs6000/dfp.md diff --git a/gcc/ChangeLog b/gcc/ChangeLog index cd50a3b..29df5e2 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,30 @@ +2007-03-02 Ben Elliston + Peter Bergner + Janis Johnson + + * config/rs6000/dfp.md: New file. + * config/rs6000/rs6000.md: Include dfp.md. + (add3_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 * 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 index 0000000..28f7b93 --- /dev/null +++ b/gcc/config/rs6000/dfp.md @@ -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")]) + diff --git a/gcc/config/rs6000/rs6000.c b/gcc/config/rs6000/rs6000.c index b9614b8..f21d5f1 100644 --- a/gcc/config/rs6000/rs6000.c +++ b/gcc/config/rs6000/rs6000.c @@ -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; diff --git a/gcc/config/rs6000/rs6000.md b/gcc/config/rs6000/rs6000.md index 54202e4..75f064a 100644 --- a/gcc/config/rs6000/rs6000.md +++ b/gcc/config/rs6000/rs6000.md @@ -1493,7 +1493,7 @@ [(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} @@ -14531,3 +14531,4 @@ (include "sync.md") (include "altivec.md") (include "spe.md") +(include "dfp.md") -- 2.7.4