Targets that cannot realign the stack cap their stack alignment and rely
on the middle end to align over-aligned stack objects inside their slots.
Win64 SEH caps MAX_STACK_ALIGNMENT at 128 bits; aarch64 does not define
MAX_STACK_ALIGNMENT at all and is capped at STACK_BOUNDARY, also 128.
In several places the middle end does not do that alignment. A 256-bit
vector requires 32-byte alignment, its slot gets the capped 16 bytes, and
MEM_ALIGN is set to 32 anyway. x86 selects vmovaps from MEM_ALIGN and
faults on the under-aligned slot (PR54412, MSYS2/MINGW-packages#1209).
There is an even bigger problem. Even with MEM_ALIGN and the slot's
alignment consistent, an escaped pointer loses the information about the
alignment cap: GCC assumes the object's natural alignment and uses an
aligned access on an under-aligned object, which faults on x86.
aarch64 does not crash only because its loads and stores do not fault on
a misaligned address; the object is under-aligned there just the same.
Add assign_stack_local_aligned, which preserves the requested alignment by
overallocating a slot and aligning an address inside it, and use it
wherever an object gets a stack slot of its own. This is what
assign_parm_setup_block already did for BLKmode parameters and what
expand_stack_vars does for locals. Such a slot has no fixed frame offset,
so it cannot be reused as a temp slot.
Regtested on x86_64-pc-linux-gnu and, under QEMU, on
aarch64-unknown-linux-gnu, with no new failures.
gcc/ChangeLog:
PR target/54412
* function.cc (assign_stack_local_aligned): New function, split
out of assign_parm_setup_block.
(assign_stack_temp_for_type): Compute the required alignment from
the type and the mode. Allocate an over-aligned temporary with
assign_stack_local_aligned and give it no temp_slot entry. Take
the resulting alignment from the allocated slot.
(assign_parm_setup_block): Use assign_stack_local_aligned.
(assign_parm_setup_reg): Likewise.
(assign_parm_setup_stack): Likewise.
Signed-off-by: Oleg Tolmatcev <[email protected]>
---
gcc/function.cc | 87 +++++++++++++++++++++++++++++++++++--------------
1 file changed, 63 insertions(+), 24 deletions(-)
diff --git a/gcc/function.cc b/gcc/function.cc
index 9358787b26..0bc8e6da89 100644
--- a/gcc/function.cc
+++ b/gcc/function.cc
@@ -551,6 +551,36 @@ assign_stack_local (machine_mode mode, poly_int64 size,
int align)
return assign_stack_local_1 (mode, size, align, ASLK_RECORD_PAD);
}
+/* 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 (align > MAX_SUPPORTED_STACK_ALIGNMENT)
+ {
+ /* align_dynamic_address emits the alignment arithmetic into a pseudo.
*/
+ gcc_assert (can_create_pseudo_p ());
+
+ /* The slot's base is aligned to MAX_SUPPORTED_STACK_ALIGNMENT, so moving
+ it to the next ALIGN boundary needs at most the difference between the
+ two alignments. Keep the size polynomial for scalable modes. */
+ poly_int64 allocsize
+ = size + ((align - MAX_SUPPORTED_STACK_ALIGNMENT) / BITS_PER_UNIT);
+ rtx slot = assign_stack_local (BLKmode, 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);
+ set_mem_align (slot, align);
+ 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.
@@ -801,6 +831,22 @@ assign_stack_temp_for_type (machine_mode mode, poly_int64
size, tree type)
gcc_assert (known_size_p (size));
+ unsigned int required_align
+ = (mode == BLKmode ? BITS_PER_UNIT : GET_MODE_ALIGNMENT (mode));
+ if (type)
+ required_align = MAX (required_align, TYPE_ALIGN (type));
+
+ /* Alignment beyond what the frame can guarantee needs an address aligned
+ at run time inside an overallocated slot. BASE_OFFSET and FULL_SIZE
+ cannot describe such a slot, so it gets no temp_slot entry: it is never
+ reused or freed. */
+ if (required_align > MAX_SUPPORTED_STACK_ALIGNMENT)
+ {
+ slot = assign_stack_local_aligned (mode, size, required_align);
+ align = MEM_ALIGN (slot);
+ goto finalize_slot;
+ }
+
align = get_stack_local_alignment (type, mode);
/* Try to find an available, already-allocated temporary of the proper
@@ -892,7 +938,7 @@ assign_stack_temp_for_type (machine_mode mode, poly_int64
size, tree type)
: size),
align, 0);
- p->align = align;
+ p->align = MEM_ALIGN (p->slot);
/* The following slot size computation is necessary because we don't
know the actual size of the temporary slot until assign_stack_local
@@ -931,8 +977,11 @@ assign_stack_temp_for_type (machine_mode mode, poly_int64
size, tree type)
insert_slot_to_list (p, pp);
insert_temp_slot_address (XEXP (p->slot, 0), p);
+ align = MEM_ALIGN (p->slot);
+
/* Create a new MEM rtx to avoid clobbering MEM flags of old slots. */
slot = gen_rtx_MEM (mode, XEXP (p->slot, 0));
+finalize_slot:
vec_safe_push (stack_slot_list, slot);
/* If we know the alias set for the memory that will be used, use
@@ -2948,24 +2997,13 @@ assign_parm_setup_block (struct assign_parm_data_all
*all,
? 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));
+ int align = MEM_ALIGN (stack_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);
+ set_mem_align (stack_parm, align);
}
/* If a BLKmode arrives in registers, copy it to a stack slot. Handle
@@ -3366,11 +3404,13 @@ assign_parm_setup_reg (struct assign_parm_data_all
*all, tree parm,
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);
+ align = MEM_ALIGN (parmreg);
set_mem_attributes (parmreg, parm, 1);
+ set_mem_align (parmreg, align);
}
/* We need to preserve an address based on VIRTUAL_STACK_VARS_REGNUM for
@@ -3523,10 +3563,9 @@ assign_parm_setup_stack (struct assign_parm_data_all
*all, tree parm,
|| targetm.slow_unaligned_access (GET_MODE (data->entry_parm),
align)))
align = GET_MODE_ALIGNMENT (GET_MODE (data->entry_parm));
- data->stack_parm
- = assign_stack_local (GET_MODE (data->entry_parm),
- GET_MODE_SIZE (GET_MODE (data->entry_parm)),
- align);
+ data->stack_parm = assign_stack_local_aligned (
+ GET_MODE (data->entry_parm),
+ GET_MODE_SIZE (GET_MODE (data->entry_parm)), align);
align = MEM_ALIGN (data->stack_parm);
set_mem_attributes (data->stack_parm, parm, 1);
set_mem_align (data->stack_parm, align);
--
2.55.0.windows.5