llvmorg-github-actions[bot] wrote:

<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-backend-x86

Author: Reid Kleckner (rnk)

<details>
<summary>Changes</summary>

This is mostly the result of reviewing #<!-- -->218017 and prompting an agent 
to go and do some more ABI edge-case testing with godbolt. It worked! It was 
able to call the compiler-explorer HTTP endpoint and capture the output of 
various GCC and MSVC versions for ABI analysis.

I was surprised that our logic is mostly already correct. I was surprised that 
GCC specifically cares about vector types, and it doesn't seem to care about 
overaligned struct types. It also only seems to recurse into structs in the 
variadic argument case. Regardless, the old code structure really prompted me 
to ask if we were covering all cases correctly, and I hope the new structure is 
clearer, even if it takes more code.

Restructure the x86_32 OS ABI dispatch logic into 3 buckets:

- Linux / mingw first: These rules match GCC, and apply to platforms where it 
is the dominant system compiler defining the ABI
- MSVC Windows second: Match MSVC there
- SysV, Darwin, BSD, everything else: These platforms prioritize ABI stability 
for legacy platforms, so we typically use 4 byte alignment, and 16 for SSE 
vectors in some cases, but in general, "no change" is best for legacy 
unsupported platforms.

Add getX86VectorRegisterVarArgAlign(), replacing the old "Ty-&gt;isVectorType() 
&amp;&amp; Align in {16, 32, 64}" check with one that also covers __float128 
and, for variadic arguments only. This helper recurses into struct/class fields 
(including array element types) and bases, returning the natural alignment of 
the widest vector-register type found. It checks the type's own fixed size 
rather than its (possibly attribute-inflated) alignment, and deliberately 
ignores a record's own forced over-alignment in favor of its widest member's 
natural one. This matches how modern versions of GCC handle alignment, as 
confirmed with experiments on godbolt.

getTypeStackAlignInBytes() needs an IsVarArg parameter, since GCC testing 
showed that recursing into record types to find vectors only applies to 
variadic arguments.

Agent-assisted

---

Patch is 28.49 KiB, truncated to 20.00 KiB below, full version: 
https://github.com/llvm/llvm-project/pull/219015.diff


4 Files Affected:

- (modified) clang/lib/CodeGen/Targets/X86.cpp (+81-15) 
- (added) clang/test/CodeGen/X86/x86_32-vaarg-msvc.c (+34) 
- (modified) clang/test/CodeGen/X86/x86_32-vaarg.c (+302-3) 
- (modified) clang/test/CodeGenCXX/x86_32-vaarg.cpp (+30-1) 


