[clang] [Clang][Windows] Preserve x86_fp80 and vector alignment under #pragma pack. (PR #208256)
Zahira Ammarguellat via cfe-commits
cfe-commits at lists.llvm.org
Tue Aug 4 05:53:04 PDT 2026
https://github.com/zahiraam updated https://github.com/llvm/llvm-project/pull/208256
>From a30928821d892d2c4495f64a1265ea8ad73db020 Mon Sep 17 00:00:00 2001
From: Ammarguellat <zahira.ammarguellat at intel.com>
Date: Wed, 8 Jul 2026 09:24:56 -0700
Subject: [PATCH 01/12] [Clang] Fix x86_fp80 misalignment with pragma pack on
Windows
---
clang/lib/AST/RecordLayoutBuilder.cpp | 8 ++-
clang/test/CodeGen/x86_fp80-alignment-win.cpp | 58 +++++++++++++++++++
2 files changed, 63 insertions(+), 3 deletions(-)
create mode 100644 clang/test/CodeGen/x86_fp80-alignment-win.cpp
diff --git a/clang/lib/AST/RecordLayoutBuilder.cpp b/clang/lib/AST/RecordLayoutBuilder.cpp
index c27572bc7f50d..6df1bc37a890d 100644
--- a/clang/lib/AST/RecordLayoutBuilder.cpp
+++ b/clang/lib/AST/RecordLayoutBuilder.cpp
@@ -2748,10 +2748,12 @@ MicrosoftRecordLayoutBuilder::getAdjustedElementInfo(
std::max(RequiredAlignment,
std::max(DirectFieldAlignment, FieldTypeRequiredAlignment));
}
- // Respect pragma pack, attribute pack and declspec align
- if (!MaxFieldAlignment.isZero())
+ // Respect pragma pack, attribute pack and declspec align, but not for types
+ // that require specific alignment for correctness (e.g., x86_fp80 needs
+ // 16-byte alignment for movaps instructions).
+ if (!MaxFieldAlignment.isZero() && !ContainsX86FP80)
Info.Alignment = std::min(Info.Alignment, MaxFieldAlignment);
- if (FD->hasAttr<PackedAttr>())
+ if (FD->hasAttr<PackedAttr>() && !ContainsX86FP80)
Info.Alignment = CharUnits::One();
// The alignment used to update the record's alignment excludes over-alignment
// applied directly to the field; the alignment used for placement includes
diff --git a/clang/test/CodeGen/x86_fp80-alignment-win.cpp b/clang/test/CodeGen/x86_fp80-alignment-win.cpp
new file mode 100644
index 0000000000000..f446474c598be
--- /dev/null
+++ b/clang/test/CodeGen/x86_fp80-alignment-win.cpp
@@ -0,0 +1,58 @@
+// RUN: %clang_cc1 -triple x86_64-pc-windows-msvc -mlong-double-80 -emit-llvm -o - %s | FileCheck %s
+
+// Test that x86_fp80 (long double with /Qlong-double flag) maintains
+// 16-byte alignment for correctness (required by movaps instructions),
+// even when #pragma pack would normally reduce it.
+
+struct Klass {
+ long double a;
+};
+
+// Simulate std::array without including headers
+template<typename T, unsigned N>
+struct array {
+ T _Elems[N];
+};
+
+// CHECK-LABEL: define {{.*}} @{{.*}}test_single_klass
+// CHECK: %k = alloca %struct.Klass, align 16
+// CHECK-NOT: align 8
+// CHECK: store x86_fp80 {{.*}}, ptr {{.*}}, align 16
+void test_single_klass() {
+ Klass k;
+ k.a = 0.0L;
+}
+
+// CHECK-LABEL: define {{.*}} @{{.*}}test_struct_array
+// CHECK: %matrix = alloca %struct.array, align 16
+// CHECK-NOT: align 8
+// CHECK: store x86_fp80 {{.*}}, ptr {{.*}}, align 16
+void test_struct_array() {
+ array<Klass, 16> matrix;
+ for (int i = 0; i < 16; i++)
+ matrix._Elems[i].a = 0.0L;
+}
+
+// CHECK-LABEL: define {{.*}} @{{.*}}test_direct_array
+// CHECK: %arr = alloca [16 x %struct.Klass], align 16
+void test_direct_array() {
+ Klass arr[16];
+ for (int i = 0; i < 16; i++)
+ arr[i].a = 0.0L;
+}
+
+// Test with explicit pragma pack(8) - should still maintain 16-byte alignment
+#pragma pack(push, 8)
+struct PackedKlass {
+ long double b;
+};
+
+// CHECK-LABEL: define {{.*}} @{{.*}}test_explicit_pack
+// CHECK: %pk = alloca %struct.PackedKlass, align 16
+// CHECK-NOT: align 8
+// CHECK: store x86_fp80 {{.*}}, ptr {{.*}}, align 16
+void test_explicit_pack() {
+ PackedKlass pk;
+ pk.b = 0.0L;
+}
+#pragma pack(pop)
>From 51049570ce04ee08ae18ec39b41e1ce5531b521f Mon Sep 17 00:00:00 2001
From: Ammarguellat <zahira.ammarguellat at intel.com>
Date: Fri, 10 Jul 2026 07:26:10 -0700
Subject: [PATCH 02/12] Leveraged FieldRequiredAlignment
---
clang/lib/AST/RecordLayoutBuilder.cpp | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/clang/lib/AST/RecordLayoutBuilder.cpp b/clang/lib/AST/RecordLayoutBuilder.cpp
index 6df1bc37a890d..8b15c0a78a89f 100644
--- a/clang/lib/AST/RecordLayoutBuilder.cpp
+++ b/clang/lib/AST/RecordLayoutBuilder.cpp
@@ -2751,9 +2751,9 @@ MicrosoftRecordLayoutBuilder::getAdjustedElementInfo(
// Respect pragma pack, attribute pack and declspec align, but not for types
// that require specific alignment for correctness (e.g., x86_fp80 needs
// 16-byte alignment for movaps instructions).
- if (!MaxFieldAlignment.isZero() && !ContainsX86FP80)
+ if (!MaxFieldAlignment.isZero())
Info.Alignment = std::min(Info.Alignment, MaxFieldAlignment);
- if (FD->hasAttr<PackedAttr>() && !ContainsX86FP80)
+ if (FD->hasAttr<PackedAttr>())
Info.Alignment = CharUnits::One();
// The alignment used to update the record's alignment excludes over-alignment
// applied directly to the field; the alignment used for placement includes
>From f55ed7ecfd387cce528e58729f982ada6b0116a3 Mon Sep 17 00:00:00 2001
From: Ammarguellat <zahira.ammarguellat at intel.com>
Date: Tue, 14 Jul 2026 08:24:02 -0700
Subject: [PATCH 03/12] Added vector type support and addressed review comments
---
clang/include/clang/AST/ASTContext.h | 3 +
clang/lib/AST/ASTContext.cpp | 28 +++-
clang/lib/AST/RecordLayoutBuilder.cpp | 96 ++++++++++++-
.../x86_fp80-alignment-pragma-pack.cpp | 136 ++++++++++++++++++
clang/test/CodeGen/x86_fp80-alignment-win.cpp | 58 --------
5 files changed, 253 insertions(+), 68 deletions(-)
create mode 100644 clang/test/CodeGen/x86_fp80-alignment-pragma-pack.cpp
delete mode 100644 clang/test/CodeGen/x86_fp80-alignment-win.cpp
diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h
index 763039e690dec..c53dd99df9cba 100644
--- a/clang/include/clang/AST/ASTContext.h
+++ b/clang/include/clang/AST/ASTContext.h
@@ -183,6 +183,9 @@ enum class AlignRequirementKind {
/// The alignment comes from an alignment attribute on a enum type.
RequiredByEnum,
+
+ /// The alignment is required by the ABI for correctness.
+ RequiredByABI,
};
struct TypeInfo {
diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index 02a3f88431f58..6054d37554ef8 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -2348,6 +2348,15 @@ TypeInfo ASTContext::getTypeInfoImpl(const Type *T) const {
Width = Target->getLongDoubleWidth();
Align = Target->getLongDoubleAlign();
}
+ // On Windows targets, x86_fp80 requires 16-byte alignment for ABI
+ // correctness (movaps instructions will fault on misaligned addresses).
+ // Mark this as an ABI requirement that must not be reduced by #pragma
+ // pack. GCC preserves such alignment on other targets, but Clang
+ // historically has not; changing this would break existing Clang ABI on
+ // non-Windows platforms.
+ if (Target->getTriple().isOSWindows() &&
+ &Target->getLongDoubleFormat() == &llvm::APFloat::x87DoubleExtended())
+ AlignRequirement = AlignRequirementKind::RequiredByABI;
break;
case BuiltinType::Float128:
if (Target->hasFloat128Type() || !getLangOpts().OpenMP ||
@@ -2539,9 +2548,22 @@ TypeInfo ASTContext::getTypeInfoImpl(const Type *T) const {
const ASTRecordLayout &Layout = getASTRecordLayout(RD);
Width = toBits(Layout.getSize());
Align = toBits(Layout.getAlignment());
- AlignRequirement = RD->hasAttr<AlignedAttr>()
- ? AlignRequirementKind::RequiredByRecord
- : AlignRequirementKind::None;
+ // Check if the record has an aligned attribute, or if it contains
+ // fields with ABI-required alignment (e.g., x86_fp80).
+ if (RD->hasAttr<AlignedAttr>()) {
+ AlignRequirement = AlignRequirementKind::RequiredByRecord;
+ } else {
+ // Check if any field has RequiredByABI alignment requirement.
+ // If so, propagate it to the record.
+ AlignRequirement = AlignRequirementKind::None;
+ for (const auto *Field : RD->fields()) {
+ TypeInfo FI = getTypeInfo(Field->getType().getTypePtr());
+ if (FI.AlignRequirement == AlignRequirementKind::RequiredByABI) {
+ AlignRequirement = AlignRequirementKind::RequiredByABI;
+ break;
+ }
+ }
+ }
break;
}
diff --git a/clang/lib/AST/RecordLayoutBuilder.cpp b/clang/lib/AST/RecordLayoutBuilder.cpp
index 8b15c0a78a89f..c0b183a2ce6a6 100644
--- a/clang/lib/AST/RecordLayoutBuilder.cpp
+++ b/clang/lib/AST/RecordLayoutBuilder.cpp
@@ -559,6 +559,61 @@ void EmptySubobjectMap::UpdateEmptyFieldSubobjects(
typedef llvm::SmallPtrSet<const CXXRecordDecl*, 4> ClassSetTy;
+/// Helper for RequiresVectorAlignment - recursively checks types.
+static bool CheckTypeForRequiredVectorAlignment(const ASTContext &Context,
+ QualType Ty) {
+ if (const auto *VT = Ty->getAs<VectorType>()) {
+ uint64_t VecWidth = Context.getTypeSize(VT);
+ return VT->getVectorKind() == VectorKind::Generic &&
+ (VecWidth == 128 || VecWidth == 256) &&
+ VecWidth == Context.getTypeAlign(VT);
+ }
+ if (const auto *AT = Ty->getAsArrayTypeUnsafe())
+ return CheckTypeForRequiredVectorAlignment(Context, AT->getElementType());
+
+ if (const auto *RT = Ty->getAs<RecordType>()) {
+ for (const auto *Field : RT->getDecl()->fields())
+ if (CheckTypeForRequiredVectorAlignment(Context, Field->getType()))
+ return true;
+ }
+ return false;
+}
+
+/// Check if a type (or any type it contains) is a standard SIMD vector requiring
+/// alignment preservation on Windows. This includes direct vectors, arrays of
+/// vectors, and structs containing vectors.
+static bool RequiresVectorAlignment(const ASTContext &Context, QualType Ty) {
+ if (!Context.getTargetInfo().getTriple().isOSWindows())
+ return false;
+ return CheckTypeForRequiredVectorAlignment(Context, Ty);
+}
+
+/// Check if we should prevent MaxFieldAlignment from reducing this field's
+/// alignment. Returns true if the field has ABI-required alignment or contains
+/// vectors requiring alignment, UNLESS the struct has explicit packed attribute.
+static bool ShouldPreserveFieldAlignment(const ASTContext &Context,
+ const FieldDecl *FD,
+ AlignRequirementKind AlignReq,
+ bool StructHasPackedAttr) {
+ if (AlignReq == AlignRequirementKind::RequiredByABI)
+ return true;
+ if (!StructHasPackedAttr && RequiresVectorAlignment(Context, FD->getType()))
+ return true;
+ return false;
+}
+
+/// Check if a record contains any fields with vectors requiring alignment.
+/// Returns false if the record has explicit __attribute__((packed)).
+static bool RecordContainsVectorRequiringAlignment(const ASTContext &Context,
+ const RecordDecl *RD) {
+ if (RD->hasAttr<PackedAttr>())
+ return false;
+ for (const auto *Field : RD->fields())
+ if (RequiresVectorAlignment(Context, Field->getType()))
+ return true;
+ return false;
+}
+
class ItaniumRecordLayoutBuilder {
protected:
// FIXME: Remove this and make the appropriate fields public.
@@ -2029,13 +2084,20 @@ void ItaniumRecordLayoutBuilder::LayoutField(const FieldDecl *D,
UnpackedFieldAlign = std::max(UnpackedFieldAlign, MaxAlignmentInChars);
// The maximum field alignment overrides the aligned attribute.
- if (!MaxFieldAlignment.isZero()) {
+ // However, do not reduce alignment for ABI-required alignments (e.g.,
+ // x86_fp80, vector types) which must be preserved for correctness.
+ // On Windows, check if the field type is a vector with standard SIMD
+ // alignment (16 or 32 bytes with size == alignment) - these need their
+ // alignment preserved under #pragma pack. However, honor explicit
+ // __attribute__((packed)) on the struct (Packed=true means the struct
+ // has the packed attribute, not the field).
+ if (!MaxFieldAlignment.isZero() &&
+ !ShouldPreserveFieldAlignment(Context, D, AlignRequirement, Packed)) {
PackedFieldAlign = std::min(PackedFieldAlign, MaxFieldAlignment);
PreferredAlign = std::min(PreferredAlign, MaxFieldAlignment);
UnpackedFieldAlign = std::min(UnpackedFieldAlign, MaxFieldAlignment);
}
-
if (!FieldPacked)
FieldAlign = UnpackedFieldAlign;
if (DefaultsToAIXPowerAlignment)
@@ -2748,10 +2810,25 @@ MicrosoftRecordLayoutBuilder::getAdjustedElementInfo(
std::max(RequiredAlignment,
std::max(DirectFieldAlignment, FieldTypeRequiredAlignment));
}
- // Respect pragma pack, attribute pack and declspec align, but not for types
- // that require specific alignment for correctness (e.g., x86_fp80 needs
- // 16-byte alignment for movaps instructions).
- if (!MaxFieldAlignment.isZero())
+ // Check if this is a vector type (or contains vectors) requiring alignment
+ // preservation on Windows. This includes direct vectors, arrays of vectors,
+ // and structs containing vectors. However, honor __attribute__((packed))
+ // on the struct even for vectors (explicit intent to pack).
+ bool IsVectorRequiringAlignment = false;
+ if (const RecordDecl *RD = FD->getParent()) {
+ if (!RD->hasAttr<PackedAttr>()) {
+ IsVectorRequiringAlignment = RequiresVectorAlignment(Context, FD->getType());
+ }
+ }
+
+ // Respect pragma pack, attribute pack and declspec align.
+ // However, do not reduce alignment for ABI-required alignments (e.g.,
+ // x86_fp80, vector types) which must be preserved for correctness.
+ bool StructHasPackedAttr =
+ FD->getParent() && FD->getParent()->hasAttr<PackedAttr>();
+ if (!MaxFieldAlignment.isZero() &&
+ !ShouldPreserveFieldAlignment(Context, FD, TInfo.AlignRequirement,
+ StructHasPackedAttr))
Info.Alignment = std::min(Info.Alignment, MaxFieldAlignment);
if (FD->hasAttr<PackedAttr>())
Info.Alignment = CharUnits::One();
@@ -3289,7 +3366,12 @@ void MicrosoftRecordLayoutBuilder::finalizeLayout(const RecordDecl *RD) {
if (!RequiredAlignment.isZero()) {
Alignment = std::max(Alignment, RequiredAlignment);
auto RoundingAlignment = Alignment;
- if (!MaxFieldAlignment.isZero())
+ // Check if this struct contains vectors that require ABI alignment before
+ // allowing MaxFieldAlignment (from #pragma pack) to reduce the overall
+ // struct alignment. However, if the struct has __attribute__((packed)),
+ // honor that explicit request even for vectors.
+ if (!MaxFieldAlignment.isZero() &&
+ !RecordContainsVectorRequiringAlignment(Context, RD))
RoundingAlignment = std::min(RoundingAlignment, MaxFieldAlignment);
RoundingAlignment = std::max(RoundingAlignment, RequiredAlignment);
Size = Size.alignTo(RoundingAlignment);
diff --git a/clang/test/CodeGen/x86_fp80-alignment-pragma-pack.cpp b/clang/test/CodeGen/x86_fp80-alignment-pragma-pack.cpp
new file mode 100644
index 0000000000000..accc79dde04f0
--- /dev/null
+++ b/clang/test/CodeGen/x86_fp80-alignment-pragma-pack.cpp
@@ -0,0 +1,136 @@
+// RUN: %clang_cc1 -triple x86_64-pc-windows-gnu -emit-llvm -o - %s \
+// RUN: | FileCheck %s --check-prefix=CHECK-FP80
+
+// RUN: %clang_cc1 -triple x86_64-pc-windows-msvc -emit-llvm -o - %s \
+// RUN: | FileCheck %s --check-prefix=CHECK-VEC
+
+// Test that x86_fp80 (long double) and vector types maintain their required
+// alignment for correctness (movaps/movapd instructions will fault on misaligned
+// addresses), even when #pragma pack(8) would normally reduce it. This issue
+// affects Windows targets where #pragma pack is commonly used.
+// Note: GCC on Linux preserves such alignment even with #pragma pack, so this fix
+// is Windows-specific to avoid breaking existing Clang ABI on other platforms.
+// Note: We use windows-gnu (MinGW) for x86_fp80 tests because windows-msvc doesn't
+// support 80-bit long double.
+
+typedef float v4f32 __attribute__((vector_size(16)));
+typedef float v8f32 __attribute__((vector_size(32)));
+
+struct Klass {
+ long double a;
+};
+
+struct VectorKlass {
+ v4f32 v;
+};
+
+// CHECK-FP80-LABEL: define {{.*}} @{{.*}}test_single_klass
+// CHECK-FP80: %k = alloca %struct.Klass, align 16
+// CHECK-FP80: store x86_fp80 {{.*}}, ptr {{.*}}, align 16
+void test_single_klass() {
+ Klass k;
+ k.a = 0.0L;
+}
+
+// CHECK-VEC-LABEL: define {{.*}} @{{.*}}test_vector_klass
+// CHECK-VEC: %v = alloca %struct.VectorKlass, align 16
+void test_vector_klass() {
+ VectorKlass v;
+ v.v = (v4f32){0.0f, 0.0f, 0.0f, 0.0f};
+}
+
+// Test with pragma pack(8) - should STILL maintain required alignment
+// This is the key test case for the bug fix
+#pragma pack(push, 8)
+
+struct PackedKlass {
+ long double b;
+};
+
+struct PackedVectorKlass {
+ v4f32 v;
+};
+
+struct PackedLargeVectorKlass {
+ v8f32 v;
+};
+
+// Simulate std::array without including headers
+template<typename T, unsigned N>
+struct array {
+ T _Elems[N];
+};
+
+// CHECK-FP80-LABEL: define {{.*}} @{{.*}}test_explicit_pack
+// CHECK-FP80: %pk = alloca %struct.PackedKlass, align 16
+// CHECK-FP80: store x86_fp80 {{.*}}, ptr {{.*}}, align 16
+void test_explicit_pack() {
+ PackedKlass pk;
+ pk.b = 0.0L;
+}
+
+// CHECK-VEC-LABEL: define {{.*}} @{{.*}}test_packed_vector
+// CHECK-VEC: %pv = alloca %struct.PackedVectorKlass, align 16
+void test_packed_vector() {
+ PackedVectorKlass pv;
+ pv.v = (v4f32){0.0f, 0.0f, 0.0f, 0.0f};
+}
+
+// CHECK-VEC-LABEL: define {{.*}} @{{.*}}test_packed_large_vector
+// CHECK-VEC: %plv = alloca %struct.PackedLargeVectorKlass, align 32
+void test_packed_large_vector() {
+ PackedLargeVectorKlass plv;
+ plv.v = (v8f32){0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f};
+}
+
+// CHECK-FP80-LABEL: define {{.*}} @{{.*}}test_struct_array_packed
+// CHECK-FP80: %matrix = alloca %struct.array, align 16
+void test_struct_array_packed() {
+ array<PackedKlass, 16> matrix;
+ for (int i = 0; i < 16; i++)
+ matrix._Elems[i].b = 0.0L;
+}
+
+// CHECK-FP80-LABEL: define {{.*}} @{{.*}}test_direct_array_packed
+// CHECK-FP80: %arr = alloca [16 x %struct.PackedKlass], align 16
+void test_direct_array_packed() {
+ PackedKlass arr[16];
+ for (int i = 0; i < 16; i++)
+ arr[i].b = 0.0L;
+}
+
+// CHECK-VEC-LABEL: define {{.*}} @{{.*}}test_vector_array_packed
+// CHECK-VEC: %varr = alloca %struct.array{{.*}}, align 16
+void test_vector_array_packed() {
+ array<PackedVectorKlass, 4> varr;
+ for (int i = 0; i < 4; i++)
+ varr._Elems[i].v = (v4f32){0.0f, 0.0f, 0.0f, 0.0f};
+}
+
+#pragma pack(pop)
+
+// Test that __attribute__((packed)) on the struct reduces vector alignment.
+// This is needed for unaligned load/store intrinsics like _mm_loadu_ps.
+struct __attribute__((packed)) ExplicitlyPackedVector {
+ v4f32 v;
+};
+
+// CHECK-VEC-LABEL: define {{.*}} @{{.*}}test_explicitly_packed_vector
+// CHECK-VEC: %epv = alloca %struct.ExplicitlyPackedVector, align 1
+void test_explicitly_packed_vector() {
+ ExplicitlyPackedVector epv;
+ epv.v = (v4f32){0.0f, 0.0f, 0.0f, 0.0f};
+}
+
+#pragma pack(push, 8)
+struct FieldPackedVector {
+ v4f32 v __attribute__((packed));
+};
+#pragma pack(pop)
+
+// CHECK-VEC-LABEL: define {{.*}} @{{.*}}test_field_packed_vector
+// CHECK-VEC: %fpv = alloca %struct.FieldPackedVector, align 1
+void test_field_packed_vector() {
+ FieldPackedVector fpv;
+ fpv.v = (v4f32){0.0f, 0.0f, 0.0f, 0.0f};
+}
diff --git a/clang/test/CodeGen/x86_fp80-alignment-win.cpp b/clang/test/CodeGen/x86_fp80-alignment-win.cpp
deleted file mode 100644
index f446474c598be..0000000000000
--- a/clang/test/CodeGen/x86_fp80-alignment-win.cpp
+++ /dev/null
@@ -1,58 +0,0 @@
-// RUN: %clang_cc1 -triple x86_64-pc-windows-msvc -mlong-double-80 -emit-llvm -o - %s | FileCheck %s
-
-// Test that x86_fp80 (long double with /Qlong-double flag) maintains
-// 16-byte alignment for correctness (required by movaps instructions),
-// even when #pragma pack would normally reduce it.
-
-struct Klass {
- long double a;
-};
-
-// Simulate std::array without including headers
-template<typename T, unsigned N>
-struct array {
- T _Elems[N];
-};
-
-// CHECK-LABEL: define {{.*}} @{{.*}}test_single_klass
-// CHECK: %k = alloca %struct.Klass, align 16
-// CHECK-NOT: align 8
-// CHECK: store x86_fp80 {{.*}}, ptr {{.*}}, align 16
-void test_single_klass() {
- Klass k;
- k.a = 0.0L;
-}
-
-// CHECK-LABEL: define {{.*}} @{{.*}}test_struct_array
-// CHECK: %matrix = alloca %struct.array, align 16
-// CHECK-NOT: align 8
-// CHECK: store x86_fp80 {{.*}}, ptr {{.*}}, align 16
-void test_struct_array() {
- array<Klass, 16> matrix;
- for (int i = 0; i < 16; i++)
- matrix._Elems[i].a = 0.0L;
-}
-
-// CHECK-LABEL: define {{.*}} @{{.*}}test_direct_array
-// CHECK: %arr = alloca [16 x %struct.Klass], align 16
-void test_direct_array() {
- Klass arr[16];
- for (int i = 0; i < 16; i++)
- arr[i].a = 0.0L;
-}
-
-// Test with explicit pragma pack(8) - should still maintain 16-byte alignment
-#pragma pack(push, 8)
-struct PackedKlass {
- long double b;
-};
-
-// CHECK-LABEL: define {{.*}} @{{.*}}test_explicit_pack
-// CHECK: %pk = alloca %struct.PackedKlass, align 16
-// CHECK-NOT: align 8
-// CHECK: store x86_fp80 {{.*}}, ptr {{.*}}, align 16
-void test_explicit_pack() {
- PackedKlass pk;
- pk.b = 0.0L;
-}
-#pragma pack(pop)
>From bdc8e5d64e3357483b6c374f8ade33dec4c014e2 Mon Sep 17 00:00:00 2001
From: Ammarguellat <zahira.ammarguellat at intel.com>
Date: Tue, 14 Jul 2026 09:15:14 -0700
Subject: [PATCH 04/12] Fix format
---
clang/lib/AST/RecordLayoutBuilder.cpp | 12 +++++++-----
1 file changed, 7 insertions(+), 5 deletions(-)
diff --git a/clang/lib/AST/RecordLayoutBuilder.cpp b/clang/lib/AST/RecordLayoutBuilder.cpp
index c0b183a2ce6a6..cd0f48071a020 100644
--- a/clang/lib/AST/RecordLayoutBuilder.cpp
+++ b/clang/lib/AST/RecordLayoutBuilder.cpp
@@ -579,9 +579,9 @@ static bool CheckTypeForRequiredVectorAlignment(const ASTContext &Context,
return false;
}
-/// Check if a type (or any type it contains) is a standard SIMD vector requiring
-/// alignment preservation on Windows. This includes direct vectors, arrays of
-/// vectors, and structs containing vectors.
+/// Check if a type (or any type it contains) is a standard SIMD vector
+/// requiring alignment preservation on Windows. This includes direct vectors,
+/// arrays of vectors, and structs containing vectors.
static bool RequiresVectorAlignment(const ASTContext &Context, QualType Ty) {
if (!Context.getTargetInfo().getTriple().isOSWindows())
return false;
@@ -590,7 +590,8 @@ static bool RequiresVectorAlignment(const ASTContext &Context, QualType Ty) {
/// Check if we should prevent MaxFieldAlignment from reducing this field's
/// alignment. Returns true if the field has ABI-required alignment or contains
-/// vectors requiring alignment, UNLESS the struct has explicit packed attribute.
+/// vectors requiring alignment, UNLESS the struct has explicit packed
+/// attribute.
static bool ShouldPreserveFieldAlignment(const ASTContext &Context,
const FieldDecl *FD,
AlignRequirementKind AlignReq,
@@ -2817,7 +2818,8 @@ MicrosoftRecordLayoutBuilder::getAdjustedElementInfo(
bool IsVectorRequiringAlignment = false;
if (const RecordDecl *RD = FD->getParent()) {
if (!RD->hasAttr<PackedAttr>()) {
- IsVectorRequiringAlignment = RequiresVectorAlignment(Context, FD->getType());
+ IsVectorRequiringAlignment =
+ RequiresVectorAlignment(Context, FD->getType());
}
}
>From b1d3572cf9eb37cd8227e5987652778172e53504 Mon Sep 17 00:00:00 2001
From: Ammarguellat <zahira.ammarguellat at intel.com>
Date: Tue, 14 Jul 2026 09:44:00 -0700
Subject: [PATCH 05/12] Removed obsolete code
---
clang/lib/AST/RecordLayoutBuilder.cpp | 11 -----------
1 file changed, 11 deletions(-)
diff --git a/clang/lib/AST/RecordLayoutBuilder.cpp b/clang/lib/AST/RecordLayoutBuilder.cpp
index cd0f48071a020..c0e8e30a3061a 100644
--- a/clang/lib/AST/RecordLayoutBuilder.cpp
+++ b/clang/lib/AST/RecordLayoutBuilder.cpp
@@ -2811,17 +2811,6 @@ MicrosoftRecordLayoutBuilder::getAdjustedElementInfo(
std::max(RequiredAlignment,
std::max(DirectFieldAlignment, FieldTypeRequiredAlignment));
}
- // Check if this is a vector type (or contains vectors) requiring alignment
- // preservation on Windows. This includes direct vectors, arrays of vectors,
- // and structs containing vectors. However, honor __attribute__((packed))
- // on the struct even for vectors (explicit intent to pack).
- bool IsVectorRequiringAlignment = false;
- if (const RecordDecl *RD = FD->getParent()) {
- if (!RD->hasAttr<PackedAttr>()) {
- IsVectorRequiringAlignment =
- RequiresVectorAlignment(Context, FD->getType());
- }
- }
// Respect pragma pack, attribute pack and declspec align.
// However, do not reduce alignment for ABI-required alignments (e.g.,
>From 9909648225825d2b89c692bbe0708dc06fc4e2a8 Mon Sep 17 00:00:00 2001
From: Ammarguellat <zahira.ammarguellat at intel.com>
Date: Thu, 16 Jul 2026 08:27:54 -0700
Subject: [PATCH 06/12] Limited changes to msvc and addressed code propagamtion
comment
---
clang/lib/AST/ASTContext.cpp | 15 +-
clang/lib/AST/RecordLayoutBuilder.cpp | 6 +-
.../vector-alignment-pragma-pack-msvc.cpp | 130 +++++++++++++++++
.../x86_fp80-alignment-pragma-pack.cpp | 136 ------------------
4 files changed, 143 insertions(+), 144 deletions(-)
create mode 100644 clang/test/CodeGen/vector-alignment-pragma-pack-msvc.cpp
delete mode 100644 clang/test/CodeGen/x86_fp80-alignment-pragma-pack.cpp
diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index 6054d37554ef8..7d345e836b5f8 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -2348,13 +2348,15 @@ TypeInfo ASTContext::getTypeInfoImpl(const Type *T) const {
Width = Target->getLongDoubleWidth();
Align = Target->getLongDoubleAlign();
}
- // On Windows targets, x86_fp80 requires 16-byte alignment for ABI
+ // On Windows MSVC targets, x86_fp80 requires 16-byte alignment for ABI
// correctness (movaps instructions will fault on misaligned addresses).
// Mark this as an ABI requirement that must not be reduced by #pragma
- // pack. GCC preserves such alignment on other targets, but Clang
- // historically has not; changing this would break existing Clang ABI on
- // non-Windows platforms.
+ // pack. This is MSVC-specific; MinGW has different layout rules. GCC
+ // preserves such alignment on other targets, but Clang historically has
+ // not; changing this would break existing Clang ABI on non-Windows
+ // platforms.
if (Target->getTriple().isOSWindows() &&
+ Target->getTriple().isWindowsMSVCEnvironment() &&
&Target->getLongDoubleFormat() == &llvm::APFloat::x87DoubleExtended())
AlignRequirement = AlignRequirementKind::RequiredByABI;
break;
@@ -2549,7 +2551,7 @@ TypeInfo ASTContext::getTypeInfoImpl(const Type *T) const {
Width = toBits(Layout.getSize());
Align = toBits(Layout.getAlignment());
// Check if the record has an aligned attribute, or if it contains
- // fields with ABI-required alignment (e.g., x86_fp80).
+ // fields with ABI-required alignment (e.g., vectors on MSVC).
if (RD->hasAttr<AlignedAttr>()) {
AlignRequirement = AlignRequirementKind::RequiredByRecord;
} else {
@@ -2558,7 +2560,8 @@ TypeInfo ASTContext::getTypeInfoImpl(const Type *T) const {
AlignRequirement = AlignRequirementKind::None;
for (const auto *Field : RD->fields()) {
TypeInfo FI = getTypeInfo(Field->getType().getTypePtr());
- if (FI.AlignRequirement == AlignRequirementKind::RequiredByABI) {
+ if (FI.AlignRequirement == AlignRequirementKind::RequiredByABI ||
+ FI.AlignRequirement == AlignRequirementKind::RequiredByRecord) {
AlignRequirement = AlignRequirementKind::RequiredByABI;
break;
}
diff --git a/clang/lib/AST/RecordLayoutBuilder.cpp b/clang/lib/AST/RecordLayoutBuilder.cpp
index c0e8e30a3061a..58ce1353de4ea 100644
--- a/clang/lib/AST/RecordLayoutBuilder.cpp
+++ b/clang/lib/AST/RecordLayoutBuilder.cpp
@@ -583,7 +583,8 @@ static bool CheckTypeForRequiredVectorAlignment(const ASTContext &Context,
/// requiring alignment preservation on Windows. This includes direct vectors,
/// arrays of vectors, and structs containing vectors.
static bool RequiresVectorAlignment(const ASTContext &Context, QualType Ty) {
- if (!Context.getTargetInfo().getTriple().isOSWindows())
+ if (!Context.getTargetInfo().getTriple().isOSWindows() ||
+ !Context.getTargetInfo().getTriple().isWindowsMSVCEnvironment())
return false;
return CheckTypeForRequiredVectorAlignment(Context, Ty);
}
@@ -596,7 +597,8 @@ static bool ShouldPreserveFieldAlignment(const ASTContext &Context,
const FieldDecl *FD,
AlignRequirementKind AlignReq,
bool StructHasPackedAttr) {
- if (AlignReq == AlignRequirementKind::RequiredByABI)
+ if (AlignReq == AlignRequirementKind::RequiredByABI ||
+ AlignReq == AlignRequirementKind::RequiredByRecord)
return true;
if (!StructHasPackedAttr && RequiresVectorAlignment(Context, FD->getType()))
return true;
diff --git a/clang/test/CodeGen/vector-alignment-pragma-pack-msvc.cpp b/clang/test/CodeGen/vector-alignment-pragma-pack-msvc.cpp
new file mode 100644
index 0000000000000..d185236f2a500
--- /dev/null
+++ b/clang/test/CodeGen/vector-alignment-pragma-pack-msvc.cpp
@@ -0,0 +1,130 @@
+// RUN: %clang_cc1 -triple x86_64-pc-windows-msvc -emit-llvm -o - %s \
+// RUN: | FileCheck %s
+
+// Test that vector types maintain their required alignment for correctness
+// (movaps/movapd instructions will fault on misaligned addresses), even when
+// #pragma pack(8) would normally reduce it. This issue affects Windows MSVC
+// targets where #pragma pack is commonly used (e.g., MSVC STL).
+
+typedef float v4f32 __attribute__((vector_size(16)));
+typedef float v8f32 __attribute__((vector_size(32)));
+
+struct VectorKlass {
+ v4f32 v;
+};
+
+// CHECK-LABEL: define {{.*}} @{{.*}}test_vector_klass
+// CHECK: %v = alloca %struct.VectorKlass, align 16
+void test_vector_klass() {
+ VectorKlass v;
+ v.v = (v4f32){0.0f, 0.0f, 0.0f, 0.0f};
+}
+
+#pragma pack(push, 8)
+struct PackedVectorKlass {
+ v4f32 v;
+};
+
+struct PackedLargeVectorKlass {
+ v8f32 v;
+};
+
+// CHECK-LABEL: define {{.*}} @{{.*}}test_packed_vector
+// CHECK: %pv = alloca %struct.PackedVectorKlass, align 16
+void test_packed_vector() {
+ PackedVectorKlass pv;
+ pv.v = (v4f32){0.0f, 0.0f, 0.0f, 0.0f};
+}
+
+// CHECK-LABEL: define {{.*}} @{{.*}}test_packed_large_vector
+// CHECK: %plv = alloca %struct.PackedLargeVectorKlass, align 32
+void test_packed_large_vector() {
+ PackedLargeVectorKlass plv;
+ plv.v = (v8f32){0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f};
+}
+
+struct InnerWithVector {
+ v4f32 v;
+};
+
+struct OuterWithVector {
+ InnerWithVector inner;
+};
+
+// CHECK-LABEL: define {{.*}} @{{.*}}test_nested_vector
+// CHECK: %outer = alloca %struct.OuterWithVector, align 16
+void test_nested_vector() {
+ OuterWithVector outer;
+ outer.inner.v = (v4f32){0.0f, 0.0f, 0.0f, 0.0f};
+}
+
+// Test array of structs containing vectors
+template<typename T, unsigned N>
+struct array {
+ T _Elems[N];
+};
+
+// CHECK-LABEL: define {{.*}} @{{.*}}test_vector_array_packed
+// CHECK: %varr = alloca %struct.array, align 16
+void test_vector_array_packed() {
+ array<PackedVectorKlass, 4> varr;
+ for (int i = 0; i < 4; i++)
+ varr._Elems[i].v = (v4f32){0.0f, 0.0f, 0.0f, 0.0f};
+}
+#pragma pack(pop)
+
+struct __attribute__((packed)) ExplicitlyPackedVector {
+ v4f32 v;
+};
+
+// CHECK-LABEL: define {{.*}} @{{.*}}test_explicitly_packed_vector
+// CHECK: %epv = alloca %struct.ExplicitlyPackedVector, align 1
+void test_explicitly_packed_vector() {
+ ExplicitlyPackedVector epv;
+ epv.v = (v4f32){0.0f, 0.0f, 0.0f, 0.0f};
+}
+
+#pragma pack(push, 8)
+struct FieldPackedVector {
+ v4f32 v __attribute__((packed));
+};
+#pragma pack(pop)
+
+// CHECK-LABEL: define {{.*}} @{{.*}}test_field_packed_vector
+// CHECK: %fpv = alloca %struct.FieldPackedVector, align 1
+void test_field_packed_vector() {
+ FieldPackedVector fpv;
+ fpv.v = (v4f32){0.0f, 0.0f, 0.0f, 0.0f};
+}
+
+#pragma pack(push, 8)
+struct alignas(16) ExplicitAlignedInner {
+ long double x;
+};
+
+struct OuterWithExplicitAligned {
+ ExplicitAlignedInner inner;
+};
+
+struct ImplicitAlignedInner {
+ long double x;
+};
+
+struct OuterWithImplicitAligned {
+ ImplicitAlignedInner inner;
+};
+#pragma pack(pop)
+
+// CHECK-FP80-LABEL: define {{.*}} @{{.*}}test_explicit_aligned_nested
+// CHECK-FP80: %outer = alloca %struct.OuterWithExplicitAligned, align 16
+void test_explicit_aligned_nested() {
+ OuterWithExplicitAligned outer;
+ outer.inner.x = 0.0L;
+}
+
+// CHECK-FP80-LABEL: define {{.*}} @{{.*}}test_implicit_aligned_nested
+// CHECK-FP80: %outer = alloca %struct.OuterWithImplicitAligned, align 16
+void test_implicit_aligned_nested() {
+ OuterWithImplicitAligned outer;
+ outer.inner.x = 0.0L;
+}
diff --git a/clang/test/CodeGen/x86_fp80-alignment-pragma-pack.cpp b/clang/test/CodeGen/x86_fp80-alignment-pragma-pack.cpp
deleted file mode 100644
index accc79dde04f0..0000000000000
--- a/clang/test/CodeGen/x86_fp80-alignment-pragma-pack.cpp
+++ /dev/null
@@ -1,136 +0,0 @@
-// RUN: %clang_cc1 -triple x86_64-pc-windows-gnu -emit-llvm -o - %s \
-// RUN: | FileCheck %s --check-prefix=CHECK-FP80
-
-// RUN: %clang_cc1 -triple x86_64-pc-windows-msvc -emit-llvm -o - %s \
-// RUN: | FileCheck %s --check-prefix=CHECK-VEC
-
-// Test that x86_fp80 (long double) and vector types maintain their required
-// alignment for correctness (movaps/movapd instructions will fault on misaligned
-// addresses), even when #pragma pack(8) would normally reduce it. This issue
-// affects Windows targets where #pragma pack is commonly used.
-// Note: GCC on Linux preserves such alignment even with #pragma pack, so this fix
-// is Windows-specific to avoid breaking existing Clang ABI on other platforms.
-// Note: We use windows-gnu (MinGW) for x86_fp80 tests because windows-msvc doesn't
-// support 80-bit long double.
-
-typedef float v4f32 __attribute__((vector_size(16)));
-typedef float v8f32 __attribute__((vector_size(32)));
-
-struct Klass {
- long double a;
-};
-
-struct VectorKlass {
- v4f32 v;
-};
-
-// CHECK-FP80-LABEL: define {{.*}} @{{.*}}test_single_klass
-// CHECK-FP80: %k = alloca %struct.Klass, align 16
-// CHECK-FP80: store x86_fp80 {{.*}}, ptr {{.*}}, align 16
-void test_single_klass() {
- Klass k;
- k.a = 0.0L;
-}
-
-// CHECK-VEC-LABEL: define {{.*}} @{{.*}}test_vector_klass
-// CHECK-VEC: %v = alloca %struct.VectorKlass, align 16
-void test_vector_klass() {
- VectorKlass v;
- v.v = (v4f32){0.0f, 0.0f, 0.0f, 0.0f};
-}
-
-// Test with pragma pack(8) - should STILL maintain required alignment
-// This is the key test case for the bug fix
-#pragma pack(push, 8)
-
-struct PackedKlass {
- long double b;
-};
-
-struct PackedVectorKlass {
- v4f32 v;
-};
-
-struct PackedLargeVectorKlass {
- v8f32 v;
-};
-
-// Simulate std::array without including headers
-template<typename T, unsigned N>
-struct array {
- T _Elems[N];
-};
-
-// CHECK-FP80-LABEL: define {{.*}} @{{.*}}test_explicit_pack
-// CHECK-FP80: %pk = alloca %struct.PackedKlass, align 16
-// CHECK-FP80: store x86_fp80 {{.*}}, ptr {{.*}}, align 16
-void test_explicit_pack() {
- PackedKlass pk;
- pk.b = 0.0L;
-}
-
-// CHECK-VEC-LABEL: define {{.*}} @{{.*}}test_packed_vector
-// CHECK-VEC: %pv = alloca %struct.PackedVectorKlass, align 16
-void test_packed_vector() {
- PackedVectorKlass pv;
- pv.v = (v4f32){0.0f, 0.0f, 0.0f, 0.0f};
-}
-
-// CHECK-VEC-LABEL: define {{.*}} @{{.*}}test_packed_large_vector
-// CHECK-VEC: %plv = alloca %struct.PackedLargeVectorKlass, align 32
-void test_packed_large_vector() {
- PackedLargeVectorKlass plv;
- plv.v = (v8f32){0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f};
-}
-
-// CHECK-FP80-LABEL: define {{.*}} @{{.*}}test_struct_array_packed
-// CHECK-FP80: %matrix = alloca %struct.array, align 16
-void test_struct_array_packed() {
- array<PackedKlass, 16> matrix;
- for (int i = 0; i < 16; i++)
- matrix._Elems[i].b = 0.0L;
-}
-
-// CHECK-FP80-LABEL: define {{.*}} @{{.*}}test_direct_array_packed
-// CHECK-FP80: %arr = alloca [16 x %struct.PackedKlass], align 16
-void test_direct_array_packed() {
- PackedKlass arr[16];
- for (int i = 0; i < 16; i++)
- arr[i].b = 0.0L;
-}
-
-// CHECK-VEC-LABEL: define {{.*}} @{{.*}}test_vector_array_packed
-// CHECK-VEC: %varr = alloca %struct.array{{.*}}, align 16
-void test_vector_array_packed() {
- array<PackedVectorKlass, 4> varr;
- for (int i = 0; i < 4; i++)
- varr._Elems[i].v = (v4f32){0.0f, 0.0f, 0.0f, 0.0f};
-}
-
-#pragma pack(pop)
-
-// Test that __attribute__((packed)) on the struct reduces vector alignment.
-// This is needed for unaligned load/store intrinsics like _mm_loadu_ps.
-struct __attribute__((packed)) ExplicitlyPackedVector {
- v4f32 v;
-};
-
-// CHECK-VEC-LABEL: define {{.*}} @{{.*}}test_explicitly_packed_vector
-// CHECK-VEC: %epv = alloca %struct.ExplicitlyPackedVector, align 1
-void test_explicitly_packed_vector() {
- ExplicitlyPackedVector epv;
- epv.v = (v4f32){0.0f, 0.0f, 0.0f, 0.0f};
-}
-
-#pragma pack(push, 8)
-struct FieldPackedVector {
- v4f32 v __attribute__((packed));
-};
-#pragma pack(pop)
-
-// CHECK-VEC-LABEL: define {{.*}} @{{.*}}test_field_packed_vector
-// CHECK-VEC: %fpv = alloca %struct.FieldPackedVector, align 1
-void test_field_packed_vector() {
- FieldPackedVector fpv;
- fpv.v = (v4f32){0.0f, 0.0f, 0.0f, 0.0f};
-}
>From 6c15a3e475d5d6b00ee383afee126e40e62d7015 Mon Sep 17 00:00:00 2001
From: Ammarguellat <zahira.ammarguellat at intel.com>
Date: Thu, 16 Jul 2026 08:34:33 -0700
Subject: [PATCH 07/12] Fixed format
---
clang/lib/AST/RecordLayoutBuilder.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang/lib/AST/RecordLayoutBuilder.cpp b/clang/lib/AST/RecordLayoutBuilder.cpp
index 58ce1353de4ea..e136495ea83e6 100644
--- a/clang/lib/AST/RecordLayoutBuilder.cpp
+++ b/clang/lib/AST/RecordLayoutBuilder.cpp
@@ -583,7 +583,7 @@ static bool CheckTypeForRequiredVectorAlignment(const ASTContext &Context,
/// requiring alignment preservation on Windows. This includes direct vectors,
/// arrays of vectors, and structs containing vectors.
static bool RequiresVectorAlignment(const ASTContext &Context, QualType Ty) {
- if (!Context.getTargetInfo().getTriple().isOSWindows() ||
+ if (!Context.getTargetInfo().getTriple().isOSWindows() ||
!Context.getTargetInfo().getTriple().isWindowsMSVCEnvironment())
return false;
return CheckTypeForRequiredVectorAlignment(Context, Ty);
>From 6eb05cc32ec760896e87d7b595d95a7733e3cbbb Mon Sep 17 00:00:00 2001
From: Ammarguellat <zahira.ammarguellat at intel.com>
Date: Thu, 23 Jul 2026 10:41:45 -0700
Subject: [PATCH 08/12] Addressed review comments
---
clang/include/clang/AST/ASTContext.h | 5 ++
clang/lib/AST/ASTContext.cpp | 26 +++++++++++
clang/lib/AST/RecordLayoutBuilder.cpp | 66 +++++++++------------------
3 files changed, 53 insertions(+), 44 deletions(-)
diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h
index c53dd99df9cba..4f1fd163f6f38 100644
--- a/clang/include/clang/AST/ASTContext.h
+++ b/clang/include/clang/AST/ASTContext.h
@@ -2761,6 +2761,11 @@ class ASTContext : public RefCountedBase<ASTContext> {
TypeInfo getTypeInfo(const Type *T) const;
TypeInfo getTypeInfo(QualType T) const { return getTypeInfo(T.getTypePtr()); }
+ /// Check if a type requires natural alignment preservation under #pragma pack
+ /// (but not explicit __attribute__((packed))). This includes x86_fp80 on
+ /// Windows MSVC and standard SIMD vectors (__m128, __m256).
+ bool typeRequiresPreserveAlignUnderPragmaPack(QualType T) const;
+
/// Get default simd alignment of the specified complete type in bits.
unsigned getOpenMPDefaultSimdAlign(QualType T) const;
diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index 7d345e836b5f8..6bcc7bda583a8 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -2100,6 +2100,32 @@ TypeInfo ASTContext::getTypeInfo(const Type *T) const {
return TI;
}
+bool ASTContext::typeRequiresPreserveAlignUnderPragmaPack(QualType T) const {
+ T = T.getCanonicalType();
+ const llvm::Triple &Triple = Target->getTriple();
+ if (Triple.isOSWindows() && Triple.isWindowsMSVCEnvironment()) {
+ if (const auto *BT = T->getAs<BuiltinType>()) {
+ if (BT->getKind() == BuiltinType::LongDouble &&
+ &Target->getLongDoubleFormat() == &llvm::APFloat::x87DoubleExtended())
+ return true;
+ }
+ if (const auto *VT = T->getAs<VectorType>()) {
+ uint64_t VecWidth = getTypeSize(VT);
+ return VT->getVectorKind() == VectorKind::Generic &&
+ (VecWidth == 128 || VecWidth == 256) &&
+ VecWidth == getTypeAlign(VT);
+ }
+ }
+ if (const auto *AT = T->getAsArrayTypeUnsafe())
+ return typeRequiresPreserveAlignUnderPragmaPack(AT->getElementType());
+ if (const auto *RT = T->getAs<RecordType>()) {
+ for (const auto *Field : RT->getDecl()->fields())
+ if (typeRequiresPreserveAlignUnderPragmaPack(Field->getType()))
+ return true;
+ }
+ return false;
+}
+
/// getTypeInfoImpl - Return the size of the specified type, in bits. This
/// method does not work on incomplete types.
///
diff --git a/clang/lib/AST/RecordLayoutBuilder.cpp b/clang/lib/AST/RecordLayoutBuilder.cpp
index e136495ea83e6..23754c87faa3d 100644
--- a/clang/lib/AST/RecordLayoutBuilder.cpp
+++ b/clang/lib/AST/RecordLayoutBuilder.cpp
@@ -559,40 +559,10 @@ void EmptySubobjectMap::UpdateEmptyFieldSubobjects(
typedef llvm::SmallPtrSet<const CXXRecordDecl*, 4> ClassSetTy;
-/// Helper for RequiresVectorAlignment - recursively checks types.
-static bool CheckTypeForRequiredVectorAlignment(const ASTContext &Context,
- QualType Ty) {
- if (const auto *VT = Ty->getAs<VectorType>()) {
- uint64_t VecWidth = Context.getTypeSize(VT);
- return VT->getVectorKind() == VectorKind::Generic &&
- (VecWidth == 128 || VecWidth == 256) &&
- VecWidth == Context.getTypeAlign(VT);
- }
- if (const auto *AT = Ty->getAsArrayTypeUnsafe())
- return CheckTypeForRequiredVectorAlignment(Context, AT->getElementType());
-
- if (const auto *RT = Ty->getAs<RecordType>()) {
- for (const auto *Field : RT->getDecl()->fields())
- if (CheckTypeForRequiredVectorAlignment(Context, Field->getType()))
- return true;
- }
- return false;
-}
-
-/// Check if a type (or any type it contains) is a standard SIMD vector
-/// requiring alignment preservation on Windows. This includes direct vectors,
-/// arrays of vectors, and structs containing vectors.
-static bool RequiresVectorAlignment(const ASTContext &Context, QualType Ty) {
- if (!Context.getTargetInfo().getTriple().isOSWindows() ||
- !Context.getTargetInfo().getTriple().isWindowsMSVCEnvironment())
- return false;
- return CheckTypeForRequiredVectorAlignment(Context, Ty);
-}
-
/// Check if we should prevent MaxFieldAlignment from reducing this field's
-/// alignment. Returns true if the field has ABI-required alignment or contains
-/// vectors requiring alignment, UNLESS the struct has explicit packed
-/// attribute.
+/// alignment. Returns true if the field has ABI-required alignment
+/// (e.g., x86_fp80, SIMD vectors on Windows), unless the struct has explicit
+/// __attribute__((packed)).
static bool ShouldPreserveFieldAlignment(const ASTContext &Context,
const FieldDecl *FD,
AlignRequirementKind AlignReq,
@@ -600,20 +570,27 @@ static bool ShouldPreserveFieldAlignment(const ASTContext &Context,
if (AlignReq == AlignRequirementKind::RequiredByABI ||
AlignReq == AlignRequirementKind::RequiredByRecord)
return true;
- if (!StructHasPackedAttr && RequiresVectorAlignment(Context, FD->getType()))
+
+ // Check if type requires alignment preservation under #pragma pack
+ // (but respect explicit __attribute__((packed))).
+ if (!StructHasPackedAttr &&
+ Context.typeRequiresPreserveAlignUnderPragmaPack(FD->getType()))
return true;
return false;
}
-/// Check if a record contains any fields with vectors requiring alignment.
+/// Check if a record contains any fields requiring alignment preservation.
/// Returns false if the record has explicit __attribute__((packed)).
-static bool RecordContainsVectorRequiringAlignment(const ASTContext &Context,
- const RecordDecl *RD) {
+static bool RecordContainsAlignPreservingFields(const ASTContext &Context,
+ const RecordDecl *RD) {
if (RD->hasAttr<PackedAttr>())
return false;
- for (const auto *Field : RD->fields())
- if (RequiresVectorAlignment(Context, Field->getType()))
+ for (const auto *Field : RD->fields()) {
+ TypeInfo TI = Context.getTypeInfo(Field->getType());
+ if (TI.isAlignRequired() ||
+ Context.typeRequiresPreserveAlignUnderPragmaPack(Field->getType()))
return true;
+ }
return false;
}
@@ -3359,12 +3336,13 @@ void MicrosoftRecordLayoutBuilder::finalizeLayout(const RecordDecl *RD) {
if (!RequiredAlignment.isZero()) {
Alignment = std::max(Alignment, RequiredAlignment);
auto RoundingAlignment = Alignment;
- // Check if this struct contains vectors that require ABI alignment before
- // allowing MaxFieldAlignment (from #pragma pack) to reduce the overall
- // struct alignment. However, if the struct has __attribute__((packed)),
- // honor that explicit request even for vectors.
+
+ // Check if this struct contains fields requiring alignment preservation
+ // (x86_fp80, vectors) before allowing MaxFieldAlignment (from #pragma pack)
+ // to reduce the overall struct alignment. However, if the struct has
+ // __attribute__((packed)), honor that explicit request.
if (!MaxFieldAlignment.isZero() &&
- !RecordContainsVectorRequiringAlignment(Context, RD))
+ !RecordContainsAlignPreservingFields(Context, RD))
RoundingAlignment = std::min(RoundingAlignment, MaxFieldAlignment);
RoundingAlignment = std::max(RoundingAlignment, RequiredAlignment);
Size = Size.alignTo(RoundingAlignment);
>From 4f5cd74bff386232db452b96abbee5dca2e17e4f Mon Sep 17 00:00:00 2001
From: Ammarguellat <zahira.ammarguellat at intel.com>
Date: Fri, 24 Jul 2026 06:17:10 -0700
Subject: [PATCH 09/12] Make x86_fp80 abd vector behave the same way
---
clang/lib/AST/ASTContext.cpp | 11 -----------
1 file changed, 11 deletions(-)
diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index 6bcc7bda583a8..b536fcec9110c 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -2374,17 +2374,6 @@ TypeInfo ASTContext::getTypeInfoImpl(const Type *T) const {
Width = Target->getLongDoubleWidth();
Align = Target->getLongDoubleAlign();
}
- // On Windows MSVC targets, x86_fp80 requires 16-byte alignment for ABI
- // correctness (movaps instructions will fault on misaligned addresses).
- // Mark this as an ABI requirement that must not be reduced by #pragma
- // pack. This is MSVC-specific; MinGW has different layout rules. GCC
- // preserves such alignment on other targets, but Clang historically has
- // not; changing this would break existing Clang ABI on non-Windows
- // platforms.
- if (Target->getTriple().isOSWindows() &&
- Target->getTriple().isWindowsMSVCEnvironment() &&
- &Target->getLongDoubleFormat() == &llvm::APFloat::x87DoubleExtended())
- AlignRequirement = AlignRequirementKind::RequiredByABI;
break;
case BuiltinType::Float128:
if (Target->hasFloat128Type() || !getLangOpts().OpenMP ||
>From aab281ef98fe466e847eabd4b10fe8db4c7016cf Mon Sep 17 00:00:00 2001
From: Zahira Ammarguellat <zahira.ammarguellat at intel.com>
Date: Mon, 3 Aug 2026 08:35:16 -0700
Subject: [PATCH 10/12] Addressed review comments
---
clang/lib/AST/ASTContext.cpp | 47 ++++++++-------
clang/lib/AST/RecordLayoutBuilder.cpp | 3 +-
.../vector-alignment-pragma-pack-itanium.cpp | 57 +++++++++++++++++++
.../vector-alignment-pragma-pack-msvc.cpp | 3 +
4 files changed, 88 insertions(+), 22 deletions(-)
create mode 100644 clang/test/CodeGen/vector-alignment-pragma-pack-itanium.cpp
diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index b536fcec9110c..0a1ad470d0c46 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -2101,28 +2101,22 @@ TypeInfo ASTContext::getTypeInfo(const Type *T) const {
}
bool ASTContext::typeRequiresPreserveAlignUnderPragmaPack(QualType T) const {
- T = T.getCanonicalType();
+ // Check if type has ABI-required alignment (e.g., x86_fp80).
+ TypeInfo TI = getTypeInfo(T.getTypePtr());
+ if (TI.AlignRequirement == AlignRequirementKind::RequiredByABI ||
+ TI.AlignRequirement == AlignRequirementKind::RequiredByRecord)
+ return true;
+
+ // For vectors, check directly without setting RequiredByABI in TypeInfo
+ // (to avoid affecting unaligned load/store codegen).
const llvm::Triple &Triple = Target->getTriple();
if (Triple.isOSWindows() && Triple.isWindowsMSVCEnvironment()) {
- if (const auto *BT = T->getAs<BuiltinType>()) {
- if (BT->getKind() == BuiltinType::LongDouble &&
- &Target->getLongDoubleFormat() == &llvm::APFloat::x87DoubleExtended())
- return true;
- }
+ T = T.getCanonicalType();
if (const auto *VT = T->getAs<VectorType>()) {
- uint64_t VecWidth = getTypeSize(VT);
return VT->getVectorKind() == VectorKind::Generic &&
- (VecWidth == 128 || VecWidth == 256) &&
- VecWidth == getTypeAlign(VT);
+ TI.Width == TI.Align;
}
}
- if (const auto *AT = T->getAsArrayTypeUnsafe())
- return typeRequiresPreserveAlignUnderPragmaPack(AT->getElementType());
- if (const auto *RT = T->getAs<RecordType>()) {
- for (const auto *Field : RT->getDecl()->fields())
- if (typeRequiresPreserveAlignUnderPragmaPack(Field->getType()))
- return true;
- }
return false;
}
@@ -2566,17 +2560,17 @@ TypeInfo ASTContext::getTypeInfoImpl(const Type *T) const {
Width = toBits(Layout.getSize());
Align = toBits(Layout.getAlignment());
// Check if the record has an aligned attribute, or if it contains
- // fields with ABI-required alignment (e.g., vectors on MSVC).
+ // fields with ABI-required alignment (e.g., x86_fp80, vectors on MSVC).
if (RD->hasAttr<AlignedAttr>()) {
AlignRequirement = AlignRequirementKind::RequiredByRecord;
} else {
- // Check if any field has RequiredByABI alignment requirement.
- // If so, propagate it to the record.
+ // Check if any field requires alignment preservation.
AlignRequirement = AlignRequirementKind::None;
for (const auto *Field : RD->fields()) {
TypeInfo FI = getTypeInfo(Field->getType().getTypePtr());
if (FI.AlignRequirement == AlignRequirementKind::RequiredByABI ||
- FI.AlignRequirement == AlignRequirementKind::RequiredByRecord) {
+ FI.AlignRequirement == AlignRequirementKind::RequiredByRecord ||
+ typeRequiresPreserveAlignUnderPragmaPack(Field->getType())) {
AlignRequirement = AlignRequirementKind::RequiredByABI;
break;
}
@@ -2696,6 +2690,19 @@ TypeInfo ASTContext::getTypeInfoImpl(const Type *T) const {
break;
}
+ // Mark types that require ABI-correct alignment preservation under pragma pack.
+ if (AlignRequirement == AlignRequirementKind::None) {
+ const llvm::Triple &Triple = Target->getTriple();
+ if (Triple.isOSWindows() && Triple.isWindowsMSVCEnvironment()) {
+ // x86_fp80 requires 16-byte alignment for correctness with movaps.
+ if (const auto *BT = T->getAs<BuiltinType>()) {
+ if (BT->getKind() == BuiltinType::LongDouble &&
+ &Target->getLongDoubleFormat() == &llvm::APFloat::x87DoubleExtended())
+ AlignRequirement = AlignRequirementKind::RequiredByABI;
+ }
+ }
+ }
+
assert(llvm::isPowerOf2_32(Align) && "Alignment must be power of 2");
return TypeInfo(Width, Align, AlignRequirement);
}
diff --git a/clang/lib/AST/RecordLayoutBuilder.cpp b/clang/lib/AST/RecordLayoutBuilder.cpp
index 23754c87faa3d..dd8a019bd7b3b 100644
--- a/clang/lib/AST/RecordLayoutBuilder.cpp
+++ b/clang/lib/AST/RecordLayoutBuilder.cpp
@@ -2066,8 +2066,7 @@ void ItaniumRecordLayoutBuilder::LayoutField(const FieldDecl *D,
// The maximum field alignment overrides the aligned attribute.
// However, do not reduce alignment for ABI-required alignments (e.g.,
// x86_fp80, vector types) which must be preserved for correctness.
- // On Windows, check if the field type is a vector with standard SIMD
- // alignment (16 or 32 bytes with size == alignment) - these need their
+ // On Windows MSVC, vectors where size equals alignment need their natural
// alignment preserved under #pragma pack. However, honor explicit
// __attribute__((packed)) on the struct (Packed=true means the struct
// has the packed attribute, not the field).
diff --git a/clang/test/CodeGen/vector-alignment-pragma-pack-itanium.cpp b/clang/test/CodeGen/vector-alignment-pragma-pack-itanium.cpp
new file mode 100644
index 0000000000000..1b3d943f7ea9e
--- /dev/null
+++ b/clang/test/CodeGen/vector-alignment-pragma-pack-itanium.cpp
@@ -0,0 +1,57 @@
+// RUN: %clang_cc1 -triple x86_64-windows-gnu -emit-llvm -o - %s \
+// RUN: | FileCheck %s --check-prefix=CHECK-GNU
+
+// RUN: %clang_cc1 -triple x86_64-linux-gnu -emit-llvm -o - %s \
+// RUN: | FileCheck %s --check-prefix=CHECK-LINUX
+
+// Test that RequiredByABI alignment works on Itanium ABI (windows-gnu) targets.
+
+typedef float v4f32 __attribute__((vector_size(16)));
+typedef float v8f32 __attribute__((vector_size(32)));
+
+// Without pragma pack, vectors should have natural alignment on all platforms.
+
+struct VectorStruct {
+ v4f32 v;
+};
+
+// CHECK-GNU-LABEL: define {{.*}} @{{.*}}test_vector_struct
+// CHECK-GNU: %v = alloca %struct.VectorStruct, align 16
+// CHECK-LINUX-LABEL: define {{.*}} @{{.*}}test_vector_struct
+// CHECK-LINUX: %v = alloca %struct.VectorStruct, align 16
+void test_vector_struct() {
+ VectorStruct v;
+ v.v = (v4f32){0.0f, 0.0f, 0.0f, 0.0f};
+}
+
+#pragma pack(push, 8)
+struct PackedVectorStruct {
+ v4f32 v;
+};
+
+struct PackedLargeVectorStruct {
+ v8f32 v;
+};
+#pragma pack(pop)
+
+// On windows-gnu and Linux, #pragma pack applies normally (no special
+// preservation), so vectors in packed structs get reduced alignment.
+// This differs from windows-msvc where we preserve vector alignment.
+
+// CHECK-GNU-LABEL: define {{.*}} @{{.*}}test_packed_vector_struct
+// CHECK-GNU: %v = alloca %struct.PackedVectorStruct, align 8
+// CHECK-LINUX-LABEL: define {{.*}} @{{.*}}test_packed_vector_struct
+// CHECK-LINUX: %v = alloca %struct.PackedVectorStruct, align 8
+void test_packed_vector_struct() {
+ PackedVectorStruct v;
+ v.v = (v4f32){0.0f, 0.0f, 0.0f, 0.0f};
+}
+
+// CHECK-GNU-LABEL: define {{.*}} @{{.*}}test_packed_large_vector_struct
+// CHECK-GNU: %v = alloca %struct.PackedLargeVectorStruct, align 8
+// CHECK-LINUX-LABEL: define {{.*}} @{{.*}}test_packed_large_vector_struct
+// CHECK-LINUX: %v = alloca %struct.PackedLargeVectorStruct, align 8
+void test_packed_large_vector_struct() {
+ PackedLargeVectorStruct v;
+ v.v = (v8f32){0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f};
+}
diff --git a/clang/test/CodeGen/vector-alignment-pragma-pack-msvc.cpp b/clang/test/CodeGen/vector-alignment-pragma-pack-msvc.cpp
index d185236f2a500..c6188f656a0eb 100644
--- a/clang/test/CodeGen/vector-alignment-pragma-pack-msvc.cpp
+++ b/clang/test/CodeGen/vector-alignment-pragma-pack-msvc.cpp
@@ -1,6 +1,9 @@
// RUN: %clang_cc1 -triple x86_64-pc-windows-msvc -emit-llvm -o - %s \
// RUN: | FileCheck %s
+// RUN: %clang_cc1 -triple x86_64-pc-windows-msvc -mlong-double-80 \
+// RUN: -emit-llvm -o - %s | FileCheck %s --check-prefix=CHECK-FP80
+
// Test that vector types maintain their required alignment for correctness
// (movaps/movapd instructions will fault on misaligned addresses), even when
// #pragma pack(8) would normally reduce it. This issue affects Windows MSVC
>From d316af3be6988611dc74c1063cd3b7e89486da72 Mon Sep 17 00:00:00 2001
From: Zahira Ammarguellat <zahira.ammarguellat at intel.com>
Date: Mon, 3 Aug 2026 09:30:58 -0700
Subject: [PATCH 11/12] Fix format
---
clang/lib/AST/ASTContext.cpp | 9 +++++----
1 file changed, 5 insertions(+), 4 deletions(-)
diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index 0a1ad470d0c46..f162eda82ea84 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -2113,8 +2113,7 @@ bool ASTContext::typeRequiresPreserveAlignUnderPragmaPack(QualType T) const {
if (Triple.isOSWindows() && Triple.isWindowsMSVCEnvironment()) {
T = T.getCanonicalType();
if (const auto *VT = T->getAs<VectorType>()) {
- return VT->getVectorKind() == VectorKind::Generic &&
- TI.Width == TI.Align;
+ return VT->getVectorKind() == VectorKind::Generic && TI.Width == TI.Align;
}
}
return false;
@@ -2690,14 +2689,16 @@ TypeInfo ASTContext::getTypeInfoImpl(const Type *T) const {
break;
}
- // Mark types that require ABI-correct alignment preservation under pragma pack.
+ // Mark types that require ABI-correct alignment preservation under pragma
+ // pack.
if (AlignRequirement == AlignRequirementKind::None) {
const llvm::Triple &Triple = Target->getTriple();
if (Triple.isOSWindows() && Triple.isWindowsMSVCEnvironment()) {
// x86_fp80 requires 16-byte alignment for correctness with movaps.
if (const auto *BT = T->getAs<BuiltinType>()) {
if (BT->getKind() == BuiltinType::LongDouble &&
- &Target->getLongDoubleFormat() == &llvm::APFloat::x87DoubleExtended())
+ &Target->getLongDoubleFormat() ==
+ &llvm::APFloat::x87DoubleExtended())
AlignRequirement = AlignRequirementKind::RequiredByABI;
}
}
>From 1bdd1fb7309c9e3b52d456c17c140ec0d8355191 Mon Sep 17 00:00:00 2001
From: Zahira Ammarguellat <zahira.ammarguellat at intel.com>
Date: Tue, 4 Aug 2026 05:52:42 -0700
Subject: [PATCH 12/12] Added tests for nested fields
---
.../vector-alignment-pragma-pack-itanium.cpp | 55 +++++++++++++++++++
1 file changed, 55 insertions(+)
diff --git a/clang/test/CodeGen/vector-alignment-pragma-pack-itanium.cpp b/clang/test/CodeGen/vector-alignment-pragma-pack-itanium.cpp
index 1b3d943f7ea9e..2f314af94fdd1 100644
--- a/clang/test/CodeGen/vector-alignment-pragma-pack-itanium.cpp
+++ b/clang/test/CodeGen/vector-alignment-pragma-pack-itanium.cpp
@@ -4,6 +4,9 @@
// RUN: %clang_cc1 -triple x86_64-linux-gnu -emit-llvm -o - %s \
// RUN: | FileCheck %s --check-prefix=CHECK-LINUX
+// RUN: %clang_cc1 -triple x86_64-windows-gnu -mlong-double-80 -emit-llvm -o - %s \
+// RUN: | FileCheck %s --check-prefix=CHECK-GNU-FP80
+
// Test that RequiredByABI alignment works on Itanium ABI (windows-gnu) targets.
typedef float v4f32 __attribute__((vector_size(16)));
@@ -55,3 +58,55 @@ void test_packed_large_vector_struct() {
PackedLargeVectorStruct v;
v.v = (v8f32){0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f};
}
+
+// Test nested structs with vectors under pragma pack.
+
+#pragma pack(push, 8)
+struct InnerWithVector {
+ v4f32 v;
+};
+
+struct OuterWithVector {
+ InnerWithVector inner;
+};
+#pragma pack(pop)
+
+// On non-MSVC targets, nested vectors also get reduced alignment under pragma
+// pack.
+
+// CHECK-GNU-LABEL: define {{.*}} @{{.*}}test_nested_vector_struct
+// CHECK-GNU: %outer = alloca %struct.OuterWithVector, align 8
+// CHECK-LINUX-LABEL: define {{.*}} @{{.*}}test_nested_vector_struct
+// CHECK-LINUX: %outer = alloca %struct.OuterWithVector, align 8
+void test_nested_vector_struct() {
+ OuterWithVector outer;
+ outer.inner.v = (v4f32){0.0f, 0.0f, 0.0f, 0.0f};
+}
+
+// Test x86_fp80 (long double with -mlong-double-80) under pragma pack.
+// On windows-gnu, RequiredByABI only applies to MSVC environment, so
+// pragma pack reduces alignment normally.
+
+#pragma pack(push, 8)
+struct LongDoubleStruct {
+ long double x;
+};
+
+struct NestedLongDoubleStruct {
+ LongDoubleStruct inner;
+};
+#pragma pack(pop)
+
+// CHECK-GNU-FP80-LABEL: define {{.*}} @{{.*}}test_long_double_struct
+// CHECK-GNU-FP80: %s = alloca %struct.LongDoubleStruct, align 8
+void test_long_double_struct() {
+ LongDoubleStruct s;
+ s.x = 0.0L;
+}
+
+// CHECK-GNU-FP80-LABEL: define {{.*}} @{{.*}}test_nested_long_double_struct
+// CHECK-GNU-FP80: %outer = alloca %struct.NestedLongDoubleStruct, align 8
+void test_nested_long_double_struct() {
+ NestedLongDoubleStruct outer;
+ outer.inner.x = 0.0L;
+}
More information about the cfe-commits
mailing list