https://github.com/SLTozer updated https://github.com/llvm/llvm-project/pull/136867
>From 90c09c8326077a1ba6797519bbefd014f32b5beb Mon Sep 17 00:00:00 2001 From: Stephen Tozer <stephen.to...@sony.com> Date: Wed, 23 Apr 2025 14:24:50 +0100 Subject: [PATCH 1/3] [Clang] Emit Fake Uses before musttail calls Fixes the issue reported following the merge of #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. --- clang/lib/CodeGen/CGCall.cpp | 12 +++- clang/test/CodeGenCXX/fake-use-musttail.cpp | 72 +++++++++++++++++++++ 2 files changed, 83 insertions(+), 1 deletion(-) create mode 100644 clang/test/CodeGenCXX/fake-use-musttail.cpp diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp index 8cb27420dd911..c8cddfc06fce7 100644 --- a/clang/lib/CodeGen/CGCall.cpp +++ b/clang/lib/CodeGen/CGCall.cpp @@ -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. + 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(); diff --git a/clang/test/CodeGenCXX/fake-use-musttail.cpp b/clang/test/CodeGenCXX/fake-use-musttail.cpp new file mode 100644 index 0000000000000..7d1420ce5fdb6 --- /dev/null +++ b/clang/test/CodeGenCXX/fake-use-musttail.cpp @@ -0,0 +1,72 @@ +// 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. + +struct Class1 { + Class1(int); +}; + +class Class2 { + static const char *foo(int *, const char *, int *, Class1, const int *, + unsigned long); + template <class> + static char *bar(int *, const char *, int *, Class1, const int *, unsigned long); +}; + +// CHECK-LABEL: define dso_local noundef ptr @_ZN6Class23fooEPiPKcS0_6Class1PKim( +// CHECK-SAME: ptr noundef [[E:%.*]], ptr noundef [[F:%.*]], ptr noundef [[G:%.*]], ptr noundef [[H:%.*]], i64 noundef [[I:%.*]]) #[[ATTR0:[0-9]+]] align 2 { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[TMP0:%.*]] = alloca [[STRUCT_CLASS1:%.*]], align 1 +// 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: [[AGG_TMP:%.*]] = alloca [[STRUCT_CLASS1]], align 1 +// 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: [[TMP1:%.*]] = load ptr, ptr [[E_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[F_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load ptr, ptr [[G_ADDR]], align 8 +// CHECK-NEXT: call void @_ZN6Class1C1Ei(ptr noundef nonnull align 1 dereferenceable(1) [[AGG_TMP]], i32 noundef 0) +// CHECK-NEXT: [[TMP4:%.*]] = load ptr, ptr [[H_ADDR]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = 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]+]] +// 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 [[STRUCT_CLASS1]], ptr [[TMP0]], align 1 +// CHECK-NEXT: notail call void (...) @llvm.fake.use([[STRUCT_CLASS1]] [[FAKE_USE2]]) #[[ATTR3]] +// CHECK-NEXT: [[FAKE_USE3:%.*]] = load ptr, ptr [[G_ADDR]], align 8 +// CHECK-NEXT: notail call void (...) @llvm.fake.use(ptr [[FAKE_USE3]]) #[[ATTR3]] +// CHECK-NEXT: [[FAKE_USE4:%.*]] = load ptr, ptr [[F_ADDR]], align 8 +// CHECK-NEXT: notail call void (...) @llvm.fake.use(ptr [[FAKE_USE4]]) #[[ATTR3]] +// CHECK-NEXT: [[FAKE_USE5:%.*]] = load ptr, ptr [[E_ADDR]], align 8 +// CHECK-NEXT: notail call void (...) @llvm.fake.use(ptr [[FAKE_USE5]]) #[[ATTR3]] +// CHECK-NEXT: [[CALL:%.*]] = musttail call noundef ptr @_ZN6Class23barIiEEPcPiPKcS2_6Class1PKim(ptr noundef [[TMP1]], ptr noundef [[TMP2]], ptr noundef [[TMP3]], ptr noundef [[TMP4]], i64 noundef [[TMP5]]) +// CHECK-NEXT: ret ptr [[CALL]] +// CHECK: [[BB6:.*:]] +// CHECK-NEXT: [[FAKE_USE6:%.*]] = load i64, ptr [[I_ADDR]], align 8 +// CHECK-NEXT: notail call void (...) @llvm.fake.use(i64 [[FAKE_USE6]]) #[[ATTR3]] +// CHECK-NEXT: [[FAKE_USE7:%.*]] = load ptr, ptr [[H_ADDR]], align 8 +// CHECK-NEXT: notail call void (...) @llvm.fake.use(ptr [[FAKE_USE7]]) #[[ATTR3]] +// CHECK-NEXT: [[FAKE_USE8:%.*]] = load [[STRUCT_CLASS1]], ptr [[TMP0]], align 1 +// CHECK-NEXT: notail call void (...) @llvm.fake.use([[STRUCT_CLASS1]] [[FAKE_USE8]]) #[[ATTR3]] +// CHECK-NEXT: [[FAKE_USE9:%.*]] = load ptr, ptr [[G_ADDR]], align 8 +// CHECK-NEXT: notail call void (...) @llvm.fake.use(ptr [[FAKE_USE9]]) #[[ATTR3]] +// CHECK-NEXT: [[FAKE_USE10:%.*]] = load ptr, ptr [[F_ADDR]], align 8 +// CHECK-NEXT: notail call void (...) @llvm.fake.use(ptr [[FAKE_USE10]]) #[[ATTR3]] +// CHECK-NEXT: [[FAKE_USE11:%.*]] = load ptr, ptr [[E_ADDR]], align 8 +// CHECK-NEXT: notail call void (...) @llvm.fake.use(ptr [[FAKE_USE11]]) #[[ATTR3]] +// CHECK-NEXT: ret ptr undef +// +const char *Class2::foo(int *e, const char *f, int *g, Class1, const int *h, + unsigned long i) { + [[clang::musttail]] return bar<int>(e, f, g, int(), h, i); +} >From 2765fb7d11c1ac294301baba20ef5f44d641be17 Mon Sep 17 00:00:00 2001 From: Stephen Tozer <stephen.to...@sony.com> Date: Thu, 24 Apr 2025 11:27:03 +0100 Subject: [PATCH 2/3] Simplify test --- clang/test/CodeGenCXX/fake-use-musttail.cpp | 65 ++++++++------------- 1 file changed, 25 insertions(+), 40 deletions(-) diff --git a/clang/test/CodeGenCXX/fake-use-musttail.cpp b/clang/test/CodeGenCXX/fake-use-musttail.cpp index 7d1420ce5fdb6..c0dacca8eab18 100644 --- a/clang/test/CodeGenCXX/fake-use-musttail.cpp +++ b/clang/test/CodeGenCXX/fake-use-musttail.cpp @@ -5,68 +5,53 @@ /// we emit the fake uses and their corresponding loads immediately prior to the /// tail call. -struct Class1 { - Class1(int); -}; +template <class> +char *bar(int *, const char *, int *, const int *, unsigned long); -class Class2 { - static const char *foo(int *, const char *, int *, Class1, const int *, - unsigned long); - template <class> - static char *bar(int *, const char *, int *, Class1, const int *, unsigned long); -}; - -// CHECK-LABEL: define dso_local noundef ptr @_ZN6Class23fooEPiPKcS0_6Class1PKim( -// CHECK-SAME: ptr noundef [[E:%.*]], ptr noundef [[F:%.*]], ptr noundef [[G:%.*]], ptr noundef [[H:%.*]], i64 noundef [[I:%.*]]) #[[ATTR0:[0-9]+]] align 2 { +// CHECK-LABEL: define dso_local noundef ptr @_Z3fooPiPKcS_PKim( +// 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: [[TMP0:%.*]] = alloca [[STRUCT_CLASS1:%.*]], align 1 // 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: [[AGG_TMP:%.*]] = alloca [[STRUCT_CLASS1]], align 1 // 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: [[TMP1:%.*]] = load ptr, ptr [[E_ADDR]], align 8 -// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[F_ADDR]], align 8 -// CHECK-NEXT: [[TMP3:%.*]] = load ptr, ptr [[G_ADDR]], align 8 -// CHECK-NEXT: call void @_ZN6Class1C1Ei(ptr noundef nonnull align 1 dereferenceable(1) [[AGG_TMP]], i32 noundef 0) -// CHECK-NEXT: [[TMP4:%.*]] = load ptr, ptr [[H_ADDR]], align 8 -// CHECK-NEXT: [[TMP5:%.*]] = load i64, 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]+]] // 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 [[STRUCT_CLASS1]], ptr [[TMP0]], align 1 -// CHECK-NEXT: notail call void (...) @llvm.fake.use([[STRUCT_CLASS1]] [[FAKE_USE2]]) #[[ATTR3]] -// CHECK-NEXT: [[FAKE_USE3:%.*]] = load ptr, ptr [[G_ADDR]], align 8 +// 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 [[F_ADDR]], align 8 +// 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: [[FAKE_USE5:%.*]] = load ptr, ptr [[E_ADDR]], align 8 -// CHECK-NEXT: notail call void (...) @llvm.fake.use(ptr [[FAKE_USE5]]) #[[ATTR3]] -// CHECK-NEXT: [[CALL:%.*]] = musttail call noundef ptr @_ZN6Class23barIiEEPcPiPKcS2_6Class1PKim(ptr noundef [[TMP1]], ptr noundef [[TMP2]], ptr noundef [[TMP3]], ptr noundef [[TMP4]], i64 noundef [[TMP5]]) +// CHECK-NEXT: [[CALL:%.*]] = musttail call noundef ptr @_Z3barIiEPcPiPKcS1_PKim(ptr noundef [[TMP0]], ptr noundef [[TMP1]], ptr noundef [[TMP2]], ptr noundef [[TMP3]], i64 noundef [[TMP4]]) // CHECK-NEXT: ret ptr [[CALL]] -// CHECK: [[BB6:.*:]] -// CHECK-NEXT: [[FAKE_USE6:%.*]] = load i64, ptr [[I_ADDR]], align 8 -// CHECK-NEXT: notail call void (...) @llvm.fake.use(i64 [[FAKE_USE6]]) #[[ATTR3]] -// CHECK-NEXT: [[FAKE_USE7:%.*]] = load ptr, ptr [[H_ADDR]], align 8 +// 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 [[STRUCT_CLASS1]], ptr [[TMP0]], align 1 -// CHECK-NEXT: notail call void (...) @llvm.fake.use([[STRUCT_CLASS1]] [[FAKE_USE8]]) #[[ATTR3]] -// CHECK-NEXT: [[FAKE_USE9:%.*]] = load ptr, ptr [[G_ADDR]], align 8 +// 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: [[FAKE_USE10:%.*]] = load ptr, ptr [[F_ADDR]], align 8 -// CHECK-NEXT: notail call void (...) @llvm.fake.use(ptr [[FAKE_USE10]]) #[[ATTR3]] -// CHECK-NEXT: [[FAKE_USE11:%.*]] = load ptr, ptr [[E_ADDR]], align 8 -// CHECK-NEXT: notail call void (...) @llvm.fake.use(ptr [[FAKE_USE11]]) #[[ATTR3]] // CHECK-NEXT: ret ptr undef // -const char *Class2::foo(int *e, const char *f, int *g, Class1, const int *h, +const char *foo(int *e, const char *f, int *g, const int *h, unsigned long i) { - [[clang::musttail]] return bar<int>(e, f, g, int(), h, i); + [[clang::musttail]] return bar<int>(e, f, g, h, i); } >From d0e4e68a2f4f16a721fa0b265f023e030b441bd5 Mon Sep 17 00:00:00 2001 From: Stephen Tozer <stephen.to...@sony.com> Date: Thu, 24 Apr 2025 12:55:09 +0100 Subject: [PATCH 3/3] Use extern C to prevent name mangling --- clang/test/CodeGenCXX/fake-use-musttail.cpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/clang/test/CodeGenCXX/fake-use-musttail.cpp b/clang/test/CodeGenCXX/fake-use-musttail.cpp index c0dacca8eab18..d0ac8ca5bea90 100644 --- a/clang/test/CodeGenCXX/fake-use-musttail.cpp +++ b/clang/test/CodeGenCXX/fake-use-musttail.cpp @@ -5,10 +5,9 @@ /// we emit the fake uses and their corresponding loads immediately prior to the /// tail call. -template <class> -char *bar(int *, const char *, int *, const int *, unsigned long); +extern "C" char *bar(int *, const char *, int *, const int *, unsigned long); -// CHECK-LABEL: define dso_local noundef ptr @_Z3fooPiPKcS_PKim( +// 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 @@ -36,7 +35,7 @@ char *bar(int *, const char *, int *, const int *, unsigned long); // 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 noundef ptr @_Z3barIiEPcPiPKcS1_PKim(ptr noundef [[TMP0]], ptr noundef [[TMP1]], ptr noundef [[TMP2]], ptr noundef [[TMP3]], i64 noundef [[TMP4]]) +// 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 @@ -51,7 +50,7 @@ char *bar(int *, const char *, int *, const int *, unsigned long); // CHECK-NEXT: notail call void (...) @llvm.fake.use(ptr [[FAKE_USE9]]) #[[ATTR3]] // CHECK-NEXT: ret ptr undef // -const char *foo(int *e, const char *f, int *g, const int *h, +extern "C" const char *foo(int *e, const char *f, int *g, const int *h, unsigned long i) { - [[clang::musttail]] return bar<int>(e, f, g, h, i); + [[clang::musttail]] return bar(e, f, g, h, i); } _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits