https://gcc.gnu.org/g:e3f8eeac3fc7d2c9dc298121126b0a1c17c6b99a

commit r17-5231-ge3f8eeac3fc7d2c9dc298121126b0a1c17c6b99a
Author: Illia Polishchuk <[email protected]>
Date:   Thu Oct 15 23:43:24 2026 -0400

    c++: Don't assume full alignment for possible base subobjects [PR103303]
    
    When a class has a virtual base that needs more alignment than the rest
    of the class, its alignment as a base (the Itanium ABI "nvalign",
    CLASSTYPE_ALIGN) is smaller than TYPE_ALIGN.  A base subobject of such a
    class, e.g. a virtual base of a derived class, may therefore be placed at
    an address that is only aligned to nvalign.  But accesses through 'this'
    or any other pointer to the class were given the full TYPE_ALIGN, so e.g.
    the SLP vectorizer used movaps to store adjacent members in the base
    constructor, which faults at runtime.
    
    Fix this by giving an INDIRECT_REF of such a class type a variant of the
    type with the base alignment, unless resolves_to_fixed_type_p indicates
    it cannot be a base subobject.
    
    Also update fixup_type_variants to only propagate alignment when called
    from finish_struct_bits or trees_in::read_class_def, so that lazily
    declared member functions do not overwrite variant alignments.
    
            PR c++/103303
    
    gcc/cp/ChangeLog:
    
            * cp-tree.h (fixup_type_variants): Add update_align parameter.
            * class.cc (fixup_type_variants): Only update alignment if
            update_align is true.
            (finish_struct_bits): Pass update_align = true.
            * module.cc (trees_in::read_class_def): Likewise.
            * cp-gimplify.cc (cp_genericize_r) <case INDIRECT_REF>: Use the
            alignment of the class as a base for references to a class that
            might be a base subobject.
    
    gcc/testsuite/ChangeLog:
    
            * g++.dg/torture/pr103303.C: New test.
            * g++.target/i386/pr103303.C: New test.
    
    Signed-off-by: Illia Polishchuk <[email protected]>

Diff:
---
 gcc/cp/cp-tree.h                         |  2 +-
 gcc/cp/class.cc                          | 14 +++++++------
 gcc/cp/cp-gimplify.cc                    | 19 +++++++++++++++++
 gcc/cp/module.cc                         |  2 +-
 gcc/testsuite/g++.dg/torture/pr103303.C  | 36 ++++++++++++++++++++++++++++++++
 gcc/testsuite/g++.target/i386/pr103303.C | 31 +++++++++++++++++++++++++++
 6 files changed, 96 insertions(+), 8 deletions(-)

diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 7b461e4f5281..4201eb2d2aba 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -7543,7 +7543,7 @@ extern void defaulted_late_check          (tree, tristate 
= tristate::unknown ());
 extern bool defaultable_fn_check               (tree);
 extern void check_abi_tags                     (tree);
 extern tree missing_abi_tags                   (tree);
-extern void fixup_type_variants                        (tree);
+extern void fixup_type_variants                        (tree, bool = false);
 extern void fixup_attribute_variants           (tree);
 extern void build_cdtor_clones                         (tree, bool, bool, 
bool);
 extern void clone_cdtor                                (tree, bool);
diff --git a/gcc/cp/class.cc b/gcc/cp/class.cc
index f4f2c4aa2351..506b41506d04 100644
--- a/gcc/cp/class.cc
+++ b/gcc/cp/class.cc
@@ -2319,10 +2319,11 @@ determine_primary_bases (tree t)
     }
 }
 
-/* Update the variant types of T.  */
+/* Update the variant types of T.  If UPDATE_ALIGN is true, also
+   update variant alignments from TYPE.  */
 
 void
