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
