/* Structure used to define the rs6000 stack */
typedef struct rs6000_stack {
+ int reload_completed; /* stack info won't change from here on */
int first_gp_reg_save; /* first callee saved GP register used */
int first_fp_reg_save; /* first callee saved FP register used */
int first_altivec_reg_save; /* first callee saved AltiVec register used */
int spe_padding_size;
HOST_WIDE_INT total_size; /* total bytes allocated for stack */
int spe_64bit_regs_used;
+ int savres_strategy;
} rs6000_stack_t;
/* A C structure for machine-specific, per-function data.
static bool rs6000_reg_live_or_pic_offset_p (int);
static tree rs6000_builtin_vectorized_libmass (tree, tree, tree);
static tree rs6000_builtin_vectorized_function (tree, tree, tree);
-static int rs6000_savres_strategy (rs6000_stack_t *, bool, int, int);
static void rs6000_restore_saved_cr (rtx, int);
+static bool rs6000_output_addr_const_extra (FILE *, rtx);
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,
int, bool);
static int rs6000_builtin_vectorization_cost (enum vect_cost_for_stmt,
tree, int);
+static enum machine_mode rs6000_preferred_simd_mode (enum machine_mode);
static void def_builtin (int, const char *, tree, int);
static bool rs6000_vector_alignment_reachable (const_tree, bool);
static rtx altivec_expand_vec_set_builtin (tree);
static rtx altivec_expand_vec_ext_builtin (tree, rtx);
static int get_element_number (tree, tree);
+static void rs6000_option_override (void);
+static void rs6000_option_init_struct (struct gcc_options *);
+static void rs6000_option_default_params (void);
static bool rs6000_handle_option (size_t, const char *, int);
static void rs6000_parse_tls_size_option (void);
static void rs6000_parse_yes_no_option (const char *, const char *, int *);
#endif
{ NULL, 0, 0, false, false, false, NULL }
};
+
+/* Implement TARGET_OPTION_OPTIMIZATION_TABLE. */
+static const struct default_options rs6000_option_optimization_table[] =
+ {
+ { OPT_LEVELS_1_PLUS, OPT_fomit_frame_pointer, NULL, 1 },
+ { OPT_LEVELS_NONE, 0, NULL, 0 }
+ };
\f
#ifndef MASK_STRICT_ALIGN
#define MASK_STRICT_ALIGN 0
#undef TARGET_ASM_FUNCTION_EPILOGUE
#define TARGET_ASM_FUNCTION_EPILOGUE rs6000_output_function_epilogue
+#undef TARGET_ASM_OUTPUT_ADDR_CONST_EXTRA
+#define TARGET_ASM_OUTPUT_ADDR_CONST_EXTRA rs6000_output_addr_const_extra
+
#undef TARGET_LEGITIMIZE_ADDRESS
#define TARGET_LEGITIMIZE_ADDRESS rs6000_legitimize_address
#undef TARGET_VECTORIZE_BUILTIN_VECTORIZATION_COST
#define TARGET_VECTORIZE_BUILTIN_VECTORIZATION_COST \
rs6000_builtin_vectorization_cost
+#undef TARGET_VECTORIZE_PREFERRED_SIMD_MODE
+#define TARGET_VECTORIZE_PREFERRED_SIMD_MODE \
+ rs6000_preferred_simd_mode
#undef TARGET_INIT_BUILTINS
#define TARGET_INIT_BUILTINS rs6000_init_builtins
#undef TARGET_HANDLE_OPTION
#define TARGET_HANDLE_OPTION rs6000_handle_option
+#undef TARGET_OPTION_OVERRIDE
+#define TARGET_OPTION_OVERRIDE rs6000_option_override
+
+#undef TARGET_OPTION_INIT_STRUCT
+#define TARGET_OPTION_INIT_STRUCT rs6000_option_init_struct
+
+#undef TARGET_OPTION_DEFAULT_PARAMS
+#define TARGET_OPTION_DEFAULT_PARAMS rs6000_option_default_params
+
+#undef TARGET_OPTION_OPTIMIZATION_TABLE
+#define TARGET_OPTION_OPTIMIZATION_TABLE rs6000_option_optimization_table
+
#undef TARGET_VECTORIZE_BUILTIN_VECTORIZED_FUNCTION
#define TARGET_VECTORIZE_BUILTIN_VECTORIZED_FUNCTION \
rs6000_builtin_vectorized_function
/* Override command line options. Mostly we process the processor
type and sometimes adjust other TARGET_ options. */
-void
-rs6000_override_options (const char *default_cpu)
+static void
+rs6000_option_override_internal (const char *default_cpu)
{
size_t i, j;
struct rs6000_cpu_select *ptr;
gcc_unreachable ();
}
- if (!PARAM_SET_P (PARAM_SIMULTANEOUS_PREFETCHES))
- set_param_value ("simultaneous-prefetches",
- rs6000_cost->simultaneous_prefetches);
- if (!PARAM_SET_P (PARAM_L1_CACHE_SIZE))
- set_param_value ("l1-cache-size", rs6000_cost->l1_cache_size);
- if (!PARAM_SET_P (PARAM_L1_CACHE_LINE_SIZE))
- 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);
+ maybe_set_param_value (PARAM_SIMULTANEOUS_PREFETCHES,
+ rs6000_cost->simultaneous_prefetches,
+ global_options.x_param_values,
+ global_options_set.x_param_values);
+ maybe_set_param_value (PARAM_L1_CACHE_SIZE, rs6000_cost->l1_cache_size,
+ global_options.x_param_values,
+ global_options_set.x_param_values);
+ maybe_set_param_value (PARAM_L1_CACHE_LINE_SIZE,
+ rs6000_cost->cache_line_size,
+ global_options.x_param_values,
+ global_options_set.x_param_values);
+ maybe_set_param_value (PARAM_L2_CACHE_SIZE, rs6000_cost->l2_cache_size,
+ global_options.x_param_values,
+ global_options_set.x_param_values);
/* If using typedef char *va_list, signal that __builtin_va_start (&ap, 0)
can be optimized to ap = __builtin_next_arg (0). */
rs6000_init_hard_regno_mode_ok ();
}
+/* Implement TARGET_OPTION_OVERRIDE. On the RS/6000 this is used to
+ define the target cpu type. */
+
+static void
+rs6000_option_override (void)
+{
+ rs6000_option_override_internal (OPTION_TARGET_CPU_DEFAULT);
+}
+
/* Implement targetm.vectorize.builtin_mask_for_load. */
static tree
rs6000_builtin_mask_for_load (void)
}
}
+/* Implement targetm.vectorize.preferred_simd_mode. */
+
+static enum machine_mode
+rs6000_preferred_simd_mode (enum machine_mode mode)
+{
+ if (TARGET_VSX)
+ switch (mode)
+ {
+ case DFmode:
+ return V2DFmode;
+ default:;
+ }
+ if (TARGET_ALTIVEC || TARGET_VSX)
+ switch (mode)
+ {
+ case SFmode:
+ return V4SFmode;
+ case DImode:
+ return V2DImode;
+ case SImode:
+ return V4SImode;
+ case HImode:
+ return V8HImode;
+ case QImode:
+ return V16QImode;
+ default:;
+ }
+ if (TARGET_SPE)
+ switch (mode)
+ {
+ case SFmode:
+ return V2SFmode;
+ case SImode:
+ return V2SImode;
+ default:;
+ }
+ if (TARGET_PAIRED_FLOAT
+ && mode == SFmode)
+ return V2SFmode;
+ return word_mode;
+}
+
/* Handle generic options of the form -mfoo=yes/no.
NAME is the option name.
VALUE is the option value.
error ("bad value %qs for -mtls-size switch", rs6000_tls_size_string);
}
-void
-optimization_options (int level ATTRIBUTE_UNUSED, int size ATTRIBUTE_UNUSED)
+/* Implement TARGET_OPTION_INIT_STRUCT. */
+
+static void
+rs6000_option_init_struct (struct gcc_options *opts)
{
if (DEFAULT_ABI == ABI_DARWIN)
/* The Darwin libraries never set errno, so we might as well
avoid calling them when that's the only reason we would. */
- flag_errno_math = 0;
+ opts->x_flag_errno_math = 0;
- /* Double growth factor to counter reduced min jump length. */
- set_param_value ("max-grow-copy-bb-insns", 16);
+ /* Enable section anchors by default. */
+ if (!TARGET_MACHO)
+ opts->x_flag_section_anchors = 1;
+}
+
+/* Implement TARGET_OPTION_DEFAULT_PARAMS. */
- /* Enable section anchors by default.
- Skip section anchors for Objective C and Objective C++
- until front-ends fixed. */
- if (!TARGET_MACHO && lang_hooks.name[4] != 'O')
- flag_section_anchors = 2;
+static void
+rs6000_option_default_params (void)
+{
+ /* Double growth factor to counter reduced min jump length. */
+ set_default_param_value (PARAM_MAX_GROW_COPY_BB_INSNS, 16);
}
static enum fpu_type_t
if (VECTOR_UNIT_ALTIVEC_P (V4SFmode))
return rs6000_builtin_decls[ALTIVEC_BUILTIN_VRFIM];
break;
+ case BUILT_IN_FMA:
+ if (VECTOR_UNIT_VSX_P (V2DFmode)
+ && out_mode == DFmode && out_n == 2
+ && in_mode == DFmode && in_n == 2)
+ return rs6000_builtin_decls[VSX_BUILTIN_XVMADDDP];
+ break;
+ case BUILT_IN_FMAF:
+ if (VECTOR_UNIT_VSX_P (V4SFmode)
+ && out_mode == SFmode && out_n == 4
+ && in_mode == SFmode && in_n == 4)
+ return rs6000_builtin_decls[VSX_BUILTIN_XVMADDSP];
+ else if (VECTOR_UNIT_ALTIVEC_P (V4SFmode)
+ && out_mode == SFmode && out_n == 4
+ && in_mode == SFmode && in_n == 4)
+ return rs6000_builtin_decls[ALTIVEC_BUILTIN_VMADDFP];
+ break;
case BUILT_IN_TRUNC:
if (VECTOR_UNIT_VSX_P (V2DFmode)
&& out_mode == DFmode && out_n == 2
switch (code)
{
- case OPT_G:
- g_switch_value = value;
- g_switch_set = true;
- break;
-
case OPT_mno_power:
target_flags &= ~(MASK_POWER | MASK_POWER2
| MASK_MULTIPLE | MASK_STRING);
case OPT_mcmodel_:
if (strcmp (arg, "small") == 0)
cmodel = CMODEL_SMALL;
+ else if (strcmp (arg, "medium") == 0)
+ cmodel = CMODEL_MEDIUM;
else if (strcmp (arg, "large") == 0)
cmodel = CMODEL_LARGE;
else
if (rs6000_sdata && g_switch_value)
{
- fprintf (file, "%s -G " HOST_WIDE_INT_PRINT_UNSIGNED, start,
+ fprintf (file, "%s -G %d", start,
g_switch_value);
start = "";
}
/* We have to be careful here, because it is the referenced address
that must be 32k from _SDA_BASE_, not just the symbol. */
summand = INTVAL (XEXP (sum, 1));
- if (summand < 0 || (unsigned HOST_WIDE_INT) summand > g_switch_value)
+ if (summand < 0 || summand > g_switch_value)
return 0;
sym_ref = XEXP (sum, 0);
return false;
return (regnum >= FIRST_VIRTUAL_REGISTER
- && regnum <= LAST_VIRTUAL_REGISTER);
+ && regnum <= LAST_VIRTUAL_POINTER_REGISTER);
}
static bool
copy_addr_to_reg (XEXP (operands[1], 0)));
}
+/* Return true if memory accesses to DECL are known to never straddle
+ a 32k boundary. */
+
+static bool
+offsettable_ok_by_alignment (tree decl)
+{
+ unsigned HOST_WIDE_INT dsize, dalign;
+
+ /* Presume any compiler generated symbol_ref is suitably aligned. */
+ if (!decl)
+ return true;
+
+ if (TREE_CODE (decl) != VAR_DECL
+ && TREE_CODE (decl) != PARM_DECL
+ && TREE_CODE (decl) != RESULT_DECL
+ && TREE_CODE (decl) != FIELD_DECL)
+ return true;
+
+ if (!DECL_SIZE_UNIT (decl))
+ return false;
+
+ if (!host_integerp (DECL_SIZE_UNIT (decl), 1))
+ return false;
+
+ dsize = tree_low_cst (DECL_SIZE_UNIT (decl), 1);
+ if (dsize <= 1)
+ return true;
+ if (dsize > 32768)
+ return false;
+
+ dalign = DECL_ALIGN_UNIT (decl);
+ return dalign >= dsize;
+}
+
/* Emit a move from SOURCE to DEST in mode MODE. */
void
rs6000_emit_move (rtx dest, rtx source, enum machine_mode mode)
/* If this is a SYMBOL_REF that refers to a constant pool entry,
and we have put it in the TOC, we just need to make a TOC-relative
reference to it. */
- if (TARGET_TOC
- && GET_CODE (operands[1]) == SYMBOL_REF
- && constant_pool_expr_p (operands[1])
- && ASM_OUTPUT_SPECIAL_POOL_ENTRY_P (get_pool_constant (operands[1]),
- get_pool_mode (operands[1])))
+ if ((TARGET_TOC
+ && GET_CODE (operands[1]) == SYMBOL_REF
+ && constant_pool_expr_p (operands[1])
+ && ASM_OUTPUT_SPECIAL_POOL_ENTRY_P (get_pool_constant (operands[1]),
+ get_pool_mode (operands[1])))
+ || (TARGET_CMODEL == CMODEL_MEDIUM
+ && GET_CODE (operands[1]) == SYMBOL_REF
+ && !CONSTANT_POOL_ADDRESS_P (operands[1])
+ && SYMBOL_REF_LOCAL_P (operands[1])
+ && offsettable_ok_by_alignment (SYMBOL_REF_DECL (operands[1]))))
{
rtx reg = NULL_RTX;
if (TARGET_CMODEL != CMODEL_SMALL)
controls this instead of DEFAULT_ABI; V.4 targets needing backward
compatibility can change DRAFT_V4_STRUCT_RET to override the
default, and -m switches get the final word. See
- rs6000_override_options for more details.
+ rs6000_option_override_internal for more details.
The PPC32 SVR4 ABI uses IEEE double extended for long double, if 128-bit
long double support is enabled. These values are returned in memory.
DECL_FIELD_CONTEXT (f_ovf) = record;
DECL_FIELD_CONTEXT (f_sav) = record;
- TREE_CHAIN (record) = type_decl;
+ TYPE_STUB_DECL (record) = type_decl;
TYPE_NAME (record) = type_decl;
TYPE_FIELDS (record) = f_gpr;
DECL_CHAIN (f_gpr) = f_fpr;
|| arg2 == error_mark_node)
return const0_rtx;
- switch (icode)
+ /* Check and prepare argument depending on the instruction code.
+
+ Note that a switch statement instead of the sequence of tests
+ would be incorrect as many of the CODE_FOR values could be
+ CODE_FOR_nothing and that would yield multiple alternatives
+ with identical values. We'd never reach here at runtime in
+ this case. */
+ if (icode == CODE_FOR_altivec_vsldoi_v4sf
+ || icode == CODE_FOR_altivec_vsldoi_v4si
+ || icode == CODE_FOR_altivec_vsldoi_v8hi
+ || icode == CODE_FOR_altivec_vsldoi_v16qi)
{
- case CODE_FOR_altivec_vsldoi_v4sf:
- case CODE_FOR_altivec_vsldoi_v4si:
- case CODE_FOR_altivec_vsldoi_v8hi:
- case CODE_FOR_altivec_vsldoi_v16qi:
/* Only allow 4-bit unsigned literals. */
STRIP_NOPS (arg2);
if (TREE_CODE (arg2) != INTEGER_CST
error ("argument 3 must be a 4-bit unsigned literal");
return const0_rtx;
}
- break;
-
- case CODE_FOR_vsx_xxpermdi_v2df:
- case CODE_FOR_vsx_xxpermdi_v2di:
- case CODE_FOR_vsx_xxsldwi_v16qi:
- case CODE_FOR_vsx_xxsldwi_v8hi:
- case CODE_FOR_vsx_xxsldwi_v4si:
- case CODE_FOR_vsx_xxsldwi_v4sf:
- case CODE_FOR_vsx_xxsldwi_v2di:
- case CODE_FOR_vsx_xxsldwi_v2df:
+ }
+ else if (icode == CODE_FOR_vsx_xxpermdi_v2df
+ || icode == CODE_FOR_vsx_xxpermdi_v2di
+ || icode == CODE_FOR_vsx_xxsldwi_v16qi
+ || icode == CODE_FOR_vsx_xxsldwi_v8hi
+ || icode == CODE_FOR_vsx_xxsldwi_v4si
+ || icode == CODE_FOR_vsx_xxsldwi_v4sf
+ || icode == CODE_FOR_vsx_xxsldwi_v2di
+ || icode == CODE_FOR_vsx_xxsldwi_v2df)
+ {
/* Only allow 2-bit unsigned literals. */
STRIP_NOPS (arg2);
if (TREE_CODE (arg2) != INTEGER_CST
error ("argument 3 must be a 2-bit unsigned literal");
return const0_rtx;
}
- break;
-
- case CODE_FOR_vsx_set_v2df:
- case CODE_FOR_vsx_set_v2di:
+ }
+ else if (icode == CODE_FOR_vsx_set_v2df
+ || icode == CODE_FOR_vsx_set_v2di)
+ {
/* Only allow 1-bit unsigned literals. */
STRIP_NOPS (arg2);
if (TREE_CODE (arg2) != INTEGER_CST
error ("argument 3 must be a 1-bit unsigned literal");
return const0_rtx;
}
- break;
-
- default:
- break;
}
if (target == 0
case SSA_NAME:
case REAL_CST:
case MEM_REF:
- case MISALIGNED_INDIRECT_REF:
case VIEW_CONVERT_EXPR:
if (TYPE_MODE (TREE_TYPE (*tp)) == SDmode)
return *tp;
return pic_offset_table_rtx;
}
\f
+static rs6000_stack_t stack_info;
+
/* Function to init struct machine_function.
This will be called, via a pointer variable,
from push_function_context. */
static struct machine_function *
rs6000_init_machine_status (void)
{
+ stack_info.reload_completed = 0;
return ggc_alloc_cleared_machine_function ();
}
\f
gcc_unreachable ();
}
\f
-/* Implement OUTPUT_ADDR_CONST_EXTRA for address X. */
+/* Implement TARGET_OUTPUT_ADDR_CONST_EXTRA. */
-bool
+static bool
rs6000_output_addr_const_extra (FILE *file, rtx x)
{
if (GET_CODE (x) == UNSPEC)
code = GET_CODE (operands[1]);
- gcc_assert (!(code == GE || code == GEU || code == LE || code == LEU || code == NE));
+ if (code == GE || code == GEU || code == LE || code == LEU || code == NE)
+ {
+ gcc_assert (GET_CODE (operands[2]) == REG
+ && GET_CODE (operands[3]) == REG);
+ PUT_CODE (operands[1], reverse_condition (code));
+ return "isel %0,%3,%2,%j1";
+ }
return "isel %0,%2,%3,%j1";
}
}
\f
+/* Determine the strategy for savings/restoring registers. */
+
+enum {
+ SAVRES_MULTIPLE = 0x1,
+ SAVE_INLINE_FPRS = 0x2,
+ SAVE_INLINE_GPRS = 0x4,
+ REST_INLINE_FPRS = 0x8,
+ REST_INLINE_GPRS = 0x10,
+ SAVE_NOINLINE_GPRS_SAVES_LR = 0x20,
+ SAVE_NOINLINE_FPRS_SAVES_LR = 0x40,
+ REST_NOINLINE_FPRS_DOESNT_RESTORE_LR = 0x80
+};
+
+static int
+rs6000_savres_strategy (rs6000_stack_t *info,
+ bool using_static_chain_p)
+{
+ int strategy = 0;
+
+ if (TARGET_MULTIPLE
+ && !TARGET_POWERPC64
+ && !(TARGET_SPE_ABI && info->spe_64bit_regs_used)
+ && info->first_gp_reg_save < 31
+ && no_global_regs_above (info->first_gp_reg_save, /*gpr=*/true))
+ strategy |= SAVRES_MULTIPLE;
+
+ if (crtl->calls_eh_return
+ || cfun->machine->ra_need_lr
+ || info->total_size > 32767)
+ strategy |= (SAVE_INLINE_FPRS | REST_INLINE_FPRS
+ | SAVE_INLINE_GPRS | REST_INLINE_GPRS);
+
+ if (info->first_fp_reg_save == 64
+ || FP_SAVE_INLINE (info->first_fp_reg_save)
+ /* The out-of-line FP routines use double-precision stores;
+ we can't use those routines if we don't have such stores. */
+ || (TARGET_HARD_FLOAT && !TARGET_DOUBLE_FLOAT)
+ || !no_global_regs_above (info->first_fp_reg_save, /*gpr=*/false))
+ strategy |= SAVE_INLINE_FPRS | REST_INLINE_FPRS;
+
+ if (info->first_gp_reg_save == 32
+ || GP_SAVE_INLINE (info->first_gp_reg_save)
+ || !((strategy & SAVRES_MULTIPLE)
+ || no_global_regs_above (info->first_gp_reg_save, /*gpr=*/true)))
+ strategy |= SAVE_INLINE_GPRS | REST_INLINE_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. */
+ if (using_static_chain_p)
+ strategy |= SAVE_INLINE_FPRS | SAVE_INLINE_GPRS;
+
+ /* 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. */
+ if ((strategy & SAVRES_MULTIPLE))
+ strategy |= SAVE_INLINE_GPRS;
+
+ /* 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. However if doesn't make sense to use the
+ out-of-line routine if that is the only reason we'd need to save
+ LR, and we can't use the "exit" out-of-line gpr restore if we
+ have saved some fprs; In those cases it is advantageous to use
+ load multiple when available. */
+ if ((strategy & SAVRES_MULTIPLE)
+ && (!info->lr_save_p
+ || info->first_fp_reg_save != 64))
+ strategy |= REST_INLINE_GPRS;
+
+ /* We can only use load multiple or the out-of-line routines to
+ restore if 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. */
+ if ((strategy & (SAVE_INLINE_GPRS | SAVRES_MULTIPLE)) == SAVE_INLINE_GPRS)
+ strategy |= REST_INLINE_GPRS;
+
+ /* Saving CR interferes with the exit routines used on the SPE, so
+ just punt here. */
+ if (TARGET_SPE_ABI
+ && info->spe_64bit_regs_used
+ && info->cr_save_p)
+ strategy |= REST_INLINE_GPRS;
+
+#ifdef POWERPC_LINUX
+ if (TARGET_64BIT)
+ {
+ if (!(strategy & SAVE_INLINE_FPRS))
+ strategy |= SAVE_NOINLINE_FPRS_SAVES_LR;
+ else if (!(strategy & SAVE_INLINE_GPRS)
+ && info->first_fp_reg_save == 64)
+ strategy |= SAVE_NOINLINE_GPRS_SAVES_LR;
+ }
+#else
+ if (TARGET_AIX && !(strategy & REST_INLINE_FPRS))
+ strategy |= REST_NOINLINE_FPRS_DOESNT_RESTORE_LR;
+#endif
+ return strategy;
+}
+
/* Calculate the stack information for the current function. This is
complicated by having two separate calling sequences, the AIX calling
sequence and the V.4 calling sequence.
static rs6000_stack_t *
rs6000_stack_info (void)
{
- static rs6000_stack_t info;
- rs6000_stack_t *info_ptr = &info;
+#ifdef ENABLE_CHECKING
+ static rs6000_stack_t info_save;
+#endif
+ rs6000_stack_t *info_ptr = &stack_info;
int reg_size = TARGET_32BIT ? 4 : 8;
int ehrd_size;
int save_align;
int first_gp;
HOST_WIDE_INT non_fixed_size;
+ bool using_static_chain_p;
+
+#ifdef ENABLE_CHECKING
+ memcpy (&info_save, &stack_info, sizeof stack_info);
+#else
+ if (reload_completed && info_ptr->reload_completed)
+ return info_ptr;
+#endif
- memset (&info, 0, sizeof (info));
+ memset (&stack_info, 0, sizeof (stack_info));
+ info_ptr->reload_completed = reload_completed;
if (TARGET_SPE)
{
info_ptr->calls_p = (! current_function_is_leaf
|| cfun->machine->ra_needs_full_frame);
- /* Determine if we need to save the link register. */
- if ((DEFAULT_ABI == ABI_AIX
- && crtl->profile
- && !TARGET_PROFILE_KERNEL)
-#ifdef TARGET_RELOCATABLE
- || (TARGET_RELOCATABLE && (get_pool_size () != 0))
-#endif
- || (info_ptr->first_fp_reg_save != 64
- && !FP_SAVE_INLINE (info_ptr->first_fp_reg_save))
- || (DEFAULT_ABI == ABI_V4 && cfun->calls_alloca)
- || info_ptr->calls_p
- || rs6000_ra_ever_killed ())
- {
- info_ptr->lr_save_p = 1;
- df_set_regs_ever_live (LR_REGNO, true);
- }
-
/* Determine if we need to save the condition code registers. */
if (df_regs_ever_live_p (CR2_REGNO)
|| df_regs_ever_live_p (CR3_REGNO)
info_ptr->total_size = RS6000_ALIGN (non_fixed_size + info_ptr->fixed_size,
ABI_STACK_BOUNDARY / BITS_PER_UNIT);
+ /* Determine if we need to save the link register. */
+ if (info_ptr->calls_p
+ || (DEFAULT_ABI == ABI_AIX
+ && crtl->profile
+ && !TARGET_PROFILE_KERNEL)
+ || (DEFAULT_ABI == ABI_V4 && cfun->calls_alloca)
+#ifdef TARGET_RELOCATABLE
+ || (TARGET_RELOCATABLE && (get_pool_size () != 0))
+#endif
+ || rs6000_ra_ever_killed ())
+ info_ptr->lr_save_p = 1;
+
+ using_static_chain_p = (cfun->static_chain_decl != NULL_TREE
+ && df_regs_ever_live_p (STATIC_CHAIN_REGNUM)
+ && call_used_regs[STATIC_CHAIN_REGNUM]);
+ info_ptr->savres_strategy = rs6000_savres_strategy (info_ptr,
+ using_static_chain_p);
+
+ if (!(info_ptr->savres_strategy & SAVE_INLINE_GPRS)
+ || !(info_ptr->savres_strategy & SAVE_INLINE_FPRS)
+ || !(info_ptr->savres_strategy & REST_INLINE_GPRS)
+ || !(info_ptr->savres_strategy & REST_INLINE_FPRS))
+ info_ptr->lr_save_p = 1;
+
+ if (info_ptr->lr_save_p)
+ df_set_regs_ever_live (LR_REGNO, true);
+
/* Determine if we need to allocate any stack frame:
For AIX we need to push the stack if a frame pointer is needed
if (! info_ptr->cr_save_p)
info_ptr->cr_save_offset = 0;
+#ifdef ENABLE_CHECKING
+ gcc_assert (!(reload_completed && info_save.reload_completed)
+ || memcmp (&info_save, &stack_info, sizeof stack_info) == 0);
+#endif
return info_ptr;
}
fprintf (asm_out_file, "\t.ref %s\n",
TARGET_STRIP_NAME_ENCODING (frame_table_label));
}
-
-/* If _Unwind_* has been called from within the same module,
- toc register is not guaranteed to be saved to 40(1) on function
- entry. Save it there in that case. */
-
-void
-rs6000_aix_emit_builtin_unwind_init (void)
-{
- rtx mem;
- rtx stack_top = gen_reg_rtx (Pmode);
- rtx opcode_addr = gen_reg_rtx (Pmode);
- rtx opcode = gen_reg_rtx (SImode);
- rtx tocompare = gen_reg_rtx (SImode);
- rtx no_toc_save_needed = gen_label_rtx ();
-
- mem = gen_frame_mem (Pmode, hard_frame_pointer_rtx);
- emit_move_insn (stack_top, mem);
-
- mem = gen_frame_mem (Pmode,
- gen_rtx_PLUS (Pmode, stack_top,
- GEN_INT (2 * GET_MODE_SIZE (Pmode))));
- emit_move_insn (opcode_addr, mem);
- emit_move_insn (opcode, gen_rtx_MEM (SImode, opcode_addr));
- emit_move_insn (tocompare, gen_int_mode (TARGET_32BIT ? 0x80410014
- : 0xE8410028, SImode));
-
- do_compare_rtx_and_jump (opcode, tocompare, EQ, 1,
- SImode, NULL_RTX, NULL_RTX,
- no_toc_save_needed, -1);
-
- mem = gen_frame_mem (Pmode,
- gen_rtx_PLUS (Pmode, stack_top,
- GEN_INT (5 * GET_MODE_SIZE (Pmode))));
- emit_move_insn (mem, gen_rtx_REG (Pmode, 2));
- emit_label (no_toc_save_needed);
-}
\f
/* This ties together stack memory (MEM with an alias set of frame_alias_set)
and the change to the stack pointer. */
static bool
rs6000_reg_live_or_pic_offset_p (int reg)
{
- return ((df_regs_ever_live_p (reg)
+ /* If the function calls eh_return, claim used all the registers that would
+ be checked for liveness otherwise. This is required for the PIC offset
+ register with -mminimal-toc on AIX, as it is advertised as "fixed" for
+ register allocation purposes in this case. */
+
+ return (((crtl->calls_eh_return || df_regs_ever_live_p (reg))
&& (!call_used_regs[reg]
|| (reg == RS6000_PIC_OFFSET_TABLE_REGNUM
&& TARGET_TOC && TARGET_MINIMAL_TOC)))
|| (DEFAULT_ABI == ABI_DARWIN && flag_pic))));
}
-enum {
- SAVRES_MULTIPLE = 0x1,
- SAVRES_INLINE_FPRS = 0x2,
- SAVRES_INLINE_GPRS = 0x4,
- SAVRES_NOINLINE_GPRS_SAVES_LR = 0x8,
- SAVRES_NOINLINE_FPRS_SAVES_LR = 0x10,
- SAVRES_NOINLINE_FPRS_DOESNT_RESTORE_LR = 0x20
-};
-
-/* 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);
- int strategy;
-
- 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)
- /* The out-of-line FP routines use
- double-precision stores; we can't use those
- routines if we don't have such stores. */
- || (TARGET_HARD_FLOAT && !TARGET_DOUBLE_FLOAT)
- || 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;
- }
-
- strategy = (using_multiple_p
- | (savres_fprs_inline << 1)
- | (savres_gprs_inline << 2));
-#ifdef POWERPC_LINUX
- if (TARGET_64BIT)
- {
- if (!savres_fprs_inline)
- strategy |= SAVRES_NOINLINE_FPRS_SAVES_LR;
- else if (!savres_gprs_inline && info->first_fp_reg_save == 64)
- strategy |= SAVRES_NOINLINE_GPRS_SAVES_LR;
- }
-#else
- if (TARGET_AIX && !savres_fprs_inline)
- strategy |= SAVRES_NOINLINE_FPRS_DOESNT_RESTORE_LR;
-#endif
- return strategy;
-}
-
/* Emit function prologue as insns. */
void
reg_size = 8;
}
- strategy = rs6000_savres_strategy (info, /*savep=*/true,
- /*static_chain_p=*/using_static_chain_p,
- /*sibcall=*/0);
+ strategy = info->savres_strategy;
using_store_multiple = strategy & SAVRES_MULTIPLE;
- saving_FPRs_inline = strategy & SAVRES_INLINE_FPRS;
- saving_GPRs_inline = strategy & SAVRES_INLINE_GPRS;
+ saving_FPRs_inline = strategy & SAVE_INLINE_FPRS;
+ saving_GPRs_inline = strategy & SAVE_INLINE_GPRS;
/* For V.4, update stack before we do any saving and set back pointer. */
if (! WORLD_SAVE_P (info)
gen_rtx_REG (Pmode, LR_REGNO));
RTX_FRAME_RELATED_P (insn) = 1;
- if (!(strategy & (SAVRES_NOINLINE_GPRS_SAVES_LR
- | SAVRES_NOINLINE_FPRS_SAVES_LR)))
+ if (!(strategy & (SAVE_NOINLINE_GPRS_SAVES_LR
+ | SAVE_NOINLINE_FPRS_SAVES_LR)))
{
addr = gen_rtx_PLUS (Pmode, frame_reg_rtx,
GEN_INT (info->lr_save_offset + sp_offset));
DFmode,
/*savep=*/true, /*gpr=*/false,
/*lr=*/(strategy
- & SAVRES_NOINLINE_FPRS_SAVES_LR)
+ & SAVE_NOINLINE_FPRS_SAVES_LR)
!= 0);
insn = emit_insn (par);
rs6000_frame_related (insn, frame_ptr_rtx, info->total_size,
reg_mode,
/*savep=*/true, /*gpr=*/true,
/*lr=*/(strategy
- & SAVRES_NOINLINE_GPRS_SAVES_LR)
+ & SAVE_NOINLINE_GPRS_SAVES_LR)
!= 0);
insn = emit_insn (par);
rs6000_frame_related (insn, frame_ptr_rtx, info->total_size,
{
unsigned int i, regno;
- /* In AIX ABI we need to pretend we save r2 here. */
- if (TARGET_AIX)
- {
- rtx addr, reg, mem;
-
- reg = gen_rtx_REG (reg_mode, 2);
- addr = gen_rtx_PLUS (Pmode, frame_reg_rtx,
- GEN_INT (sp_offset + 5 * reg_size));
- mem = gen_frame_mem (reg_mode, addr);
-
- insn = emit_move_insn (mem, reg);
- rs6000_frame_related (insn, frame_ptr_rtx, info->total_size,
- NULL_RTX, NULL_RTX);
- PATTERN (insn) = gen_blockage ();
- }
-
for (i = 0; ; ++i)
{
regno = EH_RETURN_DATA_REGNO (i);
}
}
+ /* In AIX ABI we need to make sure r2 is really saved. */
+ if (TARGET_AIX && crtl->calls_eh_return)
+ {
+ rtx tmp_reg, tmp_reg_si, hi, lo, compare_result, toc_save_done, jump;
+ long toc_restore_insn;
+
+ gcc_assert (frame_reg_rtx == frame_ptr_rtx
+ || frame_reg_rtx == sp_reg_rtx);
+ tmp_reg = gen_rtx_REG (Pmode, 11);
+ tmp_reg_si = gen_rtx_REG (SImode, 11);
+ if (using_static_chain_p)
+ emit_move_insn (gen_rtx_REG (Pmode, 0), tmp_reg);
+ gcc_assert (saving_GPRs_inline && saving_FPRs_inline);
+ emit_move_insn (tmp_reg, gen_rtx_REG (Pmode, LR_REGNO));
+ /* Peek at instruction to which this function returns. If it's
+ restoring r2, then we know we've already saved r2. We can't
+ unconditionally save r2 because the value we have will already
+ be updated if we arrived at this function via a plt call or
+ toc adjusting stub. */
+ emit_move_insn (tmp_reg_si, gen_rtx_MEM (SImode, tmp_reg));
+ toc_restore_insn = TARGET_32BIT ? 0x80410014 : 0xE8410028;
+ hi = gen_int_mode (toc_restore_insn & ~0xffff, SImode);
+ emit_insn (gen_xorsi3 (tmp_reg_si, tmp_reg_si, hi));
+ compare_result = gen_rtx_REG (CCUNSmode, CR0_REGNO);
+ validate_condition_mode (EQ, CCUNSmode);
+ lo = gen_int_mode (toc_restore_insn & 0xffff, SImode);
+ emit_insn (gen_rtx_SET (VOIDmode, compare_result,
+ gen_rtx_COMPARE (CCUNSmode, tmp_reg_si, lo)));
+ toc_save_done = gen_label_rtx ();
+ jump = gen_rtx_IF_THEN_ELSE (VOIDmode,
+ gen_rtx_EQ (VOIDmode, compare_result,
+ const0_rtx),
+ gen_rtx_LABEL_REF (VOIDmode, toc_save_done),
+ pc_rtx);
+ jump = emit_jump_insn (gen_rtx_SET (VOIDmode, pc_rtx, jump));
+ JUMP_LABEL (jump) = toc_save_done;
+ LABEL_NUSES (toc_save_done) += 1;
+
+ emit_frame_save (frame_reg_rtx, frame_ptr_rtx, reg_mode, 2,
+ sp_offset + 5 * reg_size, info->total_size);
+ emit_label (toc_save_done);
+ if (using_static_chain_p)
+ emit_move_insn (tmp_reg, gen_rtx_REG (Pmode, 0));
+ }
+
/* Save CR if we use any that must be preserved. */
if (!WORLD_SAVE_P (info) && info->cr_save_p)
{
/* Write .extern for any function we will call to save and restore
fp values. */
- if (info->first_fp_reg_save < 64
- && !FP_SAVE_INLINE (info->first_fp_reg_save))
+ if (info->first_fp_reg_save < 64)
{
char *name;
int regno = info->first_fp_reg_save - 32;
- name = rs6000_savres_routine_name (info, regno, /*savep=*/true,
- /*gpr=*/false, /*lr=*/false);
- fprintf (file, "\t.extern %s\n", name);
-
- name = rs6000_savres_routine_name (info, regno, /*savep=*/false,
- /*gpr=*/false, /*lr=*/true);
- fprintf (file, "\t.extern %s\n", name);
+ if ((info->savres_strategy & SAVE_INLINE_FPRS) == 0)
+ {
+ name = rs6000_savres_routine_name (info, regno, /*savep=*/true,
+ /*gpr=*/false, /*lr=*/false);
+ fprintf (file, "\t.extern %s\n", name);
+ }
+ if ((info->savres_strategy & REST_INLINE_FPRS) == 0)
+ {
+ name = rs6000_savres_routine_name (info, regno, /*savep=*/false,
+ /*gpr=*/false, /*lr=*/true);
+ fprintf (file, "\t.extern %s\n", name);
+ }
}
/* Write .extern for AIX common mode routines, if needed. */
reg_size = 8;
}
- strategy = rs6000_savres_strategy (info, /*savep=*/false,
- /*static_chain_p=*/0, sibcall);
+ strategy = info->savres_strategy;
using_load_multiple = strategy & SAVRES_MULTIPLE;
- restoring_FPRs_inline = strategy & SAVRES_INLINE_FPRS;
- restoring_GPRs_inline = strategy & SAVRES_INLINE_GPRS;
+ restoring_FPRs_inline = sibcall || (strategy & REST_INLINE_FPRS);
+ restoring_GPRs_inline = sibcall || (strategy & REST_INLINE_GPRS);
using_mtcr_multiple = (rs6000_cpu == PROCESSOR_PPC601
|| rs6000_cpu == PROCESSOR_PPC603
|| rs6000_cpu == PROCESSOR_PPC750
&& !frame_pointer_needed));
restore_lr = (info->lr_save_p
&& (restoring_FPRs_inline
- || (strategy & SAVRES_NOINLINE_FPRS_DOESNT_RESTORE_LR))
+ || (strategy & REST_NOINLINE_FPRS_DOESNT_RESTORE_LR))
&& (restoring_GPRs_inline
|| info->first_fp_reg_save < 64));
if (!sibcall)
{
rtvec p;
- bool lr = (strategy & SAVRES_NOINLINE_FPRS_DOESNT_RESTORE_LR) == 0;
+ bool lr = (strategy & REST_NOINLINE_FPRS_DOESNT_RESTORE_LR) == 0;
if (! restoring_FPRs_inline)
p = rtvec_alloc (4 + 64 - info->first_fp_reg_save);
else
struct toc_hash_struct *h;
void * * found;
- /* Create toc_hash_table. This can't be done at OVERRIDE_OPTIONS
+ /* Create toc_hash_table. This can't be done at TARGET_OPTION_OVERRIDE
time because GGC is not initialized at that point. */
if (toc_hash_table == NULL)
toc_hash_table = htab_create_ggc (1021, toc_hash_function,
HOST_WIDE_INT size = int_size_in_bytes (TREE_TYPE (decl));
if (size > 0
- && (unsigned HOST_WIDE_INT) size <= g_switch_value
+ && size <= g_switch_value
/* If it's not public, and we're not going to reference it there,
there's no need to put it in the small data section. */
&& (rs6000_sdata != SDATA_DATA || TREE_PUBLIC (decl)))
|| (outer_code == COMPARE
&& (satisfies_constraint_I (x)
|| satisfies_constraint_K (x)))
- || (outer_code == EQ
+ || ((outer_code == EQ || outer_code == NE)
&& (satisfies_constraint_I (x)
|| satisfies_constraint_K (x)
|| (mode == SImode