``````````diff
diff --git a/clang/lib/CodeGen/Targets/X86.cpp 
b/clang/lib/CodeGen/Targets/X86.cpp
index 1dc3bd0740baa..988b8043b7514 100644
--- a/clang/lib/CodeGen/Targets/X86.cpp
+++ b/clang/lib/CodeGen/Targets/X86.cpp
@@ -85,6 +85,56 @@ static ABIArgInfo getDirectX86Hva(llvm::Type* T = nullptr) {
 // X86-32 ABI Implementation
 
//===----------------------------------------------------------------------===//
 
+/// Returns the natural alignment (16, 32, or 64) of the X86 vector-register
+/// type \p Ty is, or (if CheckRecordFields) recursively contains; else 0.
+/// Matches GCC's varargs realignment rule on Linux/mingw, which applies only
+/// to vector/__float128 types, using their own fixed size rather than any
+/// attribute-inflated alignment (so an over-aligned typedef of a 16-byte
+/// vector still only gets 16-byte realignment, matching GCC).
+static unsigned getX86VectorRegisterVarArgAlign(ASTContext &Context,
+                                                QualType Ty,
+                                                bool CheckRecordFields) {
+  if (Ty->isFloat128Type())
+    return 16;
+
+  if (Ty->isVectorType()) {
+    uint64_t Bytes = Context.getTypeSizeInChars(Ty).getQuantity();
+    return (Bytes == 16 || Bytes == 32 || Bytes == 64) ? Bytes : 0;
+  }
+
+  // Only variadic arguments recurse into struct/class fields and bases: GCC
+  // does not extend this realignment to byval parameters of the same
+  // type (see the "misaligns parameters on stack" comment on
+  // CodeGen/X86/x86_32-arguments-linux.c).
+  if (!CheckRecordFields)
+    return 0;
+
+  if (Ty->isArrayType())
+    return getX86VectorRegisterVarArgAlign(
+        Context, Context.getBaseElementType(Ty), CheckRecordFields);
+
+  const auto *RD = Ty->getAsRecordDecl();
+  if (!RD)
+    return 0;
+
+  // Use the widest member's own alignment, not the record's (which may be
+  // further over-aligned by attribute); GCC's handling of that case is
+  // itself version-dependent, so we don't chase it here.
+  unsigned Result = 0;
+  if (const auto *CXXRD = dyn_cast<CXXRecordDecl>(RD))
+    for (const auto &Base : CXXRD->bases())
+      Result =
+          std::max(Result, getX86VectorRegisterVarArgAlign(
+                               Context, Base.getType(), CheckRecordFields));
+
+  for (const auto *Field : RD->fields())
+    Result =
+        std::max(Result, getX86VectorRegisterVarArgAlign(
+                             Context, Field->getType(), CheckRecordFields));
+
+  return Result;
+}
+
 /// Similar to llvm::CCState, but for Clang.
 struct CCState {
   CCState(CGFunctionInfo &FI)
@@ -139,8 +189,11 @@ class X86_32ABIInfo : public ABIInfo {
 
   ABIArgInfo getIndirectReturnResult(QualType Ty, CCState &State) const;
 
-  /// Return the alignment to use for the given type on the stack.
-  unsigned getTypeStackAlignInBytes(QualType Ty, unsigned Align) const;
+  /// Return the alignment to use for the given type on the stack. \p
+  /// IsVarArg distinguishes a "..." (va_arg) argument from an ordinary
+  /// (possibly byval) parameter; see getX86VectorRegisterVarArgAlign().
+  unsigned getTypeStackAlignInBytes(QualType Ty, unsigned Align,
+                                    bool IsVarArg) const;
 
   Class classify(QualType Ty) const;
   ABIArgInfo classifyReturnType(QualType RetTy, CCState &State) const;
@@ -572,24 +625,36 @@ ABIArgInfo X86_32ABIInfo::classifyReturnType(QualType 
RetTy,
                                                : ABIArgInfo::getDirect());
 }
 
-unsigned X86_32ABIInfo::getTypeStackAlignInBytes(QualType Ty,
-                                                 unsigned Align) const {
+unsigned X86_32ABIInfo::getTypeStackAlignInBytes(QualType Ty, unsigned Align,
+                                                 bool IsVarArg) const {
   // Otherwise, if the alignment is less than or equal to the minimum ABI
   // alignment, just use the default; the backend will handle this.
   if (Align <= MinABIStackAlignInBytes)
     return 0; // Use default alignment.
 
+  // Linux and the GNU-flavored Windows targets (MinGW, Cygwin) are the
+  // platforms where GCC, not Clang, defines this part of the ABI, so match
+  // it: realign "..." arguments to their natural alignment, but only for
+  // vector-register types (see getX86VectorRegisterVarArgAlign()).
+  if (IsLinuxABI) {
+    if (unsigned VecAlign = getX86VectorRegisterVarArgAlign(
+            getContext(), Ty, /*CheckRecordFields=*/IsVarArg))
+      return VecAlign;
+  }
+
+  // Real (non-GNU) Windows never honors over-alignment for "..." arguments,
+  // for any type: MSVC packs them all at the platform default (4 bytes),
+  // unlike a named parameter of the same type, which it passes indirectly
+  // by pointer once its alignment exceeds 4 bytes.
+  if (IsWin32StructABI)
+    return MinABIStackAlignInBytes;
+
+  // FreeBSD/NetBSD/OpenBSD/PS4 and Darwin keep their own narrower,
+  // long-standing behavior below: Clang isn't tracking a reference
+  // compiler's ABI for these, so don't widen what gets realigned here.
   if (Ty->isFloat128Type())
     return 16;
 
-  if (IsLinuxABI) {
-    // Exclude other System V OS (e.g Darwin, PS4 and FreeBSD) since we don't
-    // want to spend any effort dealing with the ramifications of ABI breaks.
-    //
-    // If the vector type is __m128/__m256/__m512, return the default 
alignment.
-    if (Ty->isVectorType() && (Align == 16 || Align == 32 || Align == 64))
-      return Align;
-  }
   // On non-Darwin, the stack type alignment is always 4.
   if (!IsDarwinVectorABI) {
     // Set explicit alignment, since we may need to realign the top.
@@ -618,7 +683,8 @@ ABIArgInfo X86_32ABIInfo::getIndirectResult(QualType Ty, 
bool ByVal,
 
   // Compute the byval alignment.
   unsigned TypeAlign = getContext().getTypeAlign(Ty) / 8;
-  unsigned StackAlign = getTypeStackAlignInBytes(Ty, TypeAlign);
+  unsigned StackAlign =
+      getTypeStackAlignInBytes(Ty, TypeAlign, /*IsVarArg=*/false);
   if (StackAlign == 0)
     return ABIArgInfo::getIndirect(
         CharUnits::fromQuantity(4),
@@ -1092,8 +1158,8 @@ RValue X86_32ABIInfo::EmitVAArg(CodeGenFunction &CGF, 
Address VAListAddr,
   //
   // Just messing with TypeInfo like this works because we never pass
   // anything indirectly.
-  TypeInfo.Align = CharUnits::fromQuantity(
-                getTypeStackAlignInBytes(Ty, TypeInfo.Align.getQuantity()));
+  TypeInfo.Align = CharUnits::fromQuantity(getTypeStackAlignInBytes(
+      Ty, TypeInfo.Align.getQuantity(), /*IsVarArg=*/true));
 
   return emitVoidPtrVAArg(CGF, VAListAddr, Ty, /*Indirect*/ false, TypeInfo,
                           CharUnits::fromQuantity(4),
diff --git a/clang/test/CodeGen/X86/x86_32-vaarg-msvc.c 
b/clang/test/CodeGen/X86/x86_32-vaarg-msvc.c
new file mode 100644
index 0000000000000..e7dfafc78d716
--- /dev/null
+++ b/clang/test/CodeGen/X86/x86_32-vaarg-msvc.c
@@ -0,0 +1,34 @@
+// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py 
UTC_ARGS: --version 6
+// RUN: %clang_cc1 -triple i386-pc-windows-msvc -fms-compatibility -emit-llvm 
-o - %s | FileCheck %s
+
+// Unlike the GNU/SysV-flavored x86-32 targets (see x86_32-vaarg.c), the real
+// (non-GNU) Windows ABI never realigns "..." arguments to their natural
+// alignment: va_arg reads a vector argument with an unaligned (align 4)
+// load, matching MSVC, which packs variadic arguments at the platform
+// default alignment instead of honoring over-alignment through varargs.
+typedef float __v4sf __attribute__((__vector_size__(16)));
+
+// CHECK-LABEL: define dso_local <4 x float> @vec_test(
+// CHECK-SAME: i32 noundef [[Z:%.*]], ...) #[[ATTR0:[0-9]+]] {
+// CHECK-NEXT:  [[ENTRY:.*:]]
+// CHECK-NEXT:    [[Z_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    [[LIST:%.*]] = alloca ptr, align 4
+// CHECK-NEXT:    [[X:%.*]] = alloca <4 x float>, align 16
+// CHECK-NEXT:    store i32 [[Z]], ptr [[Z_ADDR]], align 4
+// CHECK-NEXT:    call void @llvm.va_start.p0(ptr [[LIST]])
+// CHECK-NEXT:    [[ARGP_CUR:%.*]] = load ptr, ptr [[LIST]], align 4
+// CHECK-NEXT:    [[ARGP_NEXT:%.*]] = getelementptr inbounds i8, ptr 
[[ARGP_CUR]], i32 16
+// CHECK-NEXT:    store ptr [[ARGP_NEXT]], ptr [[LIST]], align 4
+// CHECK-NEXT:    [[TMP0:%.*]] = load <4 x float>, ptr [[ARGP_CUR]], align 4
+// CHECK-NEXT:    store <4 x float> [[TMP0]], ptr [[X]], align 16
+// CHECK-NEXT:    call void @llvm.va_end.p0(ptr [[LIST]])
+// CHECK-NEXT:    [[TMP1:%.*]] = load <4 x float>, ptr [[X]], align 16
+// CHECK-NEXT:    ret <4 x float> [[TMP1]]
+//
+__v4sf vec_test(int z, ...) {
+  __builtin_va_list list;
+  __builtin_va_start(list, z);
+  __v4sf x = __builtin_va_arg(list, __v4sf);
+  __builtin_va_end(list);
+  return x;
+}
diff --git a/clang/test/CodeGen/X86/x86_32-vaarg.c 
b/clang/test/CodeGen/X86/x86_32-vaarg.c
index 3fa90c0420cad..7aac1bacacb09 100644
--- a/clang/test/CodeGen/X86/x86_32-vaarg.c
+++ b/clang/test/CodeGen/X86/x86_32-vaarg.c
@@ -1,7 +1,7 @@
 // NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py 
UTC_ARGS: --version 6
-// RUN: %clang_cc1 -triple i386-unknown-linux-gnu -emit-llvm -o - %s | 
FileCheck %s
-// RUN: %clang_cc1 -triple i386-unknown-freebsd -emit-llvm -o - %s | FileCheck 
%s
-// RUN: %clang_cc1 -triple i686-windows-gnu -emit-llvm -o - %s | FileCheck %s
+// RUN: %clang_cc1 -triple i386-unknown-linux-gnu -emit-llvm -o - %s | 
FileCheck %s --check-prefixes=CHECK,LINUXABI
+// RUN: %clang_cc1 -triple i386-unknown-freebsd -emit-llvm -o - %s | FileCheck 
%s --check-prefixes=CHECK,SYSV
+// RUN: %clang_cc1 -triple i686-windows-gnu -emit-llvm -o - %s | FileCheck %s 
--check-prefixes=CHECK,LINUXABI
 
 // CHECK-LABEL: define dso_local fp128 @f128_test(
 // CHECK-SAME: i32 noundef [[Z:%.*]], ...) #[[ATTR0:[0-9]+]] {
@@ -104,3 +104,302 @@ int int_test(int z, ...) {
   __builtin_va_end(list);
   return x;
 }
+
+// A struct over-aligned only via an explicit attribute, with no vector
+// register type involved, is NOT realigned on any of these platforms:
+// GCC doesn't realign it either (contrast vector_record_test below).
+typedef struct __attribute__((aligned(16))) { long a, b; } AlignedStruct;
+
+// CHECK-LABEL: define dso_local void @aligned_struct_test(
+// CHECK-SAME: ptr dead_on_unwind noalias writable 
sret([[STRUCT_ALIGNEDSTRUCT:%.*]]) align 16 [[AGG_RESULT:%.*]], i32 noundef 
[[Z:%.*]], ...) #[[ATTR0]] {
+// CHECK-NEXT:  [[ENTRY:.*:]]
+// CHECK-NEXT:    [[RESULT_PTR:%.*]] = alloca ptr, align 4
+// CHECK-NEXT:    [[Z_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    [[LIST:%.*]] = alloca ptr, align 4
+// CHECK-NEXT:    store ptr [[AGG_RESULT]], ptr [[RESULT_PTR]], align 4
+// CHECK-NEXT:    store i32 [[Z]], ptr [[Z_ADDR]], align 4
+// CHECK-NEXT:    call void @llvm.va_start.p0(ptr [[LIST]])
+// CHECK-NEXT:    [[ARGP_CUR:%.*]] = load ptr, ptr [[LIST]], align 4
+// CHECK-NEXT:    [[ARGP_NEXT:%.*]] = getelementptr inbounds i8, ptr 
[[ARGP_CUR]], i32 16
+// CHECK-NEXT:    store ptr [[ARGP_NEXT]], ptr [[LIST]], align 4
+// CHECK-NEXT:    call void @llvm.memcpy.p0.p0.i32(ptr align 16 
[[AGG_RESULT]], ptr align 4 [[ARGP_CUR]], i32 16, i1 false)
+// CHECK-NEXT:    call void @llvm.va_end.p0(ptr [[LIST]])
+// CHECK-NEXT:    ret void
+//
+AlignedStruct aligned_struct_test(int z, ...) {
+  __builtin_va_list list;
+  __builtin_va_start(list, z);
+  AlignedStruct x = __builtin_va_arg(list, AlignedStruct);
+  __builtin_va_end(list);
+  return x;
+}
+
+// A struct containing a vector member IS realigned on Linux/MinGW/Cygwin,
+// matching real GCC, but not on other System V platforms such as FreeBSD,
+// where Clang doesn't track a reference compiler for this part of the ABI.
+typedef float __v4sf __attribute__((__vector_size__(16)));
+typedef struct { int tag; __v4sf v; } VectorRecord;
+
+// LINUXABI-LABEL: define dso_local void @vector_record_test(
+// LINUXABI-SAME: ptr dead_on_unwind noalias writable 
sret([[STRUCT_VECTORRECORD:%.*]]) align 16 [[AGG_RESULT:%.*]], i32 noundef 
[[Z:%.*]], ...) #[[ATTR0]] {
+// LINUXABI-NEXT:  [[ENTRY:.*:]]
+// LINUXABI-NEXT:    [[RESULT_PTR:%.*]] = alloca ptr, align 4
+// LINUXABI-NEXT:    [[Z_ADDR:%.*]] = alloca i32, align 4
+// LINUXABI-NEXT:    [[LIST:%.*]] = alloca ptr, align 4
+// LINUXABI-NEXT:    store ptr [[AGG_RESULT]], ptr [[RESULT_PTR]], align 4
+// LINUXABI-NEXT:    store i32 [[Z]], ptr [[Z_ADDR]], align 4
+// LINUXABI-NEXT:    call void @llvm.va_start.p0(ptr [[LIST]])
+// LINUXABI-NEXT:    [[ARGP_CUR:%.*]] = load ptr, ptr [[LIST]], align 4
+// LINUXABI-NEXT:    [[TMP0:%.*]] = getelementptr inbounds i8, ptr 
[[ARGP_CUR]], i32 15
+// LINUXABI-NEXT:    [[ARGP_CUR_ALIGNED:%.*]] = call ptr 
@llvm.ptrmask.p0.i32(ptr [[TMP0]], i32 -16)
+// LINUXABI-NEXT:    [[ARGP_NEXT:%.*]] = getelementptr inbounds i8, ptr 
[[ARGP_CUR_ALIGNED]], i32 32
+// LINUXABI-NEXT:    store ptr [[ARGP_NEXT]], ptr [[LIST]], align 4
+// LINUXABI-NEXT:    call void @llvm.memcpy.p0.p0.i32(ptr align 16 
[[AGG_RESULT]], ptr align 16 [[ARGP_CUR_ALIGNED]], i32 32, i1 false)
+// LINUXABI-NEXT:    call void @llvm.va_end.p0(ptr [[LIST]])
+// LINUXABI-NEXT:    ret void
+//
+// SYSV-LABEL: define dso_local void @vector_record_test(
+// SYSV-SAME: ptr dead_on_unwind noalias writable 
sret([[STRUCT_VECTORRECORD:%.*]]) align 16 [[AGG_RESULT:%.*]], i32 noundef 
[[Z:%.*]], ...) #[[ATTR0]] {
+// SYSV-NEXT:  [[ENTRY:.*:]]
+// SYSV-NEXT:    [[RESULT_PTR:%.*]] = alloca ptr, align 4
+// SYSV-NEXT:    [[Z_ADDR:%.*]] = alloca i32, align 4
+// SYSV-NEXT:    [[LIST:%.*]] = alloca ptr, align 4
+// SYSV-NEXT:    store ptr [[AGG_RESULT]], ptr [[RESULT_PTR]], align 4
+// SYSV-NEXT:    store i32 [[Z]], ptr [[Z_ADDR]], align 4
+// SYSV-NEXT:    call void @llvm.va_start.p0(ptr [[LIST]])
+// SYSV-NEXT:    [[ARGP_CUR:%.*]] = load ptr, ptr [[LIST]], align 4
+// SYSV-NEXT:    [[ARGP_NEXT:%.*]] = getelementptr inbounds i8, ptr 
[[ARGP_CUR]], i32 32
+// SYSV-NEXT:    store ptr [[ARGP_NEXT]], ptr [[LIST]], align 4
+// SYSV-NEXT:    call void @llvm.memcpy.p0.p0.i32(ptr align 16 [[AGG_RESULT]], 
ptr align 4 [[ARGP_CUR]], i32 32, i1 false)
+// SYSV-NEXT:    call void @llvm.va_end.p0(ptr [[LIST]])
+// SYSV-NEXT:    ret void
+//
+VectorRecord vector_record_test(int z, ...) {
+  __builtin_va_list list;
+  __builtin_va_start(list, z);
+  VectorRecord x = __builtin_va_arg(list, VectorRecord);
+  __builtin_va_end(list);
+  return x;
+}
+
+// A vector further over-aligned via a typedef attribute is realigned to
+// its own natural (16-byte) alignment, NOT the attribute-forced 32: GCC
+// ignores the extra attribute alignment here too (see the ptrmask below).
+typedef __v4sf OveralignedVector __attribute__((aligned(32)));
+
+// LINUXABI-LABEL: define dso_local <4 x float> @overaligned_vector_test(
+// LINUXABI-SAME: i32 noundef [[Z:%.*]], ...) #[[ATTR4:[0-9]+]] {
+// LINUXABI-NEXT:  [[ENTRY:.*:]]
+// LINUXABI-NEXT:    [[Z_ADDR:%.*]] = alloca i32, align 4
+// LINUXABI-NEXT:    [[LIST:%.*]] = alloca ptr, align 4
+// LINUXABI-NEXT:    [[X:%.*]] = alloca <4 x float>, align 32
+// LINUXABI-NEXT:    store i32 [[Z]], ptr [[Z_ADDR]], align 4
+// LINUXABI-NEXT:    call void @llvm.va_start.p0(ptr [[LIST]])
+// LINUXABI-NEXT:    [[ARGP_CUR:%.*]] = load ptr, ptr [[LIST]], align 4
+// LINUXABI-NEXT:    [[TMP0:%.*]] = getelementptr inbounds i8, ptr 
[[ARGP_CUR]], i32 15
+// LINUXABI-NEXT:    [[ARGP_CUR_ALIGNED:%.*]] = call ptr 
@llvm.ptrmask.p0.i32(ptr [[TMP0]], i32 -16)
+// LINUXABI-NEXT:    [[ARGP_NEXT:%.*]] = getelementptr inbounds i8, ptr 
[[ARGP_CUR_ALIGNED]], i32 16
+// LINUXABI-NEXT:    store ptr [[ARGP_NEXT]], ptr [[LIST]], align 4
+// LINUXABI-NEXT:    [[TMP1:%.*]] = load <4 x float>, ptr 
[[ARGP_CUR_ALIGNED]], align 16
+// LINUXABI-NEXT:    store <4 x float> [[TMP1]], ptr [[X]], align 32
+// LINUXABI-NEXT:    call void @llvm.va_end.p0(ptr [[LIST]])
+// LINUXABI-NEXT:    [[TMP2:%.*]] = load <4 x float>, ptr [[X]], align 32
+// LINUXABI-NEXT:    ret <4 x float> [[TMP2]]
+//
+// SYSV-LABEL: define dso_local <4 x float> @overaligned_vector_test(
+// SYSV-SAME: i32 noundef [[Z:%.*]], ...) #[[ATTR4:[0-9]+]] {
+// SYSV-NEXT:  [[ENTRY:.*:]]
+// SYSV-NEXT:    [[Z_ADDR:%.*]] = alloca i32, align 4
+// SYSV-NEXT:    [[LIST:%.*]] = alloca ptr, align 4
+// SYSV-NEXT:    [[X:%.*]] = alloca <4 x float>, align 32
+// SYSV-NEXT:    store i32 [[Z]], ptr [[Z_ADDR]], align 4
+// SYSV-NEXT:    call void @llvm.va_start.p0(ptr [[LIST]])
+// SYSV-NEXT:    [[ARGP_CUR:%.*]] = load ptr, ptr [[LIST]], align 4
+// SYSV-NEXT:    [[ARGP_NEXT:%.*]] = getelementptr inbounds i8, ptr 
[[ARGP_CUR]], i32 16
+// SYSV-NEXT:    store ptr [[ARGP_NEXT]], ptr [[LIST]], align 4
+// SYSV-NEXT:    [[TMP0:%.*]] = load <4 x float>, ptr [[ARGP_CUR]], align 4
+// SYSV-NEXT:    store <4 x float> [[TMP0]], ptr [[X]], align 32
+// SYSV-NEXT:    call void @llvm.va_end.p0(ptr [[LIST]])
+// SYSV-NEXT:    [[TMP1:%.*]] = load <4 x float>, ptr [[X]], align 32
+// SYSV-NEXT:    ret <4 x float> [[TMP1]]
+//
+OveralignedVector overaligned_vector_test(int z, ...) {
+  __builtin_va_list list;
+  __builtin_va_start(list, z);
+  OveralignedVector x = __builtin_va_arg(list, OveralignedVector);
+  __builtin_va_end(list);
+  return x;
+}
+
+// A struct containing an array of vector-register-type elements IS
+// realigned on Linux/MinGW/Cygwin, same as a struct containing a bare
+// vector member: the recursive field scan looks through array element
+// types too, matching real GCC.
+typedef struct { __v4sf arr[2]; } VectorArrayRecord;
+
+// LINUXABI-LABEL: define dso_local void @vector_array_record_test(
+// LINUXABI-SAME: ptr dead_on_unwind noalias writable 
sret([[STRUCT_VECTORARRAYRECORD:%.*]]) align 16 [[AGG_RESULT:%.*]], i32 noundef 
[[Z:%.*]], ...) #[[ATTR0]] {
+// LINUXABI-NEXT:  [[ENTRY:.*:]]
+// LINUXABI-NEXT:    [[RESULT_PTR:%.*]] = alloca ptr, align 4
+// LINUXABI-NEXT:    [[Z_ADDR:%.*]] = alloca i32, align 4
+// LINUXABI-NEXT:    [[LIST:%.*]] = alloca ptr, align 4
+// LINUXABI-NEXT:    store ptr [[AGG_RESULT]], ptr [[RESULT_PTR]], align 4
+// LINUXABI-NEXT:    store i32 [[Z]], ptr [[Z_ADDR]], align 4
+// LINUXABI-NEXT:    call void @llvm.va_start.p0(ptr [[LIST]])
+// LINUXABI-NEXT:    [[ARGP_CUR:%.*]] = load ptr, ptr [[LIST]], align 4
+// LINUXABI-NEXT:    [[TMP0:%.*]] = getelementptr inbounds i8, ptr 
[[ARGP_CUR]], i32 15
+// LINUXABI-NEXT:    [[ARGP_CUR_ALIGNED:%.*]] = call ptr 
@llvm.ptrmask.p0.i32(ptr [[TMP0]], i32 -16)
+// LINUXABI-NEXT:    [[ARGP_NEXT:%.*]] = getelementptr inbounds i8, ptr 
[[ARGP_CUR_ALIGNED]], i32 32
+// LINUXABI-NEXT:    store ptr [[ARGP_NEXT]], ptr [[LIST]], align 4
+// LINUXABI-NEXT:    call void @llvm.memcpy.p0.p0.i32(ptr align 16 
[[AGG_RESULT]], ptr align 16 [[ARGP_CUR_ALIGNED]], i32 32, i1 false)
+// LINUXABI-NEXT:    call void @llvm.va_end.p0(ptr [[LIST]])
+// LINUXABI-NEXT:    ret void
+//
+// SYSV-LABEL: define dso_local void @vector_array_record_test(
+// SYSV-SAME: ptr dead_on_unwind noalias writable 
sret([[STRUCT_VECTORARRAYRECORD:%.*]]) align 16 [[AGG_RESULT:%.*]], i32 noundef 
[[Z:%.*]], ...) #[[ATTR0]] {
+// SYSV-NEXT:  [[ENTRY:.*:]]
+// SYSV-NEXT:    [[RESULT_PTR:%.*]] = alloca ptr, align 4
+// SYSV-NEXT:    [[Z_ADDR:%.*]] = alloca i32, align 4
+// SYSV-NEXT:    [[LIST:%.*]] = alloca ptr, align 4
+// SYSV-NEXT:    store ptr [[AGG_RESULT]], ptr [[RESULT_PTR]], align 4
+// SYSV-NEXT:    store i32 [[Z]], ptr [[Z_ADDR]], align 4
+// SYSV-NEXT:    call void @llvm.va_start.p0(ptr [[LIST]])
+// SYSV-NEXT:    [[ARGP_CUR:%.*]] = load ptr, ptr [[LIST]], align 4
+// SYSV-NEXT:    [[ARGP_NEXT:%.*]] = getelementptr inbounds i8, ptr 
[[ARGP_CUR]], i32 32
+// SYSV-NEXT:    store ptr [[ARGP_NEXT]], ptr [[LIST]], align 4
+// SYSV-NEXT:    call void @llvm.memcpy.p0.p0.i32(ptr align 16 [[AGG_RESULT]], 
ptr align 4 [[ARGP_CUR]], i32 32, i1 false)
+// SYSV-NEXT:    call void @llvm.va_end.p0(ptr [[LIST]])
+// SYSV-NEXT:    ret void
+//
+VectorArrayRecord vector_array_record_test(int z, ...) {
+  __builtin_va_list list;
+  __builtin_va_start(list, z);
+  VectorArrayRecord x = __builtin_va_arg(list, VectorArrayRecord);
+  __builtin_va_end(list);
+  return x;
+}
+
+// A 256-bit (AVX) vector is realigned to 32, not just the 128-bit (SSE)
+// case: the check is on the type's own size, not a fixed 16.
+typedef float __v8sf __attribute__((__vector_size__(32)));
+
+// LINUXABI-LABEL: define dso_local <8 x float> @avx_vector_test(
+// LINUXABI-SAME: i32 noundef [[Z:%.*]], ...) #[[ATTR5:[0-9]+]] {
+// LINUXABI-NEXT:  [[ENTRY:.*:]]
+// LINUXABI-NEXT:    [[Z_ADDR:%.*]] = alloca i32, align 4
+// LINUXABI-NEXT:    [[LIST:%.*]] = alloca ptr, align 4
+// LINUXABI-NEXT:    [[X:%...
[truncated]

``````````

</details>


https://github.com/llvm/llvm-project/pull/219015
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to