Skip to content

Commit 07514fa

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 902b2a2 commit 07514fa

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
@@ -1947,8 +1947,7 @@ static void insertSpills(const FrameDataInfo &FrameData, coro::Shape &Shape) {
19471947
}
19481948
// This dbg.declare is for the main function entry point. It
19491949
// will be deleted in all coro-split functions.
1950-
coro::salvageDebugInfo(ArgToAllocaMap, *DDI, Shape.OptimizeFrame,
1951-
false /*UseEntryValue*/);
1950+
coro::salvageDebugInfo(ArgToAllocaMap, *DDI, false /*UseEntryValue*/);
19521951
};
19531952
for_each(DIs, SalvageOne);
19541953
for_each(DVRs, SalvageOne);
@@ -2885,9 +2884,8 @@ static void collectFrameAlloca(AllocaInst *AI, coro::Shape &Shape,
28852884

28862885
static std::optional<std::pair<Value &, DIExpression &>>
28872886
salvageDebugInfoImpl(SmallDenseMap<Argument *, AllocaInst *, 4> &ArgToAllocaMap,
2888-
bool OptimizeFrame, bool UseEntryValue, Function *F,
2889-
Value *Storage, DIExpression *Expr,
2890-
bool SkipOutermostLoad) {
2887+
bool UseEntryValue, Function *F, Value *Storage,
2888+
DIExpression *Expr, bool SkipOutermostLoad) {
28912889
IRBuilder<> Builder(F->getContext());
28922890
auto InsertPt = F->getEntryBlock().getFirstInsertionPt();
28932891
while (isa<IntrinsicInst>(InsertPt))
@@ -2939,10 +2937,9 @@ salvageDebugInfoImpl(SmallDenseMap<Argument *, AllocaInst *, 4> &ArgToAllocaMap,
29392937

29402938
// If the coroutine frame is an Argument, store it in an alloca to improve
29412939
// its availability (e.g. registers may be clobbered).
2942-
// Avoid this if optimizations are enabled (they would remove the alloca) or
2943-
// if the value is guaranteed to be available through other means (e.g. swift
2944-
// ABI guarantees).
2945-
if (StorageAsArg && !OptimizeFrame && !IsSwiftAsyncArg) {
2940+
// Avoid this if the value is guaranteed to be available through other means
2941+
// (e.g. swift ABI guarantees).
2942+
if (StorageAsArg && !IsSwiftAsyncArg) {
29462943
auto &Cached = ArgToAllocaMap[StorageAsArg];
29472944
if (!Cached) {
29482945
Cached = Builder.CreateAlloca(Storage->getType(), 0, nullptr,
@@ -2965,17 +2962,17 @@ salvageDebugInfoImpl(SmallDenseMap<Argument *, AllocaInst *, 4> &ArgToAllocaMap,
29652962

29662963
void coro::salvageDebugInfo(
29672964
SmallDenseMap<Argument *, AllocaInst *, 4> &ArgToAllocaMap,
2968-
DbgVariableIntrinsic &DVI, bool OptimizeFrame, bool UseEntryValue) {
2965+
DbgVariableIntrinsic &DVI, bool UseEntryValue) {
29692966

29702967
Function *F = DVI.getFunction();
29712968
// Follow the pointer arithmetic all the way to the incoming
29722969
// function argument and convert into a DIExpression.
29732970
bool SkipOutermostLoad = !isa<DbgValueInst>(DVI);
29742971
Value *OriginalStorage = DVI.getVariableLocationOp(0);
29752972

2976-
auto SalvagedInfo = ::salvageDebugInfoImpl(
2977-
ArgToAllocaMap, OptimizeFrame, UseEntryValue, F, OriginalStorage,
2978-
DVI.getExpression(), SkipOutermostLoad);
2973+
auto SalvagedInfo =
2974+
::salvageDebugInfoImpl(ArgToAllocaMap, UseEntryValue, F, OriginalStorage,
2975+
DVI.getExpression(), SkipOutermostLoad);
29792976
if (!SalvagedInfo)
29802977
return;
29812978

@@ -3007,17 +3004,17 @@ void coro::salvageDebugInfo(
30073004

30083005
void coro::salvageDebugInfo(
30093006
SmallDenseMap<Argument *, AllocaInst *, 4> &ArgToAllocaMap,
3010-
DbgVariableRecord &DVR, bool OptimizeFrame, bool UseEntryValue) {
3007+
DbgVariableRecord &DVR, bool UseEntryValue) {
30113008

30123009
Function *F = DVR.getFunction();
30133010
// Follow the pointer arithmetic all the way to the incoming
30143011
// function argument and convert into a DIExpression.
30153012
bool SkipOutermostLoad = DVR.isDbgDeclare();
30163013
Value *OriginalStorage = DVR.getVariableLocationOp(0);
30173014

3018-
auto SalvagedInfo = ::salvageDebugInfoImpl(
3019-
ArgToAllocaMap, OptimizeFrame, UseEntryValue, F, OriginalStorage,
3020-
DVR.getExpression(), SkipOutermostLoad);
3015+
auto SalvagedInfo =
3016+
::salvageDebugInfoImpl(ArgToAllocaMap, UseEntryValue, F, OriginalStorage,
3017+
DVR.getExpression(), SkipOutermostLoad);
30213018
if (!SalvagedInfo)
30223019
return;
30233020

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.
@@ -1961,11 +1959,9 @@ splitCoroutine(Function &F, SmallVectorImpl<Function *> &Clones,
19611959
SmallDenseMap<Argument *, AllocaInst *, 4> ArgToAllocaMap;
19621960
auto [DbgInsts, DbgVariableRecords] = collectDbgVariableIntrinsics(F);
19631961
for (auto *DDI : DbgInsts)
1964-
coro::salvageDebugInfo(ArgToAllocaMap, *DDI, Shape.OptimizeFrame,
1965-
false /*UseEntryValue*/);
1962+
coro::salvageDebugInfo(ArgToAllocaMap, *DDI, false /*UseEntryValue*/);
19661963
for (DbgVariableRecord *DVR : DbgVariableRecords)
1967-
coro::salvageDebugInfo(ArgToAllocaMap, *DVR, Shape.OptimizeFrame,
1968-
false /*UseEntryValue*/);
1964+
coro::salvageDebugInfo(ArgToAllocaMap, *DVR, false /*UseEntryValue*/);
19691965
return Shape;
19701966
}
19711967

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)