-fixup_type_variants (tree type)
+fixup_type_variants (tree type, bool update_align /* = false */)
 {
   if (!type)
     return;
@@ -2350,9 +2351,10 @@ fixup_type_variants (tree type)
       TYPE_SIZE (variant) = TYPE_SIZE (type);
       TYPE_SIZE_UNIT (variant) = TYPE_SIZE_UNIT (type);
 
-      if (!TYPE_USER_ALIGN (variant)
-         || TYPE_NAME (variant) == TYPE_NAME (type)
-         || TYPE_ALIGN_RAW (variant) < TYPE_ALIGN_RAW (type))
+      if (update_align
+         && (!TYPE_USER_ALIGN (variant)
+             || TYPE_NAME (variant) == TYPE_NAME (type)
+             || TYPE_ALIGN_RAW (variant) < TYPE_ALIGN_RAW (type)))
        {
          TYPE_ALIGN_RAW (variant) =  TYPE_ALIGN_RAW (type);
          TYPE_USER_ALIGN (variant) = TYPE_USER_ALIGN (type);
@@ -2453,7 +2455,7 @@ finish_struct_bits (tree t)
     get_pure_virtuals (t);
 
   /* Fix up variants (if any).  */
-  fixup_type_variants (t);
+  fixup_type_variants (t, /*update_align=*/true);
 }
 
 /* Issue warnings about T having private constructors, but no friends,
diff --git a/gcc/cp/cp-gimplify.cc b/gcc/cp/cp-gimplify.cc
index e42c01e1e820..76dc9aeb1813 100644
--- a/gcc/cp/cp-gimplify.cc
+++ b/gcc/cp/cp-gimplify.cc
@@ -2387,6 +2387,25 @@ cp_genericize_r (tree *stmt_p, int *walk_subtrees, void 
*data)
       *walk_subtrees = 0;
       break;
 
+    case INDIRECT_REF:
+      /* A pointer to a class might point to a base subobject, which is
+        only guaranteed the alignment of the class as a base (the ABI
+        "nvalign").  That is less than TYPE_ALIGN if a virtual base needs
+        more alignment than the rest of the class, so don't let the middle
+        end assume more (PR c++/103303).  */
+      {
+       tree type = TREE_TYPE (stmt);
+       if (CLASS_TYPE_P (type)
+           && COMPLETE_TYPE_P (type)
+           && CLASSTYPE_AS_BASE (type)
+           && CLASSTYPE_ALIGN (type) < TYPE_ALIGN (type)
+           && resolves_to_fixed_type_p (stmt) <= 0
+           /* Respect an alignment the user specified on a variant.  */
+           && TYPE_ALIGN (type) == TYPE_ALIGN (TYPE_MAIN_VARIANT (type)))
+         TREE_TYPE (stmt) = build_aligned_type (type, CLASSTYPE_ALIGN (type));
+      }
+      break;
+
     case NOP_EXPR:
       *stmt_p = predeclare_vla (*stmt_p);
 
diff --git a/gcc/cp/module.cc b/gcc/cp/module.cc
index 6d7d650c638f..1fb2eab99f65 100644
--- a/gcc/cp/module.cc
+++ b/gcc/cp/module.cc
@@ -13970,7 +13970,7 @@ trees_in::read_class_def (tree defn, tree 
maybe_template)
 
   /* Propagate to all variants.  */
   if (installing)
-    fixup_type_variants (type);
+    fixup_type_variants (type, /*update_align=*/true);
 
   /* IS_FAKE_BASE_TYPE is inaccurate at this point, because if this is
      the fake base, we've not hooked it into the containing class's
diff --git a/gcc/testsuite/g++.dg/torture/pr103303.C 
b/gcc/testsuite/g++.dg/torture/pr103303.C
new file mode 100644
index 000000000000..d22cd5de2e61
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/pr103303.C
@@ -0,0 +1,36 @@
+// PR c++/103303
+// { dg-do run }
+// { dg-additional-options "-ftree-slp-vectorize" }
+
+// A class whose virtual base needs more alignment than the rest of the class
+// can be a base subobject at an address that is only aligned to its
+// non-virtual alignment; accesses through 'this' must not assume more.
+
+struct alignas(16) A16 { virtual ~A16 () {} long l[2]; };
+struct B8 { virtual ~B8 () {} };
+
+struct S : virtual B8, virtual A16
+{
+  __attribute__((noipa)) S () : v1 (0), v2 (0) {}
+  __attribute__((noipa)) void set (long x) { v1 = x; v2 = x; }
+  long dummy;
+  long v1;
+  long v2;
+};
+
+struct P { virtual ~P () {} };
+struct Pad { long a = 0, b = 0; };
+
+struct D : P, Pad, virtual S { virtual ~D () {} };
+
+static_assert (alignof (S) == 16, "");
+
+int
+main ()
+{
+  D d;
+  S *s = &d;
+  s->set (42);
+  if (s->v1 != 42 || s->v2 != 42)
+    __builtin_abort ();
+}
diff --git a/gcc/testsuite/g++.target/i386/pr103303.C 
b/gcc/testsuite/g++.target/i386/pr103303.C
new file mode 100644
index 000000000000..1ce99011ed26
--- /dev/null
+++ b/gcc/testsuite/g++.target/i386/pr103303.C
@@ -0,0 +1,31 @@
+// PR c++/103303
+// { dg-do compile { target c++11 } }
+// { dg-options "-O3 -msse2" }
+
+struct alignas(16) A16 { virtual ~A16 () {} long l[2]; };
+struct B8 { virtual ~B8 () {} };
+
+struct S : virtual B8, virtual A16
+{
+  S ();
+  long dummy;
+  long v1;
+  long v2;
+};
+
+// 'this' is only guaranteed to be 8-byte aligned in the base constructor.
+S::S () : v1 (0), v2 (0) {}
+
+// But a final class can't be a base subobject.
+struct F final : virtual B8, virtual A16
+{
+  F ();
+  long dummy;
+  long v1;
+  long v2;
+};
+
+F::F () : v1 (0), v2 (0) {}
+
+// { dg-final { scan-assembler-times "movups\[^\n\r\]*16\\(%rdi\\)" 2 { target 
{ ! ia32 } } } }
+// { dg-final { scan-assembler-times "movaps\[^\n\r\]*16\\(%rdi\\)" 2 { target 
{ ! ia32 } } } }

Reply via email to