https://github.com/oltolm updated 
https://github.com/llvm/llvm-project/pull/210358

>From d24ef2c39bba15596ca1cca4878cc7639d68e953 Mon Sep 17 00:00:00 2001
From: Oleg Tolmatcev <[email protected]>
Date: Fri, 17 Jul 2026 17:17:02 +0200
Subject: [PATCH] [clang][X86] Return Win64 __int128 indirectly

Return Win64 __int128 and __uint128 values through a hidden sret pointer
instead of treating them as v2i64 values returned in XMM0.

This matches current GCC behavior and removes the old MinGW i128 direct-return
special case. Make the X86 backend follow the same rule for i128 libcalls it
creates itself: Win64 i128 div/rem/fp-conversion libcalls now use an sret
result slot, and i128 multiply is routed through the same path so __multi3 is
called with the correct ABI too.

Without this, some Win64 callers expected an i128 result directly in registers
while the callee wrote it through an sret pointer. That broke compiler-rt
builtins such as mulvti3 and muloti4 when they called __multi3.

Update the X86 codegen tests to check the indirect-return form.
---
 clang/docs/ReleaseNotes.md                    |  4 ++
 clang/lib/CodeGen/Targets/X86.cpp             |  6 +--
 clang/test/CodeGen/win64-i128.c               |  8 ++-
 llvm/lib/Target/X86/X86ISelLowering.cpp       | 52 +++++++++++++------
 llvm/lib/Target/X86/X86ISelLoweringCall.cpp   | 11 ++--
 llvm/test/CodeGen/X86/divmod128.ll            |  2 -
 .../CodeGen/X86/i128-fpconv-win64-strict.ll   | 10 +---
 llvm/test/CodeGen/X86/i128-fpconv-win64.ll    | 10 +---
 8 files changed, 57 insertions(+), 46 deletions(-)

diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index 8e47adbc64a4c..b9bc075a44d96 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -50,6 +50,10 @@ latest release, please see the [Clang Web 
Site](https://clang.llvm.org) or the
 
 ### ABI Changes in This Version
 
+- On x86_64 Windows targets, Clang now returns `__int128` and `unsigned
+  __int128` indirectly through a hidden return pointer. This matches how Clang
+  already returns 16-byte structs on the same targets.
+
 ### AST Dumping Potentially Breaking Changes
 
 ### Clang Frontend Potentially Breaking Changes
diff --git a/clang/lib/CodeGen/Targets/X86.cpp 
b/clang/lib/CodeGen/Targets/X86.cpp
index 807d372c3c8fd..c13e3a16ae098 100644
--- a/clang/lib/CodeGen/Targets/X86.cpp
+++ b/clang/lib/CodeGen/Targets/X86.cpp
@@ -3510,12 +3510,10 @@ ABIArgInfo WinX86_64ABIInfo::classify(QualType Ty, 
unsigned &FreeSSERegs,
             Align, /*AddrSpace=*/getDataLayout().getAllocaAddrSpace(),
             /*ByVal=*/false);
 
-      // Mingw64 GCC returns i128 in XMM0. Coerce to v2i64 to handle that.
-      // Clang matches them for compatibility.
       if (BT->getKind() == BuiltinType::Int128 ||
           BT->getKind() == BuiltinType::UInt128)
-        return ABIArgInfo::getDirect(llvm::FixedVectorType::get(
-            llvm::Type::getInt64Ty(getVMContext()), 2));
+        return getNaturalAlignIndirect(Ty, 
getDataLayout().getAllocaAddrSpace(),
+                                       /*ByVal=*/false);
 
       // Mingw64 GCC returns f128 via sret, and Clang matches that for
       // compatibility. This mirrors the X86 backend's CanLowerReturn logic.
diff --git a/clang/test/CodeGen/win64-i128.c b/clang/test/CodeGen/win64-i128.c
index 862dd842f7db4..a427bf025d7fd 100644
--- a/clang/test/CodeGen/win64-i128.c
+++ b/clang/test/CodeGen/win64-i128.c
@@ -11,12 +11,16 @@ typedef int int128_t __attribute__((mode(TI)));
 
 int128_t foo(void) { return 0; }
 
-// X64: define dso_local <2 x i64> @foo()
+// X64-LABEL: define dso_local void @foo(
+// X64-SAME: sret(i128) align 16
 // ARM: define dso_local i128 @foo()
 
 int128_t bar(int128_t a, int128_t b) { return a * b; }
 
-// X64: define dso_local <2 x i64> @bar(ptr noundef align 16 dead_on_return 
%0, ptr noundef align 16 dead_on_return %1)
+// X64-LABEL: define dso_local void @bar(
+// X64-SAME: sret(i128) align 16
+// X64-SAME: ptr noundef align 16 dead_on_return
+// X64-SAME: ptr noundef align 16 dead_on_return
 // ARM: define dso_local i128 @bar(i128 noundef %a, i128 noundef %b)
 
 void vararg(int a, ...) {
diff --git a/llvm/lib/Target/X86/X86ISelLowering.cpp 
b/llvm/lib/Target/X86/X86ISelLowering.cpp
index 97e783c09f0f2..3aa12c1928ccc 100644
--- a/llvm/lib/Target/X86/X86ISelLowering.cpp
+++ b/llvm/lib/Target/X86/X86ISelLowering.cpp
@@ -2710,6 +2710,7 @@ X86TargetLowering::X86TargetLowering(const 
X86TargetMachine &TM,
   setOperationAction(ISD::FSINCOS, MVT::f32, Expand);
 
   if (Subtarget.isTargetWin64()) {
+    setOperationAction(ISD::MUL, MVT::i128, Custom);
     setOperationAction(ISD::SDIV, MVT::i128, Custom);
     setOperationAction(ISD::UDIV, MVT::i128, Custom);
     setOperationAction(ISD::SREM, MVT::i128, Custom);
@@ -30640,21 +30641,21 @@ SDValue X86TargetLowering::LowerWin64_i128OP(SDValue 
Op, SelectionDAG &DAG) cons
   assert(VT.isInteger() && VT.getSizeInBits() == 128 &&
          "Unexpected return type for lowering");
 
-  if (isa<ConstantSDNode>(Op->getOperand(1))) {
+  if (Op.getOpcode() != ISD::MUL && isa<ConstantSDNode>(Op->getOperand(1))) {
     SmallVector<SDValue> Result;
     if (expandDIVREMByConstant(Op.getNode(), Result, MVT::i64, DAG))
       return DAG.getNode(ISD::BUILD_PAIR, SDLoc(Op), VT, Result[0], Result[1]);
   }
 
   RTLIB::Libcall LC;
-  bool isSigned;
   switch (Op->getOpcode()) {
   // clang-format off
   default: llvm_unreachable("Unexpected request for libcall!");
-  case ISD::SDIV:      isSigned = true;  LC = RTLIB::SDIV_I128;    break;
-  case ISD::UDIV:      isSigned = false; LC = RTLIB::UDIV_I128;    break;
-  case ISD::SREM:      isSigned = true;  LC = RTLIB::SREM_I128;    break;
-  case ISD::UREM:      isSigned = false; LC = RTLIB::UREM_I128;    break;
+  case ISD::MUL:       LC = RTLIB::MUL_I128;   break;
+  case ISD::SDIV:      LC = RTLIB::SDIV_I128;  break;
+  case ISD::UDIV:      LC = RTLIB::UDIV_I128;  break;
+  case ISD::SREM:      LC = RTLIB::SREM_I128;  break;
+  case ISD::UREM:      LC = RTLIB::UREM_I128;  break;
   // clang-format on
   }
 
@@ -30662,6 +30663,22 @@ SDValue X86TargetLowering::LowerWin64_i128OP(SDValue 
Op, SelectionDAG &DAG) cons
   SDValue InChain = DAG.getEntryNode();
 
   TargetLowering::ArgListTy Args;
+  Type *RetTy = VT.getTypeForEVT(*DAG.getContext());
+  MachineFrameInfo &MFI = DAG.getMachineFunction().getFrameInfo();
+  const Align StackAlign = DAG.getDataLayout().getPrefTypeAlign(RetTy);
+  int ResultFI =
+      MFI.CreateStackObject(DAG.getDataLayout().getTypeAllocSize(RetTy),
+                            StackAlign, false);
+  SDValue ResultPtr = DAG.getFrameIndex(ResultFI, 
getFrameIndexTy(DAG.getDataLayout()));
+
+  TargetLowering::ArgListEntry ResultEntry(
+      ResultPtr, PointerType::get(*DAG.getContext(),
+                                  DAG.getDataLayout().getAllocaAddrSpace()));
+  ResultEntry.IsSRet = true;
+  ResultEntry.Alignment = StackAlign;
+  ResultEntry.IndirectType = RetTy;
+  Args.push_back(ResultEntry);
+
   for (unsigned i = 0, e = Op->getNumOperands(); i != e; ++i) {
     EVT ArgVT = Op->getOperand(i).getValueType();
     assert(ArgVT.isInteger() && ArgVT.getSizeInBits() == 128 &&
@@ -30686,14 +30703,12 @@ SDValue X86TargetLowering::LowerWin64_i128OP(SDValue 
Op, SelectionDAG &DAG) cons
   CLI.setDebugLoc(dl)
       .setChain(InChain)
       .setLibCallee(DAG.getLibcalls().getLibcallImplCallingConv(LCImpl),
-                    EVT(MVT::v2i64).getTypeForEVT(*DAG.getContext()), Callee,
-                    std::move(Args))
-      .setInRegister()
-      .setSExtResult(isSigned)
-      .setZExtResult(!isSigned);
+                    Type::getVoidTy(*DAG.getContext()), Callee, 
std::move(Args));
 
   std::pair<SDValue, SDValue> CallInfo = LowerCallTo(CLI);
-  return DAG.getBitcast(VT, CallInfo.first);
+  MachinePointerInfo PtrInfo =
+      MachinePointerInfo::getFixedStack(DAG.getMachineFunction(), ResultFI);
+  return DAG.getLoad(VT, dl, CallInfo.second, ResultPtr, PtrInfo, StackAlign);
 }
 
 SDValue X86TargetLowering::LowerWin64_FP_TO_INT128(SDValue Op,
@@ -30722,11 +30737,8 @@ SDValue 
X86TargetLowering::LowerWin64_FP_TO_INT128(SDValue Op,
   Chain = IsStrict ? Op.getOperand(0) : DAG.getEntryNode();
 
   SDValue Result;
-  // Expect the i128 argument returned as a v2i64 in xmm0, cast back to the
-  // expected VT (i128).
-  std::tie(Result, Chain) =
-      makeLibCall(DAG, LC, MVT::v2i64, Arg, CallOptions, dl, Chain);
-  Result = DAG.getBitcast(VT, Result);
+  std::tie(Result, Chain) = makeLibCall(DAG, LC, VT, Arg, CallOptions, dl,
+                                        Chain);
   return Result;
 }
 
@@ -34999,6 +35011,12 @@ void X86TargetLowering::ReplaceNodeResults(SDNode *N,
   }
   case ISD::MUL: {
     EVT VT = N->getValueType(0);
+    if (VT == MVT::i128) {
+      SDValue V = LowerWin64_i128OP(SDValue(N, 0), DAG);
+      Results.push_back(V);
+      return;
+    }
+
     assert(getTypeAction(*DAG.getContext(), VT) == TypeWidenVector &&
            VT.getVectorElementType() == MVT::i8 && "Unexpected VT!");
     // Pre-promote these to vXi16 to avoid op legalization thinking all 16
diff --git a/llvm/lib/Target/X86/X86ISelLoweringCall.cpp 
b/llvm/lib/Target/X86/X86ISelLoweringCall.cpp
index 8f14198cf268b..ea3b0af428a5b 100644
--- a/llvm/lib/Target/X86/X86ISelLoweringCall.cpp
+++ b/llvm/lib/Target/X86/X86ISelLoweringCall.cpp
@@ -671,9 +671,9 @@ bool X86TargetLowering::CanLowerReturn(
     CallingConv::ID CallConv, MachineFunction &MF, bool isVarArg,
     const SmallVectorImpl<ISD::OutputArg> &Outs, LLVMContext &Context,
     const Type *RetTy) const {
-  // Mingw64 GCC returns f128 via sret, and LLVM matches it for compatibility.
-  // This logic exists for libcalls, a frontend should explicitly use sret
-  // rather than rely on the sret demotion here.
+  // Match the frontend ABI lowering for Win64 i128 and for mingw64 f128
+  // libcalls. A frontend should explicitly use sret rather than rely on the
+  // sret demotion here.
   //
   // Using sret is a reasonable implementation of the Windows x64 calling
   // convention:
@@ -704,8 +704,9 @@ bool X86TargetLowering::CanLowerReturn(
   };
 
   if (IsWin64F128StackCC(CallConv) &&
-      llvm::any_of(
-          Outs, [](const ISD::OutputArg &Out) { return Out.VT == MVT::f128; }))
+      llvm::any_of(Outs, [](const ISD::OutputArg &Out) {
+        return Out.VT == MVT::f128 || Out.VT == MVT::i128;
+      }))
     return false;
 
   SmallVector<CCValAssign, 16> RVLocs;
diff --git a/llvm/test/CodeGen/X86/divmod128.ll 
b/llvm/test/CodeGen/X86/divmod128.ll
index b6d7c6724f394..b6234d4f28186 100644
--- a/llvm/test/CodeGen/X86/divmod128.ll
+++ b/llvm/test/CodeGen/X86/divmod128.ll
@@ -24,7 +24,6 @@ define i64 @mod128(i128 %x) nounwind {
 ; WIN64-NEXT:    leaq {{[0-9]+}}(%rsp), %rcx
 ; WIN64-NEXT:    leaq {{[0-9]+}}(%rsp), %rdx
 ; WIN64-NEXT:    callq __modti3
-; WIN64-NEXT:    movq %xmm0, %rax
 ; WIN64-NEXT:    addq $72, %rsp
 ; WIN64-NEXT:    retq
 
@@ -54,7 +53,6 @@ define i64 @div128(i128 %x) nounwind {
 ; WIN64-NEXT:    leaq {{[0-9]+}}(%rsp), %rcx
 ; WIN64-NEXT:    leaq {{[0-9]+}}(%rsp), %rdx
 ; WIN64-NEXT:    callq __divti3
-; WIN64-NEXT:    movq %xmm0, %rax
 ; WIN64-NEXT:    addq $72, %rsp
 ; WIN64-NEXT:    retq
 
diff --git a/llvm/test/CodeGen/X86/i128-fpconv-win64-strict.ll 
b/llvm/test/CodeGen/X86/i128-fpconv-win64-strict.ll
index 64869da48e6c0..51acdc768e4c9 100644
--- a/llvm/test/CodeGen/X86/i128-fpconv-win64-strict.ll
+++ b/llvm/test/CodeGen/X86/i128-fpconv-win64-strict.ll
@@ -7,7 +7,6 @@ define i64 @double_to_i128(double %d) nounwind strictfp {
 ; WIN64:       # %bb.0:
 ; WIN64-NEXT:    subq $40, %rsp
 ; WIN64-NEXT:    callq __fixdfti
-; WIN64-NEXT:    movq %xmm0, %rax
 ; WIN64-NEXT:    addq $40, %rsp
 ; WIN64-NEXT:    retq
   %1 = tail call i128 @llvm.experimental.constrained.fptosi.i128.f64(double 
%d, metadata !"fpexcept.strict")
@@ -20,7 +19,6 @@ define i64 @double_to_ui128(double %d) nounwind strictfp {
 ; WIN64:       # %bb.0:
 ; WIN64-NEXT:    subq $40, %rsp
 ; WIN64-NEXT:    callq __fixunsdfti
-; WIN64-NEXT:    movq %xmm0, %rax
 ; WIN64-NEXT:    addq $40, %rsp
 ; WIN64-NEXT:    retq
   %1 = tail call i128 @llvm.experimental.constrained.fptoui.i128.f64(double 
%d, metadata !"fpexcept.strict")
@@ -33,7 +31,6 @@ define i64 @float_to_i128(float %d) nounwind strictfp {
 ; WIN64:       # %bb.0:
 ; WIN64-NEXT:    subq $40, %rsp
 ; WIN64-NEXT:    callq __fixsfti
-; WIN64-NEXT:    movq %xmm0, %rax
 ; WIN64-NEXT:    addq $40, %rsp
 ; WIN64-NEXT:    retq
   %1 = tail call i128 @llvm.experimental.constrained.fptosi.i128.f32(float %d, 
metadata !"fpexcept.strict")
@@ -46,7 +43,6 @@ define i64 @float_to_ui128(float %d) nounwind strictfp {
 ; WIN64:       # %bb.0:
 ; WIN64-NEXT:    subq $40, %rsp
 ; WIN64-NEXT:    callq __fixunssfti
-; WIN64-NEXT:    movq %xmm0, %rax
 ; WIN64-NEXT:    addq $40, %rsp
 ; WIN64-NEXT:    retq
   %1 = tail call i128 @llvm.experimental.constrained.fptoui.i128.f32(float %d, 
metadata !"fpexcept.strict")
@@ -59,11 +55,10 @@ define i64 @longdouble_to_i128(ptr nocapture readonly %0) 
nounwind strictfp {
 ; WIN64:       # %bb.0:
 ; WIN64-NEXT:    subq $56, %rsp
 ; WIN64-NEXT:    fldt (%rcx)
-; WIN64-NEXT:    fstpt {{[0-9]+}}(%rsp)
+; WIN64-NEXT:    fstpt 32(%rsp)
 ; WIN64-NEXT:    wait
 ; WIN64-NEXT:    leaq {{[0-9]+}}(%rsp), %rcx
 ; WIN64-NEXT:    callq __fixxfti
-; WIN64-NEXT:    movq %xmm0, %rax
 ; WIN64-NEXT:    addq $56, %rsp
 ; WIN64-NEXT:    retq
   %2 = load x86_fp80, ptr %0, align 16
@@ -77,11 +72,10 @@ define i64 @longdouble_to_ui128(ptr nocapture readonly %0) 
nounwind strictfp {
 ; WIN64:       # %bb.0:
 ; WIN64-NEXT:    subq $56, %rsp
 ; WIN64-NEXT:    fldt (%rcx)
-; WIN64-NEXT:    fstpt {{[0-9]+}}(%rsp)
+; WIN64-NEXT:    fstpt 32(%rsp)
 ; WIN64-NEXT:    wait
 ; WIN64-NEXT:    leaq {{[0-9]+}}(%rsp), %rcx
 ; WIN64-NEXT:    callq __fixunsxfti
-; WIN64-NEXT:    movq %xmm0, %rax
 ; WIN64-NEXT:    addq $56, %rsp
 ; WIN64-NEXT:    retq
   %2 = load x86_fp80, ptr %0, align 16
diff --git a/llvm/test/CodeGen/X86/i128-fpconv-win64.ll 
b/llvm/test/CodeGen/X86/i128-fpconv-win64.ll
index 414318738baf7..ea0f03dccc552 100644
--- a/llvm/test/CodeGen/X86/i128-fpconv-win64.ll
+++ b/llvm/test/CodeGen/X86/i128-fpconv-win64.ll
@@ -7,7 +7,6 @@ define i64 @double_to_i128(double %d) nounwind {
 ; WIN64:       # %bb.0:
 ; WIN64-NEXT:    subq $40, %rsp
 ; WIN64-NEXT:    callq __fixdfti
-; WIN64-NEXT:    movq %xmm0, %rax
 ; WIN64-NEXT:    addq $40, %rsp
 ; WIN64-NEXT:    retq
   %1 = fptosi double %d to i128
@@ -20,7 +19,6 @@ define i64 @double_to_ui128(double %d) nounwind {
 ; WIN64:       # %bb.0:
 ; WIN64-NEXT:    subq $40, %rsp
 ; WIN64-NEXT:    callq __fixunsdfti
-; WIN64-NEXT:    movq %xmm0, %rax
 ; WIN64-NEXT:    addq $40, %rsp
 ; WIN64-NEXT:    retq
   %1 = fptoui double %d to i128
@@ -33,7 +31,6 @@ define i64 @float_to_i128(float %d) nounwind {
 ; WIN64:       # %bb.0:
 ; WIN64-NEXT:    subq $40, %rsp
 ; WIN64-NEXT:    callq __fixsfti
-; WIN64-NEXT:    movq %xmm0, %rax
 ; WIN64-NEXT:    addq $40, %rsp
 ; WIN64-NEXT:    retq
   %1 = fptosi float %d to i128
@@ -46,7 +43,6 @@ define i64 @float_to_ui128(float %d) nounwind {
 ; WIN64:       # %bb.0:
 ; WIN64-NEXT:    subq $40, %rsp
 ; WIN64-NEXT:    callq __fixunssfti
-; WIN64-NEXT:    movq %xmm0, %rax
 ; WIN64-NEXT:    addq $40, %rsp
 ; WIN64-NEXT:    retq
   %1 = fptoui float %d to i128
@@ -59,10 +55,9 @@ define i64 @longdouble_to_i128(ptr nocapture readonly %0) 
nounwind {
 ; WIN64:       # %bb.0:
 ; WIN64-NEXT:    subq $56, %rsp
 ; WIN64-NEXT:    fldt (%rcx)
-; WIN64-NEXT:    fstpt {{[0-9]+}}(%rsp)
+; WIN64-NEXT:    fstpt 32(%rsp)
 ; WIN64-NEXT:    leaq {{[0-9]+}}(%rsp), %rcx
 ; WIN64-NEXT:    callq __fixxfti
-; WIN64-NEXT:    movq %xmm0, %rax
 ; WIN64-NEXT:    addq $56, %rsp
 ; WIN64-NEXT:    retq
   %2 = load x86_fp80, ptr %0, align 16
@@ -76,10 +71,9 @@ define i64 @longdouble_to_ui128(ptr nocapture readonly %0) 
nounwind {
 ; WIN64:       # %bb.0:
 ; WIN64-NEXT:    subq $56, %rsp
 ; WIN64-NEXT:    fldt (%rcx)
-; WIN64-NEXT:    fstpt {{[0-9]+}}(%rsp)
+; WIN64-NEXT:    fstpt 32(%rsp)
 ; WIN64-NEXT:    leaq {{[0-9]+}}(%rsp), %rcx
 ; WIN64-NEXT:    callq __fixunsxfti
-; WIN64-NEXT:    movq %xmm0, %rax
 ; WIN64-NEXT:    addq $56, %rsp
 ; WIN64-NEXT:    retq
   %2 = load x86_fp80, ptr %0, align 16

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

Reply via email to