Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion clang/lib/CodeGen/CGCall.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6001,8 +6001,18 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo,
for (auto it = EHStack.find(CurrentCleanupScopeDepth); it != EHStack.end();
++it) {
EHCleanupScope *Cleanup = dyn_cast<EHCleanupScope>(&*it);
if (!(Cleanup && Cleanup->getCleanup()->isRedundantBeforeReturn()))
// Fake uses can be safely emitted immediately prior to the tail call; we
// choose to emit the fake use before the call rather than after, to avoid
// forcing variable values from every call on the "stack" to be preserved
// simultaneously.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The second half of this comment is odd. There isn't really a choice about this; the fake uses must be emitted prior to the tail call because there cannot be anything after it.

if (Cleanup && Cleanup->isFakeUse()) {
CGBuilderTy::InsertPointGuard IPG(Builder);
Builder.SetInsertPoint(CI);
Cleanup->getCleanup()->Emit(*this, EHScopeStack::Cleanup::Flags());
} else if (!(Cleanup &&
Cleanup->getCleanup()->isRedundantBeforeReturn())) {
CGM.ErrorUnsupported(MustTailCall, "tail call skipping over cleanups");
}
}
if (CI->getType()->isVoidTy())
Builder.CreateRetVoid();
Expand Down
56 changes: 56 additions & 0 deletions clang/test/CodeGenCXX/fake-use-musttail.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 5
// RUN: %clang_cc1 -emit-llvm -fextend-variable-liveness -o - %s | FileCheck %s

/// Tests that when we have fake uses in a function ending in a musttail call,
/// we emit the fake uses and their corresponding loads immediately prior to the
/// tail call.

extern "C" char *bar(int *, const char *, int *, const int *, unsigned long);

// CHECK-LABEL: define dso_local ptr @foo(
// CHECK-SAME: ptr noundef [[E:%.*]], ptr noundef [[F:%.*]], ptr noundef [[G:%.*]], ptr noundef [[H:%.*]], i64 noundef [[I:%.*]]) #[[ATTR0:[0-9]+]] {
// CHECK-NEXT: [[ENTRY:.*:]]
// CHECK-NEXT: [[E_ADDR:%.*]] = alloca ptr, align 8
// CHECK-NEXT: [[F_ADDR:%.*]] = alloca ptr, align 8
// CHECK-NEXT: [[G_ADDR:%.*]] = alloca ptr, align 8
// CHECK-NEXT: [[H_ADDR:%.*]] = alloca ptr, align 8
// CHECK-NEXT: [[I_ADDR:%.*]] = alloca i64, align 8
// CHECK-NEXT: store ptr [[E]], ptr [[E_ADDR]], align 8
// CHECK-NEXT: store ptr [[F]], ptr [[F_ADDR]], align 8
// CHECK-NEXT: store ptr [[G]], ptr [[G_ADDR]], align 8
// CHECK-NEXT: store ptr [[H]], ptr [[H_ADDR]], align 8
// CHECK-NEXT: store i64 [[I]], ptr [[I_ADDR]], align 8
// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[E_ADDR]], align 8
// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[F_ADDR]], align 8
// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[G_ADDR]], align 8
// CHECK-NEXT: [[TMP3:%.*]] = load ptr, ptr [[H_ADDR]], align 8
// CHECK-NEXT: [[TMP4:%.*]] = load i64, ptr [[I_ADDR]], align 8
// CHECK-NEXT: [[FAKE_USE:%.*]] = load i64, ptr [[I_ADDR]], align 8
// CHECK-NEXT: notail call void (...) @llvm.fake.use(i64 [[FAKE_USE]]) #[[ATTR3:[0-9]+]]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you're not testing the attributes, you can just leave this off; matches don't have to match the entire line.

// CHECK-NEXT: [[FAKE_USE1:%.*]] = load ptr, ptr [[H_ADDR]], align 8
// CHECK-NEXT: notail call void (...) @llvm.fake.use(ptr [[FAKE_USE1]]) #[[ATTR3]]
// CHECK-NEXT: [[FAKE_USE2:%.*]] = load ptr, ptr [[G_ADDR]], align 8
// CHECK-NEXT: notail call void (...) @llvm.fake.use(ptr [[FAKE_USE2]]) #[[ATTR3]]
// CHECK-NEXT: [[FAKE_USE3:%.*]] = load ptr, ptr [[F_ADDR]], align 8
// CHECK-NEXT: notail call void (...) @llvm.fake.use(ptr [[FAKE_USE3]]) #[[ATTR3]]
// CHECK-NEXT: [[FAKE_USE4:%.*]] = load ptr, ptr [[E_ADDR]], align 8
// CHECK-NEXT: notail call void (...) @llvm.fake.use(ptr [[FAKE_USE4]]) #[[ATTR3]]
// CHECK-NEXT: [[CALL:%.*]] = musttail call ptr @bar(ptr noundef [[TMP0]], ptr noundef [[TMP1]], ptr noundef [[TMP2]], ptr noundef [[TMP3]], i64 noundef [[TMP4]])
// CHECK-NEXT: ret ptr [[CALL]]
// CHECK: [[BB5:.*:]]
// CHECK-NEXT: [[FAKE_USE5:%.*]] = load i64, ptr [[I_ADDR]], align 8
// CHECK-NEXT: notail call void (...) @llvm.fake.use(i64 [[FAKE_USE5]]) #[[ATTR3]]
// CHECK-NEXT: [[FAKE_USE6:%.*]] = load ptr, ptr [[H_ADDR]], align 8
// CHECK-NEXT: notail call void (...) @llvm.fake.use(ptr [[FAKE_USE6]]) #[[ATTR3]]
// CHECK-NEXT: [[FAKE_USE7:%.*]] = load ptr, ptr [[G_ADDR]], align 8
// CHECK-NEXT: notail call void (...) @llvm.fake.use(ptr [[FAKE_USE7]]) #[[ATTR3]]
// CHECK-NEXT: [[FAKE_USE8:%.*]] = load ptr, ptr [[F_ADDR]], align 8
// CHECK-NEXT: notail call void (...) @llvm.fake.use(ptr [[FAKE_USE8]]) #[[ATTR3]]
// CHECK-NEXT: [[FAKE_USE9:%.*]] = load ptr, ptr [[E_ADDR]], align 8
// CHECK-NEXT: notail call void (...) @llvm.fake.use(ptr [[FAKE_USE9]]) #[[ATTR3]]
// CHECK-NEXT: ret ptr undef
//
extern "C" const char *foo(int *e, const char *f, int *g, const int *h,
unsigned long i) {
[[clang::musttail]] return bar(e, f, g, h, i);
}
Loading