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

Reply via email to