[llvm-branch-commits] [llvm] release/22.x: [X86][FastISel] Restore support for struct returns (#194586) (PR #195029)

via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Thu Apr 30 21:00:53 PDT 2026


https://github.com/dyung updated https://github.com/llvm/llvm-project/pull/195029

>From 932756ad6c645d40013a6122c6a97141b17f5890 Mon Sep 17 00:00:00 2001
From: Nikita Popov <npopov at redhat.com>
Date: Wed, 29 Apr 2026 11:47:52 +0200
Subject: [PATCH] [X86][FastISel] Restore support for struct returns (#194586)

After #180322, X86 FastISel forces SDAG fallback for any call with a
struct return. This caused major compile-time regressions for debug
builds in Rust, where struct returns are very common.

The type legality check should work on the de-aggregated types, not on
the return type directly.

(cherry picked from commit 30fa4153a556f51143b1145af8c603581c80369a)
---
 llvm/lib/Target/X86/X86FastISel.cpp           | 29 ++++++----
 llvm/test/CodeGen/X86/bf16-fast-isel.ll       | 30 ++++++++++
 llvm/test/CodeGen/X86/fast-isel-struct-ret.ll | 58 +++++++++++++++++++
 3 files changed, 105 insertions(+), 12 deletions(-)
 create mode 100644 llvm/test/CodeGen/X86/fast-isel-struct-ret.ll

diff --git a/llvm/lib/Target/X86/X86FastISel.cpp b/llvm/lib/Target/X86/X86FastISel.cpp
index fc96671682ac9..8bc98c6de1e64 100644
--- a/llvm/lib/Target/X86/X86FastISel.cpp
+++ b/llvm/lib/Target/X86/X86FastISel.cpp
@@ -21,6 +21,7 @@
 #include "X86Subtarget.h"
 #include "X86TargetMachine.h"
 #include "llvm/Analysis/BranchProbabilityInfo.h"
+#include "llvm/CodeGen/Analysis.h"
 #include "llvm/CodeGen/FastISel.h"
 #include "llvm/CodeGen/FunctionLoweringInfo.h"
 #include "llvm/CodeGen/MachineConstantPool.h"
@@ -3273,18 +3274,22 @@ bool X86FastISel::fastLowerCall(CallLoweringInfo &CLI) {
   // the value from FuncInfo.ValueMap.
   // However, i1 is promoted to i8 and return i8 defined by ABI, so FastISel can
   // lower it without switching to DAGISel.
-  MVT RetVT = MVT::Other;
-  if (!isTypeLegal(CLI.RetTy, RetVT) && !CLI.RetTy->isVoidTy()) {
-    if (RetVT == MVT::Other)
-      return false; // Unknown type, let DAG ISel handle it.
-
-    // RetVT is not MVT::Other, it must be simple now. It is something rely on
-    // the logic of isTypeLegal().
-    MVT ABIVT = TLI.getRegisterTypeForCallingConv(CLI.RetTy->getContext(),
-                                                  CLI.CallConv, RetVT);
-    MVT RegVT = TLI.getRegisterType(CLI.RetTy->getContext(), RetVT);
-    if (ABIVT != RegVT)
-      return false;
+  SmallVector<Type *> RetTys;
+  ComputeValueTypes(DL, CLI.RetTy, RetTys);
+  for (Type *RetTy : RetTys) {
+    MVT RetVT = MVT::Other;
+    if (!isTypeLegal(RetTy, RetVT)) {
+      if (RetVT == MVT::Other)
+        return false; // Unknown type, let DAG ISel handle it.
+
+      // RetVT is not MVT::Other, it must be simple now. It is something rely on
+      // the logic of isTypeLegal().
+      MVT ABIVT = TLI.getRegisterTypeForCallingConv(CLI.RetTy->getContext(),
+                                                    CLI.CallConv, RetVT);
+      MVT RegVT = TLI.getRegisterType(CLI.RetTy->getContext(), RetVT);
+      if (ABIVT != RegVT)
+        return false;
+    }
   }
 
   // Call / invoke instructions with NoCfCheck attribute require special
diff --git a/llvm/test/CodeGen/X86/bf16-fast-isel.ll b/llvm/test/CodeGen/X86/bf16-fast-isel.ll
index 812ffc3ab5f19..4259b811bbfe9 100644
--- a/llvm/test/CodeGen/X86/bf16-fast-isel.ll
+++ b/llvm/test/CodeGen/X86/bf16-fast-isel.ll
@@ -116,6 +116,36 @@ entry:
   ret i8 %call2
 }
 
+declare { bfloat, bfloat } @get_bfloats()
+declare void @take_bfloats({ bfloat, bfloat })
+
+define void @call_get_bfloats() nounwind {
+; CHECK-LABEL: call_get_bfloats:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    pushq %rax
+; CHECK-NEXT:    callq get_bfloats at PLT
+; CHECK-NEXT:    pextrw $0, %xmm1, %eax
+; CHECK-NEXT:    shll $16, %eax
+; CHECK-NEXT:    movl %eax, (%rsp) # 4-byte Spill
+; CHECK-NEXT:    pextrw $0, %xmm0, %eax
+; CHECK-NEXT:    shll $16, %eax
+; CHECK-NEXT:    movd %eax, %xmm0
+; CHECK-NEXT:    callq __truncsfbf2 at PLT
+; CHECK-NEXT:    movd %xmm0, {{[-0-9]+}}(%r{{[sb]}}p) # 4-byte Folded Spill
+; CHECK-NEXT:    movss (%rsp), %xmm0 # 4-byte Reload
+; CHECK-NEXT:    # xmm0 = mem[0],zero,zero,zero
+; CHECK-NEXT:    callq __truncsfbf2 at PLT
+; CHECK-NEXT:    movaps %xmm0, %xmm1
+; CHECK-NEXT:    movss {{[-0-9]+}}(%r{{[sb]}}p), %xmm0 # 4-byte Reload
+; CHECK-NEXT:    # xmm0 = mem[0],zero,zero,zero
+; CHECK-NEXT:    callq take_bfloats at PLT
+; CHECK-NEXT:    popq %rax
+; CHECK-NEXT:    retq
+  %res = call { bfloat, bfloat } @get_bfloats()
+  call void @take_bfloats({ bfloat, bfloat } %res)
+  ret void
+}
+
 declare bfloat @foo(ptr %f)
 declare zeroext i8 @bar(bfloat)
 declare fastcc bfloat @foo_fast(ptr %f)
diff --git a/llvm/test/CodeGen/X86/fast-isel-struct-ret.ll b/llvm/test/CodeGen/X86/fast-isel-struct-ret.ll
new file mode 100644
index 0000000000000..34798ef5abe1f
--- /dev/null
+++ b/llvm/test/CodeGen/X86/fast-isel-struct-ret.ll
@@ -0,0 +1,58 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 6
+; RUN: llc -mtriple=x86_64-unknown-linux-gnu -fast-isel -fast-isel-abort=3 < %s | FileCheck %s
+
+declare { i32, i32 } @get_i32s()
+
+define i32 @call_get_i32s() nounwind {
+; CHECK-LABEL: call_get_i32s:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    pushq %rax
+; CHECK-NEXT:    callq get_i32s at PLT
+; CHECK-NEXT:    addl %edx, %eax
+; CHECK-NEXT:    popq %rcx
+; CHECK-NEXT:    retq
+  %res = call { i32, i32 } @get_i32s()
+  %res.0 = extractvalue { i32, i32 } %res, 0
+  %res.1 = extractvalue { i32, i32 } %res, 1
+  %add = add i32 %res.0, %res.1
+  ret i32 %add
+}
+
+declare { ptr, ptr } @get_ptrs()
+
+define i64 @call_get_ptrs() nounwind {
+; CHECK-LABEL: call_get_ptrs:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    pushq %rax
+; CHECK-NEXT:    callq get_ptrs at PLT
+; CHECK-NEXT:    subq %rdx, %rax
+; CHECK-NEXT:    popq %rcx
+; CHECK-NEXT:    retq
+  %res = call { ptr, ptr } @get_ptrs()
+  %res.0 = extractvalue { ptr, ptr } %res, 0
+  %res.1 = extractvalue { ptr, ptr } %res, 1
+  %res.0.addr = ptrtoaddr ptr %res.0 to i64
+  %res.1.addr = ptrtoaddr ptr %res.1 to i64
+  %sub = sub i64 %res.0.addr, %res.1.addr
+  ret i64 %sub
+}
+
+declare { i64, i1 } @get_i64_and_bool()
+
+define i64 @call_get_i64_and_bool() nounwind {
+; CHECK-LABEL: call_get_i64_and_bool:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    pushq %rax
+; CHECK-NEXT:    callq get_i64_and_bool at PLT
+; CHECK-NEXT:    andb $1, %dl
+; CHECK-NEXT:    movzbl %dl, %ecx
+; CHECK-NEXT:    addq %rcx, %rax
+; CHECK-NEXT:    popq %rcx
+; CHECK-NEXT:    retq
+  %res = call { i64, i1 } @get_i64_and_bool()
+  %res.0 = extractvalue { i64, i1 } %res, 0
+  %res.1 = extractvalue { i64, i1 } %res, 1
+  %res.1.ext = zext i1 %res.1 to i64
+  %add = add i64 %res.0, %res.1.ext
+  ret i64 %add
+}



More information about the llvm-branch-commits mailing list