[llvm] 35b3886 - [win/arm64] Enable tail call with inreg arguments when possible (#134671)

via llvm-commits llvm-commits at lists.llvm.org
Tue Apr 8 06:25:31 PDT 2025


Author: Hans Wennborg
Date: 2025-04-08T15:25:28+02:00
New Revision: 35b3886382f0172aa76bfbfc8e731e46c9c11cc3

URL: https://github.com/llvm/llvm-project/commit/35b3886382f0172aa76bfbfc8e731e46c9c11cc3
DIFF: https://github.com/llvm/llvm-project/commit/35b3886382f0172aa76bfbfc8e731e46c9c11cc3.diff

LOG: [win/arm64] Enable tail call with inreg arguments when possible (#134671)

Tail calls were disabled from callers with inreg parameters in 5dc8aeb
with a fixme to check if the callee also takes an inreg parameter.

The issue is that inreg parameters (which are passed in x0 or x1 for
free and member functions respectively) are supposed to be returned (in
x0) at the end of the function. In case of a tail call, that means the
callee needs to return the same value as the caller would.

We can check for that case, and it's not as niche as it sounds, as
that's how Clang will lower one function with an sret return value
calling another, such as:

```
struct T { int x; };
struct S {
    T foo();
    T bar();
};
T S::foo() { return bar(); } // foo's sret argument will get passed directly to bar
```

Fixes #133098

Added: 
    

Modified: 
    llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
    llvm/test/CodeGen/AArch64/arm64-windows-tailcall.ll

Removed: 
    


################################################################################
diff  --git a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
index 3f42501828400..e366d7cb54490 100644
--- a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
+++ b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
@@ -8703,13 +8703,22 @@ bool AArch64TargetLowering::isEligibleForTailCallOptimization(
       return false;
 
     // On Windows, "inreg" attributes signify non-aggregate indirect returns.
-    // In this case, it is necessary to save/restore X0 in the callee. Tail
-    // call opt interferes with this. So we disable tail call opt when the
-    // caller has an argument with "inreg" attribute.
-
-    // FIXME: Check whether the callee also has an "inreg" argument.
-    if (i->hasInRegAttr())
-      return false;
+    // In this case, it is necessary to save X0/X1 in the callee and return it
+    // in X0. Tail call opt may interfere with this, so we disable tail call
+    // opt when the caller has an "inreg" attribute -- except if the callee
+    // also has that attribute on the same argument, and the same value is
+    // passed.
+    if (i->hasInRegAttr()) {
+      unsigned ArgIdx = i - CallerF.arg_begin();
+      if (!CLI.CB || CLI.CB->arg_size() <= ArgIdx)
+        return false;
+      AttributeSet Attrs = CLI.CB->getParamAttributes(ArgIdx);
+      if (!Attrs.hasAttribute(Attribute::InReg) ||
+          !Attrs.hasAttribute(Attribute::StructRet) || !i->hasStructRetAttr() ||
+          CLI.CB->getArgOperand(ArgIdx) != i) {
+        return false;
+      }
+    }
   }
 
   if (canGuaranteeTCO(CalleeCC, getTargetMachine().Options.GuaranteedTailCallOpt))

diff  --git a/llvm/test/CodeGen/AArch64/arm64-windows-tailcall.ll b/llvm/test/CodeGen/AArch64/arm64-windows-tailcall.ll
index 55799d0dcb2d2..cd0a77a280aec 100644
--- a/llvm/test/CodeGen/AArch64/arm64-windows-tailcall.ll
+++ b/llvm/test/CodeGen/AArch64/arm64-windows-tailcall.ll
@@ -16,3 +16,27 @@ entry:
 }
 
 declare dso_local void @"?foo"(ptr dereferenceable(4))
+
+
+declare void @inreg_callee(ptr, ptr inreg sret(%class.C))
+
+define void @inreg_caller_1(ptr %a, ptr inreg sret(%class.C) %b) {
+; A 
diff erent value is passed to the inreg parameter, so tail call is not possible.
+; CHECK-LABEL: inreg_caller_1
+; CHECK: mov x19, x1
+; CHECK: bl inreg_callee
+; CHECK: mov x0, x19
+
+  tail call void @inreg_callee(ptr %b, ptr inreg sret(%class.C) %a)
+  ret void
+}
+
+define void @inreg_caller_2(ptr %a, ptr inreg sret(%class.C) %b) {
+; The inreg attribute and value line up between caller and callee, so it can
+; be tail called.
+; CHECK-LABEL: inreg_caller_2
+; CHECK: b inreg_callee
+
+  tail call void @inreg_callee(ptr %a, ptr inreg sret(%class.C) %b)
+  ret void
+}


        


More information about the llvm-commits mailing list