Author: Folkert de Vries
Date: 2026-07-07T09:55:45+02:00
New Revision: 5c01d66e97208cd8acf6d7db72d9cfcbb4cd883f

URL: 
https://github.com/llvm/llvm-project/commit/5c01d66e97208cd8acf6d7db72d9cfcbb4cd883f
DIFF: 
https://github.com/llvm/llvm-project/commit/5c01d66e97208cd8acf6d7db72d9cfcbb4cd883f.diff

LOG: [AArch64][msvc] allow higher align than slot size in va_arg (#207591)

Fixes https://github.com/llvm/llvm-project/issues/207584

On aarch64 windows, an `__int128` argument is passed with alignment 16,
but was read with an alignment of 8. Based on the MSVC assembly and how
e.g. aarch64 darwin handles this, I think it's correct to set
`AllowHigherAlign = true`.

Added: 
    

Modified: 
    clang/docs/ReleaseNotes.md
    clang/lib/CodeGen/Targets/AArch64.cpp
    clang/test/CodeGen/win64-i128.c

Removed: 
    


################################################################################
diff  --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index a2f19e4ff194c..9683dec78cec1 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -143,6 +143,11 @@ latest release, please see the [Clang Web 
Site](https://clang.llvm.org) or the
   compilers. On most targets this is not a breaking change because `fastcc`
   and the platform C calling convention agree for `void(ptr)`. It is an ABI
   break on i686, MIPS O32, PowerPC64 ELFv1, and Lanai.
+- `va_arg` on clang aarch64 msvc now reads types with 16-byte size and 
alignment
+  (e.g. `__int128`) with their actual alignment (instead of an alignment of 8
+  which was used before). Such c-variadic arguments are already passed as
+  aligned, so previously reading the argument could read padding. Clang now
+  matches how MSVC reads such c-variadic arguments.
 - Fixed incorrect struct return when single large vector (256/512-bit) used on
   x86-64 targets. (#GH203760) The bug was introduced since Clang 21. 
(#GH120670)
 - Clang now applies MSVC's MD5 shortening to over-long Microsoft C++ RTTI type

diff  --git a/clang/lib/CodeGen/Targets/AArch64.cpp 
b/clang/lib/CodeGen/Targets/AArch64.cpp
index 6df6b581ee137..2d73dd8cc2916 100644
--- a/clang/lib/CodeGen/Targets/AArch64.cpp
+++ b/clang/lib/CodeGen/Targets/AArch64.cpp
@@ -1167,6 +1167,7 @@ RValue AArch64ABIInfo::EmitDarwinVAArg(Address 
VAListAddr, QualType Ty,
 
 RValue AArch64ABIInfo::EmitMSVAArg(CodeGenFunction &CGF, Address VAListAddr,
                                    QualType Ty, AggValueSlot Slot) const {
+  bool AllowHigherAlign = false;
   bool IsIndirect = false;
 
   if (getTarget().getTriple().isWindowsArm64EC()) {
@@ -1175,6 +1176,10 @@ RValue AArch64ABIInfo::EmitMSVAArg(CodeGenFunction &CGF, 
Address VAListAddr,
     uint64_t Width = getContext().getTypeSize(Ty);
     IsIndirect = Width > 64 || !llvm::isPowerOf2_64(Width);
   } else {
+    // E.g. __int128 when passed is aligned to 16 bytes, so it must be read
+    // with the same alignment.
+    AllowHigherAlign = true;
+
     // Composites larger than 16 bytes are passed by reference.
     if (isAggregateTypeForABI(Ty) && getContext().getTypeSize(Ty) > 128)
       IsIndirect = true;
@@ -1182,8 +1187,7 @@ RValue AArch64ABIInfo::EmitMSVAArg(CodeGenFunction &CGF, 
Address VAListAddr,
 
   return emitVoidPtrVAArg(CGF, VAListAddr, Ty, IsIndirect,
                           CGF.getContext().getTypeInfoInChars(Ty),
-                          CharUnits::fromQuantity(8),
-                          /*allowHigherAlign*/ false, Slot);
+                          CharUnits::fromQuantity(8), AllowHigherAlign, Slot);
 }
 
 static bool isStreamingCompatible(const FunctionDecl *F) {

diff  --git a/clang/test/CodeGen/win64-i128.c b/clang/test/CodeGen/win64-i128.c
index 5095d11a4c81f..862dd842f7db4 100644
--- a/clang/test/CodeGen/win64-i128.c
+++ b/clang/test/CodeGen/win64-i128.c
@@ -1,29 +1,72 @@
 // RUN: %clang_cc1 -triple x86_64-windows-gnu -emit-llvm -o - %s \
-// RUN:    | FileCheck %s --check-prefix=GNU64
+// RUN:    | FileCheck %s --check-prefixes=CHECK,X64
 // RUN: %clang_cc1 -triple x86_64-windows-msvc -emit-llvm -o - %s \
-// RUN:    | FileCheck %s --check-prefix=MSC64
+// RUN:    | FileCheck %s --check-prefixes=CHECK,X64
+// RUN: %clang_cc1 -triple aarch64-windows-msvc -emit-llvm -o - %s \
+// RUN:    | FileCheck %s --check-prefixes=CHECK,ARM,ARM64
+// RUN: %clang_cc1 -triple arm64ec-windows-msvc -emit-llvm -o - %s \
+// RUN:    | FileCheck %s --check-prefixes=CHECK,ARM,ARM64EC
 
 typedef int int128_t __attribute__((mode(TI)));
 
 int128_t foo(void) { return 0; }
 
-// GNU64: define dso_local <2 x i64> @foo()
-// MSC64: define dso_local <2 x i64> @foo()
+// X64: define dso_local <2 x i64> @foo()
+// ARM: define dso_local i128 @foo()
 
 int128_t bar(int128_t a, int128_t b) { return a * b; }
 
-// GNU64: define dso_local <2 x i64> @bar(ptr noundef align 16 dead_on_return 
%0, ptr noundef align 16 dead_on_return %1)
-// MSC64: define dso_local <2 x i64> @bar(ptr noundef align 16 dead_on_return 
%0, ptr noundef align 16 dead_on_return %1)
+// X64: define dso_local <2 x i64> @bar(ptr noundef align 16 dead_on_return 
%0, ptr noundef align 16 dead_on_return %1)
+// ARM: define dso_local i128 @bar(i128 noundef %a, i128 noundef %b)
 
 void vararg(int a, ...) {
-  // GNU64-LABEL: define{{.*}} void @vararg
-  // MSC64-LABEL: define{{.*}} void @vararg
+  // CHECK: define{{.*}} void @vararg
   __builtin_va_list ap;
   __builtin_va_start(ap, a);
   int128_t i = __builtin_va_arg(ap, int128_t);
-  // GNU64: load ptr, ptr
-  // GNU64: load i128, ptr
-  // MSC64: load ptr, ptr
-  // MSC64: load i128, ptr
+
+  // __int128 is passed indirectly, so there is a double load.
+  //
+  // X64: load ptr, ptr
+  // X64: load i128, ptr
+
+  // Explicitly check that the read is properly aligned.
+  //
+  // ARM64: %argp.cur = load ptr, ptr %ap
+  // ARM64: %argp.cur.aligned = call ptr @llvm.ptrmask.p0.i64(ptr %{{.*}}, i64 
-16)
+  // ARM64: load i128, ptr %argp.cur.aligned, align 16
+
+  // On ARM64EC __int128 is passed indirectly, so there is a double load.
+  //
+  // ARM64EC: %argp.cur = load ptr, ptr %ap
+  // ARM64EC: %argp.next = getelementptr inbounds i8, ptr %argp.cur, i64 8
+  // ARM64EC: [[P:%.*]] = load ptr, ptr %argp.cur
+  // ARM64EC: load i128, ptr [[P]], align 16
+  __builtin_va_end(ap);
+}
+
+struct Align16 {
+  char x[16];
+} __attribute__((aligned(16)));
+
+void vararg_struct(int a, ...) {
+  // CHECK: define{{.*}} void @vararg_struct
+  __builtin_va_list ap;
+  __builtin_va_start(ap, a);
+  struct Align16 i = __builtin_va_arg(ap, struct Align16);
+
+  // X64,ARM64EC: %argp.cur = load ptr, ptr %ap
+  // X64,ARM64EC: %argp.next = getelementptr inbounds i8, ptr %argp.cur, i64 8
+  // X64,ARM64EC: store ptr %argp.next, ptr %ap
+  // X64,ARM64EC: [[P:%.*]] = load ptr, ptr %argp.cur
+  // X64,ARM64EC: call void @llvm.memcpy.p0.p0.i64(ptr align 16 %i, ptr align 
16 [[P]], i64 16, i1 false)
+
+  // ARM64: %argp.cur = load ptr, ptr %ap
+  // ARM64: [[ADD:%.*]] = getelementptr inbounds i8, ptr %argp.cur, i32 15
+  // ARM64: %argp.cur.aligned = call ptr @llvm.ptrmask.p0.i64(ptr [[ADD]], i64 
-16)
+  // ARM64: %argp.next = getelementptr inbounds i8, ptr %argp.cur.aligned, i64 
16
+  // ARM64: store ptr %argp.next, ptr %ap
+  // ARM64: call void @llvm.memcpy.p0.p0.i64(ptr align 16 %i, ptr align 16 
%argp.cur.aligned, i64 16, i1 false)
+
   __builtin_va_end(ap);
 }


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

Reply via email to