Skip to content

Commit 522c253

Browse files
committed
[Coroutines] Salvage the debug information for coroutine frames within optimizations
This patch tries to salvage the debug information for the coroutine frames within optimizations by creating the help alloca varaibles with optimizations too. We didn't do this when I implement it initially. I roughtly remember the reason was, we feel the additional help alloca variable may pessimize the performance, which is almost the most important thing under optimizations. But now, it looks like the new inserted help alloca variables can be optimized out by the following optimizations. So it looks like the time to make it available within optimizations. And also, it looks like the following optimizations will convert the generated dbg.declare instrinsic into dbg.value intrinsic within optimizations. In LLVM's test, there is a slightly regression that a dbg.declare for the promise object failed to be remained after this change. But it looks like we won't have a chance to see dbg.declare for the promise object when we split the coroutine as that dbg.declare will be converted into a dbg.value in early stage. So everything looks fine.
1 parent b804516 commit 522c253

File tree

5 files changed

+65
-31
lines changed

5 files changed

+65
-31
lines changed
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// Check that we can still observe the value of the coroutine frame
2+
// with optimizations.
3+
//
4+
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++20 \
5+
// RUN: -emit-llvm %s -debug-info-kind=limited -dwarf-version=5 \
6+
// RUN: -O2 -o - | FileCheck %s
7+
8+
#include "Inputs/coroutine.h"
9+
10+
template <>
11+
struct std::coroutine_traits<void> {
12+
struct promise_type {
13+
void get_return_object();
14+
std::suspend_always initial_suspend();
15+
std::suspend_always final_suspend() noexcept;
16+
void return_void();
17+
void unhandled_exception();
18+
};
19+
};
20+
21+
struct ScalarAwaiter {
22+
template <typename F> void await_suspend(F);
23+
bool await_ready();
24+
int await_resume();
25+
};
26+
27+
extern "C" void UseScalar(int);
28+
29+
extern "C" void f() {
30+
UseScalar(co_await ScalarAwaiter{});
31+
32+
int Val = co_await ScalarAwaiter{};
33+
34+
co_await ScalarAwaiter{};
35+
}
36+
37+
// CHECK: define {{.*}}@f.resume({{.*}} %[[ARG:.*]])
38+
// CHECK: #dbg_value(ptr %[[ARG]], ![[CORO_NUM:[0-9]+]], !DIExpression(DW_OP_deref)
39+
// CHECK: ![[CORO_NUM]] = !DILocalVariable(name: "__coro_frame"

llvm/lib/Transforms/Coroutines/CoroFrame.cpp

