https://github.com/akash-manna-sky created 
https://github.com/llvm/llvm-project/pull/216777

Clang crashed on `assert(IROffset == 0)` in 
`X86_64ABIInfo::GetINTEGERTypeAtOffset` when a struct containing a run of 
`__int128` bit-fields was passed or returned under the x86-64 SysV ABI: record 
lowering merges such bit-fields into a single `i128` access unit, but ABI 
classification ignores unnamed (padding) bit-fields and can therefore assign 
only one of the two eightbytes to the INTEGER class — so the `i128` gets 
queried at offset 8, or is returned as the low part while the high eightbyte is 
NO_CLASS (which trips the companion `assert(Hi == Integer)` in the callers and, 
without assertions, passes the value in a register pair the ABI never 
assigned). This patch takes the `i128` shortcut only when the `i128` starts at 
the requested offset and the high eightbyte is INTEGER too, otherwise falling 
back to the existing single-eightbyte handling that describes the value as an 
`i64` — restoring the pre-d35931c49e5b lowering — and adds regression tests for 
both field orders in return and argument position to 
`clang/test/CodeGen/X86/x86_64-arguments.c`.
 
Fixes #202205.

>From 3c0b7687c5010dc7c031e50d2f363a2868b7bb42 Mon Sep 17 00:00:00 2001
From: Akash Manna <[email protected]>
Date: Mon, 17 Aug 2026 22:03:28 +0530
Subject: [PATCH] [Clang][CodeGen][X86] Fix crash on __int128 bit-field access
 units

---
 clang/lib/CodeGen/Targets/X86.cpp         | 26 +++++++++++++----------
 clang/test/CodeGen/X86/x86_64-arguments.c | 26 +++++++++++++++++++++++
 2 files changed, 41 insertions(+), 11 deletions(-)

diff --git a/clang/lib/CodeGen/Targets/X86.cpp 
b/clang/lib/CodeGen/Targets/X86.cpp
index f0d108f3279fd..db2733558147d 100644
--- a/clang/lib/CodeGen/Targets/X86.cpp
+++ b/clang/lib/CodeGen/Targets/X86.cpp
@@ -2628,12 +2628,10 @@ GetINTEGERTypeAtOffset(llvm::Type *IRType, unsigned 
IROffset,
                                   SourceOffset);
   }
 
-  // if we have a 128-bit integer, we can pass it safely using an i128
-  // so we return that
-  if (IRType->isIntegerTy(128)) {
-    assert(IROffset == 0);
+  // A 128-bit integer can be passed safely as an i128, but only if it starts 
at
+  // this offset; otherwise it spans more than the eightbyte we're describing.
+  if (IRType->isIntegerTy(128) && IROffset == 0)
     return IRType;
-  }
 
   // Okay, we don't have any better idea of what to pass, so we pass this in an
   // integer register that isn't too big to fit the rest of the struct.
@@ -2739,10 +2737,13 @@ ABIArgInfo X86_64ABIInfo::classifyReturnType(QualType 
RetTy) const {
         return ABIArgInfo::getExtend(RetTy);
     }
 
+    // An i128 covers both eightbytes, so it only describes the value if the
+    // high eightbyte is INTEGER too; otherwise it is bit-field storage whose
+    // high eightbyte holds no data.
     if (ResType->isIntegerTy(128)) {
-      // i128 are passed directly
-      assert(Hi == Integer);
-      return ABIArgInfo::getDirect(ResType);
+      if (Hi == Integer)
+        return ABIArgInfo::getDirect(ResType);
+      ResType = llvm::Type::getInt64Ty(getVMContext());
     }
     break;
 
@@ -2889,10 +2890,13 @@ X86_64ABIInfo::classifyArgumentType(QualType Ty, 
unsigned freeIntRegs,
         return ABIArgInfo::getExtend(Ty, CGT.ConvertType(Ty));
     }
 
+    // See the matching comment in classifyReturnType.
     if (ResType->isIntegerTy(128)) {
-      assert(Hi == Integer);
-      ++neededInt;
-      return ABIArgInfo::getDirect(ResType);
+      if (Hi == Integer) {
+        ++neededInt;
+        return ABIArgInfo::getDirect(ResType);
+      }
+      ResType = llvm::Type::getInt64Ty(getVMContext());
     }
     break;
 
diff --git a/clang/test/CodeGen/X86/x86_64-arguments.c 
b/clang/test/CodeGen/X86/x86_64-arguments.c
index 580f9487395d3..9f5709e6838f8 100644
--- a/clang/test/CodeGen/X86/x86_64-arguments.c
+++ b/clang/test/CodeGen/X86/x86_64-arguments.c
@@ -590,6 +590,32 @@ _BitInt(128) f74(__uint128_t b, __uint128_t c, __uint128_t 
d, long e, _BitInt(12
   return a;
 }
 
+// check that a run of (u)int128_t bit-fields, which is lowered to an i128
+// access unit, is not passed as an i128 when only one eightbyte is INTEGER
+struct s75 {
+  __uint128_t : 124;
+  __uint128_t a : 4;
+};
+// CHECK-LABEL: define{{.*}} i64 @f75()
+struct s75 f75(void) {
+  return (struct s75){0};
+}
+// CHECK-LABEL: define{{.*}} void @f76(i64 %a.coerce)
+void f76(struct s75 a) {
+}
+
+struct s77 {
+  __uint128_t a : 4;
+  __uint128_t : 124;
+};
+// CHECK-LABEL: define{{.*}} i64 @f77()
+struct s77 f77(void) {
+  return (struct s77){0};
+}
+// CHECK-LABEL: define{{.*}} void @f78(i64 %a.coerce)
+void f78(struct s77 a) {
+}
+
 /// The synthesized __va_list_tag does not have file/line fields.
 // CHECK:      = distinct !DICompositeType(tag: DW_TAG_structure_type, name: 
"__va_list_tag",
 // CHECK-NOT:  file:

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

Reply via email to