#include "cfglayout.h"
#include "sched-int.h"
#include "tree-gimple.h"
+#include "tree-flow.h"
#include "intl.h"
#include "params.h"
#include "tm-constrs.h"
/* Offset from virtual_stack_vars_rtx to the start of the ABI_V4
varargs save area. */
HOST_WIDE_INT varargs_save_offset;
+ /* Temporary stack slot to use for SDmode copies. This slot is
+ 64-bits wide and is allocated early enough so that the offset
+ does not overflow the 16-bit load/store offset field. */
+ rtx sdmode_stack_slot;
} machine_function;
/* Target cpu type */
/* IEEE quad extended precision long double. */
int rs6000_ieeequad;
-/* Whether -mabi=altivec has appeared. */
+/* Nonzero to use AltiVec ABI. */
int rs6000_altivec_abi;
/* Nonzero if we want SPE ABI extensions. */
struct {
bool aix_struct_ret; /* True if -maix-struct-ret was used. */
bool alignment; /* True if -malign- was used. */
- bool abi; /* True if -mabi=spe/nospe was used. */
+ bool spe_abi; /* True if -mabi=spe/no-spe was used. */
+ bool altivec_abi; /* True if -mabi=altivec/no-altivec used. */
bool spe; /* True if -mspe= was used. */
bool float_gprs; /* True if -mfloat-gprs= was used. */
bool isel; /* True if -misel was used. */
bool long_double; /* True if -mlong-double- was used. */
bool ieee; /* True if -mabi=ieee/ibmlongdouble used. */
+ bool vrsave; /* True if -mvrsave was used. */
} rs6000_explicit_options;
struct builtin_description
COSTS_N_INSNS (2), /* dmul */
COSTS_N_INSNS (19), /* sdiv */
COSTS_N_INSNS (19), /* ddiv */
- 128,
+ 128, /* cache line size */
64, /* l1 cache */
512, /* l2 cache */
0, /* streams */
COSTS_N_INSNS (2), /* dmul */
COSTS_N_INSNS (17), /* sdiv */
COSTS_N_INSNS (17), /* ddiv */
- 256,
+ 256, /* cache line size */
256, /* l1 cache */
1024, /* l2 cache */
0, /* streams */
COSTS_N_INSNS (4), /* dmul */
COSTS_N_INSNS (31), /* sdiv */
COSTS_N_INSNS (31), /* ddiv */
- 128,
+ 128, /* cache line size */
128, /* l1 cache */
2048, /* l2 cache */
1, /* streams */
COSTS_N_INSNS (5), /* dmul */
COSTS_N_INSNS (10), /* sdiv */
COSTS_N_INSNS (17), /* ddiv */
- 32,
+ 32, /* cache line size */
4, /* l1 cache */
16, /* l2 cache */
1, /* streams */
COSTS_N_INSNS (11), /* dmul */
COSTS_N_INSNS (11), /* sdiv */
COSTS_N_INSNS (11), /* ddiv */
- 32,
+ 32, /* cache line size */
4, /* l1 cache */
16, /* l2 cache */
1, /* streams */
COSTS_N_INSNS (11), /* dmul */
COSTS_N_INSNS (11), /* sdiv */
COSTS_N_INSNS (11), /* ddiv */
- 32,
+ 32, /* cache line size */
16, /* l1 cache */
128, /* l2 cache */
1, /* streams */
COSTS_N_INSNS (5), /* dmul */
COSTS_N_INSNS (19), /* sdiv */
COSTS_N_INSNS (33), /* ddiv */
- 32,
+ 32, /* cache line size */
32, /* l1 cache */
256, /* l2 cache */
1, /* streams */
COSTS_N_INSNS (5), /* dmul */
COSTS_N_INSNS (17), /* sdiv */
COSTS_N_INSNS (31), /* ddiv */
- 32,
+ 32, /* cache line size */
32, /* l1 cache */
256, /* l2 cache */
1, /* streams */
COSTS_N_INSNS (4), /* dmul */
COSTS_N_INSNS (18), /* sdiv */
COSTS_N_INSNS (33), /* ddiv */
- 32,
+ 32, /* cache line size */
8, /* l1 cache */
64, /* l2 cache */
1, /* streams */
COSTS_N_INSNS (3), /* dmul */
COSTS_N_INSNS (18), /* sdiv */
COSTS_N_INSNS (32), /* ddiv */
- 32,
+ 32, /* cache line size */
16, /* l1 cache */
512, /* l2 cache */
1, /* streams */
COSTS_N_INSNS (3), /* dmul */
COSTS_N_INSNS (18), /* sdiv */
COSTS_N_INSNS (32), /* ddiv */
- 32,
+ 32, /* cache line size */
32, /* l1 cache */
1024, /* l2 cache */
1, /* streams */
COSTS_N_INSNS (3), /* dmul */
COSTS_N_INSNS (18), /* sdiv */
COSTS_N_INSNS (32), /* ddiv */
- 128,
+ 128, /* cache line size */
32, /* l1 cache */
1024, /* l2 cache */
1, /* streams */
COSTS_N_INSNS (3), /* dmul */
COSTS_N_INSNS (17), /* sdiv */
COSTS_N_INSNS (21), /* ddiv */
- 128,
+ 128, /* cache line size */
64, /* l1 cache */
1024, /* l2 cache */
1, /* streams */
COSTS_N_INSNS (10/2), /* dmul */
COSTS_N_INSNS (74/2), /* sdiv */
COSTS_N_INSNS (74/2), /* ddiv */
- 128,
+ 128, /* cache line size */
32, /* l1 cache */
512, /* l2 cache */
6, /* streams */
COSTS_N_INSNS (3), /* dmul */
COSTS_N_INSNS (17), /* sdiv */
COSTS_N_INSNS (31), /* ddiv */
- 32,
+ 32, /* cache line size */
32, /* l1 cache */
512, /* l2 cache */
1, /* streams */
COSTS_N_INSNS (5), /* dmul */
COSTS_N_INSNS (21), /* sdiv */
COSTS_N_INSNS (35), /* ddiv */
- 32,
+ 32, /* cache line size */
32, /* l1 cache */
1024, /* l2 cache */
1, /* streams */
COSTS_N_INSNS (4), /* dmul */
COSTS_N_INSNS (29), /* sdiv */
COSTS_N_INSNS (29), /* ddiv */
- 32,
+ 32, /* cache line size */
32, /* l1 cache */
256, /* l2 cache */
1, /* prefetch streams /*/
};
+/* Instruction costs on E300C2 and E300C3 cores. */
+static const
+struct processor_costs ppce300c2c3_cost = {
+ COSTS_N_INSNS (4), /* mulsi */
+ COSTS_N_INSNS (4), /* mulsi_const */
+ COSTS_N_INSNS (4), /* mulsi_const9 */
+ COSTS_N_INSNS (4), /* muldi */
+ COSTS_N_INSNS (19), /* divsi */
+ COSTS_N_INSNS (19), /* divdi */
+ COSTS_N_INSNS (3), /* fp */
+ COSTS_N_INSNS (4), /* dmul */
+ COSTS_N_INSNS (18), /* sdiv */
+ COSTS_N_INSNS (33), /* ddiv */
+ 32,
+ 16, /* l1 cache */
+ 16, /* l2 cache */
+ 1, /* prefetch streams /*/
+};
+
/* Instruction costs on POWER4 and POWER5 processors. */
static const
struct processor_costs power4_cost = {
COSTS_N_INSNS (3), /* dmul */
COSTS_N_INSNS (17), /* sdiv */
COSTS_N_INSNS (17), /* ddiv */
- 128,
+ 128, /* cache line size */
32, /* l1 cache */
1024, /* l2 cache */
8, /* prefetch streams /*/
COSTS_N_INSNS (3), /* dmul */
COSTS_N_INSNS (13), /* sdiv */
COSTS_N_INSNS (16), /* ddiv */
- 128,
+ 128, /* cache line size */
64, /* l1 cache */
2048, /* l2 cache */
16, /* prefetch streams */
static rtx rs6000_generate_compare (enum rtx_code);
static void rs6000_emit_stack_tie (void);
static void rs6000_frame_related (rtx, rtx, HOST_WIDE_INT, rtx, rtx);
-static rtx spe_synthesize_frame_save (rtx);
static bool spe_func_has_64bit_regs_p (void);
static void emit_frame_save (rtx, rtx, enum machine_mode, unsigned int,
int, HOST_WIDE_INT);
ATTRIBUTE_UNUSED;
#endif
static bool rs6000_use_blocks_for_constant_p (enum machine_mode, const_rtx);
+static void rs6000_alloc_sdmode_stack_slot (void);
+static void rs6000_instantiate_decls (void);
#if TARGET_XCOFF
static void rs6000_xcoff_asm_output_anchor (rtx);
static void rs6000_xcoff_asm_globalize_label (FILE *, const char *);
static int rs6000_sched_reorder2 (FILE *, int, rtx *, int *, int);
static int rs6000_use_sched_lookahead (void);
static int rs6000_use_sched_lookahead_guard (rtx);
+static tree rs6000_builtin_reciprocal (unsigned int, bool, bool);
static tree rs6000_builtin_mask_for_load (void);
static tree rs6000_builtin_mul_widen_even (tree);
static tree rs6000_builtin_mul_widen_odd (tree);
#endif
static tree rs6000_build_builtin_va_list (void);
+static void rs6000_va_start (tree, rtx);
static tree rs6000_gimplify_va_arg (tree, tree, tree *, tree *);
static bool rs6000_must_pass_in_stack (enum machine_mode, const_tree);
static bool rs6000_scalar_mode_supported_p (enum machine_mode);
#undef TARGET_BUILD_BUILTIN_VA_LIST
#define TARGET_BUILD_BUILTIN_VA_LIST rs6000_build_builtin_va_list
+#undef TARGET_EXPAND_BUILTIN_VA_START
+#define TARGET_EXPAND_BUILTIN_VA_START rs6000_va_start
+
#undef TARGET_GIMPLIFY_VA_ARG_EXPR
#define TARGET_GIMPLIFY_VA_ARG_EXPR rs6000_gimplify_va_arg
#undef TARGET_USE_BLOCKS_FOR_CONSTANT_P
#define TARGET_USE_BLOCKS_FOR_CONSTANT_P rs6000_use_blocks_for_constant_p
+#undef TARGET_BUILTIN_RECIPROCAL
+#define TARGET_BUILTIN_RECIPROCAL rs6000_builtin_reciprocal
+
+#undef TARGET_EXPAND_TO_RTL_HOOK
+#define TARGET_EXPAND_TO_RTL_HOOK rs6000_alloc_sdmode_stack_slot
+
+#undef TARGET_INSTANTIATE_DECLS
+#define TARGET_INSTANTIATE_DECLS rs6000_instantiate_decls
+
struct gcc_target targetm = TARGET_INITIALIZER;
\f
return
((SCALAR_FLOAT_MODE_P (mode)
&& (mode != TDmode || (regno % 2) == 0)
- && 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)
{"8540", PROCESSOR_PPC8540, POWERPC_BASE_MASK | MASK_STRICT_ALIGN},
/* 8548 has a dummy entry for now. */
{"8548", PROCESSOR_PPC8540, POWERPC_BASE_MASK | MASK_STRICT_ALIGN},
+ {"e300c2", PROCESSOR_PPCE300C2, POWERPC_BASE_MASK | MASK_SOFT_FLOAT},
+ {"e300c3", PROCESSOR_PPCE300C3, POWERPC_BASE_MASK},
{"860", PROCESSOR_MPCCORE, POWERPC_BASE_MASK | MASK_SOFT_FLOAT},
{"970", PROCESSOR_POWER4,
POWERPC_7400_MASK | MASK_PPC_GPOPT | MASK_MFCRF | MASK_POWERPC64},
if (TARGET_E500)
rs6000_isel = 1;
+ if (rs6000_cpu == PROCESSOR_PPCE300C2 || rs6000_cpu == PROCESSOR_PPCE300C3)
+ {
+ if (TARGET_ALTIVEC)
+ error ("AltiVec not supported in this target");
+ if (TARGET_SPE)
+ error ("Spe not supported in this target");
+ }
+
/* If we are optimizing big endian systems for space, use the load/store
multiple and string instructions. */
if (BYTES_BIG_ENDIAN && optimize_size)
rs6000_ieeequad = 1;
#endif
- /* Set Altivec ABI as default for powerpc64 linux. */
- if (TARGET_ELF && TARGET_64BIT)
+ /* Enable Altivec ABI for AIX -maltivec. */
+ if (TARGET_XCOFF && TARGET_ALTIVEC)
+ rs6000_altivec_abi = 1;
+
+ /* The AltiVec ABI is the default for PowerPC-64 GNU/Linux. For
+ PowerPC-32 GNU/Linux, -maltivec implies the AltiVec ABI. It can
+ be explicitly overridden in either case. */
+ if (TARGET_ELF)
{
- rs6000_altivec_abi = 1;
- TARGET_ALTIVEC_VRSAVE = 1;
+ if (!rs6000_explicit_options.altivec_abi
+ && (TARGET_64BIT || TARGET_ALTIVEC))
+ rs6000_altivec_abi = 1;
+
+ /* Enable VRSAVE for AltiVec ABI, unless explicitly overridden. */
+ if (!rs6000_explicit_options.vrsave)
+ TARGET_ALTIVEC_VRSAVE = rs6000_altivec_abi;
}
/* Set the Darwin64 ABI as default for 64-bit Darwin. */
/* For the powerpc-eabispe configuration, we set all these by
default, so let's unset them if we manually set another
CPU that is not the E500. */
- if (!rs6000_explicit_options.abi)
+ if (!rs6000_explicit_options.spe_abi)
rs6000_spe_abi = 0;
if (!rs6000_explicit_options.spe)
rs6000_spe = 0;
rs6000_cost = &ppc8540_cost;
break;
+ case PROCESSOR_PPCE300C2:
+ case PROCESSOR_PPCE300C3:
+ rs6000_cost = &ppce300c2c3_cost;
+ break;
+
case PROCESSOR_POWER4:
case PROCESSOR_POWER5:
rs6000_cost = &power4_cost;
set_param_value ("l1-cache-line-size", rs6000_cost->cache_line_size);
if (!PARAM_SET_P (PARAM_L2_CACHE_SIZE))
set_param_value ("l2-cache-size", rs6000_cost->l2_cache_size);
+
+ /* If using typedef char *va_list, signal that __builtin_va_start (&ap, 0)
+ can be optimized to ap = __builtin_next_arg (0). */
+ if (DEFAULT_ABI != ABI_V4)
+ targetm.expand_builtin_va_start = NULL;
}
/* Implement targetm.vectorize.builtin_mask_for_load. */
break;
case OPT_mvrsave_:
+ rs6000_explicit_options.vrsave = true;
rs6000_parse_yes_no_option ("vrsave", arg, &(TARGET_ALTIVEC_VRSAVE));
break;
case OPT_mabi_:
if (!strcmp (arg, "altivec"))
{
- rs6000_explicit_options.abi = true;
+ rs6000_explicit_options.altivec_abi = true;
rs6000_altivec_abi = 1;
+
+ /* Enabling the AltiVec ABI turns off the SPE ABI. */
rs6000_spe_abi = 0;
}
else if (! strcmp (arg, "no-altivec"))
{
- /* ??? Don't set rs6000_explicit_options.abi here, to allow
- the default for rs6000_spe_abi to be chosen later. */
+ rs6000_explicit_options.altivec_abi = true;
rs6000_altivec_abi = 0;
}
else if (! strcmp (arg, "spe"))
{
- rs6000_explicit_options.abi = true;
+ rs6000_explicit_options.spe_abi = true;
rs6000_spe_abi = 1;
rs6000_altivec_abi = 0;
if (!TARGET_SPE_ABI)
}
else if (! strcmp (arg, "no-spe"))
{
- rs6000_explicit_options.abi = true;
+ rs6000_explicit_options.spe_abi = true;
rs6000_spe_abi = 0;
}
return num_insns_constant_wide (INTVAL (op));
case CONST_DOUBLE:
- if (mode == SFmode)
+ if (mode == SFmode || mode == SDmode)
{
long l;
REAL_VALUE_TYPE rv;
REAL_VALUE_FROM_CONST_DOUBLE (rv, op);
- REAL_VALUE_TO_TARGET_SINGLE (rv, l);
+ if (DECIMAL_FLOAT_MODE_P (mode))
+ REAL_VALUE_TO_TARGET_DECIMAL32 (rv, l);
+ else
+ REAL_VALUE_TO_TARGET_SINGLE (rv, l);
return num_insns_constant_wide ((HOST_WIDE_INT) l);
}
return "li %0,%1\n\tevmergelo %0,%0,%0\n\tli %0,%2";
}
+/* Initialize TARGET of vector PAIRED to VALS. */
+
+void
+paired_expand_vector_init (rtx target, rtx vals)
+{
+ enum machine_mode mode = GET_MODE (target);
+ int n_elts = GET_MODE_NUNITS (mode);
+ int n_var = 0;
+ rtx x, new, tmp, constant_op, op1, op2;
+ int i;
+
+ for (i = 0; i < n_elts; ++i)
+ {
+ x = XVECEXP (vals, 0, i);
+ if (!CONSTANT_P (x))
+ ++n_var;
+ }
+ if (n_var == 0)
+ {
+ /* Load from constant pool. */
+ emit_move_insn (target, gen_rtx_CONST_VECTOR (mode, XVEC (vals, 0)));
+ return;
+ }
+
+ if (n_var == 2)
+ {
+ /* The vector is initialized only with non-constants. */
+ new = gen_rtx_VEC_CONCAT (V2SFmode, XVECEXP (vals, 0, 0),
+ XVECEXP (vals, 0, 1));
+
+ emit_move_insn (target, new);
+ return;
+ }
+
+ /* One field is non-constant and the other one is a constant. Load the
+ constant from the constant pool and use ps_merge instruction to
+ construct the whole vector. */
+ op1 = XVECEXP (vals, 0, 0);
+ op2 = XVECEXP (vals, 0, 1);
+
+ constant_op = (CONSTANT_P (op1)) ? op1 : op2;
+
+ tmp = gen_reg_rtx (GET_MODE (constant_op));
+ emit_move_insn (tmp, constant_op);
+
+ if (CONSTANT_P (op1))
+ new = gen_rtx_VEC_CONCAT (V2SFmode, tmp, op2);
+ else
+ new = gen_rtx_VEC_CONCAT (V2SFmode, op1, tmp);
+
+ emit_move_insn (target, new);
+}
+
+void
+paired_expand_vector_move (rtx operands[])
+{
+ rtx op0 = operands[0], op1 = operands[1];
+
+ emit_move_insn (op0, op1);
+}
+
+/* Emit vector compare for code RCODE. DEST is destination, OP1 and
+ OP2 are two VEC_COND_EXPR operands, CC_OP0 and CC_OP1 are the two
+ operands for the relation operation COND. This is a recursive
+ function. */
+
+static void
+paired_emit_vector_compare (enum rtx_code rcode,
+ rtx dest, rtx op0, rtx op1,
+ rtx cc_op0, rtx cc_op1)
+{
+ rtx tmp = gen_reg_rtx (V2SFmode);
+ rtx tmp1, max, min, equal_zero;
+
+ gcc_assert (TARGET_PAIRED_FLOAT);
+ gcc_assert (GET_MODE (op0) == GET_MODE (op1));
+
+ switch (rcode)
+ {
+ case LT:
+ case LTU:
+ paired_emit_vector_compare (GE, dest, op1, op0, cc_op0, cc_op1);
+ return;
+ case GE:
+ case GEU:
+ emit_insn (gen_subv2sf3 (tmp, cc_op0, cc_op1));
+ emit_insn (gen_selv2sf4 (dest, tmp, op0, op1, CONST0_RTX (SFmode)));
+ return;
+ case LE:
+ case LEU:
+ paired_emit_vector_compare (GE, dest, op0, op1, cc_op1, cc_op0);
+ return;
+ case GT:
+ paired_emit_vector_compare (LE, dest, op1, op0, cc_op0, cc_op1);
+ return;
+ case EQ:
+ tmp1 = gen_reg_rtx (V2SFmode);
+ max = gen_reg_rtx (V2SFmode);
+ min = gen_reg_rtx (V2SFmode);
+ equal_zero = gen_reg_rtx (V2SFmode);
+
+ emit_insn (gen_subv2sf3 (tmp, cc_op0, cc_op1));
+ emit_insn (gen_selv2sf4
+ (max, tmp, cc_op0, cc_op1, CONST0_RTX (SFmode)));
+ emit_insn (gen_subv2sf3 (tmp, cc_op1, cc_op0));
+ emit_insn (gen_selv2sf4
+ (min, tmp, cc_op0, cc_op1, CONST0_RTX (SFmode)));
+ emit_insn (gen_subv2sf3 (tmp1, min, max));
+ emit_insn (gen_selv2sf4 (dest, tmp1, op0, op1, CONST0_RTX (SFmode)));
+ return;
+ case NE:
+ paired_emit_vector_compare (EQ, dest, op1, op0, cc_op0, cc_op1);
+ return;
+ case UNLE:
+ paired_emit_vector_compare (LE, dest, op1, op0, cc_op0, cc_op1);
+ return;
+ case UNLT:
+ paired_emit_vector_compare (LT, dest, op1, op0, cc_op0, cc_op1);
+ return;
+ case UNGE:
+ paired_emit_vector_compare (GE, dest, op1, op0, cc_op0, cc_op1);
+ return;
+ case UNGT:
+ paired_emit_vector_compare (GT, dest, op1, op0, cc_op0, cc_op1);
+ return;
+ default:
+ gcc_unreachable ();
+ }
+
+ return;
+}
+
+/* Emit vector conditional expression.
+ DEST is destination. OP1 and OP2 are two VEC_COND_EXPR operands.
+ CC_OP0 and CC_OP1 are the two operands for the relation operation COND. */
+
+int
+paired_emit_vector_cond_expr (rtx dest, rtx op1, rtx op2,
+ rtx cond, rtx cc_op0, rtx cc_op1)
+{
+ enum rtx_code rcode = GET_CODE (cond);
+
+ if (!TARGET_PAIRED_FLOAT)
+ return 0;
+
+ paired_emit_vector_compare (rcode, dest, op1, op2, cc_op0, cc_op1);
+
+ return 1;
+}
+
/* Initialize vector TARGET to VALS. */
void
if (n_var == 0)
{
+ rtx const_vec = gen_rtx_CONST_VECTOR (mode, XVEC (vals, 0));
if (mode != V4SFmode && all_const_zero)
{
/* Zero register. */
gen_rtx_XOR (mode, target, target)));
return;
}
- else if (mode != V4SFmode && easy_vector_constant (vals, mode))
+ else if (mode != V4SFmode && easy_vector_constant (const_vec, mode))
{
/* Splat immediate. */
- emit_insn (gen_rtx_SET (VOIDmode, target, vals));
+ emit_insn (gen_rtx_SET (VOIDmode, target, const_vec));
return;
}
else if (all_same)
else
{
/* Load from constant pool. */
- emit_move_insn (target, gen_rtx_CONST_VECTOR (mode, XVEC (vals, 0)));
+ emit_move_insn (target, const_vec);
return;
}
}
&& (mode == SImode || mode == DImode || mode == TImode)
&& REG_P (SUBREG_REG (op))
&& (GET_MODE (SUBREG_REG (op)) == DFmode
- || GET_MODE (SUBREG_REG (op)) == TFmode))
+ || GET_MODE (SUBREG_REG (op)) == TFmode
+ || GET_MODE (SUBREG_REG (op)) == DDmode
+ || GET_MODE (SUBREG_REG (op)) == TDmode))
return true;
/* Reject (subreg:DF (reg:DI)); likewise with subreg:TF and
reg:TI. */
if (GET_CODE (op) == SUBREG
- && (mode == DFmode || mode == TFmode)
+ && (mode == DFmode || mode == TFmode
+ || mode == DDmode || mode == TDmode)
&& REG_P (SUBREG_REG (op))
&& (GET_MODE (SUBREG_REG (op)) == DImode
|| GET_MODE (SUBREG_REG (op)) == TImode))
if (DEFAULT_ABI != ABI_V4)
return 0;
+ /* Vector and float memory instructions have a limited offset on the
+ SPE, so using a vector or float variable directly as an operand is
+ not useful. */
+ if (TARGET_SPE
+ && (SPE_VECTOR_MODE (mode) || FLOAT_MODE_P (mode)))
+ return 0;
+
if (GET_CODE (op) == SYMBOL_REF)
sym_ref = op;
case V4SFmode:
case V4SImode:
/* AltiVec vector modes. Only reg+reg addressing is valid and
- constant offset zero should not occur due to canonicalization.
- Allow any offset when not strict before reload. */
- return !strict;
+ constant offset zero should not occur due to canonicalization. */
+ return false;
case V4HImode:
case V2SImode:
case V1DImode:
case V2SFmode:
/* Paired vector modes. Only reg+reg addressing is valid and
- constant offset zero should not occur due to canonicalization.
- Allow any offset when not strict before reload. */
+ constant offset zero should not occur due to canonicalization. */
if (TARGET_PAIRED_FLOAT)
- return !strict;
+ return false;
/* SPE vector modes. */
return SPE_CONST_OFFSET_OK (offset);
break;
case TFmode:
+ case TDmode:
if (TARGET_E500_DOUBLE)
return (SPE_CONST_OFFSET_OK (offset)
&& SPE_CONST_OFFSET_OK (offset + 8));
case TImode:
- case TDmode:
if (mode == TFmode || mode == TDmode || !TARGET_POWERPC64)
extra = 12;
else if (offset & 3)
return false;
/* Restrict addressing for DI because of our SUBREG hackery. */
if (TARGET_E500_DOUBLE && (mode == DFmode || mode == TFmode
+ || mode == DDmode || mode == TDmode
|| mode == DImode))
return false;
x = XEXP (x, 1);
return false;
if (GET_MODE_BITSIZE (mode) > 64
|| (GET_MODE_BITSIZE (mode) > 32 && !TARGET_POWERPC64
- && !(TARGET_HARD_FLOAT && TARGET_FPRS && mode == DFmode)))
+ && !(TARGET_HARD_FLOAT && TARGET_FPRS
+ && (mode == DFmode || mode == DDmode))))
return false;
return CONSTANT_P (x);
if (GET_CODE (x) == PLUS
&& GET_CODE (XEXP (x, 0)) == REG
&& GET_CODE (XEXP (x, 1)) == CONST_INT
- && (unsigned HOST_WIDE_INT) (INTVAL (XEXP (x, 1)) + 0x8000) >= 0x10000)
+ && (unsigned HOST_WIDE_INT) (INTVAL (XEXP (x, 1)) + 0x8000) >= 0x10000
+ && !(SPE_VECTOR_MODE (mode)
+ || ALTIVEC_VECTOR_MODE (mode)
+ || (TARGET_E500_DOUBLE && (mode == DFmode || mode == TFmode
+ || mode == DImode))))
{
HOST_WIDE_INT high_int, low_int;
rtx sum;
&& GET_MODE_NUNITS (mode) == 1
&& ((TARGET_HARD_FLOAT && TARGET_FPRS)
|| TARGET_POWERPC64
- || (((mode != DImode && mode != DFmode && mode != DDmode)
- || TARGET_E500_DOUBLE)
- && mode != TFmode && mode != TDmode))
+ || ((mode != DImode && mode != DFmode && mode != DDmode)
+ || TARGET_E500_DOUBLE))
&& (TARGET_POWERPC64 || mode != DImode)
- && mode != TImode)
+ && mode != TImode
+ && mode != TFmode
+ && mode != TDmode)
{
return gen_rtx_PLUS (Pmode, XEXP (x, 0),
force_reg (Pmode, force_operand (XEXP (x, 1), 0)));
/* We accept [reg + reg] and [reg + OFFSET]. */
if (GET_CODE (x) == PLUS)
- {
- rtx op1 = XEXP (x, 0);
- rtx op2 = XEXP (x, 1);
+ {
+ rtx op1 = XEXP (x, 0);
+ rtx op2 = XEXP (x, 1);
+ rtx y;
- op1 = force_reg (Pmode, op1);
+ op1 = force_reg (Pmode, op1);
- if (GET_CODE (op2) != REG
- && (GET_CODE (op2) != CONST_INT
- || !SPE_CONST_OFFSET_OK (INTVAL (op2))))
- op2 = force_reg (Pmode, op2);
+ if (GET_CODE (op2) != REG
+ && (GET_CODE (op2) != CONST_INT
+ || !SPE_CONST_OFFSET_OK (INTVAL (op2))
+ || (GET_MODE_SIZE (mode) > 8
+ && !SPE_CONST_OFFSET_OK (INTVAL (op2) + 8))))
+ op2 = force_reg (Pmode, op2);
- return gen_rtx_PLUS (Pmode, op1, op2);
- }
+ /* We can't always do [reg + reg] for these, because [reg +
+ reg + offset] is not a legitimate addressing mode. */
+ y = gen_rtx_PLUS (Pmode, op1, op2);
+
+ if (GET_MODE_SIZE (mode) > 8 && REG_P (op2))
+ return force_reg (Pmode, y);
+ else
+ return y;
+ }
return force_reg (Pmode, x);
}
&& CONSTANT_P (x)
&& GET_MODE_NUNITS (mode) == 1
&& (GET_MODE_BITSIZE (mode) <= 32
- || ((TARGET_HARD_FLOAT && TARGET_FPRS) && mode == DFmode)))
+ || ((TARGET_HARD_FLOAT && TARGET_FPRS)
+ && (mode == DFmode || mode == DDmode))))
{
rtx reg = gen_reg_rtx (Pmode);
emit_insn (gen_elf_high (reg, x));
&& GET_CODE (x) != CONST_INT
&& GET_CODE (x) != CONST_DOUBLE
&& CONSTANT_P (x)
- && ((TARGET_HARD_FLOAT && TARGET_FPRS) || mode != DFmode)
+ && ((TARGET_HARD_FLOAT && TARGET_FPRS)
+ || (mode != DFmode && mode != DDmode))
&& mode != DImode
&& mode != TImode)
{
&& GET_CODE (XEXP (x, 1)) == CONST_INT
&& !SPE_VECTOR_MODE (mode)
&& !(TARGET_E500_DOUBLE && (mode == DFmode || mode == TFmode
+ || mode == DDmode || mode == TDmode
|| mode == DImode))
&& !ALTIVEC_VECTOR_MODE (mode))
{
&& !flag_pic
#endif
/* 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
+ The same goes for DImode without 64-bit gprs and DFmode and DDmode
without fprs. */
&& mode != TFmode
&& mode != TDmode
&& (mode != DImode || TARGET_POWERPC64)
- && (mode != DFmode || TARGET_POWERPC64
+ && ((mode != DFmode && mode != DDmode) || TARGET_POWERPC64
|| (TARGET_FPRS && TARGET_HARD_FLOAT)))
{
#if TARGET_MACHO
refers to a constant pool entry of an address (or the sum of it
plus a constant), a short (16-bit signed) constant plus a register,
the sum of two registers, or a register indirect, possibly with an
- auto-increment. For DFmode and DImode with a constant plus register,
- we must ensure that both words are addressable or PowerPC64 with offset
- word aligned.
+ auto-increment. For DFmode, DDmode and DImode with a constant plus
+ register, we must ensure that both words are addressable or PowerPC64
+ with offset word aligned.
- For modes spanning multiple registers (DFmode in 32-bit GPRs,
+ For modes spanning multiple registers (DFmode and DDmode in 32-bit GPRs,
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. */
&& mode != TFmode
&& mode != TDmode
/* Restrict addressing for DI because of our SUBREG hackery. */
- && !(TARGET_E500_DOUBLE && (mode == DFmode || mode == TFmode
- || mode == DImode))
+ && !(TARGET_E500_DOUBLE
+ && (mode == DFmode || mode == DDmode || mode == DImode))
&& TARGET_UPDATE
&& legitimate_indirect_address_p (XEXP (x, 0), reg_ok_strict))
return 1;
&& mode != TDmode
&& ((TARGET_HARD_FLOAT && TARGET_FPRS)
|| TARGET_POWERPC64
- || ((mode != DFmode || TARGET_E500_DOUBLE) && mode != TFmode))
+ || ((mode != DFmode && mode != DDmode) || TARGET_E500_DOUBLE))
&& (TARGET_POWERPC64 || mode != DImode)
&& legitimate_indexed_address_p (x, reg_ok_strict))
return 1;
&& mode != TDmode
&& ((TARGET_HARD_FLOAT && TARGET_FPRS)
|| TARGET_POWERPC64
- || ((mode != DFmode || TARGET_E500_DOUBLE) && mode != TFmode))
+ || ((mode != DFmode && mode != DDmode) || TARGET_E500_DOUBLE))
&& (TARGET_POWERPC64 || mode != DImode)
&& !ALTIVEC_VECTOR_MODE (mode)
&& !SPE_VECTOR_MODE (mode)
/* Restrict addressing for DI because of our SUBREG hackery. */
- && !(TARGET_E500_DOUBLE && (mode == DFmode || mode == DImode))
+ && !(TARGET_E500_DOUBLE
+ && (mode == DFmode || mode == DDmode || mode == DImode))
&& TARGET_UPDATE
&& legitimate_indirect_address_p (XEXP (x, 0), reg_ok_strict)
&& (rs6000_legitimate_offset_address_p (mode, XEXP (x, 1), reg_ok_strict)
fixed_regs[RS6000_PIC_OFFSET_TABLE_REGNUM]
= call_used_regs[RS6000_PIC_OFFSET_TABLE_REGNUM] = 1;
- if (TARGET_ALTIVEC)
- global_regs[VSCR_REGNO] = 1;
-
if (TARGET_SPE)
{
global_regs[SPEFSCR_REGNO] = 1;
= call_really_used_regs[14] = 1;
}
- if (! TARGET_ALTIVEC)
+ if (!TARGET_ALTIVEC)
{
for (i = FIRST_ALTIVEC_REGNO; i <= LAST_ALTIVEC_REGNO; ++i)
fixed_regs[i] = call_used_regs[i] = call_really_used_regs[i] = 1;
call_really_used_regs[VRSAVE_REGNO] = 1;
}
+ if (TARGET_ALTIVEC)
+ global_regs[VSCR_REGNO] = 1;
+
if (TARGET_ALTIVEC_ABI)
- for (i = FIRST_ALTIVEC_REGNO; i < FIRST_ALTIVEC_REGNO + 20; ++i)
- call_used_regs[i] = call_really_used_regs[i] = 1;
+ {
+ for (i = FIRST_ALTIVEC_REGNO; i < FIRST_ALTIVEC_REGNO + 20; ++i)
+ call_used_regs[i] = call_really_used_regs[i] = 1;
+
+ /* AIX reserves VR20:31 in non-extended ABI mode. */
+ if (TARGET_XCOFF)
+ for (i = FIRST_ALTIVEC_REGNO + 20; i < FIRST_ALTIVEC_REGNO + 32; ++i)
+ fixed_regs[i] = call_used_regs[i] = call_really_used_regs[i] = 1;
+ }
}
\f
/* Try to output insns to set TARGET equal to the constant C if it can
return;
}
+ if (reload_in_progress && cfun->machine->sdmode_stack_slot != NULL_RTX)
+ cfun->machine->sdmode_stack_slot =
+ eliminate_regs (cfun->machine->sdmode_stack_slot, VOIDmode, NULL_RTX);
+
+ if (reload_in_progress
+ && mode == SDmode
+ && MEM_P (operands[0])
+ && rtx_equal_p (operands[0], cfun->machine->sdmode_stack_slot)
+ && REG_P (operands[1]))
+ {
+ if (FP_REGNO_P (REGNO (operands[1])))
+ {
+ rtx mem = adjust_address_nv (operands[0], DDmode, 0);
+ mem = eliminate_regs (mem, VOIDmode, NULL_RTX);
+ emit_insn (gen_movsd_store (mem, operands[1]));
+ }
+ else if (INT_REGNO_P (REGNO (operands[1])))
+ {
+ rtx mem = adjust_address_nv (operands[0], mode, 4);
+ mem = eliminate_regs (mem, VOIDmode, NULL_RTX);
+ emit_insn (gen_movsd_hardfloat (mem, operands[1]));
+ }
+ else
+ gcc_unreachable();
+ return;
+ }
+ if (reload_in_progress
+ && mode == SDmode
+ && REG_P (operands[0])
+ && MEM_P (operands[1])
+ && rtx_equal_p (operands[1], cfun->machine->sdmode_stack_slot))
+ {
+ if (FP_REGNO_P (REGNO (operands[0])))
+ {
+ rtx mem = adjust_address_nv (operands[1], DDmode, 0);
+ mem = eliminate_regs (mem, VOIDmode, NULL_RTX);
+ emit_insn (gen_movsd_load (operands[0], mem));
+ }
+ else if (INT_REGNO_P (REGNO (operands[0])))
+ {
+ rtx mem = adjust_address_nv (operands[1], mode, 4);
+ mem = eliminate_regs (mem, VOIDmode, NULL_RTX);
+ emit_insn (gen_movsd_hardfloat (operands[0], mem));
+ }
+ else
+ gcc_unreachable();
+ return;
+ }
+
/* FIXME: In the long term, this switch statement should go away
and be replaced by a sequence of tests based on things like
mode == Pmode. */
case DFmode:
case DDmode:
case SFmode:
+ case SDmode:
if (CONSTANT_P (operands[1])
&& ! easy_fp_constant (operands[1], mode))
operands[1] = force_const_mem (mode, operands[1]);
/* 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) \
- && (MODE) != SDmode \
&& (CUM)->fregno <= FP_ARG_MAX_REG \
&& TARGET_HARD_FLOAT && TARGET_FPRS)
{
if (TARGET_HARD_FLOAT && TARGET_FPRS
&& (mode == SFmode || mode == DFmode
- || mode == DDmode || mode == TDmode
+ || mode == SDmode || mode == DDmode || mode == TDmode
|| (mode == TFmode && !TARGET_IEEEQUAD)))
{
/* _Decimal128 must use an even/odd register pair. This assumes
else
{
cum->fregno = FP_ARG_V4_MAX_REG + 1;
- if (mode == DFmode || mode == TFmode || mode == DDmode || mode == TDmode)
+ if (mode == DFmode || mode == TFmode
+ || mode == DDmode || mode == TDmode)
cum->words += cum->words & 1;
cum->words += rs6000_arg_size (mode, type);
}
cum->words = align_words + n_words;
if (SCALAR_FLOAT_MODE_P (mode)
- && mode != SDmode
&& TARGET_HARD_FLOAT && TARGET_FPRS)
{
/* _Decimal128 must be passed in an even/odd float register pair.
switch (mode)
{
case DFmode:
+ case DDmode:
r1 = gen_rtx_REG (DImode, gregno);
r1 = gen_rtx_EXPR_LIST (VOIDmode, r1, const0_rtx);
return gen_rtx_PARALLEL (mode, gen_rtvec (1, r1));
case DCmode:
case TFmode:
+ case TDmode:
r1 = gen_rtx_REG (DImode, gregno);
r1 = gen_rtx_EXPR_LIST (VOIDmode, r1, const0_rtx);
r3 = gen_rtx_REG (DImode, gregno + 2);
/* On E500 v2, double arithmetic is done on the full 64-bit GPR, but
are passed and returned in a pair of GPRs for ABI compatibility. */
- if (TARGET_E500_DOUBLE && (mode == DFmode || mode == DCmode
- || mode == TFmode || mode == TCmode))
+ if (TARGET_E500_DOUBLE && (mode == DFmode || mode == TFmode
+ || mode == DDmode || mode == TDmode
+ || mode == DCmode || mode == TCmode))
{
int n_words = rs6000_arg_size (mode, type);
/* Doubles go in an odd/even register pair (r5/r6, etc). */
- if (mode == DFmode)
+ if (mode == DFmode || mode == DDmode)
gregno += (1 - gregno) & 1;
/* Multi-reg args are not split between registers and stack. */
if (TARGET_HARD_FLOAT && TARGET_FPRS
&& (mode == SFmode || mode == DFmode
|| (mode == TFmode && !TARGET_IEEEQUAD)
- || mode == DDmode || mode == TDmode))
+ || mode == SDmode || mode == DDmode || mode == TDmode))
{
/* _Decimal128 must use an even/odd register pair. This assumes
that the register number is odd when fregno is odd. */
/* Implement va_start. */
-void
+static void
rs6000_va_start (tree valist, rtx nextarg)
{
HOST_WIDE_INT words, n_gpr, n_fpr;
&& (TYPE_MODE (type) == SFmode
|| TYPE_MODE (type) == DFmode
|| TYPE_MODE (type) == TFmode
+ || TYPE_MODE (type) == SDmode
|| TYPE_MODE (type) == DDmode
|| TYPE_MODE (type) == TDmode))
{
n_reg = (size + 7) / 8;
sav_ofs = 8*4;
sav_scale = 8;
- if (TYPE_MODE (type) != SFmode)
+ if (TYPE_MODE (type) != SFmode && TYPE_MODE (type) != SDmode)
align = 8;
}
else
else if (reg == fpr && TYPE_MODE (type) == TDmode)
{
regalign = 1;
- t = build2 (BIT_IOR_EXPR, TREE_TYPE (reg), reg, size_int (1));
+ t = build2 (BIT_IOR_EXPR, TREE_TYPE (reg), reg,
+ build_int_cst (TREE_TYPE (reg), 1));
u = build2 (MODIFY_EXPR, void_type_node, reg, t);
}
u = build2 (MULT_EXPR, sizetype, u, size_int (sav_scale));
t = build2 (POINTER_PLUS_EXPR, ptr_type_node, t, u);
+ /* _Decimal32 varargs are located in the second word of the 64-bit
+ FP register for 32-bit binaries. */
+ if (!TARGET_POWERPC64 && TYPE_MODE (type) == SDmode)
+ t = build2 (POINTER_PLUS_EXPR, TREE_TYPE (t), t, size_int (size));
+
t = build2 (GIMPLE_MODIFY_STMT, void_type_node, addr, t);
gimplify_and_add (t, pre_p);
{
/* Ensure that we don't find any more args in regs.
Alignment has taken care of for special cases. */
- t = build2 (GIMPLE_MODIFY_STMT, TREE_TYPE (reg), reg, size_int (8));
+ t = build_gimple_modify_stmt (reg,
+ build_int_cst (TREE_TYPE (reg), 8));
gimplify_and_add (t, pre_p);
}
}
{ 0, CODE_FOR_paired_nmadd, "__builtin_paired_nmadd", PAIRED_BUILTIN_NMADD },
{ 0, CODE_FOR_paired_sum0, "__builtin_paired_sum0", PAIRED_BUILTIN_SUM0 },
{ 0, CODE_FOR_paired_sum1, "__builtin_paired_sum1", PAIRED_BUILTIN_SUM1 },
+ { 0, CODE_FOR_selv2sf4, "__builtin_paired_selv2sf4", PAIRED_BUILTIN_SELV2SF4 },
};
/* DST operations: void foo (void *, const int, const char). */
{ MASK_ALTIVEC, CODE_FOR_altivec_vrlb, "__builtin_altivec_vrlb", ALTIVEC_BUILTIN_VRLB },
{ MASK_ALTIVEC, CODE_FOR_altivec_vrlh, "__builtin_altivec_vrlh", ALTIVEC_BUILTIN_VRLH },
{ MASK_ALTIVEC, CODE_FOR_altivec_vrlw, "__builtin_altivec_vrlw", ALTIVEC_BUILTIN_VRLW },
- { MASK_ALTIVEC, CODE_FOR_altivec_vslb, "__builtin_altivec_vslb", ALTIVEC_BUILTIN_VSLB },
- { MASK_ALTIVEC, CODE_FOR_altivec_vslh, "__builtin_altivec_vslh", ALTIVEC_BUILTIN_VSLH },
- { MASK_ALTIVEC, CODE_FOR_altivec_vslw, "__builtin_altivec_vslw", ALTIVEC_BUILTIN_VSLW },
+ { MASK_ALTIVEC, CODE_FOR_ashlv16qi3, "__builtin_altivec_vslb", ALTIVEC_BUILTIN_VSLB },
+ { MASK_ALTIVEC, CODE_FOR_ashlv8hi3, "__builtin_altivec_vslh", ALTIVEC_BUILTIN_VSLH },
+ { MASK_ALTIVEC, CODE_FOR_ashlv4si3, "__builtin_altivec_vslw", ALTIVEC_BUILTIN_VSLW },
{ MASK_ALTIVEC, CODE_FOR_altivec_vsl, "__builtin_altivec_vsl", ALTIVEC_BUILTIN_VSL },
{ MASK_ALTIVEC, CODE_FOR_altivec_vslo, "__builtin_altivec_vslo", ALTIVEC_BUILTIN_VSLO },
{ MASK_ALTIVEC, CODE_FOR_altivec_vspltb, "__builtin_altivec_vspltb", ALTIVEC_BUILTIN_VSPLTB },
if (! (*insn_data[icode].operand[3].predicate) (op2, mode2))
op2 = copy_to_mode_reg (mode2, op2);
- pat = GEN_FCN (icode) (target, op0, op1, op2);
+ if (TARGET_PAIRED_FLOAT && icode == CODE_FOR_selv2sf4)
+ pat = GEN_FCN (icode) (target, op0, op1, op2, CONST0_RTX (SFmode));
+ else
+ pat = GEN_FCN (icode) (target, op0, op1, op2);
if (! pat)
return 0;
emit_insn (pat);
rtx ret;
bool success;
+ if (fcode == RS6000_BUILTIN_RECIP)
+ return rs6000_expand_binop_builtin (CODE_FOR_recipdf3, exp, target);
+
+ if (fcode == RS6000_BUILTIN_RECIPF)
+ return rs6000_expand_binop_builtin (CODE_FOR_recipsf3, exp, target);
+
+ if (fcode == RS6000_BUILTIN_RSQRTF)
+ return rs6000_expand_unop_builtin (CODE_FOR_rsqrtsf2, exp, target);
+
if (fcode == ALTIVEC_BUILTIN_MASK_FOR_LOAD
|| fcode == ALTIVEC_BUILTIN_MASK_FOR_STORE)
{
altivec_init_builtins ();
if (TARGET_ALTIVEC || TARGET_SPE || TARGET_PAIRED_FLOAT)
rs6000_common_init_builtins ();
+ if (TARGET_PPC_GFXOPT)
+ {
+ tree ftype = build_function_type_list (float_type_node,
+ float_type_node,
+ float_type_node,
+ NULL_TREE);
+ def_builtin (MASK_PPC_GFXOPT, "__builtin_recipdivf", ftype,
+ RS6000_BUILTIN_RECIPF);
+
+ ftype = build_function_type_list (float_type_node,
+ float_type_node,
+ NULL_TREE);
+ def_builtin (MASK_PPC_GFXOPT, "__builtin_rsqrtf", ftype,
+ RS6000_BUILTIN_RSQRTF);
+ }
+ if (TARGET_POPCNTB)
+ {
+ tree ftype = build_function_type_list (double_type_node,
+ double_type_node,
+ double_type_node,
+ NULL_TREE);
+ def_builtin (MASK_POPCNTB, "__builtin_recipdiv", ftype,
+ RS6000_BUILTIN_RECIP);
+
+ }
#if TARGET_XCOFF
/* AIX libm provides clog as __clog. */
if (built_in_decls [BUILT_IN_CLOG])
set_user_assembler_name (built_in_decls [BUILT_IN_CLOG], "__clog");
#endif
+
+#ifdef SUBTARGET_INIT_BUILTINS
+ SUBTARGET_INIT_BUILTINS;
+#endif
}
/* Search through a set of builtins and enable the mask bits.
return 1;
}
\f
+
+rtx
+rs6000_secondary_memory_needed_rtx (enum machine_mode mode)
+{
+ static bool eliminated = false;
+ if (mode != SDmode)
+ return assign_stack_local (mode, GET_MODE_SIZE (mode), 0);
+ else
+ {
+ rtx mem = cfun->machine->sdmode_stack_slot;
+ gcc_assert (mem != NULL_RTX);
+
+ if (!eliminated)
+ {
+ mem = eliminate_regs (mem, VOIDmode, NULL_RTX);
+ cfun->machine->sdmode_stack_slot = mem;
+ eliminated = true;
+ }
+ return mem;
+ }
+}
+
+static tree
+rs6000_check_sdmode (tree *tp, int *walk_subtrees, void *data ATTRIBUTE_UNUSED)
+{
+ /* Don't walk into types. */
+ if (*tp == NULL_TREE || *tp == error_mark_node || TYPE_P (*tp))
+ {
+ *walk_subtrees = 0;
+ return NULL_TREE;
+ }
+
+ switch (TREE_CODE (*tp))
+ {
+ case VAR_DECL:
+ case PARM_DECL:
+ case FIELD_DECL:
+ case RESULT_DECL:
+ case REAL_CST:
+ if (TYPE_MODE (TREE_TYPE (*tp)) == SDmode)
+ return *tp;
+ break;
+ default:
+ break;
+ }
+
+ return NULL_TREE;
+}
+
+
+/* Allocate a 64-bit stack slot to be used for copying SDmode
+ values through if this function has any SDmode references. */
+
+static void
+rs6000_alloc_sdmode_stack_slot (void)
+{
+ tree t;
+ basic_block bb;
+ block_stmt_iterator bsi;
+
+ gcc_assert (cfun->machine->sdmode_stack_slot == NULL_RTX);
+
+ FOR_EACH_BB (bb)
+ for (bsi = bsi_start (bb); !bsi_end_p (bsi); bsi_next (&bsi))
+ {
+ tree ret = walk_tree_without_duplicates (bsi_stmt_ptr (bsi),
+ rs6000_check_sdmode, NULL);
+ if (ret)
+ {
+ rtx stack = assign_stack_local (DDmode, GET_MODE_SIZE (DDmode), 0);
+ cfun->machine->sdmode_stack_slot = adjust_address_nv (stack,
+ SDmode, 0);
+ return;
+ }
+ }
+
+ /* Check for any SDmode parameters of the function. */
+ for (t = DECL_ARGUMENTS (cfun->decl); t; t = TREE_CHAIN (t))
+ {
+ if (TREE_TYPE (t) == error_mark_node)
+ continue;
+
+ if (TYPE_MODE (TREE_TYPE (t)) == SDmode
+ || TYPE_MODE (DECL_ARG_TYPE (t)) == SDmode)
+ {
+ rtx stack = assign_stack_local (DDmode, GET_MODE_SIZE (DDmode), 0);
+ cfun->machine->sdmode_stack_slot = adjust_address_nv (stack,
+ SDmode, 0);
+ return;
+ }
+ }
+}
+
+static void
+rs6000_instantiate_decls (void)
+{
+ if (cfun->machine->sdmode_stack_slot != NULL_RTX)
+ instantiate_decl_rtl (cfun->machine->sdmode_stack_slot);
+}
+
/* Return the register class of a scratch register needed to copy IN into
or out of a register in CLASS in MODE. If it can be done directly,
NO_REGS is returned. */
/* Constants, memory, and FP registers can go into FP registers. */
if ((regno == -1 || FP_REGNO_P (regno))
&& (class == FLOAT_REGS || class == NON_SPECIAL_REGS))
- return NO_REGS;
+ return (mode != SDmode) ? NO_REGS : GENERAL_REGS;
/* Memory, and AltiVec registers can go into AltiVec registers. */
if ((regno == -1 || ALTIVEC_REGNO_P (regno))
emit_insn (gen_sync_compare_and_swapqhi_internal (wdst, mask,
oldval, newval, mem));
+ /* Shift the result back. */
+ emit_insn (gen_lshrsi3 (wdst, wdst, shift));
+
emit_move_insn (dst, gen_lowpart (mode, wdst));
}
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)
- reg_mode = DFmode;
+ else if (TARGET_E500_DOUBLE && (mode == TFmode || mode == TDmode))
+ reg_mode = DECIMAL_FLOAT_MODE_P (mode) ? DDmode : DFmode;
else
reg_mode = word_mode;
reg_mode_size = GET_MODE_SIZE (reg_mode);
#endif
|| (info_ptr->first_fp_reg_save != 64
&& !FP_SAVE_INLINE (info_ptr->first_fp_reg_save))
- || info_ptr->first_altivec_reg_save <= LAST_ALTIVEC_REGNO
|| (DEFAULT_ABI == ABI_V4 && current_function_calls_alloca)
|| info_ptr->calls_p
|| rs6000_ra_ever_killed ())
if (SPE_VECTOR_MODE (mode))
return true;
- if (TARGET_E500_DOUBLE && (mode == DFmode || mode == TFmode))
+ if (TARGET_E500_DOUBLE && (mode == DFmode || mode == TFmode
+ || mode == DDmode || mode == TDmode))
return true;
}
}
}
}
- if (TARGET_SPE)
- real = spe_synthesize_frame_save (real);
-
RTX_FRAME_RELATED_P (insn) = 1;
REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_FRAME_RELATED_EXPR,
real,
REG_NOTES (insn));
}
-/* Given an SPE frame note, return a PARALLEL of SETs with the
- original note, plus a synthetic register save. */
-
-static rtx
-spe_synthesize_frame_save (rtx real)
-{
- rtx synth, offset, reg, real2;
-
- if (GET_CODE (real) != SET
- || GET_MODE (SET_SRC (real)) != V2SImode)
- return real;
-
- /* For the SPE, registers saved in 64-bits, get a PARALLEL for their
- frame related note. The parallel contains a set of the register
- being saved, and another set to a synthetic register (n+1200).
- This is so we can differentiate between 64-bit and 32-bit saves.
- Words cannot describe this nastiness. */
-
- gcc_assert (GET_CODE (SET_DEST (real)) == MEM
- && GET_CODE (XEXP (SET_DEST (real), 0)) == PLUS
- && GET_CODE (SET_SRC (real)) == REG);
-
- /* Transform:
- (set (mem (plus (reg x) (const y)))
- (reg z))
- into:
- (set (mem (plus (reg x) (const y+4)))
- (reg z+1200))
- */
-
- real2 = copy_rtx (real);
- PUT_MODE (SET_DEST (real2), SImode);
- reg = SET_SRC (real2);
- real2 = replace_rtx (real2, reg, gen_rtx_REG (SImode, REGNO (reg)));
- synth = copy_rtx (real2);
-
- if (BYTES_BIG_ENDIAN)
- {
- offset = XEXP (XEXP (SET_DEST (real2), 0), 1);
- real2 = replace_rtx (real2, offset, GEN_INT (INTVAL (offset) + 4));
- }
-
- reg = SET_SRC (synth);
-
- synth = replace_rtx (synth, reg,
- gen_rtx_REG (SImode, REGNO (reg) + 1200));
-
- offset = XEXP (XEXP (SET_DEST (synth), 0), 1);
- synth = replace_rtx (synth, offset,
- GEN_INT (INTVAL (offset)
- + (BYTES_BIG_ENDIAN ? 0 : 4)));
-
- RTX_FRAME_RELATED_P (synth) = 1;
- RTX_FRAME_RELATED_P (real2) = 1;
- if (BYTES_BIG_ENDIAN)
- real = gen_rtx_PARALLEL (VOIDmode, gen_rtvec (2, synth, real2));
- else
- real = gen_rtx_PARALLEL (VOIDmode, gen_rtvec (2, real2, synth));
-
- return real;
-}
-
/* Returns an insn that has a vrsave set operation with the
appropriate CLOBBERs. */
/* Some cases that need register indexed addressing. */
if ((TARGET_ALTIVEC_ABI && ALTIVEC_VECTOR_MODE (mode))
- || (TARGET_E500_DOUBLE && mode == DFmode)
+ || (TARGET_E500_DOUBLE && (mode == DFmode || mode == DDmode))
|| (TARGET_SPE_ABI
&& SPE_VECTOR_MODE (mode)
&& !SPE_CONST_OFFSET_OK (offset)))
int_rtx = GEN_INT (offset);
if ((TARGET_SPE_ABI && SPE_VECTOR_MODE (mode))
- || (TARGET_E500_DOUBLE && mode == DFmode))
+ || (TARGET_E500_DOUBLE && (mode == DFmode || mode == DDmode)))
{
offset_rtx = gen_rtx_REG (Pmode, FIXED_SCRATCH);
emit_move_insn (offset_rtx, int_rtx);
if (spe_regs_addressable_via_sp)
{
- spe_save_area_ptr = sp_reg_rtx;
+ spe_save_area_ptr = frame_reg_rtx;
spe_offset = info->spe_gp_save_offset + sp_offset;
}
else
}
spe_save_area_ptr = gen_rtx_REG (Pmode, 11);
- emit_insn (gen_addsi3 (spe_save_area_ptr, sp_reg_rtx,
+ emit_insn (gen_addsi3 (spe_save_area_ptr, frame_reg_rtx,
GEN_INT (info->spe_gp_save_offset + sp_offset)));
spe_offset = 0;
return;
}
- /* Set sp_offset based on the stack push from the prologue. */
- if (info->total_size < 32767)
+ /* frame_reg_rtx + sp_offset points to the top of this stack frame. */
+ if (info->push_p)
sp_offset = info->total_size;
/* Restore AltiVec registers if needed. */
}
}
- /* Restore VRSAVE if needed. */
- if (TARGET_ALTIVEC && TARGET_ALTIVEC_VRSAVE
- && info->vrsave_mask != 0)
- {
- rtx addr, mem, reg;
-
- addr = gen_rtx_PLUS (Pmode, frame_reg_rtx,
- GEN_INT (info->vrsave_save_offset + sp_offset));
- mem = gen_frame_mem (SImode, addr);
- reg = gen_rtx_REG (SImode, 12);
- emit_move_insn (reg, mem);
-
- emit_insn (generate_set_vrsave (reg, info, 1));
- }
-
- sp_offset = 0;
-
/* If we have a frame pointer, a call to alloca, or a large stack
frame, restore the old stack pointer using the backchain. Otherwise,
we know what size to update it with. */
emit_move_insn (frame_reg_rtx,
gen_rtx_MEM (Pmode, sp_reg_rtx));
+ sp_offset = 0;
}
- else if (info->push_p)
+ else if (info->push_p
+ && DEFAULT_ABI != ABI_V4
+ && !current_function_calls_eh_return)
{
- if (DEFAULT_ABI == ABI_V4
- || current_function_calls_eh_return)
- sp_offset = info->total_size;
- else
- {
- emit_insn (TARGET_32BIT
- ? gen_addsi3 (sp_reg_rtx, sp_reg_rtx,
- GEN_INT (info->total_size))
- : gen_adddi3 (sp_reg_rtx, sp_reg_rtx,
- GEN_INT (info->total_size)));
- }
+ emit_insn (TARGET_32BIT
+ ? gen_addsi3 (sp_reg_rtx, sp_reg_rtx,
+ GEN_INT (info->total_size))
+ : gen_adddi3 (sp_reg_rtx, sp_reg_rtx,
+ GEN_INT (info->total_size)));
+ sp_offset = 0;
+ }
+
+ /* Restore VRSAVE if needed. */
+ if (TARGET_ALTIVEC && TARGET_ALTIVEC_VRSAVE
+ && info->vrsave_mask != 0)
+ {
+ rtx addr, mem, reg;
+
+ addr = gen_rtx_PLUS (Pmode, frame_reg_rtx,
+ GEN_INT (info->vrsave_save_offset + sp_offset));
+ mem = gen_frame_mem (SImode, addr);
+ reg = gen_rtx_REG (SImode, 12);
+ emit_move_insn (reg, mem);
+
+ emit_insn (generate_set_vrsave (reg, info, 1));
}
/* Get the old lr if we saved it. */
&& info->spe_64bit_regs_used != 0
&& info->first_gp_reg_save != 32)
{
- rtx spe_save_area_ptr;
/* Determine whether we can address all of the registers that need
to be saved with an offset from the stack pointer that fits in
the small const field for SPE memory instructions. */
int spe_offset;
if (spe_regs_addressable_via_sp)
- {
- spe_save_area_ptr = frame_reg_rtx;
- spe_offset = info->spe_gp_save_offset + sp_offset;
- }
+ spe_offset = info->spe_gp_save_offset + sp_offset;
else
{
+ rtx old_frame_reg_rtx = frame_reg_rtx;
/* Make r11 point to the start of the SPE save area. We worried about
not clobbering it when we were saving registers in the prologue.
There's no need to worry here because the static chain is passed
anew to every function. */
- spe_save_area_ptr = gen_rtx_REG (Pmode, 11);
-
- emit_insn (gen_addsi3 (spe_save_area_ptr, frame_reg_rtx,
+ if (frame_reg_rtx == sp_reg_rtx)
+ frame_reg_rtx = gen_rtx_REG (Pmode, 11);
+ emit_insn (gen_addsi3 (frame_reg_rtx, old_frame_reg_rtx,
GEN_INT (info->spe_gp_save_offset + sp_offset)));
+ /* Keep the invariant that frame_reg_rtx + sp_offset points
+ at the top of the stack frame. */
+ sp_offset = -info->spe_gp_save_offset;
spe_offset = 0;
}
gcc_assert (SPE_CONST_OFFSET_OK (spe_offset + reg_size * i));
offset = GEN_INT (spe_offset + reg_size * i);
- addr = gen_rtx_PLUS (Pmode, spe_save_area_ptr, offset);
+ addr = gen_rtx_PLUS (Pmode, frame_reg_rtx, offset);
mem = gen_rtx_MEM (V2SImode, addr);
emit_move_insn (gen_rtx_REG (reg_mode, info->first_gp_reg_save + i),
/* This blockage is needed so that sched doesn't decide to move
the sp change before the register restores. */
rs6000_emit_stack_tie ();
- if (TARGET_SPE_ABI
- && info->spe_64bit_regs_used != 0
- && info->first_gp_reg_save != 32)
- emit_insn (gen_addsi3 (sp_reg_rtx, gen_rtx_REG (Pmode, 11),
- GEN_INT (-(info->spe_gp_save_offset + sp_offset))));
+ if (sp_offset != 0)
+ emit_insn (gen_addsi3 (sp_reg_rtx, frame_reg_rtx,
+ GEN_INT (sp_offset)));
else
emit_move_insn (sp_reg_rtx, frame_reg_rtx);
}
switch (mode)
{
case SFmode:
+ case SDmode:
bits = 0x2;
break;
final_start_function (insn, file, 1);
final (insn, file, 1);
final_end_function ();
+ free_after_compilation (cfun);
reload_completed = 0;
epilogue_completed = 0;
|| (GET_CODE (XEXP (b, 0)) == PLUS
&& GET_CODE (XEXP (XEXP (b, 0), 1)) == CONST_INT)))
{
- HOST_WIDE_INT val0 = 0, val1 = 0;
+ HOST_WIDE_INT val0 = 0, val1 = 0, val_diff;
rtx reg0, reg1;
- int val_diff;
if (GET_CODE (XEXP (a, 0)) == PLUS)
{
val_diff = val1 - val0;
return ((REGNO (reg0) == REGNO (reg1))
- && (val_diff == INTVAL (MEM_SIZE (a))
- || val_diff == -INTVAL (MEM_SIZE (b))));
+ && ((MEM_SIZE (a) && val_diff == INTVAL (MEM_SIZE (a)))
+ || (MEM_SIZE (b) && val_diff == -INTVAL (MEM_SIZE (b)))));
}
return false;
case CPU_PPC7400:
case CPU_PPC8540:
case CPU_CELL:
+ case CPU_PPCE300C2:
+ case CPU_PPCE300C3:
return 2;
case CPU_RIOS2:
case CPU_PPC604:
int i, j;
bool ret = false;
+ /* stack_tie does not produce any real memory traffic. */
+ if (GET_CODE (pat) == UNSPEC
+ && XINT (pat, 1) == UNSPEC_TIE)
+ return false;
+
if (GET_CODE (pat) == MEM)
return true;
return 4 + rs6000_register_move_cost (mode, class, GENERAL_REGS);
}
+/* Returns a code for a target-specific builtin that implements
+ reciprocal of the function, or NULL_TREE if not available. */
+
+static tree
+rs6000_builtin_reciprocal (unsigned int fn, bool md_fn,
+ bool sqrt ATTRIBUTE_UNUSED)
+{
+ if (! (TARGET_RECIP && TARGET_PPC_GFXOPT && !optimize_size
+ && flag_finite_math_only && !flag_trapping_math
+ && flag_unsafe_math_optimizations))
+ return NULL_TREE;
+
+ if (md_fn)
+ return NULL_TREE;
+ else
+ switch (fn)
+ {
+ case BUILT_IN_SQRTF:
+ return rs6000_builtin_decls[RS6000_BUILTIN_RSQRTF];
+
+ default:
+ return NULL_TREE;
+ }
+}
+
/* Newton-Raphson approximation of single-precision floating point divide n/d.
Assumes no trapping math and finite arguments. */
void
-rs6000_emit_swdivsf (rtx res, rtx n, rtx d)
+rs6000_emit_swdivsf (rtx dst, rtx n, rtx d)
{
rtx x0, e0, e1, y1, u0, v0, one;
emit_insn (gen_rtx_SET (VOIDmode, v0,
gen_rtx_MINUS (SFmode, n,
gen_rtx_MULT (SFmode, d, u0))));
- /* res = u0 + v0 * y1 */
- emit_insn (gen_rtx_SET (VOIDmode, res,
+ /* dst = u0 + v0 * y1 */
+ emit_insn (gen_rtx_SET (VOIDmode, dst,
gen_rtx_PLUS (SFmode,
gen_rtx_MULT (SFmode, v0, y1), u0)));
}
Assumes no trapping math and finite arguments. */
void
-rs6000_emit_swdivdf (rtx res, rtx n, rtx d)
+rs6000_emit_swdivdf (rtx dst, rtx n, rtx d)
{
rtx x0, e0, e1, e2, y1, y2, y3, u0, v0, one;
emit_insn (gen_rtx_SET (VOIDmode, v0,
gen_rtx_MINUS (DFmode, n,
gen_rtx_MULT (DFmode, d, u0))));
- /* res = u0 + v0 * y3 */
- emit_insn (gen_rtx_SET (VOIDmode, res,
+ /* dst = u0 + v0 * y3 */
+ emit_insn (gen_rtx_SET (VOIDmode, dst,
gen_rtx_PLUS (DFmode,
gen_rtx_MULT (DFmode, v0, y3), u0)));
}
+/* Newton-Raphson approximation of single-precision floating point rsqrt.
+ Assumes no trapping math and finite arguments. */
+
+void
+rs6000_emit_swrsqrtsf (rtx dst, rtx src)
+{
+ rtx x0, x1, x2, y1, u0, u1, u2, v0, v1, v2, t0,
+ half, one, halfthree, c1, cond, label;
+
+ x0 = gen_reg_rtx (SFmode);
+ x1 = gen_reg_rtx (SFmode);
+ x2 = gen_reg_rtx (SFmode);
+ y1 = gen_reg_rtx (SFmode);
+ u0 = gen_reg_rtx (SFmode);
+ u1 = gen_reg_rtx (SFmode);
+ u2 = gen_reg_rtx (SFmode);
+ v0 = gen_reg_rtx (SFmode);
+ v1 = gen_reg_rtx (SFmode);
+ v2 = gen_reg_rtx (SFmode);
+ t0 = gen_reg_rtx (SFmode);
+ halfthree = gen_reg_rtx (SFmode);
+ cond = gen_rtx_REG (CCFPmode, CR1_REGNO);
+ label = gen_rtx_LABEL_REF (VOIDmode, gen_label_rtx ());
+
+ /* check 0.0, 1.0, NaN, Inf by testing src * src = src */
+ emit_insn (gen_rtx_SET (VOIDmode, t0,
+ gen_rtx_MULT (SFmode, src, src)));
+
+ emit_insn (gen_rtx_SET (VOIDmode, cond,
+ gen_rtx_COMPARE (CCFPmode, t0, src)));
+ c1 = gen_rtx_EQ (VOIDmode, cond, const0_rtx);
+ emit_unlikely_jump (c1, label);
+
+ half = force_reg (SFmode, CONST_DOUBLE_FROM_REAL_VALUE (dconsthalf, SFmode));
+ one = force_reg (SFmode, CONST_DOUBLE_FROM_REAL_VALUE (dconst1, SFmode));
+
+ /* halfthree = 1.5 = 1.0 + 0.5 */
+ emit_insn (gen_rtx_SET (VOIDmode, halfthree,
+ gen_rtx_PLUS (SFmode, one, half)));
+
+ /* x0 = rsqrt estimate */
+ emit_insn (gen_rtx_SET (VOIDmode, x0,
+ gen_rtx_UNSPEC (SFmode, gen_rtvec (1, src),
+ UNSPEC_RSQRT)));
+
+ /* y1 = 0.5 * src = 1.5 * src - src -> fewer constants */
+ emit_insn (gen_rtx_SET (VOIDmode, y1,
+ gen_rtx_MINUS (SFmode,
+ gen_rtx_MULT (SFmode, src, halfthree),
+ src)));
+
+ /* x1 = x0 * (1.5 - y1 * (x0 * x0)) */
+ emit_insn (gen_rtx_SET (VOIDmode, u0,
+ gen_rtx_MULT (SFmode, x0, x0)));
+ emit_insn (gen_rtx_SET (VOIDmode, v0,
+ gen_rtx_MINUS (SFmode,
+ halfthree,
+ gen_rtx_MULT (SFmode, y1, u0))));
+ emit_insn (gen_rtx_SET (VOIDmode, x1,
+ gen_rtx_MULT (SFmode, x0, v0)));
+
+ /* x2 = x1 * (1.5 - y1 * (x1 * x1)) */
+ emit_insn (gen_rtx_SET (VOIDmode, u1,
+ gen_rtx_MULT (SFmode, x1, x1)));
+ emit_insn (gen_rtx_SET (VOIDmode, v1,
+ gen_rtx_MINUS (SFmode,
+ halfthree,
+ gen_rtx_MULT (SFmode, y1, u1))));
+ emit_insn (gen_rtx_SET (VOIDmode, x2,
+ gen_rtx_MULT (SFmode, x1, v1)));
+
+ /* dst = x2 * (1.5 - y1 * (x2 * x2)) */
+ emit_insn (gen_rtx_SET (VOIDmode, u2,
+ gen_rtx_MULT (SFmode, x2, x2)));
+ emit_insn (gen_rtx_SET (VOIDmode, v2,
+ gen_rtx_MINUS (SFmode,
+ halfthree,
+ gen_rtx_MULT (SFmode, y1, u2))));
+ emit_insn (gen_rtx_SET (VOIDmode, dst,
+ gen_rtx_MULT (SFmode, x2, v2)));
+
+ emit_label (XEXP (label, 0));
+}
+
/* Emit popcount intrinsic on TARGET_POPCNTB targets. DST is the
target, and SRC is the argument operand. */
|| POINTER_TYPE_P (valtype))
mode = TARGET_32BIT ? SImode : DImode;
- if (DECIMAL_FLOAT_MODE_P (mode))
- {
- 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;
- }
+ if (DECIMAL_FLOAT_MODE_P (mode) && TARGET_HARD_FLOAT && TARGET_FPRS)
+ /* _Decimal128 must use an even/odd register pair. */
+ regno = (mode == TDmode) ? FP_ARG_RETURN + 1 : FP_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
&& ALTIVEC_VECTOR_MODE (mode))
regno = ALTIVEC_ARG_RETURN;
else if (TARGET_E500_DOUBLE && TARGET_HARD_FLOAT
- && (mode == DFmode || mode == DCmode
- || mode == TFmode || mode == TCmode))
+ && (mode == DFmode || mode == DDmode || mode == DCmode
+ || mode == TFmode || mode == TDmode || mode == TCmode))
return spe_build_register_parallel (mode, GP_ARG_RETURN);
else
regno = GP_ARG_RETURN;
GEN_INT (4))));
}
- if (DECIMAL_FLOAT_MODE_P (mode))
- {
- 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;
- }
+ if (DECIMAL_FLOAT_MODE_P (mode) && TARGET_HARD_FLOAT && TARGET_FPRS)
+ /* _Decimal128 must use an even/odd register pair. */
+ regno = (mode == TDmode) ? FP_ARG_RETURN + 1 : FP_ARG_RETURN;
else if (SCALAR_FLOAT_MODE_P (mode)
&& TARGET_HARD_FLOAT && TARGET_FPRS)
regno = FP_ARG_RETURN;
else if (COMPLEX_MODE_P (mode) && targetm.calls.split_complex_arg)
return rs6000_complex_function_value (mode);
else if (TARGET_E500_DOUBLE && TARGET_HARD_FLOAT
- && (mode == DFmode || mode == DCmode
- || mode == TFmode || mode == TCmode))
+ && (mode == DFmode || mode == DDmode || mode == DCmode
+ || mode == TFmode || mode == TDmode || mode == TCmode))
return spe_build_register_parallel (mode, GP_ARG_RETURN);
else
regno = GP_ARG_RETURN;
if (TARGET_SPE
&& (SPE_VECTOR_MODE (GET_MODE (reg))
- || (TARGET_E500_DOUBLE && GET_MODE (reg) == DFmode)))
+ || (TARGET_E500_DOUBLE
+ && (GET_MODE (reg) == DFmode || GET_MODE (reg) == DDmode))))
;
else
return NULL_RTX;