[clang] [MIPS] fix `__int128` register alignment (PR #214322)
Folkert de Vries via cfe-commits
cfe-commits at lists.llvm.org
Wed Aug 5 12:54:05 PDT 2026
https://github.com/folkertdev created https://github.com/llvm/llvm-project/pull/214322
fixes https://github.com/llvm/llvm-project/issues/213541
On `mips64` and `mips64el`, an `i128` needs to be register-aligned to an even-numbered register to be compatible with GCC. Insert padding to make that true.
GCC applies the additional alignment here:
https://github.com/gcc-mirror/gcc/blob/529304e456c9be38139325e3c2567f7d2a93e7c8/gcc/config/mips/mips.cc#L6210-L6223
>From 863ff962f5a485df1cfe4d0f173f157d17011852 Mon Sep 17 00:00:00 2001
From: Folkert de Vries <folkert at folkertdev.nl>
Date: Mon, 3 Aug 2026 10:22:41 +0200
Subject: [PATCH] fix i128 register alignment
---
clang/docs/ReleaseNotes.md | 3 ++
clang/lib/CodeGen/Targets/Mips.cpp | 21 ++++++++-----
clang/test/CodeGen/mips64-padding-arg.c | 41 +++++++++++++++++++++++++
3 files changed, 58 insertions(+), 7 deletions(-)
diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index d4b1cf9e44945..3e6397ae5707b 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -85,6 +85,9 @@ features cannot lower the translation-unit ABI level;
always passed the parts separately. `-fclang-abi-compat=23` restores the previous
behavior. (#GH212109)
+- On MIPS N32/N64, an `__int128` now correctly start in an even-numbered register
+ or 16-byte aligned stack slot, matching GCC.
+
### AST Dumping Potentially Breaking Changes
### Clang Frontend Potentially Breaking Changes
diff --git a/clang/lib/CodeGen/Targets/Mips.cpp b/clang/lib/CodeGen/Targets/Mips.cpp
index c093cfc668c2e..90d1b5e5dcb20 100644
--- a/clang/lib/CodeGen/Targets/Mips.cpp
+++ b/clang/lib/CodeGen/Targets/Mips.cpp
@@ -49,7 +49,7 @@ class MipsABIInfo : public ABIInfo {
void computeInfo(CGFunctionInfo &FI) const override;
RValue EmitVAArg(CodeGenFunction &CGF, Address VAListAddr, QualType Ty,
AggValueSlot Slot) const override;
- ABIArgInfo extendType(QualType Ty) const;
+ ABIArgInfo extendType(QualType Ty, llvm::Type *Padding = nullptr) const;
};
class MIPSTargetCodeGenInfo : public TargetCodeGenInfo {
@@ -292,12 +292,19 @@ MipsABIInfo::classifyArgumentType(QualType Ty, uint64_t &Offset) const {
!getContext().getTargetInfo().hasInt128Type()))
return getNaturalAlignIndirect(Ty, getDataLayout().getAllocaAddrSpace());
+ // Scalars never get explicit padding on O32: CC_MipsO32 already does the
+ // alignment itself based on the argument's original alignment.
+ //
+ // For __int128 and other types that are 16-byte aligned this padding ensures
+ // that the value starts in an even-numbered register or stack slot.
+ llvm::Type *Padding =
+ IsO32 ? nullptr : getPaddingType(OrigOffset, CurrOffset);
+
// All integral types are promoted to the GPR width.
if (Ty->isIntegralOrEnumerationType())
- return extendType(Ty);
+ return extendType(Ty, Padding);
- return ABIArgInfo::getDirect(
- nullptr, 0, IsO32 ? nullptr : getPaddingType(OrigOffset, CurrOffset));
+ return ABIArgInfo::getDirect(nullptr, 0, Padding);
}
llvm::Type*
@@ -468,14 +475,14 @@ RValue MipsABIInfo::EmitVAArg(CodeGenFunction &CGF, Address VAListAddr,
return Res;
}
-ABIArgInfo MipsABIInfo::extendType(QualType Ty) const {
+ABIArgInfo MipsABIInfo::extendType(QualType Ty, llvm::Type *Padding) const {
int TySize = getContext().getTypeSize(Ty);
// MIPS64 ABI requires unsigned 32 bit integers to be sign extended.
if (Ty->isUnsignedIntegerOrEnumerationType() && TySize == 32)
- return ABIArgInfo::getSignExtend(Ty);
+ return ABIArgInfo::getSignExtend(Ty, /*T=*/nullptr, Padding);
- return ABIArgInfo::getExtend(Ty);
+ return ABIArgInfo::getExtend(Ty, /*T=*/nullptr, Padding);
}
bool
diff --git a/clang/test/CodeGen/mips64-padding-arg.c b/clang/test/CodeGen/mips64-padding-arg.c
index bb92a841c3f61..e2078c9294a0b 100644
--- a/clang/test/CodeGen/mips64-padding-arg.c
+++ b/clang/test/CodeGen/mips64-padding-arg.c
@@ -43,6 +43,47 @@ S0 foo5(long double a0) {
return foo6(1, 2, a0);
}
+#ifdef __SIZEOF_INT128__
+// Insert padding before a 128-bit integer argument, which is 16-byte aligned, to make it start in an even-numbered register.
+//
+// N64-LABEL: define{{.*}} void @foo11(i32 noundef signext %a0, i64 %0, i128 noundef signext %a1)
+// N64: tail call void @foo12(i32 noundef signext 1, i32 noundef signext 2, i32 noundef signext %a0, i64 undef, i128 noundef signext %a1)
+// N64: declare void @foo12(i32 noundef signext, i32 noundef signext, i32 noundef signext, i64, i128 noundef signext)
+
+extern void foo12(int, int, int, __int128);
+
+void foo11(int a0, __int128 a1) {
+ foo12(1, 2, a0, a1);
+}
+
+// Do not insert padding if the 128-bit integer is already 16-byte aligned.
+// Hence foo13 needs padding but foo14 does not.
+//
+// N64-LABEL: define{{.*}} void @foo13(i32 noundef signext %a0, i64 %0, i128 noundef zeroext %a1)
+// N64: tail call void @foo14(i32 noundef signext 1, i32 noundef signext %a0, i128 noundef zeroext %a1)
+// N64: declare void @foo14(i32 noundef signext, i32 noundef signext, i128 noundef zeroext)
+
+extern void foo14(int, int, unsigned __int128);
+
+void foo13(int a0, unsigned __int128 a1) {
+ foo14(1, a0, a1);
+}
+
+// Test a case where all but one argument register is exhausted.
+// The padding is still inserted, and the full __int128 value is
+// passed via the stack, it is not split.
+//
+// N64-LABEL: define{{.*}} void @foo15(i64 noundef signext %a0, i64 noundef signext %a1, i64 noundef signext %a2, i64 noundef signext %a3, i64 noundef signext %a4, i64 noundef signext %a5, i64 noundef signext %a6, i64 %0, i128 noundef signext %a7)
+// N64: tail call void @foo16(i64 noundef signext %a0, i64 noundef signext %a1, i64 noundef signext %a2, i64 noundef signext %a3, i64 noundef signext %a4, i64 noundef signext %a5, i64 noundef signext %a6, i64 undef, i128 noundef signext %a7)
+// N64: declare void @foo16(i64 noundef signext, i64 noundef signext, i64 noundef signext, i64 noundef signext, i64 noundef signext, i64 noundef signext, i64 noundef signext, i64, i128 noundef signext)
+
+extern void foo16(long, long, long, long, long, long, long, __int128);
+
+void foo15(long a0, long a1, long a2, long a3, long a4, long a5, long a6, __int128 a7) {
+ foo16(a0, a1, a2, a3, a4, a5, a6, a7);
+}
+#endif
+
// Do not insert padding if ABI is O32.
//
// O32-LABEL: define{{.*}} void @foo7(float noundef %a0, double noundef %a1)
More information about the cfe-commits
mailing list