Lines changed: 14 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1914,8 +1914,7 @@ static void insertSpills(const FrameDataInfo &FrameData, coro::Shape &Shape) {
19141914
}
19151915
// This dbg.declare is for the main function entry point. It
19161916
// will be deleted in all coro-split functions.
1917-
coro::salvageDebugInfo(ArgToAllocaMap, *DDI, Shape.OptimizeFrame,
1918-
false /*UseEntryValue*/);
1917+
coro::salvageDebugInfo(ArgToAllocaMap, *DDI, false /*UseEntryValue*/);
19191918
};
19201919
for_each(DIs, SalvageOne);
19211920
for_each(DVRs, SalvageOne);
@@ -2852,9 +2851,8 @@ static void collectFrameAlloca(AllocaInst *AI, coro::Shape &Shape,
28522851

28532852
static std::optional<std::pair<Value &, DIExpression &>>
28542853
salvageDebugInfoImpl(SmallDenseMap<Argument *, AllocaInst *, 4> &ArgToAllocaMap,
2855-
bool OptimizeFrame, bool UseEntryValue, Function *F,
2856-
Value *Storage, DIExpression *Expr,
2857-
bool SkipOutermostLoad) {
2854+
bool UseEntryValue, Function *F, Value *Storage,
2855+
DIExpression *Expr, bool SkipOutermostLoad) {
28582856
IRBuilder<> Builder(F->getContext());
28592857
auto InsertPt = F->getEntryBlock().getFirstInsertionPt();
28602858
while (isa<IntrinsicInst>(InsertPt))
@@ -2906,10 +2904,9 @@ salvageDebugInfoImpl(SmallDenseMap<Argument *, AllocaInst *, 4> &ArgToAllocaMap,
29062904

29072905
// If the coroutine frame is an Argument, store it in an alloca to improve
29082906
// its availability (e.g. registers may be clobbered).
2909-
// Avoid this if optimizations are enabled (they would remove the alloca) or
2910-
// if the value is guaranteed to be available through other means (e.g. swift
2911-
// ABI guarantees).
2912-
if (StorageAsArg && !OptimizeFrame && !IsSwiftAsyncArg) {
2907+
// Avoid this if the value is guaranteed to be available through other means
2908+
// (e.g. swift ABI guarantees).
2909+
if (StorageAsArg && !IsSwiftAsyncArg) {
29132910
auto &Cached = ArgToAllocaMap[StorageAsArg];
29142911
if (!Cached) {
29152912
Cached = Builder.CreateAlloca(Storage->getType(), 0, nullptr,
@@ -2932,17 +2929,17 @@ salvageDebugInfoImpl(SmallDenseMap<Argument *, AllocaInst *, 4> &ArgToAllocaMap,
29322929

29332930
void coro::salvageDebugInfo(
29342931
SmallDenseMap<Argument *, AllocaInst *, 4> &ArgToAllocaMap,
2935-
DbgVariableIntrinsic &DVI, bool OptimizeFrame, bool UseEntryValue) {
2932+
DbgVariableIntrinsic &DVI, bool UseEntryValue) {
29362933

29372934
Function *F = DVI.getFunction();
29382935
// Follow the pointer arithmetic all the way to the incoming
29392936
// function argument and convert into a DIExpression.
29402937
bool SkipOutermostLoad = !isa<DbgValueInst>(DVI);
29412938
Value *OriginalStorage = DVI.getVariableLocationOp(0);
29422939

2943-
auto SalvagedInfo = ::salvageDebugInfoImpl(
2944-
ArgToAllocaMap, OptimizeFrame, UseEntryValue, F, OriginalStorage,
2945-
DVI.getExpression(), SkipOutermostLoad);
2940+
auto SalvagedInfo =
2941+
::salvageDebugInfoImpl(ArgToAllocaMap, UseEntryValue, F, OriginalStorage,
2942+
DVI.getExpression(), SkipOutermostLoad);
29462943
if (!SalvagedInfo)
29472944
return;
29482945

@@ -2974,17 +2971,17 @@ void coro::salvageDebugInfo(
29742971

29752972
void coro::salvageDebugInfo(
29762973
SmallDenseMap<Argument *, AllocaInst *, 4> &ArgToAllocaMap,
2977-
DbgVariableRecord &DVR, bool OptimizeFrame, bool UseEntryValue) {
2974+
DbgVariableRecord &DVR, bool UseEntryValue) {
29782975

29792976
Function *F = DVR.getFunction();
29802977
// Follow the pointer arithmetic all the way to the incoming
29812978
// function argument and convert into a DIExpression.
29822979
bool SkipOutermostLoad = DVR.isDbgDeclare();
29832980
Value *OriginalStorage = DVR.getVariableLocationOp(0);
29842981

2985-
auto SalvagedInfo = ::salvageDebugInfoImpl(
2986-
ArgToAllocaMap, OptimizeFrame, UseEntryValue, F, OriginalStorage,
2987-
DVR.getExpression(), SkipOutermostLoad);
2982+
auto SalvagedInfo =
2983+
::salvageDebugInfoImpl(ArgToAllocaMap, UseEntryValue, F, OriginalStorage,
2984+
DVR.getExpression(), SkipOutermostLoad);
29882985
if (!SalvagedInfo)
29892986
return;
29902987

llvm/lib/Transforms/Coroutines/CoroInternal.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,14 +29,14 @@ void replaceCoroFree(CoroIdInst *CoroId, bool Elide);
2929
/// Attempts to rewrite the location operand of debug intrinsics in terms of
3030
/// the coroutine frame pointer, folding pointer offsets into the DIExpression
3131
/// of the intrinsic.
32-
/// If the frame pointer is an Argument, store it into an alloca if
33-
/// OptimizeFrame is false.
32+
/// If the frame pointer is an Argument, store it into an alloca to enhance the
33+
/// debugability.
3434
void salvageDebugInfo(
3535
SmallDenseMap<Argument *, AllocaInst *, 4> &ArgToAllocaMap,
36-
DbgVariableIntrinsic &DVI, bool OptimizeFrame, bool IsEntryPoint);
36+
DbgVariableIntrinsic &DVI, bool IsEntryPoint);
3737
void salvageDebugInfo(
3838
SmallDenseMap<Argument *, AllocaInst *, 4> &ArgToAllocaMap,
39-
DbgVariableRecord &DVR, bool OptimizeFrame, bool UseEntryValue);
39+
DbgVariableRecord &DVR, bool UseEntryValue);
4040

4141
// Keeps data and helper functions for lowering coroutine intrinsics.
4242
struct LowererBase {

llvm/lib/Transforms/Coroutines/CoroSplit.cpp

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -735,11 +735,9 @@ void CoroCloner::salvageDebugInfo() {
735735
bool UseEntryValue =
736736
llvm::Triple(OrigF.getParent()->getTargetTriple()).isArch64Bit();
737737
for (DbgVariableIntrinsic *DVI : Worklist)
738-
coro::salvageDebugInfo(ArgToAllocaMap, *DVI, Shape.OptimizeFrame,
739-
UseEntryValue);
738+
coro::salvageDebugInfo(ArgToAllocaMap, *DVI, UseEntryValue);
740739
for (DbgVariableRecord *DVR : DbgVariableRecords)
741-
coro::salvageDebugInfo(ArgToAllocaMap, *DVR, Shape.OptimizeFrame,
742-
UseEntryValue);
740+
coro::salvageDebugInfo(ArgToAllocaMap, *DVR, UseEntryValue);
743741

744742
// Remove all salvaged dbg.declare intrinsics that became
745743
// either unreachable or stale due to the CoroSplit transformation.
@@ -1962,11 +1960,9 @@ splitCoroutine(Function &F, SmallVectorImpl<Function *> &Clones,
19621960
SmallDenseMap<Argument *, AllocaInst *, 4> ArgToAllocaMap;
19631961
auto [DbgInsts, DbgVariableRecords] = collectDbgVariableIntrinsics(F);
19641962
for (auto *DDI : DbgInsts)
1965-
coro::salvageDebugInfo(ArgToAllocaMap, *DDI, Shape.OptimizeFrame,
1966-
false /*UseEntryValue*/);
1963+
coro::salvageDebugInfo(ArgToAllocaMap, *DDI, false /*UseEntryValue*/);
19671964
for (DbgVariableRecord *DVR : DbgVariableRecords)
1968-
coro::salvageDebugInfo(ArgToAllocaMap, *DVR, Shape.OptimizeFrame,
1969-
false /*UseEntryValue*/);
1965+
coro::salvageDebugInfo(ArgToAllocaMap, *DVR, false /*UseEntryValue*/);
19701966
return Shape;
19711967
}
19721968

llvm/test/Transforms/Coroutines/coro-debug-O2.ll

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
; RUN: opt < %s -passes='module(coro-early),cgscc(coro-split<reuse-storage>),function(sroa)' -S | FileCheck %s
22
; RUN: opt --try-experimental-debuginfo-iterators < %s -passes='module(coro-early),cgscc(coro-split<reuse-storage>),function(sroa)' -S | FileCheck %s
33

4-
; Checks whether the dbg.declare for `__promise` remains valid under O2.
4+
; Checks the dbg informations about promise and coroutine frames under O2.
55

66
; CHECK-LABEL: define internal fastcc void @f.resume({{.*}})
77
; CHECK: entry.resume:
8-
; CHECK: #dbg_declare(ptr %begin, ![[PROMISEVAR_RESUME:[0-9]+]], !DIExpression(
8+
; CHECK: #dbg_value(ptr poison, ![[PROMISEVAR_RESUME:[0-9]+]], !DIExpression(DW_OP_deref, DW_OP_plus_uconst, 16
9+
; CHECK: #dbg_value(ptr %begin, ![[CORO_FRAME:[0-9]+]], !DIExpression(DW_OP_deref)
910
;
11+
; CHECK: ![[CORO_FRAME]] = !DILocalVariable(name: "__coro_frame"
1012
; CHECK: ![[PROMISEVAR_RESUME]] = !DILocalVariable(name: "__promise"
1113
%promise_type = type { i32, i32, double }
1214

0 commit comments

Comments
 (0)