#include "reload.h"
#include "cfglayout.h"
#include "sched-int.h"
-#include "tree-gimple.h"
+#include "gimple.h"
#include "tree-flow.h"
#include "intl.h"
#include "params.h"
1, /* prefetch streams /*/
};
+/* Instruction costs on PPCE500MC processors. */
+static const
+struct processor_costs ppce500mc_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 (14), /* divsi */
+ COSTS_N_INSNS (14), /* divdi */
+ COSTS_N_INSNS (8), /* fp */
+ COSTS_N_INSNS (10), /* dmul */
+ COSTS_N_INSNS (36), /* sdiv */
+ COSTS_N_INSNS (66), /* ddiv */
+ 64, /* cache line size */
+ 32, /* l1 cache */
+ 128, /* l2 cache */
+ 1, /* prefetch streams /*/
+};
+
/* Instruction costs on POWER4 and POWER5 processors. */
static const
struct processor_costs power4_cost = {
static void emit_frame_save (rtx, rtx, enum machine_mode, unsigned int,
int, HOST_WIDE_INT);
static rtx gen_frame_mem_offset (enum machine_mode, rtx, int);
-static void rs6000_emit_allocate_stack (HOST_WIDE_INT, int);
+static void rs6000_emit_allocate_stack (HOST_WIDE_INT, int, int);
static unsigned rs6000_hash_constant (rtx);
static unsigned toc_hash_function (const void *);
static int toc_hash_eq (const void *, const void *);
static bool legitimate_lo_sum_address_p (enum machine_mode, rtx, int);
static struct machine_function * rs6000_init_machine_status (void);
static bool rs6000_assemble_integer (rtx, unsigned int, int);
-static bool no_global_regs_above (int);
+static bool no_global_regs_above (int, bool);
#ifdef HAVE_GAS_HIDDEN
static void rs6000_assemble_visibility (tree, int);
#endif
static const char *rs6000_mangle_type (const_tree);
extern const struct attribute_spec rs6000_attribute_table[];
static void rs6000_set_default_type_attributes (tree);
+static rtx rs6000_savres_routine_sym (rs6000_stack_t *, bool, bool, bool);
+static void rs6000_emit_stack_reset (rs6000_stack_t *, rtx, rtx, int, bool);
+static rtx rs6000_make_savres_rtx (rs6000_stack_t *, rtx, int,
+ enum machine_mode, bool, bool, bool);
static bool rs6000_reg_live_or_pic_offset_p (int);
+static int rs6000_savres_strategy (rs6000_stack_t *, bool, int, int);
+static void rs6000_restore_saved_cr (rtx, int);
static void rs6000_output_function_prologue (FILE *, HOST_WIDE_INT);
static void rs6000_output_function_epilogue (FILE *, HOST_WIDE_INT);
static void rs6000_output_mi_thunk (FILE *, tree, HOST_WIDE_INT, HOST_WIDE_INT,
static void rs6000_xcoff_file_end (void);
#endif
static int rs6000_variable_issue (FILE *, int, rtx, int);
-static bool rs6000_rtx_costs (rtx, int, int, int *);
+static bool rs6000_rtx_costs (rtx, int, int, int *, bool);
static int rs6000_adjust_cost (rtx, rtx, rtx, int);
static void rs6000_sched_init (FILE *, int, int);
static bool is_microcoded_insn (rtx);
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 void * rs6000_alloc_sched_context (void);
+static void rs6000_init_sched_context (void *, bool);
+static void rs6000_set_sched_context (void *);
+static void rs6000_free_sched_context (void *);
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);
static tree rs6000_builtin_conversion (enum tree_code, tree);
+static tree rs6000_builtin_vec_perm (tree, tree *);
static void def_builtin (int, const char *, tree, int);
static bool rs6000_vector_alignment_reachable (const_tree, bool);
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 tree rs6000_gimplify_va_arg (tree, tree, gimple_seq *, gimple_seq *);
static bool rs6000_must_pass_in_stack (enum machine_mode, const_tree);
static bool rs6000_scalar_mode_supported_p (enum machine_mode);
static bool rs6000_vector_mode_supported_p (enum machine_mode);
#undef TARGET_SCHED_FIRST_CYCLE_MULTIPASS_DFA_LOOKAHEAD_GUARD
#define TARGET_SCHED_FIRST_CYCLE_MULTIPASS_DFA_LOOKAHEAD_GUARD rs6000_use_sched_lookahead_guard
+#undef TARGET_SCHED_ALLOC_SCHED_CONTEXT
+#define TARGET_SCHED_ALLOC_SCHED_CONTEXT rs6000_alloc_sched_context
+#undef TARGET_SCHED_INIT_SCHED_CONTEXT
+#define TARGET_SCHED_INIT_SCHED_CONTEXT rs6000_init_sched_context
+#undef TARGET_SCHED_SET_SCHED_CONTEXT
+#define TARGET_SCHED_SET_SCHED_CONTEXT rs6000_set_sched_context
+#undef TARGET_SCHED_FREE_SCHED_CONTEXT
+#define TARGET_SCHED_FREE_SCHED_CONTEXT rs6000_free_sched_context
+
#undef TARGET_VECTORIZE_BUILTIN_MASK_FOR_LOAD
#define TARGET_VECTORIZE_BUILTIN_MASK_FOR_LOAD rs6000_builtin_mask_for_load
#undef TARGET_VECTORIZE_BUILTIN_MUL_WIDEN_EVEN
#define TARGET_VECTORIZE_BUILTIN_MUL_WIDEN_ODD rs6000_builtin_mul_widen_odd
#undef TARGET_VECTORIZE_BUILTIN_CONVERSION
#define TARGET_VECTORIZE_BUILTIN_CONVERSION rs6000_builtin_conversion
+#undef TARGET_VECTORIZE_BUILTIN_VEC_PERM
+#define TARGET_VECTORIZE_BUILTIN_VEC_PERM rs6000_builtin_vec_perm
#undef TARGET_VECTOR_ALIGNMENT_REACHABLE
#define TARGET_VECTOR_ALIGNMENT_REACHABLE rs6000_vector_alignment_reachable
#undef TARGET_RTX_COSTS
#define TARGET_RTX_COSTS rs6000_rtx_costs
#undef TARGET_ADDRESS_COST
-#define TARGET_ADDRESS_COST hook_int_rtx_0
+#define TARGET_ADDRESS_COST hook_int_rtx_bool_0
#undef TARGET_VECTOR_OPAQUE_P
#define TARGET_VECTOR_OPAQUE_P rs6000_is_opaque_type
{"8548", PROCESSOR_PPC8540, POWERPC_BASE_MASK | MASK_STRICT_ALIGN},
{"e300c2", PROCESSOR_PPCE300C2, POWERPC_BASE_MASK | MASK_SOFT_FLOAT},
{"e300c3", PROCESSOR_PPCE300C3, POWERPC_BASE_MASK},
+ {"e500mc", PROCESSOR_PPCE500MC, POWERPC_BASE_MASK | MASK_PPC_GFXOPT},
{"860", PROCESSOR_MPCCORE, POWERPC_BASE_MASK | MASK_SOFT_FLOAT},
{"970", PROCESSOR_POWER4,
POWERPC_7400_MASK | MASK_PPC_GPOPT | MASK_MFCRF | MASK_POWERPC64},
{"power3", PROCESSOR_PPC630,
POWERPC_BASE_MASK | MASK_PPC_GFXOPT | MASK_POWERPC64},
{"power4", PROCESSOR_POWER4,
- POWERPC_BASE_MASK | MASK_PPC_GFXOPT | MASK_MFCRF | MASK_POWERPC64},
+ POWERPC_BASE_MASK | MASK_POWERPC64 | MASK_PPC_GPOPT | MASK_PPC_GFXOPT
+ | MASK_MFCRF},
{"power5", PROCESSOR_POWER5,
- POWERPC_BASE_MASK | MASK_POWERPC64 | MASK_PPC_GFXOPT
+ POWERPC_BASE_MASK | MASK_POWERPC64 | MASK_PPC_GPOPT | MASK_PPC_GFXOPT
| MASK_MFCRF | MASK_POPCNTB},
{"power5+", PROCESSOR_POWER5,
- POWERPC_BASE_MASK | MASK_POWERPC64 | MASK_PPC_GFXOPT
+ POWERPC_BASE_MASK | MASK_POWERPC64 | MASK_PPC_GPOPT | MASK_PPC_GFXOPT
| MASK_MFCRF | MASK_POPCNTB | MASK_FPRND},
{"power6", PROCESSOR_POWER6,
- POWERPC_7400_MASK | MASK_POWERPC64 | MASK_MFCRF | MASK_POPCNTB
- | MASK_FPRND | MASK_CMPB | MASK_DFP },
+ POWERPC_BASE_MASK | MASK_POWERPC64 | MASK_PPC_GPOPT | MASK_PPC_GFXOPT
+ | MASK_MFCRF | MASK_POPCNTB | MASK_FPRND | MASK_CMPB | MASK_DFP},
{"power6x", PROCESSOR_POWER6,
- POWERPC_7400_MASK | MASK_POWERPC64 | MASK_MFCRF | MASK_POPCNTB
- | MASK_FPRND | MASK_CMPB | MASK_MFPGPR | MASK_DFP },
+ POWERPC_BASE_MASK | MASK_POWERPC64 | MASK_PPC_GPOPT | MASK_PPC_GFXOPT
+ | MASK_MFCRF | MASK_POPCNTB | MASK_FPRND | MASK_CMPB | MASK_DFP
+ | MASK_MFPGPR},
+ {"power7", PROCESSOR_POWER5,
+ POWERPC_7400_MASK | MASK_POWERPC64 | MASK_PPC_GPOPT | MASK_MFCRF
+ | MASK_POPCNTB | MASK_FPRND | MASK_CMPB | MASK_DFP},
{"powerpc", PROCESSOR_POWERPC, POWERPC_BASE_MASK},
{"powerpc64", PROCESSOR_POWERPC64,
POWERPC_BASE_MASK | MASK_PPC_GFXOPT | MASK_POWERPC64},
}
}
- if (TARGET_E500)
+ if ((TARGET_E500 || rs6000_cpu == PROCESSOR_PPCE500MC)
+ && !rs6000_explicit_options.isel)
rs6000_isel = 1;
- if (rs6000_cpu == PROCESSOR_PPCE300C2 || rs6000_cpu == PROCESSOR_PPCE300C3)
+ if (rs6000_cpu == PROCESSOR_PPCE300C2 || rs6000_cpu == PROCESSOR_PPCE300C3
+ || rs6000_cpu == PROCESSOR_PPCE500MC)
{
if (TARGET_ALTIVEC)
error ("AltiVec not supported in this target");
SUB3TARGET_OVERRIDE_OPTIONS;
#endif
- if (TARGET_E500)
+ if (TARGET_E500 || rs6000_cpu == PROCESSOR_PPCE500MC)
{
- /* The e500 does not have string instructions, and we set
+ /* The e500 and e500mc do not have string instructions, and we set
MASK_STRING above when optimizing for size. */
if ((target_flags & MASK_STRING) != 0)
target_flags = target_flags & ~MASK_STRING;
rs6000_cost = &ppce300c2c3_cost;
break;
+ case PROCESSOR_PPCE500MC:
+ rs6000_cost = &ppce500mc_cost;
+ break;
+
case PROCESSOR_POWER4:
case PROCESSOR_POWER5:
rs6000_cost = &power4_cost;
return 0;
}
-/* Implement targetm.vectorize.builtin_conversion. */
+/* Implement targetm.vectorize.builtin_conversion.
+ Returns a decl of a function that implements conversion of an integer vector
+ into a floating-point vector, or vice-versa. TYPE is the type of the integer
+ side of the conversion.
+ Return NULL_TREE if it is not available. */
static tree
rs6000_builtin_conversion (enum tree_code code, tree type)
{
switch (code)
{
+ case FIX_TRUNC_EXPR:
+ switch (TYPE_MODE (type))
+ {
+ case V4SImode:
+ return TYPE_UNSIGNED (type)
+ ? rs6000_builtin_decls[ALTIVEC_BUILTIN_VCTUXS]
+ : rs6000_builtin_decls[ALTIVEC_BUILTIN_VCTSXS];
+ default:
+ return NULL_TREE;
+ }
+
case FLOAT_EXPR:
switch (TYPE_MODE (type))
{
case V4SImode:
- return TYPE_UNSIGNED (type) ?
- rs6000_builtin_decls[ALTIVEC_BUILTIN_VCFUX] :
- rs6000_builtin_decls[ALTIVEC_BUILTIN_VCFSX];
+ return TYPE_UNSIGNED (type)
+ ? rs6000_builtin_decls[ALTIVEC_BUILTIN_VCFUX]
+ : rs6000_builtin_decls[ALTIVEC_BUILTIN_VCFSX];
default:
return NULL_TREE;
}
+
default:
return NULL_TREE;
}
switch (TYPE_MODE (type))
{
case V8HImode:
- return TYPE_UNSIGNED (type) ?
- rs6000_builtin_decls[ALTIVEC_BUILTIN_VMULEUH] :
- rs6000_builtin_decls[ALTIVEC_BUILTIN_VMULESH];
+ return TYPE_UNSIGNED (type)
+ ? rs6000_builtin_decls[ALTIVEC_BUILTIN_VMULEUH]
+ : rs6000_builtin_decls[ALTIVEC_BUILTIN_VMULESH];
case V16QImode:
- return TYPE_UNSIGNED (type) ?
- rs6000_builtin_decls[ALTIVEC_BUILTIN_VMULEUB] :
- rs6000_builtin_decls[ALTIVEC_BUILTIN_VMULESB];
+ return TYPE_UNSIGNED (type)
+ ? rs6000_builtin_decls[ALTIVEC_BUILTIN_VMULEUB]
+ : rs6000_builtin_decls[ALTIVEC_BUILTIN_VMULESB];
default:
return NULL_TREE;
}
switch (TYPE_MODE (type))
{
case V8HImode:
- return TYPE_UNSIGNED (type) ?
- rs6000_builtin_decls[ALTIVEC_BUILTIN_VMULOUH] :
- rs6000_builtin_decls[ALTIVEC_BUILTIN_VMULOSH];
+ return TYPE_UNSIGNED (type)
+ ? rs6000_builtin_decls[ALTIVEC_BUILTIN_VMULOUH]
+ : rs6000_builtin_decls[ALTIVEC_BUILTIN_VMULOSH];
case V16QImode:
- return TYPE_UNSIGNED (type) ?
- rs6000_builtin_decls[ALTIVEC_BUILTIN_VMULOUB] :
- rs6000_builtin_decls[ALTIVEC_BUILTIN_VMULOSB];
+ return TYPE_UNSIGNED (type)
+ ? rs6000_builtin_decls[ALTIVEC_BUILTIN_VMULOUB]
+ : rs6000_builtin_decls[ALTIVEC_BUILTIN_VMULOSB];
default:
return NULL_TREE;
}
}
}
+/* Implement targetm.vectorize.builtin_vec_perm. */
+tree
+rs6000_builtin_vec_perm (tree type, tree *mask_element_type)
+{
+ tree d;
+
+ *mask_element_type = unsigned_char_type_node;
+
+ switch (TYPE_MODE (type))
+ {
+ case V16QImode:
+ d = rs6000_builtin_decls[ALTIVEC_BUILTIN_VPERM_16QI];
+ break;
+
+ case V8HImode:
+ d = rs6000_builtin_decls[ALTIVEC_BUILTIN_VPERM_8HI];
+ break;
+
+ case V4SImode:
+ d = rs6000_builtin_decls[ALTIVEC_BUILTIN_VPERM_4SI];
+ break;
+
+ case V4SFmode:
+ d = rs6000_builtin_decls[ALTIVEC_BUILTIN_VPERM_4SF];
+ break;
+
+ default:
+ return NULL_TREE;
+ }
+
+ gcc_assert (d);
+ return d;
+}
+
/* Handle generic options of the form -mfoo=yes/no.
NAME is the option name.
VALUE is the option value.
Skip section anchors for Objective C and Objective C++
until front-ends fixed. */
if (!TARGET_MACHO && lang_hooks.name[4] != 'O')
- flag_section_anchors = 1;
+ flag_section_anchors = 2;
}
/* Implement TARGET_HANDLE_OPTION. */
corresponding element of the vector, but for V4SFmode and V2SFmode,
the corresponding "float" is interpreted as an SImode integer. */
-static HOST_WIDE_INT
+HOST_WIDE_INT
const_vector_elt_as_int (rtx op, unsigned int elt)
{
rtx tmp = CONST_VECTOR_ELT (op, elt);
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;
+ rtx x, new_rtx, tmp, constant_op, op1, op2;
int i;
for (i = 0; i < n_elts; ++i)
if (n_var == 2)
{
/* The vector is initialized only with non-constants. */
- new = gen_rtx_VEC_CONCAT (V2SFmode, XVECEXP (vals, 0, 0),
+ new_rtx = gen_rtx_VEC_CONCAT (V2SFmode, XVECEXP (vals, 0, 0),
XVECEXP (vals, 0, 1));
- emit_move_insn (target, new);
+ emit_move_insn (target, new_rtx);
return;
}
emit_move_insn (tmp, constant_op);
if (CONSTANT_P (op1))
- new = gen_rtx_VEC_CONCAT (V2SFmode, tmp, op2);
+ new_rtx = gen_rtx_VEC_CONCAT (V2SFmode, tmp, op2);
else
- new = gen_rtx_VEC_CONCAT (V2SFmode, op1, tmp);
+ new_rtx = gen_rtx_VEC_CONCAT (V2SFmode, op1, tmp);
- emit_move_insn (target, new);
+ emit_move_insn (target, new_rtx);
}
void
if (TARGET_E500_DOUBLE)
{
/* Reject (subreg:SI (reg:DF)); likewise with subreg:DI or
- subreg:TI and reg:TF. */
+ subreg:TI and reg:TF. Decimal float modes are like integer
+ modes (only low part of each register used) for this
+ purpose. */
if (GET_CODE (op) == SUBREG
- && (mode == SImode || mode == DImode || mode == TImode)
+ && (mode == SImode || mode == DImode || mode == TImode
+ || mode == DDmode || mode == TDmode)
&& REG_P (SUBREG_REG (op))
&& (GET_MODE (SUBREG_REG (op)) == DFmode
- || GET_MODE (SUBREG_REG (op)) == TFmode
- || GET_MODE (SUBREG_REG (op)) == DDmode
- || GET_MODE (SUBREG_REG (op)) == TDmode))
+ || GET_MODE (SUBREG_REG (op)) == TFmode))
return true;
/* Reject (subreg:DF (reg:DI)); likewise with subreg:TF and
reg:TI. */
if (GET_CODE (op) == SUBREG
- && (mode == DFmode || mode == TFmode
- || mode == DDmode || mode == TDmode)
+ && (mode == DFmode || mode == TFmode)
&& REG_P (SUBREG_REG (op))
&& (GET_MODE (SUBREG_REG (op)) == DImode
- || GET_MODE (SUBREG_REG (op)) == TImode))
+ || GET_MODE (SUBREG_REG (op)) == TImode
+ || GET_MODE (SUBREG_REG (op)) == DDmode
+ || GET_MODE (SUBREG_REG (op)) == TDmode))
return true;
}
return SPE_CONST_OFFSET_OK (offset);
case DFmode:
- case DDmode:
if (TARGET_E500_DOUBLE)
return SPE_CONST_OFFSET_OK (offset);
+ case DDmode:
case DImode:
/* On e500v2, we may have:
break;
case TFmode:
- case TDmode:
if (TARGET_E500_DOUBLE)
return (SPE_CONST_OFFSET_OK (offset)
&& SPE_CONST_OFFSET_OK (offset + 8));
+ case TDmode:
case TImode:
if (mode == TFmode || mode == TDmode || !TARGET_POWERPC64)
extra = 12;
&& !(SPE_VECTOR_MODE (mode)
|| ALTIVEC_VECTOR_MODE (mode)
|| (TARGET_E500_DOUBLE && (mode == DFmode || mode == TFmode
- || mode == DImode))))
+ || mode == DImode || mode == DDmode
+ || mode == TDmode))))
{
HOST_WIDE_INT high_int, low_int;
rtx sum;
&& ((TARGET_HARD_FLOAT && TARGET_FPRS)
|| TARGET_POWERPC64
|| ((mode != DImode && mode != DFmode && mode != DDmode)
- || TARGET_E500_DOUBLE))
+ || (TARGET_E500_DOUBLE && mode != DDmode)))
&& (TARGET_POWERPC64 || mode != DImode)
&& mode != TImode
&& mode != TFmode
reg + offset] is not a legitimate addressing mode. */
y = gen_rtx_PLUS (Pmode, op1, op2);
- if (GET_MODE_SIZE (mode) > 8 && REG_P (op2))
+ if ((GET_MODE_SIZE (mode) > 8 || mode == DDmode) && REG_P (op2))
return force_reg (Pmode, y);
else
return y;
insn = emit_call_insn (insn);
RTL_CONST_CALL_P (insn) = 1;
use_reg (&CALL_INSN_FUNCTION_USAGE (insn), r3);
+ if (DEFAULT_ABI == ABI_V4 && TARGET_SECURE_PLT && flag_pic)
+ use_reg (&CALL_INSN_FUNCTION_USAGE (insn), pic_offset_table_rtx);
insn = get_insns ();
end_sequence ();
emit_libcall_block (insn, dest, r3, addr);
insn = emit_call_insn (insn);
RTL_CONST_CALL_P (insn) = 1;
use_reg (&CALL_INSN_FUNCTION_USAGE (insn), r3);
+ if (DEFAULT_ABI == ABI_V4 && TARGET_SECURE_PLT && flag_pic)
+ use_reg (&CALL_INSN_FUNCTION_USAGE (insn), pic_offset_table_rtx);
insn = get_insns ();
end_sequence ();
tmp1 = gen_reg_rtx (Pmode);
&& mode != TDmode
&& ((TARGET_HARD_FLOAT && TARGET_FPRS)
|| TARGET_POWERPC64
- || ((mode != DFmode && mode != DDmode) || TARGET_E500_DOUBLE))
+ || (mode != DFmode && mode != DDmode)
+ || (TARGET_E500_DOUBLE && mode != DDmode))
&& (TARGET_POWERPC64 || mode != DImode)
&& legitimate_indexed_address_p (x, reg_ok_strict))
return 1;
case LO_SUM:
return true;
- case PRE_INC:
- case PRE_DEC:
+ /* Auto-increment cases are now treated generically in recog.c. */
case PRE_MODIFY:
return TARGET_UPDATE;
would require function_arg and rs6000_spe_function_arg to handle
SCmode so as to pass the value correctly in a pair of
registers. */
- if (TARGET_E500_DOUBLE && FLOAT_MODE_P (mode) && mode != SCmode)
+ if (TARGET_E500_DOUBLE && FLOAT_MODE_P (mode) && mode != SCmode
+ && !DECIMAL_FLOAT_MODE_P (mode))
return (GET_MODE_SIZE (mode) + UNITS_PER_FP_WORD - 1) / UNITS_PER_FP_WORD;
return (GET_MODE_SIZE (mode) + UNITS_PER_WORD - 1) / UNITS_PER_WORD;
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 == 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 || mode == DDmode)
+ if (mode == DFmode)
gregno += (1 - gregno) & 1;
/* Multi-reg args are not split between registers and stack. */
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);
valist = build_va_arg_indirect_ref (valist);
gpr = build3 (COMPONENT_REF, TREE_TYPE (f_gpr), valist, f_gpr, NULL_TREE);
- fpr = build3 (COMPONENT_REF, TREE_TYPE (f_fpr), valist, f_fpr, NULL_TREE);
- ovf = build3 (COMPONENT_REF, TREE_TYPE (f_ovf), valist, f_ovf, NULL_TREE);
- sav = build3 (COMPONENT_REF, TREE_TYPE (f_sav), valist, f_sav, NULL_TREE);
+ fpr = build3 (COMPONENT_REF, TREE_TYPE (f_fpr), unshare_expr (valist),
+ f_fpr, NULL_TREE);
+ ovf = build3 (COMPONENT_REF, TREE_TYPE (f_ovf), unshare_expr (valist),
+ f_ovf, NULL_TREE);
+ sav = build3 (COMPONENT_REF, TREE_TYPE (f_sav), unshare_expr (valist),
+ f_sav, NULL_TREE);
/* Count number of gp and fp argument registers used. */
words = crtl->args.info.words;
if (cfun->va_list_gpr_size)
{
- t = build2 (GIMPLE_MODIFY_STMT, TREE_TYPE (gpr), gpr,
+ t = build2 (MODIFY_EXPR, TREE_TYPE (gpr), gpr,
build_int_cst (NULL_TREE, n_gpr));
TREE_SIDE_EFFECTS (t) = 1;
expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
if (cfun->va_list_fpr_size)
{
- t = build2 (GIMPLE_MODIFY_STMT, TREE_TYPE (fpr), fpr,
+ t = build2 (MODIFY_EXPR, TREE_TYPE (fpr), fpr,
build_int_cst (NULL_TREE, n_fpr));
TREE_SIDE_EFFECTS (t) = 1;
expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
if (words != 0)
t = build2 (POINTER_PLUS_EXPR, TREE_TYPE (ovf), t,
size_int (words * UNITS_PER_WORD));
- t = build2 (GIMPLE_MODIFY_STMT, TREE_TYPE (ovf), ovf, t);
+ t = build2 (MODIFY_EXPR, TREE_TYPE (ovf), ovf, t);
TREE_SIDE_EFFECTS (t) = 1;
expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
if (cfun->machine->varargs_save_offset)
t = build2 (POINTER_PLUS_EXPR, TREE_TYPE (sav), t,
size_int (cfun->machine->varargs_save_offset));
- t = build2 (GIMPLE_MODIFY_STMT, TREE_TYPE (sav), sav, t);
+ t = build2 (MODIFY_EXPR, TREE_TYPE (sav), sav, t);
TREE_SIDE_EFFECTS (t) = 1;
expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
}
/* Implement va_arg. */
tree
-rs6000_gimplify_va_arg (tree valist, tree type, tree *pre_p, tree *post_p)
+rs6000_gimplify_va_arg (tree valist, tree type, gimple_seq *pre_p,
+ gimple_seq *post_p)
{
tree f_gpr, f_fpr, f_res, f_ovf, f_sav;
tree gpr, fpr, ovf, sav, reg, t, u;
int align;
tree ptrtype = build_pointer_type (type);
int regalign = 0;
+ gimple stmt;
if (pass_by_reference (NULL, TYPE_MODE (type), type, false))
{
if (elem_size < UNITS_PER_WORD)
{
tree real_part, imag_part;
- tree post = NULL_TREE;
+ gimple_seq post = NULL;
real_part = rs6000_gimplify_va_arg (valist, elem_type, pre_p,
&post);
/* Copy the value into a temporary, lest the formal temporary
be reused out from under us. */
real_part = get_initialized_tmp_var (real_part, pre_p, &post);
- append_to_statement_list (post, pre_p);
+ gimple_seq_add_seq (pre_p, post);
imag_part = rs6000_gimplify_va_arg (valist, elem_type, pre_p,
post_p);
valist = build_va_arg_indirect_ref (valist);
gpr = build3 (COMPONENT_REF, TREE_TYPE (f_gpr), valist, f_gpr, NULL_TREE);
- fpr = build3 (COMPONENT_REF, TREE_TYPE (f_fpr), valist, f_fpr, NULL_TREE);
- ovf = build3 (COMPONENT_REF, TREE_TYPE (f_ovf), valist, f_ovf, NULL_TREE);
- sav = build3 (COMPONENT_REF, TREE_TYPE (f_sav), valist, f_sav, NULL_TREE);
+ fpr = build3 (COMPONENT_REF, TREE_TYPE (f_fpr), unshare_expr (valist),
+ f_fpr, NULL_TREE);
+ ovf = build3 (COMPONENT_REF, TREE_TYPE (f_ovf), unshare_expr (valist),
+ f_ovf, NULL_TREE);
+ sav = build3 (COMPONENT_REF, TREE_TYPE (f_sav), unshare_expr (valist),
+ f_sav, NULL_TREE);
size = int_size_in_bytes (type);
rsize = (size + 3) / 4;
if (n_reg == 2 && reg == gpr)
{
regalign = 1;
- u = build2 (BIT_AND_EXPR, TREE_TYPE (reg), reg,
+ u = build2 (BIT_AND_EXPR, TREE_TYPE (reg), unshare_expr (reg),
build_int_cst (TREE_TYPE (reg), n_reg - 1));
- u = build2 (POSTINCREMENT_EXPR, TREE_TYPE (reg), reg, u);
+ u = build2 (POSTINCREMENT_EXPR, TREE_TYPE (reg),
+ unshare_expr (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,
+ t = build2 (BIT_IOR_EXPR, TREE_TYPE (reg), unshare_expr (reg),
build_int_cst (TREE_TYPE (reg), 1));
- u = build2 (MODIFY_EXPR, void_type_node, reg, t);
+ u = build2 (MODIFY_EXPR, void_type_node, unshare_expr (reg), t);
}
t = fold_convert (TREE_TYPE (reg), size_int (8 - n_reg + 1));
if (sav_ofs)
t = build2 (POINTER_PLUS_EXPR, ptr_type_node, sav, size_int (sav_ofs));
- u = build2 (POSTINCREMENT_EXPR, TREE_TYPE (reg), reg,
+ u = build2 (POSTINCREMENT_EXPR, TREE_TYPE (reg), unshare_expr (reg),
build_int_cst (TREE_TYPE (reg), n_reg));
u = fold_convert (sizetype, u);
u = build2 (MULT_EXPR, sizetype, u, size_int (sav_scale));
/* _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)
+ if (!TARGET_POWERPC64
+ && TARGET_HARD_FLOAT && TARGET_FPRS
+ && 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);
+ gimplify_assign (addr, t, pre_p);
- t = build1 (GOTO_EXPR, void_type_node, lab_over);
- gimplify_and_add (t, pre_p);
+ gimple_seq_add_stmt (pre_p, gimple_build_goto (lab_over));
- t = build1 (LABEL_EXPR, void_type_node, lab_false);
- append_to_statement_list (t, pre_p);
+ stmt = gimple_build_label (lab_false);
+ gimple_seq_add_stmt (pre_p, stmt);
if ((n_reg == 2 && !regalign) || n_reg > 2)
{
/* Ensure that we don't find any more args in regs.
Alignment has taken care of for special cases. */
- t = build_gimple_modify_stmt (reg,
- build_int_cst (TREE_TYPE (reg), 8));
- gimplify_and_add (t, pre_p);
+ gimplify_assign (reg, build_int_cst (TREE_TYPE (reg), 8), pre_p);
}
}
}
gimplify_expr (&t, pre_p, NULL, is_gimple_val, fb_rvalue);
- u = build2 (GIMPLE_MODIFY_STMT, void_type_node, addr, t);
- gimplify_and_add (u, pre_p);
+ gimplify_assign (unshare_expr (addr), t, pre_p);
t = build2 (POINTER_PLUS_EXPR, TREE_TYPE (t), t, size_int (size));
- t = build2 (GIMPLE_MODIFY_STMT, TREE_TYPE (ovf), ovf, t);
- gimplify_and_add (t, pre_p);
+ gimplify_assign (unshare_expr (ovf), t, pre_p);
if (lab_over)
{
- t = build1 (LABEL_EXPR, void_type_node, lab_over);
- append_to_statement_list (t, pre_p);
+ stmt = gimple_build_label (lab_over);
+ gimple_seq_add_stmt (pre_p, stmt);
}
if (STRICT_ALIGNMENT
/* FIXME: There's got to be a nicer way to handle this case than
constructing a new CALL_EXPR. */
if (fcode == ALTIVEC_BUILTIN_VCFUX
- || fcode == ALTIVEC_BUILTIN_VCFSX)
+ || fcode == ALTIVEC_BUILTIN_VCFSX
+ || fcode == ALTIVEC_BUILTIN_VCTUXS
+ || fcode == ALTIVEC_BUILTIN_VCTSXS)
{
if (call_expr_nargs (exp) == 1)
exp = build_call_nary (TREE_TYPE (exp), CALL_EXPR_FN (exp),
{
node = copy_node (node);
TYPE_MAIN_VARIANT (node) = node;
+ TYPE_CANONICAL (node) = node;
return build_vector_type (node, nunits);
}
{
tree t;
basic_block bb;
- block_stmt_iterator bsi;
+ gimple_stmt_iterator gsi;
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))
+ for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi))
{
- tree ret = walk_tree_without_duplicates (bsi_stmt_ptr (bsi),
- rs6000_check_sdmode, NULL);
+ tree ret = walk_gimple_op (gsi_stmt (gsi), rs6000_check_sdmode, NULL);
if (ret)
{
rtx stack = assign_stack_local (DDmode, GET_MODE_SIZE (DDmode), 0);
}
/* 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,
+ or out of a register in RCLASS in MODE. If it can be done directly,
NO_REGS is returned. */
enum reg_class
-rs6000_secondary_reload_class (enum reg_class class,
+rs6000_secondary_reload_class (enum reg_class rclass,
enum machine_mode mode ATTRIBUTE_UNUSED,
rtx in)
{
On Darwin, pic addresses require a load from memory, which
needs a base register. */
- if (class != BASE_REGS
+ if (rclass != BASE_REGS
&& (GET_CODE (in) == SYMBOL_REF
|| GET_CODE (in) == HIGH
|| GET_CODE (in) == LABEL_REF
/* We can place anything into GENERAL_REGS and can put GENERAL_REGS
into anything. */
- if (class == GENERAL_REGS || class == BASE_REGS
+ if (rclass == GENERAL_REGS || rclass == BASE_REGS
|| (regno >= 0 && INT_REGNO_P (regno)))
return NO_REGS;
/* Constants, memory, and FP registers can go into FP registers. */
if ((regno == -1 || FP_REGNO_P (regno))
- && (class == FLOAT_REGS || class == NON_SPECIAL_REGS))
+ && (rclass == FLOAT_REGS || rclass == NON_SPECIAL_REGS))
return (mode != SDmode) ? NO_REGS : GENERAL_REGS;
/* Memory, and AltiVec registers can go into AltiVec registers. */
if ((regno == -1 || ALTIVEC_REGNO_P (regno))
- && class == ALTIVEC_REGS)
+ && rclass == ALTIVEC_REGS)
return NO_REGS;
/* We can copy among the CR registers. */
- if ((class == CR_REGS || class == CR0_REGS)
+ if ((rclass == CR_REGS || rclass == CR0_REGS)
&& regno >= 0 && CR_REGNO_P (regno))
return NO_REGS;
static struct machine_function *
rs6000_init_machine_status (void)
{
- return ggc_alloc_cleared (sizeof (machine_function));
+ return GGC_CNEW (machine_function);
}
\f
/* These macros test for integers and extract the low-order bits. */
fprintf (file, "0,%s", reg_names[REGNO (tmp)]);
else
{
- gcc_assert (GET_CODE (tmp) == PLUS
- && REG_P (XEXP (tmp, 0))
- && REG_P (XEXP (tmp, 1)));
+ if (!GET_CODE (tmp) == PLUS
+ || !REG_P (XEXP (tmp, 0))
+ || !REG_P (XEXP (tmp, 1)))
+ {
+ output_operand_lossage ("invalid %%y value, try using the 'Z' constraint");
+ break;
+ }
if (REGNO (XEXP (tmp, 0)) == 0)
fprintf (file, "%s,%s", reg_names[ REGNO (XEXP (tmp, 1)) ],
char *newname;
name = XSTR (symref, 0);
- newname = alloca (strlen (name) + sizeof ("@toc"));
+ newname = XALLOCAVEC (char, strlen (name) + sizeof ("@toc"));
strcpy (newname, name);
strcat (newname, "@toc");
XSTR (symref, 0) = newname;
enum machine_mode mode = GET_MODE (mem);
rtx label, x, cond = gen_rtx_REG (CCmode, CR0_REGNO);
- emit_insn (gen_memory_barrier ());
-
label = gen_rtx_LABEL_REF (VOIDmode, gen_label_rtx ());
emit_label (XEXP (label, 0));
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 || mode == TDmode))
- reg_mode = DECIMAL_FLOAT_MODE_P (mode) ? DDmode : DFmode;
+ else if (TARGET_E500_DOUBLE && mode == TFmode)
+ reg_mode = DFmode;
else
reg_mode = word_mode;
reg_mode_size = GET_MODE_SIZE (reg_mode);
{
/* Align stack so SPE GPR save area is aligned on a
double-word boundary. */
- if (info_ptr->spe_gp_size != 0)
+ if (info_ptr->spe_gp_size != 0 && info_ptr->cr_save_offset != 0)
info_ptr->spe_padding_size
= 8 - (-info_ptr->cr_save_offset % 8);
else
if (SPE_VECTOR_MODE (mode))
return true;
- if (TARGET_E500_DOUBLE && (mode == DFmode || mode == TFmode
- || mode == DDmode || mode == TDmode))
+ if (TARGET_E500_DOUBLE && (mode == DFmode || mode == TFmode))
return true;
}
}
/* Emit the correct code for allocating stack space, as insns.
If COPY_R12, make sure a copy of the old frame is left in r12.
+ If COPY_R11, make sure a copy of the old frame is left in r11,
+ in preference to r12 if COPY_R12.
The generated code may use hard register 0 as a temporary. */
static void
-rs6000_emit_allocate_stack (HOST_WIDE_INT size, int copy_r12)
+rs6000_emit_allocate_stack (HOST_WIDE_INT size, int copy_r12, int copy_r11)
{
rtx insn;
rtx stack_reg = gen_rtx_REG (Pmode, STACK_POINTER_REGNUM);
warning (0, "stack limit expression is not supported");
}
- if (copy_r12 || ! TARGET_UPDATE)
- emit_move_insn (gen_rtx_REG (Pmode, 12), stack_reg);
+ if (copy_r12 || copy_r11 || ! TARGET_UPDATE)
+ emit_move_insn (copy_r11
+ ? gen_rtx_REG (Pmode, 11)
+ : gen_rtx_REG (Pmode, 12),
+ stack_reg);
if (TARGET_UPDATE)
{
+ rtx par, set, mem;
+
if (size > 32767)
{
/* Need a note here so that try_split doesn't get confused. */
todec, stack_reg)
: gen_movdi_di_update (stack_reg, stack_reg,
todec, stack_reg));
+ /* Since we didn't use gen_frame_mem to generate the MEM, grab
+ it now and set the alias set/attributes. The above gen_*_update
+ calls will generate a PARALLEL with the MEM set being the first
+ operation. */
+ par = PATTERN (insn);
+ gcc_assert (GET_CODE (par) == PARALLEL);
+ set = XVECEXP (par, 0, 0);
+ gcc_assert (GET_CODE (set) == SET);
+ mem = SET_DEST (set);
+ gcc_assert (MEM_P (mem));
+ MEM_NOTRAP_P (mem) = 1;
+ set_mem_alias_set (mem, get_frame_alias_set ());
}
else
{
insn = emit_insn (TARGET_32BIT
? gen_addsi3 (stack_reg, stack_reg, todec)
: gen_adddi3 (stack_reg, stack_reg, todec));
- emit_move_insn (gen_rtx_MEM (Pmode, stack_reg),
- gen_rtx_REG (Pmode, 12));
+ emit_move_insn (gen_frame_mem (Pmode, stack_reg),
+ copy_r11
+ ? gen_rtx_REG (Pmode, 11)
+ : gen_rtx_REG (Pmode, 12));
}
RTX_FRAME_RELATED_P (insn) = 1;
/* Some cases that need register indexed addressing. */
if ((TARGET_ALTIVEC_ABI && ALTIVEC_VECTOR_MODE (mode))
- || (TARGET_E500_DOUBLE && (mode == DFmode || mode == DDmode))
+ || (TARGET_E500_DOUBLE && mode == DFmode)
|| (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 || mode == DDmode)))
+ || (TARGET_E500_DOUBLE && mode == DFmode))
{
offset_rtx = gen_rtx_REG (Pmode, FIXED_SCRATCH);
emit_move_insn (offset_rtx, int_rtx);
and cannot use stmw/lmw if there are any in its range. */
static bool
-no_global_regs_above (int first_greg)
+no_global_regs_above (int first, bool gpr)
{
int i;
- for (i = 0; i < 32 - first_greg; i++)
- if (global_regs[first_greg + i])
+ for (i = first; i < gpr ? 32 : 64 ; i++)
+ if (global_regs[i])
return false;
return true;
}
#define TARGET_FIX_AND_CONTINUE 0
#endif
+/* It's really GPR 13 and FPR 14, but we need the smaller of the two. */
+#define FIRST_SAVRES_REGISTER FIRST_SAVED_GP_REGNO
+#define LAST_SAVRES_REGISTER 31
+#define N_SAVRES_REGISTERS (LAST_SAVRES_REGISTER - FIRST_SAVRES_REGISTER + 1)
+
+static GTY(()) rtx savres_routine_syms[N_SAVRES_REGISTERS][8];
+
+/* Return the symbol for an out-of-line register save/restore routine.
+ We are saving/restoring GPRs if GPR is true. */
+
+static rtx
+rs6000_savres_routine_sym (rs6000_stack_t *info, bool savep, bool gpr, bool exitp)
+{
+ int regno = gpr ? info->first_gp_reg_save : (info->first_fp_reg_save - 32);
+ rtx sym;
+ int select = ((savep ? 1 : 0) << 2
+ | (gpr
+ /* On the SPE, we never have any FPRs, but we do have
+ 32/64-bit versions of the routines. */
+ ? (TARGET_SPE_ABI && info->spe_64bit_regs_used ? 1 : 0)
+ : 0) << 1
+ | (exitp ? 1: 0));
+
+ /* Don't generate bogus routine names. */
+ gcc_assert (FIRST_SAVRES_REGISTER <= regno && regno <= LAST_SAVRES_REGISTER);
+
+ sym = savres_routine_syms[regno-FIRST_SAVRES_REGISTER][select];
+
+ if (sym == NULL)
+ {
+ char name[30];
+ const char *action;
+ const char *regkind;
+ const char *exit_suffix;
+
+ action = savep ? "save" : "rest";
+
+ /* SPE has slightly different names for its routines depending on
+ whether we are saving 32-bit or 64-bit registers. */
+ if (TARGET_SPE_ABI)
+ {
+ /* No floating point saves on the SPE. */
+ gcc_assert (gpr);
+
+ regkind = info->spe_64bit_regs_used ? "64gpr" : "32gpr";
+ }
+ else
+ regkind = gpr ? "gpr" : "fpr";
+
+ exit_suffix = exitp ? "_x" : "";
+
+ sprintf (name, "_%s%s_%d%s", action, regkind, regno, exit_suffix);
+
+ sym = savres_routine_syms[regno-FIRST_SAVRES_REGISTER][select]
+ = gen_rtx_SYMBOL_REF (Pmode, ggc_strdup (name));
+ }
+
+ return sym;
+}
+
+/* Emit a sequence of insns, including a stack tie if needed, for
+ resetting the stack pointer. If SAVRES is true, then don't reset the
+ stack pointer, but move the base of the frame into r11 for use by
+ out-of-line register restore routines. */
+
+static void
+rs6000_emit_stack_reset (rs6000_stack_t *info,
+ rtx sp_reg_rtx, rtx frame_reg_rtx,
+ int sp_offset, bool savres)
+{
+ /* This blockage is needed so that sched doesn't decide to move
+ the sp change before the register restores. */
+ if (frame_reg_rtx != sp_reg_rtx
+ || (TARGET_SPE_ABI
+ && info->spe_64bit_regs_used != 0
+ && info->first_gp_reg_save != 32))
+ rs6000_emit_stack_tie ();
+
+ if (frame_reg_rtx != sp_reg_rtx)
+ {
+ rs6000_emit_stack_tie ();
+ if (sp_offset != 0)
+ emit_insn (gen_addsi3 (sp_reg_rtx, frame_reg_rtx,
+ GEN_INT (sp_offset)));
+ else if (!savres)
+ emit_move_insn (sp_reg_rtx, frame_reg_rtx);
+ }
+ else if (sp_offset != 0)
+ {
+ /* If we are restoring registers out-of-line, we will be using the
+ "exit" variants of the restore routines, which will reset the
+ stack for us. But we do need to point r11 into the right place
+ for those routines. */
+ rtx dest_reg = (savres
+ ? gen_rtx_REG (Pmode, 11)
+ : sp_reg_rtx);
+
+ emit_insn (TARGET_32BIT
+ ? gen_addsi3 (dest_reg, sp_reg_rtx,
+ GEN_INT (sp_offset))
+ : gen_adddi3 (dest_reg, sp_reg_rtx,
+ GEN_INT (sp_offset)));
+ }
+}
+
+/* Construct a parallel rtx describing the effect of a call to an
+ out-of-line register save/restore routine. */
+
+static rtx
+rs6000_make_savres_rtx (rs6000_stack_t *info,
+ rtx frame_reg_rtx, int save_area_offset,
+ enum machine_mode reg_mode,
+ bool savep, bool gpr, bool exitp)
+{
+ int i;
+ int offset, start_reg, end_reg, n_regs;
+ int reg_size = GET_MODE_SIZE (reg_mode);
+ rtx sym;
+ rtvec p;
+
+ offset = 0;
+ start_reg = (gpr
+ ? info->first_gp_reg_save
+ : info->first_fp_reg_save);
+ end_reg = gpr ? 32 : 64;
+ n_regs = end_reg - start_reg;
+ p = rtvec_alloc ((exitp ? 4 : 3) + n_regs);
+
+ /* If we're saving registers, then we should never say we're exiting. */
+ gcc_assert ((savep && !exitp) || !savep);
+
+ if (exitp)
+ RTVEC_ELT (p, offset++) = gen_rtx_RETURN (VOIDmode);
+
+ RTVEC_ELT (p, offset++)
+ = gen_rtx_CLOBBER (VOIDmode, gen_rtx_REG (Pmode, 65));
+
+ sym = rs6000_savres_routine_sym (info, savep, gpr, exitp);
+ RTVEC_ELT (p, offset++) = gen_rtx_USE (VOIDmode, sym);
+ RTVEC_ELT (p, offset++) = gen_rtx_USE (VOIDmode, gen_rtx_REG (Pmode, 11));
+
+ for (i = 0; i < end_reg - start_reg; i++)
+ {
+ rtx addr, reg, mem;
+ reg = gen_rtx_REG (reg_mode, start_reg + i);
+ addr = gen_rtx_PLUS (Pmode, frame_reg_rtx,
+ GEN_INT (save_area_offset + reg_size*i));
+ mem = gen_frame_mem (reg_mode, addr);
+
+ RTVEC_ELT (p, i + offset) = gen_rtx_SET (VOIDmode,
+ savep ? mem : reg,
+ savep ? reg : mem);
+ }
+
+ return gen_rtx_PARALLEL (VOIDmode, p);
+}
+
/* Determine whether the gp REG is really used. */
static bool
|| (DEFAULT_ABI == ABI_DARWIN && flag_pic))));
}
+enum {
+ SAVRES_MULTIPLE = 0x1,
+ SAVRES_INLINE_FPRS = 0x2,
+ SAVRES_INLINE_GPRS = 0x4
+};
+
+/* Determine the strategy for savings/restoring registers. */
+
+static int
+rs6000_savres_strategy (rs6000_stack_t *info, bool savep,
+ int using_static_chain_p, int sibcall)
+{
+ bool using_multiple_p;
+ bool common;
+ bool savres_fprs_inline;
+ bool savres_gprs_inline;
+ bool noclobber_global_gprs
+ = no_global_regs_above (info->first_gp_reg_save, /*gpr=*/true);
+
+ using_multiple_p = (TARGET_MULTIPLE && ! TARGET_POWERPC64
+ && (!TARGET_SPE_ABI
+ || info->spe_64bit_regs_used == 0)
+ && info->first_gp_reg_save < 31
+ && noclobber_global_gprs);
+ /* Don't bother to try to save things out-of-line if r11 is occupied
+ by the static chain. It would require too much fiddling and the
+ static chain is rarely used anyway. */
+ common = (using_static_chain_p
+ || sibcall
+ || crtl->calls_eh_return
+ || !info->lr_save_p
+ || cfun->machine->ra_need_lr
+ || info->total_size > 32767);
+ savres_fprs_inline = (common
+ || info->first_fp_reg_save == 64
+ || !no_global_regs_above (info->first_fp_reg_save,
+ /*gpr=*/false)
+ || FP_SAVE_INLINE (info->first_fp_reg_save));
+ savres_gprs_inline = (common
+ /* Saving CR interferes with the exit routines
+ used on the SPE, so just punt here. */
+ || (!savep
+ && TARGET_SPE_ABI
+ && info->spe_64bit_regs_used != 0
+ && info->cr_save_p != 0)
+ || info->first_gp_reg_save == 32
+ || !noclobber_global_gprs
+ || GP_SAVE_INLINE (info->first_gp_reg_save));
+
+ if (savep)
+ /* If we are going to use store multiple, then don't even bother
+ with the out-of-line routines, since the store-multiple instruction
+ will always be smaller. */
+ savres_gprs_inline = savres_gprs_inline || using_multiple_p;
+ else
+ {
+ /* The situation is more complicated with load multiple. We'd
+ prefer to use the out-of-line routines for restores, since the
+ "exit" out-of-line routines can handle the restore of LR and
+ the frame teardown. But we can only use the out-of-line
+ routines if we know that we've used store multiple or
+ out-of-line routines in the prologue, i.e. if we've saved all
+ the registers from first_gp_reg_save. Otherwise, we risk
+ loading garbage from the stack. Furthermore, we can only use
+ the "exit" out-of-line gpr restore if we haven't saved any
+ fprs. */
+ bool saved_all = !savres_gprs_inline || using_multiple_p;
+
+ if (saved_all && info->first_fp_reg_save != 64)
+ /* We can't use the exit routine; use load multiple if it's
+ available. */
+ savres_gprs_inline = savres_gprs_inline || using_multiple_p;
+ }
+
+ return (using_multiple_p
+ | (savres_fprs_inline << 1)
+ | (savres_gprs_inline << 2));
+}
+
/* Emit function prologue as insns. */
void
rtx frame_reg_rtx = sp_reg_rtx;
rtx cr_save_rtx = NULL_RTX;
rtx insn;
+ int strategy;
int saving_FPRs_inline;
+ int saving_GPRs_inline;
int using_store_multiple;
+ int using_static_chain_p = (cfun->static_chain_decl != NULL_TREE
+ && df_regs_ever_live_p (STATIC_CHAIN_REGNUM)
+ && !call_used_regs[STATIC_CHAIN_REGNUM]);
HOST_WIDE_INT sp_offset = 0;
if (TARGET_FIX_AND_CONTINUE)
reg_size = 8;
}
- using_store_multiple = (TARGET_MULTIPLE && ! TARGET_POWERPC64
- && (!TARGET_SPE_ABI
- || info->spe_64bit_regs_used == 0)
- && info->first_gp_reg_save < 31
- && no_global_regs_above (info->first_gp_reg_save));
- saving_FPRs_inline = (info->first_fp_reg_save == 64
- || FP_SAVE_INLINE (info->first_fp_reg_save)
- || crtl->calls_eh_return
- || cfun->machine->ra_need_lr);
+ strategy = rs6000_savres_strategy (info, /*savep=*/true,
+ /*static_chain_p=*/using_static_chain_p,
+ /*sibcall=*/0);
+ using_store_multiple = strategy & SAVRES_MULTIPLE;
+ saving_FPRs_inline = strategy & SAVRES_INLINE_FPRS;
+ saving_GPRs_inline = strategy & SAVRES_INLINE_GPRS;
/* For V.4, update stack before we do any saving and set back pointer. */
if (! WORLD_SAVE_P (info)
&& (DEFAULT_ABI == ABI_V4
|| crtl->calls_eh_return))
{
+ bool need_r11 = (TARGET_SPE
+ ? (!saving_GPRs_inline
+ && info->spe_64bit_regs_used == 0)
+ : (!saving_FPRs_inline || !saving_GPRs_inline));
if (info->total_size < 32767)
sp_offset = info->total_size;
else
- frame_reg_rtx = frame_ptr_rtx;
+ frame_reg_rtx = (need_r11
+ ? gen_rtx_REG (Pmode, 11)
+ : frame_ptr_rtx);
rs6000_emit_allocate_stack (info->total_size,
(frame_reg_rtx != sp_reg_rtx
&& (info->cr_save_p
|| info->lr_save_p
|| info->first_fp_reg_save < 64
|| info->first_gp_reg_save < 32
- )));
+ )),
+ need_r11);
if (frame_reg_rtx != sp_reg_rtx)
rs6000_emit_stack_tie ();
}
}
else if (!WORLD_SAVE_P (info) && info->first_fp_reg_save != 64)
{
+ rtx par;
+
+ par = rs6000_make_savres_rtx (info, frame_reg_rtx,
+ info->fp_save_offset + sp_offset,
+ DFmode,
+ /*savep=*/true, /*gpr=*/false,
+ /*exitp=*/false);
+ insn = emit_insn (par);
+ rs6000_frame_related (insn, frame_ptr_rtx, info->total_size,
+ NULL_RTX, NULL_RTX);
+ }
+
+ /* Save GPRs. This is done as a PARALLEL if we are using
+ the store-multiple instructions. */
+ if (!WORLD_SAVE_P (info)
+ && TARGET_SPE_ABI
+ && info->spe_64bit_regs_used != 0
+ && info->first_gp_reg_save != 32)
+ {
int i;
- char rname[30];
- const char *alloc_rname;
- rtvec p;
- p = rtvec_alloc (2 + 64 - info->first_fp_reg_save);
+ 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_regs_addressable_via_sp
+ = (SPE_CONST_OFFSET_OK(info->spe_gp_save_offset + sp_offset
+ + (32 - info->first_gp_reg_save - 1) * reg_size)
+ && saving_GPRs_inline);
+ 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;
+ }
+ else
+ {
+ /* Make r11 point to the start of the SPE save area. We need
+ to be careful here if r11 is holding the static chain. If
+ it is, then temporarily save it in r0. We would use r0 as
+ our base register here, but using r0 as a base register in
+ loads and stores means something different from what we
+ would like. */
+ int ool_adjust = (saving_GPRs_inline
+ ? 0
+ : (info->first_gp_reg_save
+ - (FIRST_SAVRES_REGISTER+1))*8);
+ HOST_WIDE_INT offset = (info->spe_gp_save_offset
+ + sp_offset - ool_adjust);
+
+ if (using_static_chain_p)
+ {
+ rtx r0 = gen_rtx_REG (Pmode, 0);
+ gcc_assert (info->first_gp_reg_save > 11);
+
+ emit_move_insn (r0, gen_rtx_REG (Pmode, 11));
+ }
+
+ spe_save_area_ptr = gen_rtx_REG (Pmode, 11);
+ insn = emit_insn (gen_addsi3 (spe_save_area_ptr,
+ frame_reg_rtx,
+ GEN_INT (offset)));
+ /* We need to make sure the move to r11 gets noted for
+ properly outputting unwind information. */
+ if (!saving_GPRs_inline)
+ rs6000_frame_related (insn, frame_reg_rtx, offset,
+ NULL_RTX, NULL_RTX);
+ spe_offset = 0;
+ }
+
+ if (saving_GPRs_inline)
+ {
+ for (i = 0; i < 32 - info->first_gp_reg_save; i++)
+ if (rs6000_reg_live_or_pic_offset_p (info->first_gp_reg_save + i))
+ {
+ rtx reg = gen_rtx_REG (reg_mode, info->first_gp_reg_save + i);
+ rtx offset, addr, mem;
- RTVEC_ELT (p, 0) = gen_rtx_CLOBBER (VOIDmode,
- gen_rtx_REG (Pmode,
- LR_REGNO));
- sprintf (rname, "%s%d%s", SAVE_FP_PREFIX,
- info->first_fp_reg_save - 32, SAVE_FP_SUFFIX);
- alloc_rname = ggc_strdup (rname);
- RTVEC_ELT (p, 1) = gen_rtx_USE (VOIDmode,
- gen_rtx_SYMBOL_REF (Pmode,
- alloc_rname));
- for (i = 0; i < 64 - info->first_fp_reg_save; i++)
+ /* We're doing all this to ensure that the offset fits into
+ the immediate offset of 'evstdd'. */
+ gcc_assert (SPE_CONST_OFFSET_OK (reg_size * i + spe_offset));
+
+ offset = GEN_INT (reg_size * i + spe_offset);
+ addr = gen_rtx_PLUS (Pmode, spe_save_area_ptr, offset);
+ mem = gen_rtx_MEM (V2SImode, addr);
+
+ insn = emit_move_insn (mem, reg);
+
+ rs6000_frame_related (insn, spe_save_area_ptr,
+ info->spe_gp_save_offset
+ + sp_offset + reg_size * i,
+ offset, const0_rtx);
+ }
+ }
+ else
{
- rtx addr, reg, mem;
- reg = gen_rtx_REG (DFmode, info->first_fp_reg_save + i);
- addr = gen_rtx_PLUS (Pmode, frame_reg_rtx,
- GEN_INT (info->fp_save_offset
- + sp_offset + 8*i));
- mem = gen_frame_mem (DFmode, addr);
+ rtx par;
- RTVEC_ELT (p, i + 2) = gen_rtx_SET (VOIDmode, mem, reg);
+ par = rs6000_make_savres_rtx (info, gen_rtx_REG (Pmode, 11),
+ 0, reg_mode,
+ /*savep=*/true, /*gpr=*/true,
+ /*exitp=*/false);
+ insn = emit_insn (par);
+ rs6000_frame_related (insn, frame_ptr_rtx, info->total_size,
+ NULL_RTX, NULL_RTX);
}
- insn = emit_insn (gen_rtx_PARALLEL (VOIDmode, p));
+
+
+ /* Move the static chain pointer back. */
+ if (using_static_chain_p && !spe_regs_addressable_via_sp)
+ emit_move_insn (gen_rtx_REG (Pmode, 11), gen_rtx_REG (Pmode, 0));
+ }
+ else if (!WORLD_SAVE_P (info) && !saving_GPRs_inline)
+ {
+ rtx par;
+
+ /* Need to adjust r11 if we saved any FPRs. */
+ if (info->first_fp_reg_save != 64)
+ {
+ rtx r11 = gen_rtx_REG (reg_mode, 11);
+ rtx offset = GEN_INT (info->total_size
+ + (-8 * (64-info->first_fp_reg_save)));
+ rtx ptr_reg = (sp_reg_rtx == frame_reg_rtx
+ ? sp_reg_rtx : r11);
+
+ emit_insn (TARGET_32BIT
+ ? gen_addsi3 (r11, ptr_reg, offset)
+ : gen_adddi3 (r11, ptr_reg, offset));
+ }
+
+ par = rs6000_make_savres_rtx (info, frame_reg_rtx,
+ info->gp_save_offset + sp_offset,
+ reg_mode,
+ /*savep=*/true, /*gpr=*/true,
+ /*exitp=*/false);
+ insn = emit_insn (par);
rs6000_frame_related (insn, frame_ptr_rtx, info->total_size,
NULL_RTX, NULL_RTX);
}
-
- /* Save GPRs. This is done as a PARALLEL if we are using
- the store-multiple instructions. */
- if (!WORLD_SAVE_P (info) && using_store_multiple)
+ else if (!WORLD_SAVE_P (info) && using_store_multiple)
{
rtvec p;
int i;
rs6000_frame_related (insn, frame_ptr_rtx, info->total_size,
NULL_RTX, NULL_RTX);
}
- else if (!WORLD_SAVE_P (info)
- && TARGET_SPE_ABI
- && info->spe_64bit_regs_used != 0
- && info->first_gp_reg_save != 32)
- {
- int i;
- rtx spe_save_area_ptr;
- int using_static_chain_p = (cfun->static_chain_decl != NULL_TREE
- && df_regs_ever_live_p (STATIC_CHAIN_REGNUM)
- && !call_used_regs[STATIC_CHAIN_REGNUM]);
-
- /* 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_regs_addressable_via_sp
- = SPE_CONST_OFFSET_OK(info->spe_gp_save_offset + sp_offset
- + (32 - info->first_gp_reg_save - 1) * reg_size);
- 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;
- }
- else
- {
- /* Make r11 point to the start of the SPE save area. We need
- to be careful here if r11 is holding the static chain. If
- it is, then temporarily save it in r0. We would use r0 as
- our base register here, but using r0 as a base register in
- loads and stores means something different from what we
- would like. */
- if (using_static_chain_p)
- {
- rtx r0 = gen_rtx_REG (Pmode, 0);
-
- gcc_assert (info->first_gp_reg_save > 11);
-
- emit_move_insn (r0, gen_rtx_REG (Pmode, 11));
- }
-
- spe_save_area_ptr = gen_rtx_REG (Pmode, 11);
- emit_insn (gen_addsi3 (spe_save_area_ptr, frame_reg_rtx,
- GEN_INT (info->spe_gp_save_offset + sp_offset)));
-
- spe_offset = 0;
- }
-
- for (i = 0; i < 32 - info->first_gp_reg_save; i++)
- if (rs6000_reg_live_or_pic_offset_p (info->first_gp_reg_save + i))
- {
- rtx reg = gen_rtx_REG (reg_mode, info->first_gp_reg_save + i);
- rtx offset, addr, mem;
-
- /* We're doing all this to ensure that the offset fits into
- the immediate offset of 'evstdd'. */
- gcc_assert (SPE_CONST_OFFSET_OK (reg_size * i + spe_offset));
-
- offset = GEN_INT (reg_size * i + spe_offset);
- addr = gen_rtx_PLUS (Pmode, spe_save_area_ptr, offset);
- mem = gen_rtx_MEM (V2SImode, addr);
-
- insn = emit_move_insn (mem, reg);
-
- rs6000_frame_related (insn, spe_save_area_ptr,
- info->spe_gp_save_offset
- + sp_offset + reg_size * i,
- offset, const0_rtx);
- }
-
- /* Move the static chain pointer back. */
- if (using_static_chain_p && !spe_regs_addressable_via_sp)
- emit_move_insn (gen_rtx_REG (Pmode, 11), gen_rtx_REG (Pmode, 0));
- }
else if (!WORLD_SAVE_P (info))
{
int i;
(frame_reg_rtx != sp_reg_rtx
&& ((info->altivec_size != 0)
|| (info->vrsave_mask != 0)
- )));
+ )),
+ FALSE);
if (frame_reg_rtx != sp_reg_rtx)
rs6000_emit_stack_tie ();
}
&& !FP_SAVE_INLINE (info->first_fp_reg_save))
fprintf (file, "\t.extern %s%d%s\n\t.extern %s%d%s\n",
SAVE_FP_PREFIX, info->first_fp_reg_save - 32, SAVE_FP_SUFFIX,
- RESTORE_FP_PREFIX, info->first_fp_reg_save - 32,
- RESTORE_FP_SUFFIX);
+ RESTORE_FP_PREFIX, info->first_fp_reg_save - 32, RESTORE_FP_SUFFIX);
/* Write .extern for AIX common mode routines, if needed. */
if (! TARGET_POWER && ! TARGET_POWERPC && ! common_mode_defined)
we restore after the pop when possible. */
#define ALWAYS_RESTORE_ALTIVEC_BEFORE_POP 0
+/* Reload CR from REG. */
+
+static void
+rs6000_restore_saved_cr (rtx reg, int using_mfcr_multiple)
+{
+ int count = 0;
+ int i;
+
+ if (using_mfcr_multiple)
+ {
+ for (i = 0; i < 8; i++)
+ if (df_regs_ever_live_p (CR0_REGNO+i) && ! call_used_regs[CR0_REGNO+i])
+ count++;
+ gcc_assert (count);
+ }
+
+ if (using_mfcr_multiple && count > 1)
+ {
+ rtvec p;
+ int ndx;
+
+ p = rtvec_alloc (count);
+
+ ndx = 0;
+ for (i = 0; i < 8; i++)
+ if (df_regs_ever_live_p (CR0_REGNO+i) && ! call_used_regs[CR0_REGNO+i])
+ {
+ rtvec r = rtvec_alloc (2);
+ RTVEC_ELT (r, 0) = reg;
+ RTVEC_ELT (r, 1) = GEN_INT (1 << (7-i));
+ RTVEC_ELT (p, ndx) =
+ gen_rtx_SET (VOIDmode, gen_rtx_REG (CCmode, CR0_REGNO+i),
+ gen_rtx_UNSPEC (CCmode, r, UNSPEC_MOVESI_TO_CR));
+ ndx++;
+ }
+ emit_insn (gen_rtx_PARALLEL (VOIDmode, p));
+ gcc_assert (ndx == count);
+ }
+ else
+ for (i = 0; i < 8; i++)
+ if (df_regs_ever_live_p (CR0_REGNO+i) && ! call_used_regs[CR0_REGNO+i])
+ {
+ emit_insn (gen_movsi_to_cr_one (gen_rtx_REG (CCmode,
+ CR0_REGNO+i),
+ reg));
+ }
+}
+
/* Emit function epilogue as insns.
At present, dwarf2out_frame_debug_expr doesn't understand
rs6000_emit_epilogue (int sibcall)
{
rs6000_stack_t *info;
+ int restoring_GPRs_inline;
int restoring_FPRs_inline;
int using_load_multiple;
int using_mtcr_multiple;
int use_backchain_to_restore_sp;
+ int restore_lr;
+ int strategy;
int sp_offset = 0;
rtx sp_reg_rtx = gen_rtx_REG (Pmode, 1);
rtx frame_reg_rtx = sp_reg_rtx;
reg_size = 8;
}
- using_load_multiple = (TARGET_MULTIPLE && ! TARGET_POWERPC64
- && (!TARGET_SPE_ABI
- || info->spe_64bit_regs_used == 0)
- && info->first_gp_reg_save < 31
- && no_global_regs_above (info->first_gp_reg_save));
- restoring_FPRs_inline = (sibcall
- || crtl->calls_eh_return
- || info->first_fp_reg_save == 64
- || FP_SAVE_INLINE (info->first_fp_reg_save));
+ strategy = rs6000_savres_strategy (info, /*savep=*/false,
+ /*static_chain_p=*/0, sibcall);
+ using_load_multiple = strategy & SAVRES_MULTIPLE;
+ restoring_FPRs_inline = strategy & SAVRES_INLINE_FPRS;
+ restoring_GPRs_inline = strategy & SAVRES_INLINE_GPRS;
using_mtcr_multiple = (rs6000_cpu == PROCESSOR_PPC601
|| rs6000_cpu == PROCESSOR_PPC603
|| rs6000_cpu == PROCESSOR_PPC750
frame pointer for alloca, but the generic parts of the compiler
give us one anyway. */
use_backchain_to_restore_sp = (info->total_size > 32767
+ || info->total_size
+ + (info->lr_save_p ? info->lr_save_offset : 0)
+ > 32767
|| (cfun->calls_alloca
&& !frame_pointer_needed));
+ restore_lr = (info->lr_save_p
+ && restoring_GPRs_inline
+ && restoring_FPRs_inline);
if (WORLD_SAVE_P (info))
{
emit_insn (generate_set_vrsave (reg, info, 1));
}
- /* Get the old lr if we saved it. */
- if (info->lr_save_p)
+ /* Get the old lr if we saved it. If we are restoring registers
+ out-of-line, then the out-of-line routines can do this for us. */
+ if (restore_lr)
{
rtx mem = gen_frame_mem_offset (Pmode, frame_reg_rtx,
info->lr_save_offset + sp_offset);
}
/* Set LR here to try to overlap restores below. */
- if (info->lr_save_p)
+ if (restore_lr)
emit_move_insn (gen_rtx_REG (Pmode, LR_REGNO),
gen_rtx_REG (Pmode, 0));
/* Restore GPRs. This is done as a PARALLEL if we are using
the load-multiple instructions. */
- if (using_load_multiple)
- {
- rtvec p;
- p = rtvec_alloc (32 - info->first_gp_reg_save);
- for (i = 0; i < 32 - info->first_gp_reg_save; i++)
- {
- rtx addr = gen_rtx_PLUS (Pmode, frame_reg_rtx,
- GEN_INT (info->gp_save_offset
- + sp_offset
- + reg_size * i));
- rtx mem = gen_frame_mem (reg_mode, addr);
-
- RTVEC_ELT (p, i) =
- gen_rtx_SET (VOIDmode,
- gen_rtx_REG (reg_mode, info->first_gp_reg_save + i),
- mem);
- }
- emit_insn (gen_rtx_PARALLEL (VOIDmode, p));
- }
- else if (TARGET_SPE_ABI
- && info->spe_64bit_regs_used != 0
- && info->first_gp_reg_save != 32)
+ if (TARGET_SPE_ABI
+ && info->spe_64bit_regs_used != 0
+ && info->first_gp_reg_save != 32)
{
/* 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_regs_addressable_via_sp
- = SPE_CONST_OFFSET_OK(info->spe_gp_save_offset + sp_offset
- + (32 - info->first_gp_reg_save - 1) * reg_size);
+ = (SPE_CONST_OFFSET_OK(info->spe_gp_save_offset + sp_offset
+ + (32 - info->first_gp_reg_save - 1) * reg_size)
+ && restoring_GPRs_inline);
int spe_offset;
if (spe_regs_addressable_via_sp)
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. */
+ int ool_adjust = (restoring_GPRs_inline
+ ? 0
+ : (info->first_gp_reg_save
+ - (FIRST_SAVRES_REGISTER+1))*8);
+
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)));
+ GEN_INT (info->spe_gp_save_offset
+ + sp_offset
+ - ool_adjust)));
/* 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;
}
- for (i = 0; i < 32 - info->first_gp_reg_save; i++)
- if (rs6000_reg_live_or_pic_offset_p (info->first_gp_reg_save + i))
- {
- rtx offset, addr, mem;
+ if (restoring_GPRs_inline)
+ {
+ for (i = 0; i < 32 - info->first_gp_reg_save; i++)
+ if (rs6000_reg_live_or_pic_offset_p (info->first_gp_reg_save + i))
+ {
+ rtx offset, addr, mem;
- /* We're doing all this to ensure that the immediate offset
- fits into the immediate field of 'evldd'. */
- gcc_assert (SPE_CONST_OFFSET_OK (spe_offset + reg_size * i));
+ /* We're doing all this to ensure that the immediate offset
+ fits into the immediate field of 'evldd'. */
+ gcc_assert (SPE_CONST_OFFSET_OK (spe_offset + reg_size * i));
- offset = GEN_INT (spe_offset + reg_size * i);
- addr = gen_rtx_PLUS (Pmode, frame_reg_rtx, offset);
- mem = gen_rtx_MEM (V2SImode, addr);
+ offset = GEN_INT (spe_offset + reg_size * i);
+ 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),
- mem);
- }
+ emit_move_insn (gen_rtx_REG (reg_mode, info->first_gp_reg_save + i),
+ mem);
+ }
+ }
+ else
+ {
+ rtx par;
+
+ par = rs6000_make_savres_rtx (info, gen_rtx_REG (Pmode, 11),
+ 0, reg_mode,
+ /*savep=*/false, /*gpr=*/true,
+ /*exitp=*/true);
+ emit_jump_insn (par);
+
+ /* We don't want anybody else emitting things after we jumped
+ back. */
+ return;
+ }
}
- else
- for (i = 0; i < 32 - info->first_gp_reg_save; i++)
- if (rs6000_reg_live_or_pic_offset_p (info->first_gp_reg_save + i))
+ else if (!restoring_GPRs_inline)
+ {
+ /* We are jumping to an out-of-line function. */
+ bool can_use_exit = info->first_fp_reg_save == 64;
+ rtx par;
+
+ /* Emit stack reset code if we need it. */
+ if (can_use_exit)
+ rs6000_emit_stack_reset (info, sp_reg_rtx, frame_reg_rtx,
+ sp_offset, can_use_exit);
+ else
+ emit_insn (gen_addsi3 (gen_rtx_REG (Pmode, 11),
+ sp_reg_rtx,
+ GEN_INT (sp_offset - info->fp_size)));
+
+ par = rs6000_make_savres_rtx (info, frame_reg_rtx,
+ info->gp_save_offset, reg_mode,
+ /*savep=*/false, /*gpr=*/true,
+ /*exitp=*/can_use_exit);
+
+ if (can_use_exit)
+ {
+ if (info->cr_save_p)
+ rs6000_restore_saved_cr (gen_rtx_REG (SImode, 12),
+ using_mtcr_multiple);
+
+ emit_jump_insn (par);
+
+ /* We don't want anybody else emitting things after we jumped
+ back. */
+ return;
+ }
+ else
+ emit_insn (par);
+ }
+ else if (using_load_multiple)
+ {
+ rtvec p;
+ p = rtvec_alloc (32 - info->first_gp_reg_save);
+ for (i = 0; i < 32 - info->first_gp_reg_save; i++)
{
rtx addr = gen_rtx_PLUS (Pmode, frame_reg_rtx,
GEN_INT (info->gp_save_offset
+ reg_size * i));
rtx mem = gen_frame_mem (reg_mode, addr);
- emit_move_insn (gen_rtx_REG (reg_mode,
- info->first_gp_reg_save + i), mem);
+ RTVEC_ELT (p, i) =
+ gen_rtx_SET (VOIDmode,
+ gen_rtx_REG (reg_mode, info->first_gp_reg_save + i),
+ mem);
}
+ emit_insn (gen_rtx_PARALLEL (VOIDmode, p));
+ }
+ else
+ {
+ for (i = 0; i < 32 - info->first_gp_reg_save; i++)
+ if (rs6000_reg_live_or_pic_offset_p (info->first_gp_reg_save + i))
+ {
+ rtx addr = gen_rtx_PLUS (Pmode, frame_reg_rtx,
+ GEN_INT (info->gp_save_offset
+ + sp_offset
+ + reg_size * i));
+ rtx mem = gen_frame_mem (reg_mode, addr);
+
+ emit_move_insn (gen_rtx_REG (reg_mode,
+ info->first_gp_reg_save + i), mem);
+ }
+ }
/* Restore fpr's if we need to do it without calling a function. */
if (restoring_FPRs_inline)
/* If we saved cr, restore it here. Just those that were used. */
if (info->cr_save_p)
- {
- rtx r12_rtx = gen_rtx_REG (SImode, 12);
- int count = 0;
-
- if (using_mtcr_multiple)
- {
- for (i = 0; i < 8; i++)
- if (df_regs_ever_live_p (CR0_REGNO+i) && ! call_used_regs[CR0_REGNO+i])
- count++;
- gcc_assert (count);
- }
-
- if (using_mtcr_multiple && count > 1)
- {
- rtvec p;
- int ndx;
-
- p = rtvec_alloc (count);
-
- ndx = 0;
- for (i = 0; i < 8; i++)
- if (df_regs_ever_live_p (CR0_REGNO+i) && ! call_used_regs[CR0_REGNO+i])
- {
- rtvec r = rtvec_alloc (2);
- RTVEC_ELT (r, 0) = r12_rtx;
- RTVEC_ELT (r, 1) = GEN_INT (1 << (7-i));
- RTVEC_ELT (p, ndx) =
- gen_rtx_SET (VOIDmode, gen_rtx_REG (CCmode, CR0_REGNO+i),
- gen_rtx_UNSPEC (CCmode, r, UNSPEC_MOVESI_TO_CR));
- ndx++;
- }
- emit_insn (gen_rtx_PARALLEL (VOIDmode, p));
- gcc_assert (ndx == count);
- }
- else
- for (i = 0; i < 8; i++)
- if (df_regs_ever_live_p (CR0_REGNO+i) && ! call_used_regs[CR0_REGNO+i])
- {
- emit_insn (gen_movsi_to_cr_one (gen_rtx_REG (CCmode,
- CR0_REGNO+i),
- r12_rtx));
- }
- }
+ rs6000_restore_saved_cr (gen_rtx_REG (SImode, 12), using_mtcr_multiple);
/* If this is V.4, unwind the stack pointer after all of the loads
have been done. */
- if (frame_reg_rtx != sp_reg_rtx)
- {
- /* This blockage is needed so that sched doesn't decide to move
- the sp change before the register restores. */
- rs6000_emit_stack_tie ();
- 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);
- }
- else if (sp_offset != 0)
- emit_insn (TARGET_32BIT
- ? gen_addsi3 (sp_reg_rtx, sp_reg_rtx,
- GEN_INT (sp_offset))
- : gen_adddi3 (sp_reg_rtx, sp_reg_rtx,
- GEN_INT (sp_offset)));
+ rs6000_emit_stack_reset (info, sp_reg_rtx, frame_reg_rtx,
+ sp_offset, !restoring_FPRs_inline);
if (crtl->calls_eh_return)
{
{
rtvec p;
if (! restoring_FPRs_inline)
- p = rtvec_alloc (3 + 64 - info->first_fp_reg_save);
+ p = rtvec_alloc (4 + 64 - info->first_fp_reg_save);
else
p = rtvec_alloc (2);
RTVEC_ELT (p, 0) = gen_rtx_RETURN (VOIDmode);
- RTVEC_ELT (p, 1) = gen_rtx_USE (VOIDmode,
- gen_rtx_REG (Pmode,
- LR_REGNO));
+ RTVEC_ELT (p, 1) = (restoring_FPRs_inline
+ ? gen_rtx_USE (VOIDmode, gen_rtx_REG (Pmode, 65))
+ : gen_rtx_CLOBBER (VOIDmode,
+ gen_rtx_REG (Pmode, 65)));
/* If we have to restore more than two FP registers, branch to the
restore function. It will return to our caller. */
if (! restoring_FPRs_inline)
{
int i;
- char rname[30];
- const char *alloc_rname;
-
- sprintf (rname, "%s%d%s", RESTORE_FP_PREFIX,
- info->first_fp_reg_save - 32, RESTORE_FP_SUFFIX);
- alloc_rname = ggc_strdup (rname);
- RTVEC_ELT (p, 2) = gen_rtx_USE (VOIDmode,
- gen_rtx_SYMBOL_REF (Pmode,
- alloc_rname));
-
+ rtx sym;
+
+ sym = rs6000_savres_routine_sym (info,
+ /*savep=*/false,
+ /*gpr=*/false,
+ /*exitp=*/true);
+ RTVEC_ELT (p, 2) = gen_rtx_USE (VOIDmode, sym);
+ RTVEC_ELT (p, 3) = gen_rtx_USE (VOIDmode,
+ gen_rtx_REG (Pmode, 11));
for (i = 0; i < 64 - info->first_fp_reg_save; i++)
{
rtx addr, mem;
GEN_INT (info->fp_save_offset + 8*i));
mem = gen_frame_mem (DFmode, addr);
- RTVEC_ELT (p, i+3) =
+ RTVEC_ELT (p, i+4) =
gen_rtx_SET (VOIDmode,
gen_rtx_REG (DFmode, info->first_fp_reg_save + i),
mem);
HOST_WIDE_INT delta, HOST_WIDE_INT vcall_offset,
tree function)
{
- rtx this, insn, funexp;
+ rtx this_rtx, insn, funexp;
reload_completed = 1;
epilogue_completed = 1;
/* Find the "this" pointer. If the function returns a structure,
the structure return pointer is in r3. */
if (aggregate_value_p (TREE_TYPE (TREE_TYPE (function)), function))
- this = gen_rtx_REG (Pmode, 4);
+ this_rtx = gen_rtx_REG (Pmode, 4);
else
- this = gen_rtx_REG (Pmode, 3);
+ this_rtx = gen_rtx_REG (Pmode, 3);
/* Apply the constant offset, if required. */
if (delta)
{
rtx delta_rtx = GEN_INT (delta);
emit_insn (TARGET_32BIT
- ? gen_addsi3 (this, this, delta_rtx)
- : gen_adddi3 (this, this, delta_rtx));
+ ? gen_addsi3 (this_rtx, this_rtx, delta_rtx)
+ : gen_adddi3 (this_rtx, this_rtx, delta_rtx));
}
/* Apply the offset from the vtable, if required. */
rtx vcall_offset_rtx = GEN_INT (vcall_offset);
rtx tmp = gen_rtx_REG (Pmode, 12);
- emit_move_insn (tmp, gen_rtx_MEM (Pmode, this));
+ emit_move_insn (tmp, gen_rtx_MEM (Pmode, this_rtx));
if (((unsigned HOST_WIDE_INT) vcall_offset) + 0x8000 >= 0x10000)
{
emit_insn (TARGET_32BIT
emit_move_insn (tmp, gen_rtx_MEM (Pmode, loc));
}
emit_insn (TARGET_32BIT
- ? gen_addsi3 (this, this, tmp)
- : gen_adddi3 (this, this, tmp));
+ ? gen_addsi3 (this_rtx, this_rtx, tmp)
+ : gen_adddi3 (this_rtx, this_rtx, tmp));
}
/* Generate a tail call to the target function. */
toc_hash_table = htab_create_ggc (1021, toc_hash_function,
toc_hash_eq, NULL);
- h = ggc_alloc (sizeof (*h));
+ h = GGC_NEW (struct toc_hash_struct);
h->key = x;
h->key_mode = mode;
h->labelno = labelno;
case CPU_CELL:
case CPU_PPCE300C2:
case CPU_PPCE300C3:
+ case CPU_PPCE500MC:
return 2;
case CPU_RIOS2:
case CPU_PPC604:
for (i=pos; i<*pn_ready-1; i++)
ready[i] = ready[i + 1];
ready[*pn_ready-1] = tmp;
- if INSN_PRIORITY_KNOWN (tmp)
+
+ if (!sel_sched_p () && INSN_PRIORITY_KNOWN (tmp))
INSN_PRIORITY (tmp)++;
break;
}
while (pos >= 0)
{
if (is_load_insn (ready[pos])
- && INSN_PRIORITY_KNOWN (ready[pos]))
+ && !sel_sched_p ()
+ && INSN_PRIORITY_KNOWN (ready[pos]))
{
INSN_PRIORITY (ready[pos])++;
for (i=pos; i<*pn_ready-1; i++)
ready[i] = ready[i + 1];
ready[*pn_ready-1] = tmp;
- if INSN_PRIORITY_KNOWN (tmp)
+
+ if (!sel_sched_p () && INSN_PRIORITY_KNOWN (tmp))
INSN_PRIORITY (tmp)++;
+
first_store_pos = -1;
break;
for (i=first_store_pos; i<*pn_ready-1; i++)
ready[i] = ready[i + 1];
ready[*pn_ready-1] = tmp;
- if INSN_PRIORITY_KNOWN (tmp)
+ if (!sel_sched_p () && INSN_PRIORITY_KNOWN (tmp))
INSN_PRIORITY (tmp)++;
}
}
while (pos >= 0)
{
if (is_store_insn (ready[pos])
- && INSN_PRIORITY_KNOWN (ready[pos]))
+ && !sel_sched_p ()
+ && INSN_PRIORITY_KNOWN (ready[pos]))
{
INSN_PRIORITY (ready[pos])++;
/* Initialize. */
issue_rate = rs6000_issue_rate ();
- group_insns = alloca (issue_rate * sizeof (rtx));
+ group_insns = XALLOCAVEC (rtx, issue_rate);
for (i = 0; i < issue_rate; i++)
{
group_insns[i] = 0;
if (group_end)
{
/* If the scheduler had marked group termination at this location
- (between insn and next_indn), and neither insn nor next_insn will
+ (between insn and next_insn), and neither insn nor next_insn will
force group termination, pad the group with nops to force group
termination. */
if (can_issue_more
if (reload_completed && rs6000_sched_groups)
{
+ /* Do not run sched_finish hook when selective scheduling enabled. */
+ if (sel_sched_p ())
+ return;
+
if (rs6000_sched_insert_nops == sched_finish_none)
return;
}
}
}
+
+struct _rs6000_sched_context
+{
+ short cached_can_issue_more;
+ rtx last_scheduled_insn;
+ int load_store_pendulum;
+};
+
+typedef struct _rs6000_sched_context rs6000_sched_context_def;
+typedef rs6000_sched_context_def *rs6000_sched_context_t;
+
+/* Allocate store for new scheduling context. */
+static void *
+rs6000_alloc_sched_context (void)
+{
+ return xmalloc (sizeof (rs6000_sched_context_def));
+}
+
+/* If CLEAN_P is true then initializes _SC with clean data,
+ and from the global context otherwise. */
+static void
+rs6000_init_sched_context (void *_sc, bool clean_p)
+{
+ rs6000_sched_context_t sc = (rs6000_sched_context_t) _sc;
+
+ if (clean_p)
+ {
+ sc->cached_can_issue_more = 0;
+ sc->last_scheduled_insn = NULL_RTX;
+ sc->load_store_pendulum = 0;
+ }
+ else
+ {
+ sc->cached_can_issue_more = cached_can_issue_more;
+ sc->last_scheduled_insn = last_scheduled_insn;
+ sc->load_store_pendulum = load_store_pendulum;
+ }
+}
+
+/* Sets the global scheduling context to the one pointed to by _SC. */
+static void
+rs6000_set_sched_context (void *_sc)
+{
+ rs6000_sched_context_t sc = (rs6000_sched_context_t) _sc;
+
+ gcc_assert (sc != NULL);
+
+ cached_can_issue_more = sc->cached_can_issue_more;
+ last_scheduled_insn = sc->last_scheduled_insn;
+ load_store_pendulum = sc->load_store_pendulum;
+}
+
+/* Free _SC. */
+static void
+rs6000_free_sched_context (void *_sc)
+{
+ gcc_assert (_sc != NULL);
+
+ free (_sc);
+}
+
\f
/* Length in units of the trampoline for entering a nested function. */
default: break;
}
- if (result && result != type && TYPE_READONLY (type))
- result = build_qualified_type (result, TYPE_QUAL_CONST);
+ /* Propagate qualifiers attached to the element type
+ onto the vector type. */
+ if (result && result != type && TYPE_QUALS (type))
+ result = build_qualified_type (result, TYPE_QUALS (type));
*no_add_attrs = true; /* No need to hang on to the attribute. */
{
rtx sym_ref = XEXP (rtl, 0);
size_t len = strlen (XSTR (sym_ref, 0));
- char *str = alloca (len + 2);
+ char *str = XALLOCAVEC (char, len + 2);
str[0] = '.';
memcpy (str + 1, XSTR (sym_ref, 0), len + 1);
XSTR (sym_ref, 0) = ggc_alloc_string (str, len + 1);
}
static inline bool
-compare_section_name (const char *section, const char *template)
+compare_section_name (const char *section, const char *templ)
{
int len;
- len = strlen (template);
- return (strncmp (section, template, len) == 0
+ len = strlen (templ);
+ return (strncmp (section, templ, len) == 0
&& (section[len] == 0 || section[len] == '.'));
}
length = strlen (symb);
- symbol_name = alloca (length + 32);
+ symbol_name = XALLOCAVEC (char, length + 32);
GEN_SYMBOL_NAME_FOR_SYMBOL (symbol_name, symb, length);
- lazy_ptr_name = alloca (length + 32);
+ lazy_ptr_name = XALLOCAVEC (char, length + 32);
GEN_LAZY_PTR_NAME_FOR_SYMBOL (lazy_ptr_name, symb, length);
if (flag_pic == 2)
fprintf (file, "\t.indirect_symbol %s\n", symbol_name);
label++;
- local_label_0 = alloca (sizeof ("\"L00000000000$spb\""));
+ local_label_0 = XALLOCAVEC (char, sizeof ("\"L00000000000$spb\""));
sprintf (local_label_0, "\"L%011d$spb\"", label);
fprintf (file, "\tmflr r0\n");
scanned. In either case, *TOTAL contains the cost result. */
static bool
-rs6000_rtx_costs (rtx x, int code, int outer_code, int *total)
+rs6000_rtx_costs (rtx x, int code, int outer_code, int *total,
+ bool speed)
{
enum machine_mode mode = GET_MODE (x);
/* When optimizing for size, MEM should be slightly more expensive
than generating address, e.g., (plus (reg) (const)).
L1 cache latency is about two instructions. */
- *total = optimize_size ? COSTS_N_INSNS (1) + 1 : COSTS_N_INSNS (2);
+ *total = !speed ? COSTS_N_INSNS (1) + 1 : COSTS_N_INSNS (2);
return true;
case LABEL_REF:
case CALL:
case IF_THEN_ELSE:
- if (optimize_size)
+ if (!speed)
{
*total = COSTS_N_INSNS (1);
return true;
or from memory. */
int
-rs6000_memory_move_cost (enum machine_mode mode, enum reg_class class,
+rs6000_memory_move_cost (enum machine_mode mode, enum reg_class rclass,
int in ATTRIBUTE_UNUSED)
{
- if (reg_classes_intersect_p (class, GENERAL_REGS))
+ if (reg_classes_intersect_p (rclass, GENERAL_REGS))
return 4 * hard_regno_nregs[0][mode];
- else if (reg_classes_intersect_p (class, FLOAT_REGS))
+ else if (reg_classes_intersect_p (rclass, FLOAT_REGS))
return 4 * hard_regno_nregs[32][mode];
- else if (reg_classes_intersect_p (class, ALTIVEC_REGS))
+ else if (reg_classes_intersect_p (rclass, ALTIVEC_REGS))
return 4 * hard_regno_nregs[FIRST_ALTIVEC_REGNO][mode];
else
- return 4 + rs6000_register_move_cost (mode, class, GENERAL_REGS);
+ return 4 + rs6000_register_move_cost (mode, rclass, GENERAL_REGS);
}
/* Returns a code for a target-specific builtin that implements
&& ALTIVEC_VECTOR_MODE (mode))
regno = ALTIVEC_ARG_RETURN;
else if (TARGET_E500_DOUBLE && TARGET_HARD_FLOAT
- && (mode == DFmode || mode == DDmode || mode == DCmode
- || mode == TFmode || mode == TDmode || mode == TCmode))
+ && (mode == DFmode || mode == DCmode
+ || mode == TFmode || mode == TCmode))
return spe_build_register_parallel (mode, GP_ARG_RETURN);
else
regno = GP_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 == DDmode || mode == DCmode
- || mode == TFmode || mode == TDmode || mode == TCmode))
+ && (mode == DFmode || mode == DCmode
+ || mode == TFmode || mode == TCmode))
return spe_build_register_parallel (mode, GP_ARG_RETURN);
else
regno = GP_ARG_RETURN;
{
return (type == opaque_V2SI_type_node
|| type == opaque_V2SF_type_node
- || type == opaque_p_V2SI_type_node
|| type == opaque_V4SI_type_node);
}