Skip to content

Commit 35b3886

Browse files
authored
[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
1 parent fdf2094 commit 35b3886

File tree

2 files changed

+40
-7
lines changed

2 files changed

+40
-7
lines changed

llvm/lib/Target/AArch64/AArch64ISelLowering.cpp

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8703,13 +8703,22 @@ bool AArch64TargetLowering::isEligibleForTailCallOptimization(
87038703
return false;
87048704

87058705
// On Windows, "inreg" attributes signify non-aggregate indirect returns.
8706-
// In this case, it is necessary to save/restore X0 in the callee. Tail
8707-
// call opt interferes with this. So we disable tail call opt when the
8708-
// caller has an argument with "inreg" attribute.
8709-
8710-
// FIXME: Check whether the callee also has an "inreg" argument.
8711-
if (i->hasInRegAttr())
8712-
return false;
8706+
// In this case, it is necessary to save X0/X1 in the callee and return it
8707+
// in X0. Tail call opt may interfere with this, so we disable tail call
8708+
// opt when the caller has an "inreg" attribute -- except if the callee
8709+
// also has that attribute on the same argument, and the same value is
8710+
// passed.
8711+
if (i->hasInRegAttr()) {
8712+
unsigned ArgIdx = i - CallerF.arg_begin();
8713+
if (!CLI.CB || CLI.CB->arg_size() <= ArgIdx)
8714+
return false;
8715+
AttributeSet Attrs = CLI.CB->getParamAttributes(ArgIdx);
8716+
if (!Attrs.hasAttribute(Attribute::InReg) ||
8717+
!Attrs.hasAttribute(Attribute::StructRet) || !i->hasStructRetAttr() ||
8718+
CLI.CB->getArgOperand(ArgIdx) != i) {
8719+
return false;
8720+
}
8721+
}
87138722
}
87148723

87158724
if (canGuaranteeTCO(CalleeCC, getTargetMachine().Options.GuaranteedTailCallOpt))

llvm/test/CodeGen/AArch64/arm64-windows-tailcall.ll

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,27 @@ entry:
1616
}
1717

1818
declare dso_local void @"?foo"(ptr dereferenceable(4))
19+
20+
21+
declare void @inreg_callee(ptr, ptr inreg sret(%class.C))
22+
23+
define void @inreg_caller_1(ptr %a, ptr inreg sret(%class.C) %b) {
24+
; A different value is passed to the inreg parameter, so tail call is not possible.
25+
; CHECK-LABEL: inreg_caller_1
26+
; CHECK: mov x19, x1
27+
; CHECK: bl inreg_callee
28+
; CHECK: mov x0, x19
29+
30+
tail call void @inreg_callee(ptr %b, ptr inreg sret(%class.C) %a)
31+
ret void
32+
}
33+
34+
define void @inreg_caller_2(ptr %a, ptr inreg sret(%class.C) %b) {
35+
; The inreg attribute and value line up between caller and callee, so it can
36+
; be tail called.
37+
; CHECK-LABEL: inreg_caller_2
38+
; CHECK: b inreg_callee
39+
40+
tail call void @inreg_callee(ptr %a, ptr inreg sret(%class.C) %b)
41+
ret void
42+
}

0 commit comments

Comments
 (0)