Skip to content

Commit 90c09c8

Browse files
committed
[Clang] Emit Fake Uses before musttail calls
Fixes the issue reported following the merge of llvm#118026. When a valid `musttail` call is made, the function it is made from must return immediately after the call; if there are any cleanups left in the function, then an error is triggered. This is not necessary for fake uses however - it is perfectly valid to simply emit the fake use "cleanup" code before the tail call, and indeed LLVM will automatically move any fake uses following a tail call to come before the tail call. Therefore, this patch specifically choose to handle fake use cleanups when a musttail call is present by simply emitting them immediately before the call.
1 parent 3cd6b86 commit 90c09c8

File tree

2 files changed

+83
-1
lines changed

2 files changed

+83
-1
lines changed

clang/lib/CodeGen/CGCall.cpp

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6001,8 +6001,18 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo,
60016001
for (auto it = EHStack.find(CurrentCleanupScopeDepth); it != EHStack.end();
60026002
++it) {
60036003
EHCleanupScope *Cleanup = dyn_cast<EHCleanupScope>(&*it);
6004-
if (!(Cleanup && Cleanup->getCleanup()->isRedundantBeforeReturn()))
6004+
// Fake uses can be safely emitted immediately prior to the tail call; we
6005+
// choose to emit the fake use before the call rather than after, to avoid
6006+
// forcing variable values from every call on the "stack" to be preserved
6007+
// simultaneously.
6008+
if (Cleanup && Cleanup->isFakeUse()) {
6009+
CGBuilderTy::InsertPointGuard IPG(Builder);
6010+
Builder.SetInsertPoint(CI);
6011+
Cleanup->getCleanup()->Emit(*this, EHScopeStack::Cleanup::Flags());
6012+
} else if (!(Cleanup &&
6013+
Cleanup->getCleanup()->isRedundantBeforeReturn())) {
60056014
CGM.ErrorUnsupported(MustTailCall, "tail call skipping over cleanups");
6015+
}
60066016
}
60076017
if (CI->getType()->isVoidTy())
60086018
Builder.CreateRetVoid();
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 5
2+
// RUN: %clang_cc1 -emit-llvm -fextend-variable-liveness -o - %s | FileCheck %s
3+
4+
/// Tests that when we have fake uses in a function ending in a musttail call,
5+
/// we emit the fake uses and their corresponding loads immediately prior to the
6+
/// tail call.
7+
8+
struct Class1 {
9+
Class1(int);
10+
};
11+
12+
class Class2 {
13+
static const char *foo(int *, const char *, int *, Class1, const int *,
14+
unsigned long);
15+
template <class>
16+
static char *bar(int *, const char *, int *, Class1, const int *, unsigned long);
17+
};
18+
19+
// CHECK-LABEL: define dso_local noundef ptr @_ZN6Class23fooEPiPKcS0_6Class1PKim(
20+
// CHECK-SAME: ptr noundef [[E:%.*]], ptr noundef [[F:%.*]], ptr noundef [[G:%.*]], ptr noundef [[H:%.*]], i64 noundef [[I:%.*]]) #[[ATTR0:[0-9]+]] align 2 {
21+
// CHECK-NEXT: [[ENTRY:.*:]]
22+
// CHECK-NEXT: [[TMP0:%.*]] = alloca [[STRUCT_CLASS1:%.*]], align 1
23+
// CHECK-NEXT: [[E_ADDR:%.*]] = alloca ptr, align 8
24+
// CHECK-NEXT: [[F_ADDR:%.*]] = alloca ptr, align 8
25+
// CHECK-NEXT: [[G_ADDR:%.*]] = alloca ptr, align 8
26+
// CHECK-NEXT: [[H_ADDR:%.*]] = alloca ptr, align 8
27+
// CHECK-NEXT: [[I_ADDR:%.*]] = alloca i64, align 8
28+
// CHECK-NEXT: [[AGG_TMP:%.*]] = alloca [[STRUCT_CLASS1]], align 1
29+
// CHECK-NEXT: store ptr [[E]], ptr [[E_ADDR]], align 8
30+
// CHECK-NEXT: store ptr [[F]], ptr [[F_ADDR]], align 8
31+
// CHECK-NEXT: store ptr [[G]], ptr [[G_ADDR]], align 8
32+
// CHECK-NEXT: store ptr [[H]], ptr [[H_ADDR]], align 8
33+
// CHECK-NEXT: store i64 [[I]], ptr [[I_ADDR]], align 8
34+
// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[E_ADDR]], align 8
35+
// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[F_ADDR]], align 8
36+
// CHECK-NEXT: [[TMP3:%.*]] = load ptr, ptr [[G_ADDR]], align 8
37+
// CHECK-NEXT: call void @_ZN6Class1C1Ei(ptr noundef nonnull align 1 dereferenceable(1) [[AGG_TMP]], i32 noundef 0)
38+
// CHECK-NEXT: [[TMP4:%.*]] = load ptr, ptr [[H_ADDR]], align 8
39+
// CHECK-NEXT: [[TMP5:%.*]] = load i64, ptr [[I_ADDR]], align 8
40+
// CHECK-NEXT: [[FAKE_USE:%.*]] = load i64, ptr [[I_ADDR]], align 8
41+
// CHECK-NEXT: notail call void (...) @llvm.fake.use(i64 [[FAKE_USE]]) #[[ATTR3:[0-9]+]]
42+
// CHECK-NEXT: [[FAKE_USE1:%.*]] = load ptr, ptr [[H_ADDR]], align 8
43+
// CHECK-NEXT: notail call void (...) @llvm.fake.use(ptr [[FAKE_USE1]]) #[[ATTR3]]
44+
// CHECK-NEXT: [[FAKE_USE2:%.*]] = load [[STRUCT_CLASS1]], ptr [[TMP0]], align 1
45+
// CHECK-NEXT: notail call void (...) @llvm.fake.use([[STRUCT_CLASS1]] [[FAKE_USE2]]) #[[ATTR3]]
46+
// CHECK-NEXT: [[FAKE_USE3:%.*]] = load ptr, ptr [[G_ADDR]], align 8
47+
// CHECK-NEXT: notail call void (...) @llvm.fake.use(ptr [[FAKE_USE3]]) #[[ATTR3]]
48+
// CHECK-NEXT: [[FAKE_USE4:%.*]] = load ptr, ptr [[F_ADDR]], align 8
49+
// CHECK-NEXT: notail call void (...) @llvm.fake.use(ptr [[FAKE_USE4]]) #[[ATTR3]]
50+
// CHECK-NEXT: [[FAKE_USE5:%.*]] = load ptr, ptr [[E_ADDR]], align 8
51+
// CHECK-NEXT: notail call void (...) @llvm.fake.use(ptr [[FAKE_USE5]]) #[[ATTR3]]
52+
// CHECK-NEXT: [[CALL:%.*]] = musttail call noundef ptr @_ZN6Class23barIiEEPcPiPKcS2_6Class1PKim(ptr noundef [[TMP1]], ptr noundef [[TMP2]], ptr noundef [[TMP3]], ptr noundef [[TMP4]], i64 noundef [[TMP5]])
53+
// CHECK-NEXT: ret ptr [[CALL]]
54+
// CHECK: [[BB6:.*:]]
55+
// CHECK-NEXT: [[FAKE_USE6:%.*]] = load i64, ptr [[I_ADDR]], align 8
56+
// CHECK-NEXT: notail call void (...) @llvm.fake.use(i64 [[FAKE_USE6]]) #[[ATTR3]]
57+
// CHECK-NEXT: [[FAKE_USE7:%.*]] = load ptr, ptr [[H_ADDR]], align 8
58+
// CHECK-NEXT: notail call void (...) @llvm.fake.use(ptr [[FAKE_USE7]]) #[[ATTR3]]
59+
// CHECK-NEXT: [[FAKE_USE8:%.*]] = load [[STRUCT_CLASS1]], ptr [[TMP0]], align 1
60+
// CHECK-NEXT: notail call void (...) @llvm.fake.use([[STRUCT_CLASS1]] [[FAKE_USE8]]) #[[ATTR3]]
61+
// CHECK-NEXT: [[FAKE_USE9:%.*]] = load ptr, ptr [[G_ADDR]], align 8
62+
// CHECK-NEXT: notail call void (...) @llvm.fake.use(ptr [[FAKE_USE9]]) #[[ATTR3]]
63+
// CHECK-NEXT: [[FAKE_USE10:%.*]] = load ptr, ptr [[F_ADDR]], align 8
64+
// CHECK-NEXT: notail call void (...) @llvm.fake.use(ptr [[FAKE_USE10]]) #[[ATTR3]]
65+
// CHECK-NEXT: [[FAKE_USE11:%.*]] = load ptr, ptr [[E_ADDR]], align 8
66+
// CHECK-NEXT: notail call void (...) @llvm.fake.use(ptr [[FAKE_USE11]]) #[[ATTR3]]
67+
// CHECK-NEXT: ret ptr undef
68+
//
69+
const char *Class2::foo(int *e, const char *f, int *g, Class1, const int *h,
70+
unsigned long i) {
71+
[[clang::musttail]] return bar<int>(e, f, g, int(), h, i);
72+
}

0 commit comments

Comments
 (0)