static rtx rtx_for_function_call (tree, tree);
static void load_register_parameters (struct arg_data *, int, rtx *, int,
int, bool *);
+static rtx allocate_call_dynamic_stack_space (rtx, unsigned int, HOST_WIDE_INT,
+ rtx *, poly_int64 *);
static int special_function_p (const_tree, int);
static bool check_sibcall_argument_overlap_1 (rtx);
static bool check_sibcall_argument_overlap (rtx_insn *, struct arg_data *,
stack_usage_watermark = MIN (stack_usage_watermark, const_lower);
}
+/* Allocate temporary call-related stack space with ALIGN alignment.
+ Save the stack pointer on first use so the caller can restore it after
+ the call sequence completes. */
+
+static rtx
+allocate_call_dynamic_stack_space (rtx size, unsigned int align,
+ HOST_WIDE_INT known_size,
+ rtx *old_stack_level,
+ poly_int64 *old_pending_adj)
+{
+ if (*old_stack_level == 0)
+ {
+ emit_stack_save (SAVE_BLOCK, old_stack_level);
+ *old_pending_adj = pending_stack_adjust;
+ pending_stack_adjust = 0;
+ }
+
+ /* We can pass TRUE as the 5th argument because we just saved the stack
+ pointer and will restore it right after the call. */
+ return allocate_dynamic_stack_space (size, align, align, known_size, true);
+}
+
/* Force FUNEXP into a form suitable for the address of a CALL,
and return that as an rtx. Also load the static chain register
if FNDECL is a nested function.
if (!COMPLETE_TYPE_P (type)
|| TREE_CODE (TYPE_SIZE_UNIT (type)) != INTEGER_CST
+ || (targetm.calls.overaligned_stack_slot_required ()
+ && TYPE_ALIGN (type) > MAX_SUPPORTED_STACK_ALIGNMENT)
|| (flag_stack_check == GENERIC_STACK_CHECK
&& compare_tree_int (TYPE_SIZE_UNIT (type),
STACK_CHECK_MAX_VAR_SIZE) > 0))
{
- /* This is a variable-sized object. Make space on the stack
- for it. */
+ /* Variable-sized or over-aligned by-reference arguments cannot
+ use a regular stack temp when the target can't guarantee the
+ requested alignment for stack slots. Allocate temporary
+ space dynamically and restore the stack right after the call. */
rtx size_rtx = expr_size (args[i].tree_value);
- if (*old_stack_level == 0)
- {
- emit_stack_save (SAVE_BLOCK, old_stack_level);
- *old_pending_adj = pending_stack_adjust;
- pending_stack_adjust = 0;
- }
-
- /* We can pass TRUE as the 4th argument because we just
- saved the stack pointer and will restore it right after
- the call. */
- copy = allocate_dynamic_stack_space (size_rtx,
- TYPE_ALIGN (type),
- TYPE_ALIGN (type),
- max_int_size_in_bytes
- (type),
- true);
+ copy = allocate_call_dynamic_stack_space (size_rtx,
+ TYPE_ALIGN (type),
+ max_int_size_in_bytes (type),
+ old_stack_level,
+ old_pending_adj);
copy = gen_rtx_MEM (BLKmode, copy);
set_mem_attributes (copy, type, 1);
}
/* For variable-sized objects, we must be called with a target
specified. If we were to allocate space on the stack here,
we would have no way of knowing when to free it. */
- rtx d = assign_temp (rettype, 1, 1);
- structure_value_addr = XEXP (d, 0);
+ if (targetm.calls.overaligned_stack_slot_required ()
+ && TYPE_ALIGN (rettype) > MAX_SUPPORTED_STACK_ALIGNMENT)
+ {
+ unsigned HOST_WIDE_INT size;
+
+ gcc_checking_assert (TREE_CODE (TYPE_SIZE_UNIT (rettype))
+ == INTEGER_CST);
+ size = tree_to_uhwi (TYPE_SIZE_UNIT (rettype));
+ structure_value_addr = allocate_call_dynamic_stack_space (
+ gen_int_mode (size, Pmode), TYPE_ALIGN (rettype), size,
+ &old_stack_level, &old_pending_adj);
+ }
+ else
+ {
+ rtx d = assign_temp (rettype, 1, 1);
+ structure_value_addr = XEXP (d, 0);
+ }
target = 0;
}
}
static bool ix86_function_value_regno_p (const unsigned int);
static unsigned int ix86_function_arg_boundary (machine_mode,
const_tree);
+static bool ix86_overaligned_stack_slot_required (void);
static rtx ix86_static_chain (const_tree, bool);
static int ix86_function_regparm (const_tree, const_tree);
static void ix86_compute_frame_layout (void);
&& arg.type && TREE_CODE (arg.type) != VECTOR_TYPE);
}
+/* Implement TARGET_OVERALIGNED_STACK_SLOT_REQUIRED. */
+
+static bool
+ix86_overaligned_stack_slot_required (void)
+{
+ return TARGET_SEH;
+}
+
/* It returns the size, in bytes, of the area reserved for arguments passed
in registers for the function represented by fndecl dependent to the used
abi format. */
#define TARGET_SETUP_INCOMING_VARARGS ix86_setup_incoming_varargs
#undef TARGET_MUST_PASS_IN_STACK
#define TARGET_MUST_PASS_IN_STACK ix86_must_pass_in_stack
+#undef TARGET_OVERALIGNED_STACK_SLOT_REQUIRED
+#define TARGET_OVERALIGNED_STACK_SLOT_REQUIRED ix86_overaligned_stack_slot_required
#undef TARGET_ALLOCATE_STACK_SLOTS_FOR_ARGS
#define TARGET_ALLOCATE_STACK_SLOTS_FOR_ARGS ix86_allocate_stack_slots_for_args
#undef TARGET_FUNCTION_ARG_ADVANCE
documentation.
@end deftypefn
+@deftypefn {Target Hook} bool TARGET_OVERALIGNED_STACK_SLOT_REQUIRED (void)
+This hook should return @code{true} when call-related stack slots whose
+requested alignment exceeds @code{MAX_SUPPORTED_STACK_ALIGNMENT} need special
+handling on the target. This covers stack slots created by call expansion
+(such as by-reference argument copies and hidden structure return storage) and
+incoming argument setup. When @code{true}, GCC may avoid normal fixed stack
+slots for such cases and use over-allocation plus dynamic address alignment
+instead.
+@end deftypefn
+
@deftypefn {Target Hook} rtx TARGET_FUNCTION_INCOMING_ARG (cumulative_args_t @var{ca}, const function_arg_info @var{&arg})
Define this hook if the caller and callee on the target have different
views of where arguments are passed. Also define this hook if there are
@hook TARGET_MUST_PASS_IN_STACK
+@hook TARGET_OVERALIGNED_STACK_SLOT_REQUIRED
+
@hook TARGET_FUNCTION_INCOMING_ARG
@hook TARGET_USE_PSEUDO_PIC_REG
static void prepare_function_start (void);
static void do_clobber_return_reg (rtx, void *);
static void do_use_return_reg (rtx, void *);
+static rtx assign_stack_local_aligned (machine_mode, poly_int64,
+ unsigned int);
\f
/* Stack of nested functions. */
return assign_stack_local_1 (mode, size, align, ASLK_RECORD_PAD);
}
\f
+/* Like assign_stack_local, but preserve requested over-alignment by
+ overallocating a BLKmode slot and aligning an address within it. */
+
+static rtx
+assign_stack_local_aligned (machine_mode mode, poly_int64 size,
+ unsigned int align)
+{
+ if (targetm.calls.overaligned_stack_slot_required ()
+ && align > MAX_SUPPORTED_STACK_ALIGNMENT)
+ {
+ if (!size.is_constant ())
+ return assign_stack_local (mode, size, MAX_SUPPORTED_STACK_ALIGNMENT);
+
+ rtx allocsize = gen_int_mode (size, Pmode);
+ get_dynamic_stack_size (&allocsize, 0, align, NULL);
+
+ if (!CONST_INT_P (allocsize))
+ return assign_stack_local (mode, size, MAX_SUPPORTED_STACK_ALIGNMENT);
+
+ rtx slot = assign_stack_local (BLKmode, UINTVAL (allocsize),
+ MAX_SUPPORTED_STACK_ALIGNMENT);
+ rtx addr = align_dynamic_address (XEXP (slot, 0), align);
+ mark_reg_pointer (addr, align);
+ slot = gen_rtx_MEM (mode, addr);
+ MEM_NOTRAP_P (slot) = 1;
+ return slot;
+ }
+ return assign_stack_local (mode, size, align);
+}
+
/* In order to evaluate some expressions, such as function calls returning
structures in memory, we need to temporarily allocate stack locations.
We record each allocated temporary in the following structure.
? MAX (DECL_ALIGN (parm), BITS_PER_WORD) : DECL_ALIGN (parm));
SET_DECL_ALIGN (parm, parm_align);
- if (DECL_ALIGN (parm) > MAX_SUPPORTED_STACK_ALIGNMENT)
- {
- rtx allocsize = gen_int_mode (size_stored, Pmode);
- get_dynamic_stack_size (&allocsize, 0, DECL_ALIGN (parm), NULL);
- stack_parm = assign_stack_local (BLKmode, UINTVAL (allocsize),
- MAX_SUPPORTED_STACK_ALIGNMENT);
- rtx addr = align_dynamic_address (XEXP (stack_parm, 0),
- DECL_ALIGN (parm));
- mark_reg_pointer (addr, DECL_ALIGN (parm));
- stack_parm = gen_rtx_MEM (GET_MODE (stack_parm), addr);
- MEM_NOTRAP_P (stack_parm) = 1;
- }
- else
- stack_parm = assign_stack_local (BLKmode, size_stored,
- DECL_ALIGN (parm));
+ stack_parm
+ = assign_stack_local_aligned (BLKmode, size_stored, DECL_ALIGN (parm));
if (known_eq (GET_MODE_SIZE (GET_MODE (entry_parm)), size))
PUT_MODE (stack_parm, GET_MODE (entry_parm));
set_mem_attributes (stack_parm, parm, 1);
int align = STACK_SLOT_ALIGNMENT (TREE_TYPE (parm),
TYPE_MODE (TREE_TYPE (parm)),
TYPE_ALIGN (TREE_TYPE (parm)));
- parmreg
- = assign_stack_local (TYPE_MODE (TREE_TYPE (parm)),
- GET_MODE_SIZE (TYPE_MODE (TREE_TYPE (parm))),
- align);
+ parmreg = assign_stack_local_aligned (TYPE_MODE (TREE_TYPE (parm)),
+ GET_MODE_SIZE (
+ TYPE_MODE (TREE_TYPE (parm))),
+ align);
set_mem_attributes (parmreg, parm, 1);
}
bool, (const function_arg_info &arg),
must_pass_in_stack_var_size_or_pad)
+DEFHOOK
+(overaligned_stack_slot_required,
+ "This hook should return @code{true} when call-related stack slots whose\n\
+requested alignment exceeds @code{MAX_SUPPORTED_STACK_ALIGNMENT} need special\n\
+handling on the target. This covers stack slots created by call expansion\n\
+(such as by-reference argument copies and hidden structure return storage) and\n\
+incoming argument setup. When @code{true}, GCC may avoid normal fixed stack\n\
+slots for such cases and use over-allocation plus dynamic address alignment\n\
+instead.",
+ bool, (void),
+ hook_bool_void_false)
+
/* Return true if type TYPE, mode MODE, which is passed by reference,
should have the object copy generated by the callee rather than
the caller. It is never called for TYPE requiring constructors. */