#undef RS6000_BUILTIN
#undef RS6000_BUILTIN_EQUATE
+/* Support for -mveclibabi=<xxx> to control which vector library to use. */
+static tree (*rs6000_veclib_handler) (tree, tree, tree);
+
\f
static bool rs6000_function_ok_for_sibcall (tree, tree);
static const char *rs6000_invalid_within_doloop (const_rtx);
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 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,
static void rs6000_xcoff_file_end (void);
#endif
static int rs6000_variable_issue (FILE *, int, rtx, int);
+static int rs6000_register_move_cost (enum machine_mode,
+ reg_class_t, reg_class_t);
+static int rs6000_memory_move_cost (enum machine_mode, reg_class_t, bool);
static bool rs6000_rtx_costs (rtx, int, int, int *, bool);
static bool rs6000_debug_rtx_costs (rtx, int, int, int *, bool);
static int rs6000_debug_address_cost (rtx, bool);
int, bool);
static int rs6000_builtin_vectorization_cost (enum vect_cost_for_stmt,
tree, int);
+static unsigned int rs6000_units_per_simd_word (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_optimization (int, int);
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 *);
static const char *rs6000_get_some_local_dynamic_name (void);
static int rs6000_get_some_local_dynamic_name_1 (rtx *, void *);
static rtx rs6000_complex_function_value (enum machine_mode);
-static rtx rs6000_spe_function_arg (CUMULATIVE_ARGS *,
- enum machine_mode, tree);
+static rtx rs6000_spe_function_arg (const CUMULATIVE_ARGS *,
+ enum machine_mode, const_tree);
static void rs6000_darwin64_record_arg_advance_flush (CUMULATIVE_ARGS *,
- HOST_WIDE_INT);
+ HOST_WIDE_INT, int);
static void rs6000_darwin64_record_arg_advance_recurse (CUMULATIVE_ARGS *,
- tree, HOST_WIDE_INT);
+ const_tree,
+ HOST_WIDE_INT);
static void rs6000_darwin64_record_arg_flush (CUMULATIVE_ARGS *,
HOST_WIDE_INT,
rtx[], int *);
static void rs6000_darwin64_record_arg_recurse (CUMULATIVE_ARGS *,
const_tree, HOST_WIDE_INT,
rtx[], int *);
-static rtx rs6000_darwin64_record_arg (CUMULATIVE_ARGS *, const_tree, int, bool);
-static rtx rs6000_mixed_function_arg (enum machine_mode, tree, int);
+static rtx rs6000_darwin64_record_arg (CUMULATIVE_ARGS *, const_tree, bool, bool);
+static rtx rs6000_mixed_function_arg (enum machine_mode, const_tree, int);
+static void rs6000_function_arg_advance (CUMULATIVE_ARGS *, enum machine_mode,
+ const_tree, bool);
+static rtx rs6000_function_arg (CUMULATIVE_ARGS *, enum machine_mode,
+ const_tree, bool);
static void rs6000_move_block_from_reg (int regno, rtx x, int nregs);
static void setup_incoming_varargs (CUMULATIVE_ARGS *,
enum machine_mode, tree,
#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_UNITS_PER_SIMD_WORD
+#define TARGET_VECTORIZE_UNITS_PER_SIMD_WORD \
+ rs6000_units_per_simd_word
#undef TARGET_INIT_BUILTINS
#define TARGET_INIT_BUILTINS rs6000_init_builtins
#undef TARGET_INVALID_WITHIN_DOLOOP
#define TARGET_INVALID_WITHIN_DOLOOP rs6000_invalid_within_doloop
+#undef TARGET_REGISTER_MOVE_COST
+#define TARGET_REGISTER_MOVE_COST rs6000_register_move_cost
+#undef TARGET_MEMORY_MOVE_COST
+#define TARGET_MEMORY_MOVE_COST rs6000_memory_move_cost
#undef TARGET_RTX_COSTS
#define TARGET_RTX_COSTS rs6000_rtx_costs
#undef TARGET_ADDRESS_COST
#define TARGET_PASS_BY_REFERENCE rs6000_pass_by_reference
#undef TARGET_ARG_PARTIAL_BYTES
#define TARGET_ARG_PARTIAL_BYTES rs6000_arg_partial_bytes
+#undef TARGET_FUNCTION_ARG_ADVANCE
+#define TARGET_FUNCTION_ARG_ADVANCE rs6000_function_arg_advance
+#undef TARGET_FUNCTION_ARG
+#define TARGET_FUNCTION_ARG rs6000_function_arg
#undef TARGET_BUILD_BUILTIN_VA_LIST
#define TARGET_BUILD_BUILTIN_VA_LIST rs6000_build_builtin_va_list
#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_OPTIMIZATION
+#define TARGET_OPTION_OPTIMIZATION rs6000_option_optimization
+
#undef TARGET_VECTORIZE_BUILTIN_VECTORIZED_FUNCTION
#define TARGET_VECTORIZE_BUILTIN_VECTORIZED_FUNCTION \
rs6000_builtin_vectorized_function
{
flag_pic = 2;
}
+ if (TARGET_64BIT)
+ darwin_one_byte_bool = 1;
}
if (TARGET_64BIT && ! TARGET_POWERPC64)
{
/* 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;
POWERPC_BASE_MASK | MASK_POWERPC64 | MASK_PPC_GPOPT | MASK_PPC_GFXOPT
| MASK_MFCRF | MASK_POPCNTB | MASK_FPRND | MASK_CMPB | MASK_DFP
| MASK_MFPGPR | MASK_RECIP_PRECISION},
- {"power7", PROCESSOR_POWER7,
+ {"power7", PROCESSOR_POWER7, /* Don't add MASK_ISEL by default */
POWERPC_7400_MASK | MASK_POWERPC64 | MASK_PPC_GPOPT | MASK_MFCRF
| MASK_POPCNTB | MASK_FPRND | MASK_CMPB | MASK_DFP | MASK_POPCNTD
- | MASK_VSX| MASK_RECIP_PRECISION}, /* Don't add MASK_ISEL by default */
+ | MASK_VSX | MASK_RECIP_PRECISION},
{"powerpc", PROCESSOR_POWERPC, POWERPC_BASE_MASK},
{"powerpc64", PROCESSOR_POWERPC64,
POWERPC_BASE_MASK | MASK_PPC_GFXOPT | MASK_POWERPC64},
ISA_2_1_MASKS = MASK_MFCRF,
ISA_2_2_MASKS = (ISA_2_1_MASKS | MASK_POPCNTB | MASK_FPRND),
- /* For ISA 2.05, do not add MFPGPR, since it isn't in ISA 2.06, and
- don't add ALTIVEC, since in general it isn't a win on power6. */
- ISA_2_5_MASKS = (ISA_2_2_MASKS | MASK_CMPB | MASK_RECIP_PRECISION
- | MASK_DFP),
+ /* For ISA 2.05, do not add MFPGPR, since it isn't in ISA 2.06, and don't
+ add ALTIVEC, since in general it isn't a win on power6. In ISA 2.04,
+ fsel, fre, fsqrt, etc. were no longer documented as optional. Group
+ masks by server and embedded. */
+ ISA_2_5_MASKS_EMBEDDED = (ISA_2_2_MASKS | MASK_CMPB | MASK_RECIP_PRECISION
+ | MASK_PPC_GFXOPT | MASK_PPC_GPOPT),
+ ISA_2_5_MASKS_SERVER = (ISA_2_5_MASKS_EMBEDDED | MASK_DFP),
/* For ISA 2.06, don't add ISEL, since in general it isn't a win, but
altivec is a win so enable it. */
- ISA_2_6_MASKS = (ISA_2_5_MASKS | MASK_ALTIVEC | MASK_POPCNTD
- | MASK_VSX | MASK_RECIP_PRECISION)
+ ISA_2_6_MASKS_EMBEDDED = (ISA_2_5_MASKS_EMBEDDED | MASK_POPCNTD),
+ ISA_2_6_MASKS_SERVER = (ISA_2_5_MASKS_SERVER | MASK_POPCNTD | MASK_ALTIVEC
+ | MASK_VSX)
};
/* Numerous experiment shows that IRA based loop pressure
{
warning (0, msg);
target_flags &= ~ MASK_VSX;
+ target_flags_explicit |= MASK_VSX;
}
}
/* For the newer switches (vsx, dfp, etc.) set some of the older options,
unless the user explicitly used the -mno-<option> to disable the code. */
if (TARGET_VSX)
- target_flags |= (ISA_2_6_MASKS & ~target_flags_explicit);
+ target_flags |= (ISA_2_6_MASKS_SERVER & ~target_flags_explicit);
+ else if (TARGET_POPCNTD)
+ target_flags |= (ISA_2_6_MASKS_EMBEDDED & ~target_flags_explicit);
else if (TARGET_DFP)
- target_flags |= (ISA_2_5_MASKS & ~target_flags_explicit);
+ target_flags |= (ISA_2_5_MASKS_SERVER & ~target_flags_explicit);
+ else if (TARGET_CMPB)
+ target_flags |= (ISA_2_5_MASKS_EMBEDDED & ~target_flags_explicit);
+ else if (TARGET_POPCNTB || TARGET_FPRND)
+ target_flags |= (ISA_2_2_MASKS & ~target_flags_explicit);
else if (TARGET_ALTIVEC)
target_flags |= (MASK_PPC_GFXOPT & ~target_flags_explicit);
+ /* E500mc does "better" if we inline more aggressively. Respect the
+ user's opinion, though. */
+ if (rs6000_block_move_inline_limit == 0
+ && (rs6000_cpu == PROCESSOR_PPCE500MC
+ || rs6000_cpu == PROCESSOR_PPCE500MC64))
+ rs6000_block_move_inline_limit = 128;
+
+ /* store_one_arg depends on expand_block_move to handle at least the
+ size of reg_parm_stack_space. */
+ if (rs6000_block_move_inline_limit < (TARGET_POWERPC64 ? 64 : 32))
+ rs6000_block_move_inline_limit = (TARGET_POWERPC64 ? 64 : 32);
+
/* Set debug flags */
if (rs6000_debug_name)
{
rs6000_traceback_name);
}
+ if (rs6000_veclibabi_name)
+ {
+ if (strcmp (rs6000_veclibabi_name, "mass") == 0)
+ rs6000_veclib_handler = rs6000_builtin_vectorized_libmass;
+ else
+ error ("unknown vectorization library ABI type (%s) for "
+ "-mveclibabi= switch", rs6000_veclibabi_name);
+ }
+
if (!rs6000_explicit_options.long_double)
rs6000_long_double_type_size = RS6000_DEFAULT_LONG_DOUBLE_SIZE;
TARGET_ALTIVEC_VRSAVE = rs6000_altivec_abi;
}
- /* Set the Darwin64 ABI as default for 64-bit Darwin. */
- if (DEFAULT_ABI == ABI_DARWIN && TARGET_64BIT)
+ /* Set the Darwin64 ABI as default for 64-bit Darwin.
+ So far, the only darwin64 targets are also MACH-O. */
+ if (TARGET_MACHO
+ && DEFAULT_ABI == ABI_DARWIN
+ && TARGET_64BIT)
{
rs6000_darwin64_abi = 1;
-#if TARGET_MACHO
- darwin_one_byte_bool = 1;
-#endif
/* Default to natural alignment, for better performance. */
rs6000_alignment_flags = MASK_ALIGN_NATURAL;
}
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.units_per_simd_word. */
+
+static unsigned int
+rs6000_units_per_simd_word (enum machine_mode mode ATTRIBUTE_UNUSED)
+{
+ return (TARGET_VSX ? UNITS_PER_VSX_WORD
+ : (TARGET_ALTIVEC ? UNITS_PER_ALTIVEC_WORD
+ : (TARGET_SPE ? UNITS_PER_SPE_WORD
+ : (TARGET_PAIRED_FLOAT ? UNITS_PER_PAIRED_WORD
+ : UNITS_PER_WORD))));
+}
+
/* 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)
+static void
+rs6000_option_optimization (int level ATTRIBUTE_UNUSED,
+ int size ATTRIBUTE_UNUSED)
{
if (DEFAULT_ABI == ABI_DARWIN)
/* The Darwin libraries never set errno, so we might as well
return FPU_NONE;
}
+
+/* Handler for the Mathematical Acceleration Subsystem (mass) interface to a
+ library with vectorized intrinsics. */
+
+static tree
+rs6000_builtin_vectorized_libmass (tree fndecl, tree type_out, tree type_in)
+{
+ char name[32];
+ const char *suffix = NULL;
+ tree fntype, new_fndecl, bdecl = NULL_TREE;
+ int n_args = 1;
+ const char *bname;
+ enum machine_mode el_mode, in_mode;
+ int n, in_n;
+
+ /* Libmass is suitable for unsafe math only as it does not correctly support
+ parts of IEEE with the required precision such as denormals. Only support
+ it if we have VSX to use the simd d2 or f4 functions.
+ XXX: Add variable length support. */
+ if (!flag_unsafe_math_optimizations || !TARGET_VSX)
+ return NULL_TREE;
+
+ el_mode = TYPE_MODE (TREE_TYPE (type_out));
+ n = TYPE_VECTOR_SUBPARTS (type_out);
+ in_mode = TYPE_MODE (TREE_TYPE (type_in));
+ in_n = TYPE_VECTOR_SUBPARTS (type_in);
+ if (el_mode != in_mode
+ || n != in_n)
+ return NULL_TREE;
+
+ if (DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL)
+ {
+ enum built_in_function fn = DECL_FUNCTION_CODE (fndecl);
+ switch (fn)
+ {
+ case BUILT_IN_ATAN2:
+ case BUILT_IN_HYPOT:
+ case BUILT_IN_POW:
+ n_args = 2;
+ /* fall through */
+
+ case BUILT_IN_ACOS:
+ case BUILT_IN_ACOSH:
+ case BUILT_IN_ASIN:
+ case BUILT_IN_ASINH:
+ case BUILT_IN_ATAN:
+ case BUILT_IN_ATANH:
+ case BUILT_IN_CBRT:
+ case BUILT_IN_COS:
+ case BUILT_IN_COSH:
+ case BUILT_IN_ERF:
+ case BUILT_IN_ERFC:
+ case BUILT_IN_EXP2:
+ case BUILT_IN_EXP:
+ case BUILT_IN_EXPM1:
+ case BUILT_IN_LGAMMA:
+ case BUILT_IN_LOG10:
+ case BUILT_IN_LOG1P:
+ case BUILT_IN_LOG2:
+ case BUILT_IN_LOG:
+ case BUILT_IN_SIN:
+ case BUILT_IN_SINH:
+ case BUILT_IN_SQRT:
+ case BUILT_IN_TAN:
+ case BUILT_IN_TANH:
+ bdecl = implicit_built_in_decls[fn];
+ suffix = "d2"; /* pow -> powd2 */
+ if (el_mode != DFmode
+ || n != 2)
+ return NULL_TREE;
+ break;
+
+ case BUILT_IN_ATAN2F:
+ case BUILT_IN_HYPOTF:
+ case BUILT_IN_POWF:
+ n_args = 2;
+ /* fall through */
+
+ case BUILT_IN_ACOSF:
+ case BUILT_IN_ACOSHF:
+ case BUILT_IN_ASINF:
+ case BUILT_IN_ASINHF:
+ case BUILT_IN_ATANF:
+ case BUILT_IN_ATANHF:
+ case BUILT_IN_CBRTF:
+ case BUILT_IN_COSF:
+ case BUILT_IN_COSHF:
+ case BUILT_IN_ERFF:
+ case BUILT_IN_ERFCF:
+ case BUILT_IN_EXP2F:
+ case BUILT_IN_EXPF:
+ case BUILT_IN_EXPM1F:
+ case BUILT_IN_LGAMMAF:
+ case BUILT_IN_LOG10F:
+ case BUILT_IN_LOG1PF:
+ case BUILT_IN_LOG2F:
+ case BUILT_IN_LOGF:
+ case BUILT_IN_SINF:
+ case BUILT_IN_SINHF:
+ case BUILT_IN_SQRTF:
+ case BUILT_IN_TANF:
+ case BUILT_IN_TANHF:
+ bdecl = implicit_built_in_decls[fn];
+ suffix = "4"; /* powf -> powf4 */
+ if (el_mode != SFmode
+ || n != 4)
+ return NULL_TREE;
+ break;
+
+ default:
+ return NULL_TREE;
+ }
+ }
+ else
+ return NULL_TREE;
+
+ gcc_assert (suffix != NULL);
+ bname = IDENTIFIER_POINTER (DECL_NAME (bdecl));
+ strcpy (name, bname + sizeof ("__builtin_") - 1);
+ strcat (name, suffix);
+
+ if (n_args == 1)
+ fntype = build_function_type_list (type_out, type_in, NULL);
+ else if (n_args == 2)
+ fntype = build_function_type_list (type_out, type_in, type_in, NULL);
+ else
+ gcc_unreachable ();
+
+ /* Build a function declaration for the vectorized function. */
+ new_fndecl = build_decl (BUILTINS_LOCATION,
+ FUNCTION_DECL, get_identifier (name), fntype);
+ TREE_PUBLIC (new_fndecl) = 1;
+ DECL_EXTERNAL (new_fndecl) = 1;
+ DECL_IS_NOVOPS (new_fndecl) = 1;
+ TREE_READONLY (new_fndecl) = 1;
+
+ return new_fndecl;
+}
+
/* Returns a function decl for a vectorized version of the builtin function
with builtin function code FN and the result vector type TYPE, or NULL_TREE
if it is not available. */
}
}
+ /* Generate calls to libmass if appropriate. */
+ if (rs6000_veclib_handler)
+ return rs6000_veclib_handler (fndecl, type_out, type_in);
+
return NULL_TREE;
}
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 = "";
}
/* Skip all non field decls */
while (field != NULL && TREE_CODE (field) != FIELD_DECL)
- field = TREE_CHAIN (field);
+ field = DECL_CHAIN (field);
if (field != NULL && field != type)
{
tree field = TYPE_FIELDS (type);
/* Skip all non field decls */
while (field != NULL && TREE_CODE (field) != FIELD_DECL)
- field = TREE_CHAIN (field);
+ field = DECL_CHAIN (field);
if (! field)
break;
/* A packed field does not contribute any extra alignment. */
/* 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)
function doing the returning, or @code{NULL} for libcalls.
The AIX ABI for the RS/6000 specifies that all structures are
- returned in memory. The Darwin ABI does the same. The SVR4 ABI
- specifies that structures <= 8 bytes are returned in r3/r4, but a
- draft put them in memory, and GCC used to implement the draft
+ returned in memory. The Darwin ABI does the same.
+
+ For the Darwin 64 Bit ABI, a function result can be returned in
+ registers or in memory, depending on the size of the return data
+ type. If it is returned in registers, the value occupies the same
+ registers as it would if it were the first and only function
+ argument. Otherwise, the function places its result in memory at
+ the location pointed to by GPR3.
+
+ The SVR4 ABI specifies that structures <= 8 bytes are returned in r3/r4,
+ but a draft put them in memory, and GCC used to implement the draft
instead of the final standard. Therefore, aix_struct_return
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.
static bool
rs6000_return_in_memory (const_tree type, const_tree fntype ATTRIBUTE_UNUSED)
{
- /* In the darwin64 abi, try to use registers for larger structs
- if possible. */
- if (rs6000_darwin64_abi
+ /* For the Darwin64 ABI, test if we can fit the return value in regs. */
+ if (TARGET_MACHO
+ && rs6000_darwin64_abi
&& TREE_CODE (type) == RECORD_TYPE
&& int_size_in_bytes (type) > 0)
{
valcum.vregno = ALTIVEC_ARG_MIN_REG;
/* Do a trial code generation as if this were going to be passed
as an argument; if any part goes in memory, we return NULL. */
- valret = rs6000_darwin64_record_arg (&valcum, type, 1, true);
+ valret = rs6000_darwin64_record_arg (&valcum, type, true, true);
if (valret)
return false;
/* Otherwise fall through to more conventional ABI rules. */
cum->call_cookie = ((DEFAULT_ABI == ABI_V4 && libcall)
? CALL_LIBCALL : CALL_NORMAL);
cum->sysv_gregno = GP_ARG_MIN_REG;
- cum->stdarg = fntype
- && (TYPE_ARG_TYPES (fntype) != 0
- && (TREE_VALUE (tree_last (TYPE_ARG_TYPES (fntype)))
- != void_type_node));
+ cum->stdarg = stdarg_p (fntype);
cum->nargs_prototype = 0;
if (incoming || cum->prototype)
Quadword align large synthetic vector types. */
int
-function_arg_boundary (enum machine_mode mode, tree type)
+function_arg_boundary (enum machine_mode mode, const_tree type)
{
if (DEFAULT_ABI == ABI_V4
&& (GET_MODE_SIZE (mode) == 8
|| (type && TREE_CODE (type) == VECTOR_TYPE
&& int_size_in_bytes (type) >= 16))
return 128;
- else if (rs6000_darwin64_abi && mode == BLKmode
+ else if (TARGET_MACHO
+ && rs6000_darwin64_abi
+ && mode == BLKmode
&& type && TYPE_ALIGN (type) > 64)
return 128;
else
the parameter area. NWORDS of the parameter area are already used. */
static unsigned int
-rs6000_parm_start (enum machine_mode mode, tree type, unsigned int nwords)
+rs6000_parm_start (enum machine_mode mode, const_tree type,
+ unsigned int nwords)
{
unsigned int align;
unsigned int parm_offset;
/* Compute the size (in words) of a function argument. */
static unsigned long
-rs6000_arg_size (enum machine_mode mode, tree type)
+rs6000_arg_size (enum machine_mode mode, const_tree type)
{
unsigned long size;
static void
rs6000_darwin64_record_arg_advance_flush (CUMULATIVE_ARGS *cum,
- HOST_WIDE_INT bitpos)
+ HOST_WIDE_INT bitpos, int final)
{
unsigned int startbit, endbit;
int intregs, intoffset;
enum machine_mode mode;
+ /* Handle the situations where a float is taking up the first half
+ of the GPR, and the other half is empty (typically due to
+ alignment restrictions). We can detect this by a 8-byte-aligned
+ int field, or by seeing that this is the final flush for this
+ argument. Count the word and continue on. */
+ if (cum->floats_in_gpr == 1
+ && (cum->intoffset % 64 == 0
+ || (cum->intoffset == -1 && final)))
+ {
+ cum->words++;
+ cum->floats_in_gpr = 0;
+ }
+
if (cum->intoffset == -1)
return;
intoffset = cum->intoffset;
cum->intoffset = -1;
+ cum->floats_in_gpr = 0;
if (intoffset % BITS_PER_WORD != 0)
{
endbit = (bitpos + BITS_PER_WORD - 1) & -BITS_PER_WORD;
intregs = (endbit - startbit) / BITS_PER_WORD;
cum->words += intregs;
+ /* words should be unsigned. */
+ if ((unsigned)cum->words < (endbit/BITS_PER_WORD))
+ {
+ int pad = (endbit/BITS_PER_WORD) - cum->words;
+ cum->words += pad;
+ }
}
/* The darwin64 ABI calls for us to recurse down through structs,
static void
rs6000_darwin64_record_arg_advance_recurse (CUMULATIVE_ARGS *cum,
- tree type,
+ const_tree type,
HOST_WIDE_INT startbitpos)
{
tree f;
- for (f = TYPE_FIELDS (type); f ; f = TREE_CHAIN (f))
+ for (f = TYPE_FIELDS (type); f ; f = DECL_CHAIN (f))
if (TREE_CODE (f) == FIELD_DECL)
{
HOST_WIDE_INT bitpos = startbitpos;
rs6000_darwin64_record_arg_advance_recurse (cum, ftype, bitpos);
else if (USE_FP_FOR_ARG_P (cum, mode, ftype))
{
- rs6000_darwin64_record_arg_advance_flush (cum, bitpos);
+ rs6000_darwin64_record_arg_advance_flush (cum, bitpos, 0);
cum->fregno += (GET_MODE_SIZE (mode) + 7) >> 3;
- cum->words += (GET_MODE_SIZE (mode) + 7) >> 3;
+ /* Single-precision floats present a special problem for
+ us, because they are smaller than an 8-byte GPR, and so
+ the structure-packing rules combined with the standard
+ varargs behavior mean that we want to pack float/float
+ and float/int combinations into a single register's
+ space. This is complicated by the arg advance flushing,
+ which works on arbitrarily large groups of int-type
+ fields. */
+ if (mode == SFmode)
+ {
+ if (cum->floats_in_gpr == 1)
+ {
+ /* Two floats in a word; count the word and reset
+ the float count. */
+ cum->words++;
+ cum->floats_in_gpr = 0;
+ }
+ else if (bitpos % 64 == 0)
+ {
+ /* A float at the beginning of an 8-byte word;
+ count it and put off adjusting cum->words until
+ we see if a arg advance flush is going to do it
+ for us. */
+ cum->floats_in_gpr++;
+ }
+ else
+ {
+ /* The float is at the end of a word, preceded
+ by integer fields, so the arg advance flush
+ just above has already set cum->words and
+ everything is taken care of. */
+ }
+ }
+ else
+ cum->words += (GET_MODE_SIZE (mode) + 7) >> 3;
}
else if (USE_ALTIVEC_FOR_ARG_P (cum, mode, type, 1))
{
- rs6000_darwin64_record_arg_advance_flush (cum, bitpos);
+ rs6000_darwin64_record_arg_advance_flush (cum, bitpos, 0);
cum->vregno++;
cum->words += 2;
}
}
}
+/* Check for an item that needs to be considered specially under the darwin 64
+ bit ABI. These are record types where the mode is BLK or the structure is
+ 8 bytes in size. */
+static int
+rs6000_darwin64_struct_check_p (enum machine_mode mode, const_tree type)
+{
+ return rs6000_darwin64_abi
+ && ((mode == BLKmode
+ && TREE_CODE (type) == RECORD_TYPE
+ && int_size_in_bytes (type) > 0)
+ || (type && TREE_CODE (type) == RECORD_TYPE
+ && int_size_in_bytes (type) == 8)) ? 1 : 0;
+}
+
/* Update the data in CUM to advance over an argument
of mode MODE and data type TYPE.
(TYPE is null for libcalls where that information may not be available.)
with MODE and TYPE set to that of the pointer to the arg, not the arg
itself. */
-void
-function_arg_advance (CUMULATIVE_ARGS *cum, enum machine_mode mode,
- tree type, int named, int depth)
+static void
+rs6000_function_arg_advance_1 (CUMULATIVE_ARGS *cum, enum machine_mode mode,
+ const_tree type, bool named, int depth)
{
- int size;
/* Only tick off an argument if we're not recursing. */
if (depth == 0)
&& cum->sysv_gregno <= GP_ARG_MAX_REG)
cum->sysv_gregno++;
- else if (rs6000_darwin64_abi
- && mode == BLKmode
- && TREE_CODE (type) == RECORD_TYPE
- && (size = int_size_in_bytes (type)) > 0)
+ else if (TARGET_MACHO && rs6000_darwin64_struct_check_p (mode, type))
{
+ int size = int_size_in_bytes (type);
/* Variable sized types have size == -1 and are
treated as if consisting entirely of ints.
Pad to 16 byte boundary if needed. */
{ int; double; int; } [powerpc alignment]. We have to
grovel through the fields for these too. */
cum->intoffset = 0;
+ cum->floats_in_gpr = 0;
rs6000_darwin64_record_arg_advance_recurse (cum, type, 0);
rs6000_darwin64_record_arg_advance_flush (cum,
- size * BITS_PER_UNIT);
+ size * BITS_PER_UNIT, 1);
}
+ if (TARGET_DEBUG_ARG)
+ {
+ fprintf (stderr, "function_adv: words = %2d, align=%d, size=%d",
+ cum->words, TYPE_ALIGN (type), size);
+ fprintf (stderr,
+ "nargs = %4d, proto = %d, mode = %4s (darwin64 abi)\n",
+ cum->nargs_prototype, cum->prototype,
+ GET_MODE_NAME (mode));
+ }
}
else if (DEFAULT_ABI == ABI_V4)
{
}
}
+static void
+rs6000_function_arg_advance (CUMULATIVE_ARGS *cum, enum machine_mode mode,
+ const_tree type, bool named)
+{
+ rs6000_function_arg_advance_1 (cum, mode, type, named, 0);
+}
+
static rtx
spe_build_register_parallel (enum machine_mode mode, int gregno)
{
/* Determine where to put a SIMD argument on the SPE. */
static rtx
-rs6000_spe_function_arg (CUMULATIVE_ARGS *cum, enum machine_mode mode,
- tree type)
+rs6000_spe_function_arg (const CUMULATIVE_ARGS *cum, enum machine_mode mode,
+ const_tree type)
{
int gregno = cum->sysv_gregno;
{
tree f;
- for (f = TYPE_FIELDS (type); f ; f = TREE_CHAIN (f))
+ for (f = TYPE_FIELDS (type); f ; f = DECL_CHAIN (f))
if (TREE_CODE (f) == FIELD_DECL)
{
HOST_WIDE_INT bitpos = startbitpos;
static rtx
rs6000_darwin64_record_arg (CUMULATIVE_ARGS *orig_cum, const_tree type,
- int named, bool retval)
+ bool named, bool retval)
{
rtx rvec[FIRST_PSEUDO_REGISTER];
int k = 1, kbase = 1;
/* Determine where to place an argument in 64-bit mode with 32-bit ABI. */
static rtx
-rs6000_mixed_function_arg (enum machine_mode mode, tree type, int align_words)
+rs6000_mixed_function_arg (enum machine_mode mode, const_tree type,
+ int align_words)
{
int n_units;
int i, k;
with MODE and TYPE set to that of the pointer to the arg, not the arg
itself. */
-rtx
-function_arg (CUMULATIVE_ARGS *cum, enum machine_mode mode,
- tree type, int named)
+static rtx
+rs6000_function_arg (CUMULATIVE_ARGS *cum, enum machine_mode mode,
+ const_tree type, bool named)
{
enum rs6000_abi abi = DEFAULT_ABI;
return GEN_INT (cum->call_cookie);
}
- if (rs6000_darwin64_abi && mode == BLKmode
- && TREE_CODE (type) == RECORD_TYPE)
+ if (TARGET_MACHO && rs6000_darwin64_struct_check_p (mode, type))
{
rtx rslt = rs6000_darwin64_record_arg (cum, type, named, false);
if (rslt != NULL_RTX)
return 0;
/* In this complicated case we just disable the partial_nregs code. */
- if (rs6000_darwin64_abi && mode == BLKmode
- && TREE_CODE (type) == RECORD_TYPE
- && int_size_in_bytes (type) > 0)
+ if (TARGET_MACHO && rs6000_darwin64_struct_check_p (mode, type))
return 0;
align_words = rs6000_parm_start (mode, type, cum->words);
/* Skip the last named argument. */
next_cum = *cum;
- function_arg_advance (&next_cum, mode, type, 1, 0);
+ rs6000_function_arg_advance_1 (&next_cum, mode, type, true, 0);
if (DEFAULT_ABI == ABI_V4)
{
TREE_CHAIN (record) = type_decl;
TYPE_NAME (record) = type_decl;
TYPE_FIELDS (record) = f_gpr;
- TREE_CHAIN (f_gpr) = f_fpr;
- TREE_CHAIN (f_fpr) = f_res;
- TREE_CHAIN (f_res) = f_ovf;
- TREE_CHAIN (f_ovf) = f_sav;
+ DECL_CHAIN (f_gpr) = f_fpr;
+ DECL_CHAIN (f_fpr) = f_res;
+ DECL_CHAIN (f_res) = f_ovf;
+ DECL_CHAIN (f_ovf) = f_sav;
layout_type (record);
}
f_gpr = TYPE_FIELDS (TREE_TYPE (va_list_type_node));
- f_fpr = TREE_CHAIN (f_gpr);
- f_res = TREE_CHAIN (f_fpr);
- f_ovf = TREE_CHAIN (f_res);
- f_sav = TREE_CHAIN (f_ovf);
+ f_fpr = DECL_CHAIN (f_gpr);
+ f_res = DECL_CHAIN (f_fpr);
+ f_ovf = DECL_CHAIN (f_res);
+ f_sav = DECL_CHAIN (f_ovf);
valist = build_va_arg_indirect_ref (valist);
gpr = build3 (COMPONENT_REF, TREE_TYPE (f_gpr), valist, f_gpr, NULL_TREE);
return build_va_arg_indirect_ref (t);
}
+ /* We need to deal with the fact that the darwin ppc64 ABI is defined by an
+ earlier version of gcc, with the property that it always applied alignment
+ adjustments to the va-args (even for zero-sized types). The cheapest way
+ to deal with this is to replicate the effect of the part of
+ std_gimplify_va_arg_expr that carries out the align adjust, for the case
+ of relevance.
+ We don't need to check for pass-by-reference because of the test above.
+ We can return a simplifed answer, since we know there's no offset to add. */
+
+ if (TARGET_MACHO
+ && rs6000_darwin64_abi
+ && integer_zerop (TYPE_SIZE (type)))
+ {
+ unsigned HOST_WIDE_INT align, boundary;
+ tree valist_tmp = get_initialized_tmp_var (valist, pre_p, NULL);
+ align = PARM_BOUNDARY / BITS_PER_UNIT;
+ boundary = FUNCTION_ARG_BOUNDARY (TYPE_MODE (type), type);
+ if (boundary > MAX_SUPPORTED_STACK_ALIGNMENT)
+ boundary = MAX_SUPPORTED_STACK_ALIGNMENT;
+ boundary /= BITS_PER_UNIT;
+ if (boundary > align)
+ {
+ tree t ;
+ /* This updates arg ptr by the amount that would be necessary
+ to align the zero-sized (but not zero-alignment) item. */
+ t = build2 (MODIFY_EXPR, TREE_TYPE (valist), valist_tmp,
+ fold_build2 (POINTER_PLUS_EXPR,
+ TREE_TYPE (valist),
+ valist_tmp, size_int (boundary - 1)));
+ gimplify_and_add (t, pre_p);
+
+ t = fold_convert (sizetype, valist_tmp);
+ t = build2 (MODIFY_EXPR, TREE_TYPE (valist), valist_tmp,
+ fold_convert (TREE_TYPE (valist),
+ fold_build2 (BIT_AND_EXPR, sizetype, t,
+ size_int (-boundary))));
+ t = build2 (MODIFY_EXPR, TREE_TYPE (valist), valist, t);
+ gimplify_and_add (t, pre_p);
+ }
+ /* Since it is zero-sized there's no increment for the item itself. */
+ valist_tmp = fold_convert (build_pointer_type (type), valist_tmp);
+ return build_va_arg_indirect_ref (valist_tmp);
+ }
+
if (DEFAULT_ABI != ABI_V4)
{
if (targetm.calls.split_complex_arg && TREE_CODE (type) == COMPLEX_TYPE)
}
f_gpr = TYPE_FIELDS (TREE_TYPE (va_list_type_node));
- f_fpr = TREE_CHAIN (f_gpr);
- f_res = TREE_CHAIN (f_fpr);
- f_ovf = TREE_CHAIN (f_res);
- f_sav = TREE_CHAIN (f_ovf);
+ f_fpr = DECL_CHAIN (f_gpr);
+ f_res = DECL_CHAIN (f_fpr);
+ f_ovf = DECL_CHAIN (f_res);
+ f_sav = DECL_CHAIN (f_ovf);
valist = build_va_arg_indirect_ref (valist);
gpr = build3 (COMPONENT_REF, TREE_TYPE (f_gpr), valist, f_gpr, NULL_TREE);
|| 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
gcc_assert (TARGET_ALTIVEC);
arg = CALL_EXPR_ARG (exp, 0);
- gcc_assert (TREE_CODE (TREE_TYPE (arg)) == POINTER_TYPE);
+ gcc_assert (POINTER_TYPE_P (TREE_TYPE (arg)));
op = expand_expr (arg, NULL_RTX, Pmode, EXPAND_NORMAL);
addr = memory_address (mode, op);
if (fcode == ALTIVEC_BUILTIN_MASK_FOR_STORE)
if (bytes <= 0)
return 1;
- /* store_one_arg depends on expand_block_move to handle at least the size of
- reg_parm_stack_space. */
- if (bytes > (TARGET_POWERPC64 ? 64 : 32))
+ if (bytes > rs6000_block_move_inline_limit)
return 0;
for (offset = 0; bytes > 0; offset += move_bytes, bytes -= move_bytes)
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;
}
/* Check for any SDmode parameters of the function. */
- for (t = DECL_ARGUMENTS (cfun->decl); t; t = TREE_CHAIN (t))
+ for (t = DECL_ARGUMENTS (cfun->decl); t; t = DECL_CHAIN (t))
{
if (TREE_TYPE (t) == error_mark_node)
continue;
{
const char *name = XSTR (x, 0);
#if TARGET_MACHO
- if (MACHOPIC_INDIRECT
+ if (darwin_emit_branch_islands
+ && MACHOPIC_INDIRECT
&& machopic_classify_symbol (x) == MACHOPIC_UNDEFINED_FUNCTION)
name = machopic_indirection_name (x, /*stub_p=*/true);
#endif
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)
}
-/* Emit the RTL for an sCOND pattern. */
+/* Emit the RTL for an sISEL pattern. */
void
-rs6000_emit_sISEL (enum machine_mode mode, rtx operands[])
+rs6000_emit_sISEL (enum machine_mode mode ATTRIBUTE_UNUSED, rtx operands[])
{
- rtx condition_rtx;
- enum machine_mode op_mode;
- enum rtx_code cond_code;
- rtx result = operands[0];
-
- condition_rtx = rs6000_generate_compare (operands[1], mode);
- cond_code = GET_CODE (condition_rtx);
-
- op_mode = GET_MODE (XEXP (operands[1], 0));
- if (op_mode == VOIDmode)
- op_mode = GET_MODE (XEXP (operands[1], 1));
-
- if (TARGET_POWERPC64 && GET_MODE (result) == DImode)
- {
- PUT_MODE (condition_rtx, DImode);
- if (cond_code == GEU || cond_code == GTU || cond_code == LEU
- || cond_code == LTU)
- emit_insn (gen_isel_unsigned_di (result, condition_rtx,
- force_reg (DImode, const1_rtx),
- force_reg (DImode, const0_rtx),
- XEXP (condition_rtx, 0)));
- else
- emit_insn (gen_isel_signed_di (result, condition_rtx,
- force_reg (DImode, const1_rtx),
- force_reg (DImode, const0_rtx),
- XEXP (condition_rtx, 0)));
- }
- else
- {
- PUT_MODE (condition_rtx, SImode);
- if (cond_code == GEU || cond_code == GTU || cond_code == LEU
- || cond_code == LTU)
- emit_insn (gen_isel_unsigned_si (result, condition_rtx,
- force_reg (SImode, const1_rtx),
- force_reg (SImode, const0_rtx),
- XEXP (condition_rtx, 0)));
- else
- emit_insn (gen_isel_signed_si (result, condition_rtx,
- force_reg (SImode, const1_rtx),
- force_reg (SImode, const0_rtx),
- XEXP (condition_rtx, 0)));
- }
+ rs6000_emit_int_cmove (operands[0], operands[1], const1_rtx, const0_rtx);
}
void
{
rtx condition_rtx, cr;
enum machine_mode mode = GET_MODE (dest);
+ enum rtx_code cond_code;
+ rtx (*isel_func) (rtx, rtx, rtx, rtx, rtx);
+ bool signedp;
if (mode != SImode && (!TARGET_POWERPC64 || mode != DImode))
return 0;
compare, it just looks at the CRx bits set by a previous compare
instruction. */
condition_rtx = rs6000_generate_compare (op, mode);
+ cond_code = GET_CODE (condition_rtx);
cr = XEXP (condition_rtx, 0);
+ signedp = GET_MODE (cr) == CCmode;
- if (mode == SImode)
- {
- if (GET_MODE (cr) == CCmode)
- emit_insn (gen_isel_signed_si (dest, condition_rtx,
- true_cond, false_cond, cr));
- else
- emit_insn (gen_isel_unsigned_si (dest, condition_rtx,
- true_cond, false_cond, cr));
- }
- else
+ isel_func = (mode == SImode
+ ? (signedp ? gen_isel_signed_si : gen_isel_unsigned_si)
+ : (signedp ? gen_isel_signed_di : gen_isel_unsigned_di));
+
+ switch (cond_code)
{
- if (GET_MODE (cr) == CCmode)
- emit_insn (gen_isel_signed_di (dest, condition_rtx,
- true_cond, false_cond, cr));
- else
- emit_insn (gen_isel_unsigned_di (dest, condition_rtx,
- true_cond, false_cond, cr));
+ case LT: case GT: case LTU: case GTU: case EQ:
+ /* isel handles these directly. */
+ break;
+
+ default:
+ /* We need to swap the sense of the comparison. */
+ {
+ rtx t = true_cond;
+ true_cond = false_cond;
+ false_cond = t;
+ PUT_CODE (condition_rtx, reverse_condition (cond_code));
+ }
+ break;
}
+ false_cond = force_reg (mode, false_cond);
+ if (true_cond != const0_rtx)
+ true_cond = force_reg (mode, true_cond);
+
+ emit_insn (isel_func (dest, condition_rtx, true_cond, false_cond, cr));
+
return 1;
}
enum rtx_code code;
code = GET_CODE (operands[1]);
- if (code == GE || code == GEU || code == LE || code == LEU || code == NE)
- {
- PUT_CODE (operands[1], reverse_condition (code));
- return "isel %0,%3,%2,%j1";
- }
- else
- return "isel %0,%2,%3,%j1";
+
+ gcc_assert (!(code == GE || code == GEU || code == LE || code == LEU || code == NE));
+
+ return "isel %0,%2,%3,%j1";
}
void
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. */
GEN_INT (-size))));
}
+#define PROBE_INTERVAL (1 << STACK_CHECK_PROBE_INTERVAL_EXP)
+
+#if PROBE_INTERVAL > 32768
+#error Cannot use indexed addressing mode for stack probing
+#endif
+
+/* Emit code to probe a range of stack addresses from FIRST to FIRST+SIZE,
+ inclusive. These are offsets from the current stack pointer. */
+
+static void
+rs6000_emit_probe_stack_range (HOST_WIDE_INT first, HOST_WIDE_INT size)
+{
+ /* See if we have a constant small number of probes to generate. If so,
+ that's the easy case. */
+ if (first + size <= 32768)
+ {
+ HOST_WIDE_INT i;
+
+ /* Probe at FIRST + N * PROBE_INTERVAL for values of N from 1 until
+ it exceeds SIZE. If only one probe is needed, this will not
+ generate any code. Then probe at FIRST + SIZE. */
+ for (i = PROBE_INTERVAL; i < size; i += PROBE_INTERVAL)
+ emit_stack_probe (plus_constant (stack_pointer_rtx, -(first + i)));
+
+ emit_stack_probe (plus_constant (stack_pointer_rtx, -(first + size)));
+ }
+
+ /* Otherwise, do the same as above, but in a loop. Note that we must be
+ extra careful with variables wrapping around because we might be at
+ the very top (or the very bottom) of the address space and we have
+ to be able to handle this case properly; in particular, we use an
+ equality test for the loop condition. */
+ else
+ {
+ HOST_WIDE_INT rounded_size;
+ rtx r12 = gen_rtx_REG (Pmode, 12);
+ rtx r0 = gen_rtx_REG (Pmode, 0);
+
+ /* Sanity check for the addressing mode we're going to use. */
+ gcc_assert (first <= 32768);
+
+ /* Step 1: round SIZE to the previous multiple of the interval. */
+
+ rounded_size = size & -PROBE_INTERVAL;
+
+
+ /* Step 2: compute initial and final value of the loop counter. */
+
+ /* TEST_ADDR = SP + FIRST. */
+ emit_insn (gen_rtx_SET (VOIDmode, r12,
+ plus_constant (stack_pointer_rtx, -first)));
+
+ /* LAST_ADDR = SP + FIRST + ROUNDED_SIZE. */
+ if (rounded_size > 32768)
+ {
+ emit_move_insn (r0, GEN_INT (-rounded_size));
+ emit_insn (gen_rtx_SET (VOIDmode, r0,
+ gen_rtx_PLUS (Pmode, r12, r0)));
+ }
+ else
+ emit_insn (gen_rtx_SET (VOIDmode, r0,
+ plus_constant (r12, -rounded_size)));
+
+
+ /* Step 3: the loop
+
+ while (TEST_ADDR != LAST_ADDR)
+ {
+ TEST_ADDR = TEST_ADDR + PROBE_INTERVAL
+ probe at TEST_ADDR
+ }
+
+ probes at FIRST + N * PROBE_INTERVAL for values of N from 1
+ until it is equal to ROUNDED_SIZE. */
+
+ if (TARGET_64BIT)
+ emit_insn (gen_probe_stack_rangedi (r12, r12, r0));
+ else
+ emit_insn (gen_probe_stack_rangesi (r12, r12, r0));
+
+
+ /* Step 4: probe at FIRST + SIZE if we cannot assert at compile-time
+ that SIZE is equal to ROUNDED_SIZE. */
+
+ if (size != rounded_size)
+ emit_stack_probe (plus_constant (r12, rounded_size - size));
+ }
+}
+
+/* Probe a range of stack addresses from REG1 to REG2 inclusive. These are
+ absolute addresses. */
+
+const char *
+output_probe_stack_range (rtx reg1, rtx reg2)
+{
+ static int labelno = 0;
+ char loop_lab[32], end_lab[32];
+ rtx xops[2];
+
+ ASM_GENERATE_INTERNAL_LABEL (loop_lab, "LPSRL", labelno);
+ ASM_GENERATE_INTERNAL_LABEL (end_lab, "LPSRE", labelno++);
+
+ ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, loop_lab);
+
+ /* Jump to END_LAB if TEST_ADDR == LAST_ADDR. */
+ xops[0] = reg1;
+ xops[1] = reg2;
+ if (TARGET_64BIT)
+ output_asm_insn ("{cmp|cmpd} 0,%0,%1", xops);
+ else
+ output_asm_insn ("{cmp|cmpw} 0,%0,%1", xops);
+
+ fputs ("\tbeq 0,", asm_out_file);
+ assemble_name_raw (asm_out_file, end_lab);
+ fputc ('\n', asm_out_file);
+
+ /* TEST_ADDR = TEST_ADDR + PROBE_INTERVAL. */
+ xops[1] = GEN_INT (-PROBE_INTERVAL);
+ output_asm_insn ("{cal %0,%1(%0)|addi %0,%0,%1}", xops);
+
+ /* Probe at TEST_ADDR and branch. */
+ output_asm_insn ("{st|stw} 0,0(%0)", xops);
+ fprintf (asm_out_file, "\tb ");
+ assemble_name_raw (asm_out_file, loop_lab);
+ fputc ('\n', asm_out_file);
+
+ ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, end_lab);
+
+ return "";
+}
+
/* Add to 'insn' a note which is PATTERN (INSN) but with REG replaced
with (plus:P (reg 1) VAL), and with REG2 replaced with RREG if REG2
is not NULL. It would be nice if dwarf2out_frame_debug_expr could
&& call_used_regs[STATIC_CHAIN_REGNUM]);
HOST_WIDE_INT sp_offset = 0;
+ if (flag_stack_usage)
+ current_function_static_stack_size = info->total_size;
+
+ if (flag_stack_check == STATIC_BUILTIN_STACK_CHECK && info->total_size)
+ rs6000_emit_probe_stack_range (STACK_CHECK_PROTECT, info->total_size);
+
if (TARGET_FIX_AND_CONTINUE)
{
/* gdb on darwin arranges to forward a function from the old
{
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)
{
int next_parm_info_bit = 31;
for (decl = DECL_ARGUMENTS (current_function_decl);
- decl; decl = TREE_CHAIN (decl))
+ decl; decl = DECL_CHAIN (decl))
{
rtx parameter = DECL_INCOMING_RTL (decl);
enum machine_mode mode = GET_MODE (parameter);
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)))
#if TARGET_MACHO
-static tree branch_island_list = 0;
+typedef struct branch_island_d {
+ tree function_name;
+ tree label_name;
+ int line_number;
+} branch_island;
+
+DEF_VEC_O(branch_island);
+DEF_VEC_ALLOC_O(branch_island,gc);
+
+static VEC(branch_island,gc) *branch_islands;
/* Remember to generate a branch island for far calls to the given
function. */
add_compiler_branch_island (tree label_name, tree function_name,
int line_number)
{
- tree branch_island = build_tree_list (function_name, label_name);
- TREE_TYPE (branch_island) = build_int_cst (NULL_TREE, line_number);
- TREE_CHAIN (branch_island) = branch_island_list;
- branch_island_list = branch_island;
-}
+ branch_island *bi = VEC_safe_push (branch_island, gc, branch_islands, NULL);
-#define BRANCH_ISLAND_LABEL_NAME(BRANCH_ISLAND) TREE_VALUE (BRANCH_ISLAND)
-#define BRANCH_ISLAND_FUNCTION_NAME(BRANCH_ISLAND) TREE_PURPOSE (BRANCH_ISLAND)
-#define BRANCH_ISLAND_LINE_NUMBER(BRANCH_ISLAND) \
- TREE_INT_CST_LOW (TREE_TYPE (BRANCH_ISLAND))
+ bi->function_name = function_name;
+ bi->label_name = label_name;
+ bi->line_number = line_number;
+}
-/* Generate far-jump branch islands for everything on the
- branch_island_list. Invoked immediately after the last instruction
- of the epilogue has been emitted; the branch-islands must be
- appended to, and contiguous with, the function body. Mach-O stubs
- are generated in machopic_output_stub(). */
+/* Generate far-jump branch islands for everything recorded in
+ branch_islands. Invoked immediately after the last instruction of
+ the epilogue has been emitted; the branch islands must be appended
+ to, and contiguous with, the function body. Mach-O stubs are
+ generated in machopic_output_stub(). */
static void
macho_branch_islands (void)
{
char tmp_buf[512];
- tree branch_island;
- for (branch_island = branch_island_list;
- branch_island;
- branch_island = TREE_CHAIN (branch_island))
+ while (!VEC_empty (branch_island, branch_islands))
{
- const char *label =
- IDENTIFIER_POINTER (BRANCH_ISLAND_LABEL_NAME (branch_island));
- const char *name =
- IDENTIFIER_POINTER (BRANCH_ISLAND_FUNCTION_NAME (branch_island));
+ branch_island *bi = VEC_last (branch_island, branch_islands);
+ const char *label = IDENTIFIER_POINTER (bi->label_name);
+ const char *name = IDENTIFIER_POINTER (bi->function_name);
char name_buf[512];
/* Cheap copy of the details from the Darwin ASM_OUTPUT_LABELREF(). */
if (name[0] == '*' || name[0] == '&')
strcat (tmp_buf, label);
#if defined (DBX_DEBUGGING_INFO) || defined (XCOFF_DEBUGGING_INFO)
if (write_symbols == DBX_DEBUG || write_symbols == XCOFF_DEBUG)
- dbxout_stabd (N_SLINE, BRANCH_ISLAND_LINE_NUMBER (branch_island));
+ dbxout_stabd (N_SLINE, bi->line_number);
#endif /* DBX_DEBUGGING_INFO || XCOFF_DEBUGGING_INFO */
if (flag_pic)
{
output_asm_insn (tmp_buf, 0);
#if defined (DBX_DEBUGGING_INFO) || defined (XCOFF_DEBUGGING_INFO)
if (write_symbols == DBX_DEBUG || write_symbols == XCOFF_DEBUG)
- dbxout_stabd (N_SLINE, BRANCH_ISLAND_LINE_NUMBER (branch_island));
+ dbxout_stabd (N_SLINE, bi->line_number);
#endif /* DBX_DEBUGGING_INFO || XCOFF_DEBUGGING_INFO */
+ VEC_pop (branch_island, branch_islands);
}
-
- branch_island_list = 0;
}
/* NO_PREVIOUS_DEF checks in the link list whether the function name is
static int
no_previous_def (tree function_name)
{
- tree branch_island;
- for (branch_island = branch_island_list;
- branch_island;
- branch_island = TREE_CHAIN (branch_island))
- if (function_name == BRANCH_ISLAND_FUNCTION_NAME (branch_island))
+ branch_island *bi;
+ unsigned ix;
+
+ FOR_EACH_VEC_ELT (branch_island, branch_islands, ix, bi)
+ if (function_name == bi->function_name)
return 0;
return 1;
}
static tree
get_prev_label (tree function_name)
{
- tree branch_island;
- for (branch_island = branch_island_list;
- branch_island;
- branch_island = TREE_CHAIN (branch_island))
- if (function_name == BRANCH_ISLAND_FUNCTION_NAME (branch_island))
- return BRANCH_ISLAND_LABEL_NAME (branch_island);
- return 0;
-}
+ branch_island *bi;
+ unsigned ix;
-#ifndef DARWIN_LINKER_GENERATES_ISLANDS
-#define DARWIN_LINKER_GENERATES_ISLANDS 0
-#endif
-
-/* KEXTs still need branch islands. */
-#define DARWIN_GENERATE_ISLANDS (!DARWIN_LINKER_GENERATES_ISLANDS \
- || flag_mkernel || flag_apple_kext)
+ FOR_EACH_VEC_ELT (branch_island, branch_islands, ix, bi)
+ if (function_name == bi->function_name)
+ return bi->label_name;
+ return NULL_TREE;
+}
/* INSN is either a function call or a millicode call. It may have an
unconditional jump in its delay slot.
int cookie_operand_number)
{
static char buf[256];
- if (DARWIN_GENERATE_ISLANDS
+ if (darwin_emit_branch_islands
&& GET_CODE (operands[dest_operand_number]) == SYMBOL_REF
&& (INTVAL (operands[cookie_operand_number]) & CALL_LONG))
{
return false;
case POPCOUNT:
- *total = COSTS_N_INSNS (6);
+ *total = COSTS_N_INSNS (TARGET_POPCNTD ? 1 : 6);
+ return false;
+
+ case PARITY:
+ *total = COSTS_N_INSNS (TARGET_CMPB ? 2 : 6);
return false;
case NOT:
/* A C expression returning the cost of moving data from a register of class
CLASS1 to one of CLASS2. */
-int
+static int
rs6000_register_move_cost (enum machine_mode mode,
- enum reg_class from, enum reg_class to)
+ reg_class_t from, reg_class_t to)
{
int ret;
from = to;
if (from == FLOAT_REGS || from == ALTIVEC_REGS || from == VSX_REGS)
- ret = (rs6000_memory_move_cost (mode, from, 0)
- + rs6000_memory_move_cost (mode, GENERAL_REGS, 0));
+ ret = (rs6000_memory_move_cost (mode, from, false)
+ + rs6000_memory_move_cost (mode, GENERAL_REGS, false));
/* It's more expensive to move CR_REGS than CR0_REGS because of the
shift. */
/* A C expressions returning the cost of moving data of MODE from a register to
or from memory. */
-int
-rs6000_memory_move_cost (enum machine_mode mode, enum reg_class rclass,
- int in ATTRIBUTE_UNUSED)
+static int
+rs6000_memory_move_cost (enum machine_mode mode, reg_class_t rclass,
+ bool in ATTRIBUTE_UNUSED)
{
int ret;
if (TARGET_POPCNTD)
{
if (mode == SImode)
- emit_insn (gen_popcntwsi2 (dst, src));
+ emit_insn (gen_popcntdsi2 (dst, src));
else
emit_insn (gen_popcntddi2 (dst, src));
return;
rtx tmp;
tmp = gen_reg_rtx (mode);
+
+ /* Use the PPC ISA 2.05 prtyw/prtyd instruction if we can. */
+ if (TARGET_CMPB)
+ {
+ if (mode == SImode)
+ {
+ emit_insn (gen_popcntbsi2 (tmp, src));
+ emit_insn (gen_paritysi2_cmpb (dst, tmp));
+ }
+ else
+ {
+ emit_insn (gen_popcntbdi2 (tmp, src));
+ emit_insn (gen_paritydi2_cmpb (dst, tmp));
+ }
+ return;
+ }
+
if (mode == SImode)
{
/* Is mult+shift >= shift+xor+shift+xor? */
unsigned int regno;
/* Special handling for structs in darwin64. */
- if (rs6000_darwin64_abi
- && TYPE_MODE (valtype) == BLKmode
- && TREE_CODE (valtype) == RECORD_TYPE
- && int_size_in_bytes (valtype) > 0)
+ if (TARGET_MACHO
+ && rs6000_darwin64_struct_check_p (TYPE_MODE (valtype), valtype))
{
CUMULATIVE_ARGS valcum;
rtx valret;
valcum.vregno = ALTIVEC_ARG_MIN_REG;
/* Do a trial code generation as if this were going to be passed as
an argument; if any part goes in memory, we return NULL. */
- valret = rs6000_darwin64_record_arg (&valcum, valtype, 1, true);
+ valret = rs6000_darwin64_record_arg (&valcum, valtype, true, true);
if (valret)
return valret;
/* Otherwise fall through to standard ABI rules. */
}
}
+\f
+/* Allocate a stack temp and fixup the address so it meets the particular
+ memory requirements (either offetable or REG+REG addressing). */
+
+rtx
+rs6000_allocate_stack_temp (enum machine_mode mode,
+ bool offsettable_p,
+ bool reg_reg_p)
+{
+ rtx stack = assign_stack_temp (mode, GET_MODE_SIZE (mode), 0);
+ rtx addr = XEXP (stack, 0);
+ int strict_p = (reload_in_progress || reload_completed);
+
+ if (!legitimate_indirect_address_p (addr, strict_p))
+ {
+ if (offsettable_p
+ && !rs6000_legitimate_offset_address_p (mode, addr, strict_p))
+ stack = replace_equiv_address (stack, copy_addr_to_reg (addr));
+
+ else if (reg_reg_p && !legitimate_indexed_address_p (addr, strict_p))
+ stack = replace_equiv_address (stack, copy_addr_to_reg (addr));
+ }
+
+ return stack;
+}
+
+/* Given a memory reference, if it is not a reg or reg+reg addressing, convert
+ to such a form to deal with memory reference instructions like STFIWX that
+ only take reg+reg addressing. */
+
+rtx
+rs6000_address_for_fpconvert (rtx x)
+{
+ int strict_p = (reload_in_progress || reload_completed);
+ rtx addr;
+
+ gcc_assert (MEM_P (x));
+ addr = XEXP (x, 0);
+ if (! legitimate_indirect_address_p (addr, strict_p)
+ && ! legitimate_indexed_address_p (addr, strict_p))
+ x = replace_equiv_address (x, copy_addr_to_reg (addr));
+
+ return x;
+}
+
+/* Expand 32-bit int -> floating point conversions. Return true if
+ successful. */
+
+void
+rs6000_expand_convert_si_to_sfdf (rtx dest, rtx src, bool unsigned_p)
+{
+ enum machine_mode dmode = GET_MODE (dest);
+ rtx (*func_si) (rtx, rtx, rtx, rtx);
+ rtx (*func_si_mem) (rtx, rtx);
+ rtx (*func_di) (rtx, rtx);
+ rtx reg, stack;
+
+ gcc_assert (GET_MODE (src) == SImode);
+
+ if (dmode == SFmode)
+ {
+ if (unsigned_p)
+ {
+ gcc_assert (TARGET_FCFIDUS && TARGET_LFIWZX);
+ func_si = gen_floatunssisf2_lfiwzx;
+ func_si_mem = gen_floatunssisf2_lfiwzx_mem;
+ func_di = gen_floatunsdisf2;
+ }
+ else
+ {
+ gcc_assert (TARGET_FCFIDS && TARGET_LFIWAX);
+ func_si = gen_floatsisf2_lfiwax;
+ func_si_mem = gen_floatsisf2_lfiwax_mem;
+ func_di = gen_floatdisf2;
+ }
+ }
+
+ else if (dmode == DFmode)
+ {
+ if (unsigned_p)
+ {
+ gcc_assert (TARGET_FCFIDU && TARGET_LFIWZX);
+ func_si = gen_floatunssidf2_lfiwzx;
+ func_si_mem = gen_floatunssidf2_lfiwzx_mem;
+ func_di = gen_floatunsdidf2;
+ }
+ else
+ {
+ gcc_assert (TARGET_FCFID && TARGET_LFIWAX);
+ func_si = gen_floatsidf2_lfiwax;
+ func_si_mem = gen_floatsidf2_lfiwax_mem;
+ func_di = gen_floatdidf2;
+ }
+ }
+
+ else
+ gcc_unreachable ();
+
+ if (MEM_P (src))
+ {
+ src = rs6000_address_for_fpconvert (src);
+ emit_insn (func_si_mem (dest, src));
+ }
+ else if (!TARGET_MFPGPR)
+ {
+ reg = gen_reg_rtx (DImode);
+ stack = rs6000_allocate_stack_temp (SImode, false, true);
+ emit_insn (func_si (dest, src, stack, reg));
+ }
+ else
+ {
+ if (!REG_P (src))
+ src = force_reg (SImode, src);
+ reg = convert_to_mode (DImode, src, unsigned_p);
+ emit_insn (func_di (dest, reg));
+ }
+}
+
#include "gt-rs6000.h"