[CodeGen] Strip Coroutine suffixes when generating pseudo probe (#173834)

CoroSplit pass now creates separate DWARF symbols with the `.resume`,
`.destroy`, `.cleanup` suffixes.
https://github.com/llvm/llvm-project/pull/141889 But pseudo probes are
created in an earlier pass (`SampleProfileProbePass`) before the
CoroSplit, which creates a mismatch of Function GUIDs between the
original function name and the function names with the coroutine
suffixes during the CodeGen when the AsmPrinter iterates through the
`InlinedAt` chain and generates the `InlineStack`.

This will create mismatched pseudo probes in the final binary and
llvm-profgen will also fail when parsing the pseudo probe section. This
fix simply strips the coroutine suffixes from the inline callers' name,
so the CoroSplit changes will be transparent.
This commit is contained in:
HighW4y2H3ll 2026-01-08 11:46:34 -10:00 committed by GitHub
parent a209ff855b
commit 51c37f4c1e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 357 additions and 0 deletions

View File

@ -1219,6 +1219,13 @@ public:
return getCanonicalFnName(FnName, KnownSuffixes, Attr);
}
static StringRef getCanonicalCoroFnName(StringRef FnName,
StringRef Attr = "selected") {
const SmallVector<StringRef, 3> CoroSuffixes{".cleanup", ".destroy",
".resume"};
return getCanonicalFnName(FnName, CoroSuffixes, Attr);
}
static StringRef getCanonicalFnName(StringRef FnName,
ArrayRef<StringRef> Suffixes,
StringRef Attr = "selected") {

View File

@ -41,6 +41,7 @@ add_llvm_component_library(LLVMAsmPrinter
DebugInfoDWARFLowLevel
MC
MCParser
ProfileData
Remarks
Support
Target

View File

@ -17,6 +17,7 @@
#include "llvm/IR/PseudoProbe.h"
#include "llvm/MC/MCPseudoProbe.h"
#include "llvm/MC/MCStreamer.h"
#include "llvm/ProfileData/SampleProf.h"
#ifndef NDEBUG
#include "llvm/IR/Module.h"
@ -36,6 +37,9 @@ void PseudoProbeHandler::emitPseudoProbe(uint64_t Guid, uint64_t Index,
auto *InlinedAt = DebugLoc ? DebugLoc->getInlinedAt() : nullptr;
while (InlinedAt) {
auto Name = InlinedAt->getSubprogramLinkageName();
// Strip Coroutine suffixes from CoroSplit Pass, since pseudo probes are
// generated in an earlier stage.
Name = FunctionSamples::getCanonicalCoroFnName(Name);
// Use caching to avoid redundant md5 computation for build speed.
uint64_t &CallerGuid = NameGuidMap[Name];
if (!CallerGuid)

View File

@ -21,6 +21,7 @@
#include "llvm/IR/Module.h"
#include "llvm/IR/PseudoProbe.h"
#include "llvm/InitializePasses.h"
#include "llvm/ProfileData/SampleProf.h"
#define DEBUG_TYPE "pseudo-probe-inserter"
@ -129,6 +130,10 @@ public:
private:
uint64_t getFuncGUID(Module *M, DILocation *DL) {
auto Name = DL->getSubprogramLinkageName();
// CoroSplit Pass will change the debug info with suffixes i.e. `.resume`,
// `.destroy`, `.cleanup`. Strip these suffixes to make the GUID consistent
// with the pseudo probe
Name = FunctionSamples::getCanonicalCoroFnName(Name);
return Function::getGUIDAssumingExternalLinkage(Name);
}

View File

@ -0,0 +1,340 @@
; RUN: opt < %s -passes='pseudo-probe,cgscc(coro-split),coro-cleanup,always-inline' -mtriple=x86_64 -pass-remarks=inline -S -o %t.ll
; RUN: llc -mtriple=x86_64 -stop-after=pseudo-probe-inserter < %t.ll --filetype=asm -o - | FileCheck -check-prefix=MIR %s
; Making sure PseudoProbeInserter is not generating GUID for `_Z3foov.resume`
; MIR-NOT: PSEUDO_PROBE 4448042984153125393,
; Checking the same for `_Z3foov.cleanup`
; MIR-NOT: PSEUDO_PROBE 3532999944647676065,
; Checking the same for `_Z3foov.destroy`
; MIR-NOT: PSEUDO_PROBE -7235034626494519075,
; RUN: llc -mtriple=x86_64 < %t.ll --filetype=obj -o %t.obj
; RUN: obj2yaml %t.obj | FileCheck -check-prefix=OBJ --match-full-lines %s
; OBJ: - Name: '.pseudo_probe (1)'{{$}}
; OBJ-NEXT: Type: SHT_PROGBITS{{$}}
; OBJ-NEXT: Flags: [ SHF_LINK_ORDER ]{{$}}
; OBJ-NEXT: Link: .text{{$}}
; OBJ-NEXT: AddressAlign: 0x1{{$}}
; Making sure `_Z3foov.resume` is not a top-level function except when its a sentinel probe (i.e. 20{GUID})
; OBJ-NOT: Content: 11921D00879FBA3D{{[0-9A-F]+$}}
; OBJ-NOT: {{([013-9A-F][0-9A-F])|(2[1-9A-F])}}11921D00879FBA3D{{[0-9A-F]+$}}
; Checking the same for `_Z3foov.cleanup`
; OBJ-NOT: Content: A1103324BBBC0731{{[0-9A-F]+$}}
; OBJ-NOT: {{([013-9A-F][0-9A-F])|(2[1-9A-F])}}A1103324BBBC0731{{[0-9A-F]+$}}
; Checking the same for `_Z3foov.destroy`
; OBJ-NOT: Content: DDA8240E57FE979B{{[0-9A-F]+$}}
; OBJ-NOT: {{([013-9A-F][0-9A-F])|(2[1-9A-F])}}DDA8240E57FE979B{{[0-9A-F]+$}}
; OBJ: - Name: .pseudo_probe{{$}}
; OBJ-NEXT: Type: SHT_PROGBITS{{$}}
; OBJ-NEXT: Flags: [ SHF_LINK_ORDER, SHF_GROUP ]{{$}}
; OBJ-NEXT: Link: .text.__clang_call_terminate{{$}}
; Reduced from original source code:
; clang++ -S -g -O0 SampleProfile/Inputs/pseudo-probe-coro-debug-fix.cpp -emit-llvm -Xclang -disable-llvm-passes -std=c++20 -o SampleProfile/pseudo-probe-coro-debug-fix.ll
;
; #include <coroutine>
; struct co_sleep {
; co_sleep(int n) : delay{n} {}
; constexpr bool await_ready() const noexcept { return false; }
; void await_suspend(std::coroutine_handle<> h) const noexcept {}
; void await_resume() const noexcept {}
; int delay;
; };
; struct Task {
; struct promise_type {
; promise_type() = default;
; Task get_return_object() { return {}; }
; std::suspend_never initial_suspend() { return {}; }
; std::suspend_always final_suspend() noexcept { return {}; }
; void unhandled_exception() {}
; };
; };
; Task foo() noexcept {
; co_await co_sleep{10};
; }
;
; int main() {
; foo();
; }
; ModuleID = 'pseudo-probe-coro-debug-fix.cpp'
source_filename = "pseudo-probe-coro-debug-fix.cpp"
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128"
target triple = "x86_64-redhat-linux-gnu"
%"struct.Task::promise_type" = type { i8 }
%struct.Task = type { i8 }
%"struct.std::__n4861::suspend_never" = type { i8 }
%struct.co_sleep = type { i32 }
%"struct.std::__n4861::suspend_always" = type { i8 }
$__clang_call_terminate = comdat any
; Function Attrs: mustprogress noinline nounwind optnone presplitcoroutine uwtable
define dso_local void @_Z3foov() #0 personality ptr @__gxx_personality_v0 !dbg !30 {
entry:
%__promise = alloca %"struct.Task::promise_type", align 1
%undef.agg.tmp = alloca %struct.Task, align 1
%ref.tmp = alloca %"struct.std::__n4861::suspend_never", align 1
%undef.agg.tmp3 = alloca %"struct.std::__n4861::suspend_never", align 1
%ref.tmp5 = alloca %struct.co_sleep, align 4
%exn.slot = alloca ptr, align 8
%ehselector.slot = alloca i32, align 4
%ref.tmp13 = alloca %"struct.std::__n4861::suspend_always", align 1
%undef.agg.tmp14 = alloca %"struct.std::__n4861::suspend_always", align 1
%0 = call token @llvm.coro.id(i32 16, ptr %__promise, ptr null, ptr null), !dbg !33
%1 = call i1 @llvm.coro.alloc(token %0), !dbg !33
br i1 %1, label %coro.alloc, label %coro.init, !dbg !33
coro.alloc: ; preds = %entry
%2 = call i64 @llvm.coro.size.i64(), !dbg !34
%call = invoke noalias noundef nonnull ptr @_Znwm(i64 noundef %2) #12
to label %invoke.cont unwind label %terminate.lpad, !dbg !34
invoke.cont: ; preds = %coro.alloc
br label %coro.init, !dbg !33
coro.init: ; preds = %invoke.cont, %entry
%3 = phi ptr [ null, %entry ], [ %call, %invoke.cont ], !dbg !33
%4 = call ptr @llvm.coro.begin(token %0, ptr %3), !dbg !33
call void @llvm.lifetime.start.p0(ptr %__promise) #2, !dbg !34
#dbg_declare(ptr %__promise, !35, !DIExpression(), !41)
call void @llvm.lifetime.start.p0(ptr %ref.tmp5) #2, !dbg !42
%5 = call token @llvm.coro.save(ptr null), !dbg !44
call void @llvm.coro.await.suspend.void(ptr %ref.tmp5, ptr %4, ptr @_Z3foov.__await_suspend_wrapper__await) #2, !dbg !44
%6 = call i8 @llvm.coro.suspend(token %5, i1 false), !dbg !44
switch i8 %6, label %coro.ret [
i8 0, label %cleanup8
i8 1, label %await.cleanup
], !dbg !44
await.cleanup: ; preds = %coro.init
br label %cleanup8, !dbg !44
coro.final: ; preds = %cleanup8
call void @llvm.lifetime.start.p0(ptr %ref.tmp13) #2, !dbg !34
%7 = call token @llvm.coro.save(ptr null), !dbg !34
call void @llvm.coro.await.suspend.void(ptr %ref.tmp13, ptr %4, ptr @_Z3foov.__await_suspend_wrapper__final) #2, !dbg !34
%8 = call i8 @llvm.coro.suspend(token %7, i1 true), !dbg !34
switch i8 %8, label %coro.ret [
i8 0, label %cleanup16
i8 1, label %final.cleanup
], !dbg !34
final.cleanup: ; preds = %coro.final
br label %cleanup16, !dbg !34
cleanup8: ; preds = %await.cleanup, %coro.init
%cleanup.dest.slot.1 = phi i32 [ 0, %coro.init ], [ 2, %await.cleanup ]
call void @llvm.lifetime.end.p0(ptr %ref.tmp5) #2, !dbg !44
switch i32 %cleanup.dest.slot.1, label %cleanup19 [
i32 0, label %coro.final
]
cleanup16: ; preds = %final.cleanup, %coro.final
%cleanup.dest.slot.2 = phi i32 [ 0, %coro.final ], [ 2, %final.cleanup ]
call void @llvm.lifetime.end.p0(ptr %ref.tmp13) #2, !dbg !34
switch i32 %cleanup.dest.slot.2, label %cleanup19 [
i32 0, label %cleanup.cont18
]
cleanup.cont18: ; preds = %cleanup16
br label %cleanup19, !dbg !34
cleanup19: ; preds = %cleanup.cont18, %cleanup16, %cleanup8
%cleanup.dest.slot.3 = phi i32 [ %cleanup.dest.slot.1, %cleanup8 ], [ %cleanup.dest.slot.2, %cleanup16 ], [ 0, %cleanup.cont18 ], !dbg !41
call void @llvm.lifetime.end.p0(ptr %__promise) #2, !dbg !34
%9 = call ptr @llvm.coro.free(token %0, ptr %4), !dbg !34
%10 = icmp ne ptr %9, null, !dbg !34
br i1 %10, label %coro.free, label %after.coro.free, !dbg !34
coro.free: ; preds = %cleanup19
%11 = call i64 @llvm.coro.size.i64(), !dbg !34
call void @_ZdlPvm(ptr noundef %9, i64 noundef %11) #2, !dbg !34
br label %after.coro.free, !dbg !34
after.coro.free: ; preds = %coro.free, %cleanup19
switch i32 %cleanup.dest.slot.3, label %unreachable [
i32 0, label %cleanup.cont22
i32 2, label %coro.ret
]
cleanup.cont22: ; preds = %after.coro.free
br label %coro.ret, !dbg !34
coro.ret: ; preds = %cleanup.cont22, %after.coro.free, %coro.final, %coro.init
call void @llvm.coro.end(ptr null, i1 false, token none), !dbg !34
ret void, !dbg !34
terminate.lpad: ; preds = %coro.alloc
%12 = landingpad { ptr, i32 }
catch ptr null, !dbg !34
%13 = extractvalue { ptr, i32 } %12, 0, !dbg !34
call void @__clang_call_terminate(ptr %13) #13, !dbg !34
unreachable, !dbg !34
unreachable: ; preds = %after.coro.free
unreachable
}
; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: read)
declare token @llvm.coro.id(i32, ptr readnone, ptr readonly captures(none), ptr) #1
; Function Attrs: nounwind
declare i1 @llvm.coro.alloc(token) #2
; Function Attrs: nobuiltin allocsize(0)
declare dso_local noundef nonnull ptr @_Znwm(i64 noundef) #3
; Function Attrs: nounwind memory(none)
declare i64 @llvm.coro.size.i64() #4
declare dso_local i32 @__gxx_personality_v0(...)
; Function Attrs: noinline noreturn nounwind uwtable
define linkonce_odr hidden void @__clang_call_terminate(ptr noundef %0) #5 comdat {
%2 = call ptr @__cxa_begin_catch(ptr %0) #2
call void @_ZSt9terminatev() #13
unreachable
}
declare dso_local ptr @__cxa_begin_catch(ptr)
declare dso_local void @_ZSt9terminatev()
; Function Attrs: nounwind
declare ptr @llvm.coro.begin(token, ptr writeonly) #2
; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
declare void @llvm.lifetime.start.p0(ptr captures(none)) #6
; Function Attrs: nomerge nounwind
declare token @llvm.coro.save(ptr) #7
; Function Attrs: alwaysinline mustprogress
define internal void @_Z3foov.__await_suspend_wrapper__init(ptr noundef nonnull %0, ptr noundef %1) #8 !dbg !45 {
entry:
call void @_ZSt9terminatev() #13
ret void, !dbg !47
}
declare void @llvm.coro.await.suspend.void(ptr, ptr, ptr)
; Function Attrs: nounwind
declare i8 @llvm.coro.suspend(token, i1) #2
; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
declare void @llvm.lifetime.end.p0(ptr captures(none)) #6
; Function Attrs: alwaysinline mustprogress
define internal void @_Z3foov.__await_suspend_wrapper__await(ptr noundef nonnull %0, ptr noundef %1) #8 !dbg !48 {
entry:
call void @_ZSt9terminatev() #13
ret void, !dbg !49
}
declare dso_local void @__cxa_end_catch()
; Function Attrs: alwaysinline mustprogress
define internal void @_Z3foov.__await_suspend_wrapper__final(ptr noundef nonnull %0, ptr noundef %1) #8 !dbg !50 {
entry:
call void @_ZSt9terminatev() #13
ret void, !dbg !51
}
; Function Attrs: nobuiltin nounwind
declare dso_local void @_ZdlPvm(ptr noundef, i64 noundef) #9
; Function Attrs: nounwind memory(argmem: read)
declare ptr @llvm.coro.free(token, ptr readonly captures(none)) #10
; Function Attrs: nounwind
declare void @llvm.coro.end(ptr, i1, token) #2
; Function Attrs: mustprogress noinline norecurse nounwind optnone uwtable
define dso_local noundef i32 @main() #11 !dbg !52 {
entry:
%undef.agg.tmp = alloca %struct.Task, align 1
call void @_Z3foov() #2, !dbg !55
ret i32 0, !dbg !56
}
attributes #0 = { mustprogress noinline nounwind optnone presplitcoroutine uwtable "frame-pointer"="all" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }
attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: read) }
attributes #2 = { nounwind }
attributes #3 = { nobuiltin allocsize(0) "frame-pointer"="all" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }
attributes #4 = { nounwind memory(none) }
attributes #5 = { noinline noreturn nounwind uwtable "frame-pointer"="all" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }
attributes #6 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
attributes #7 = { nomerge nounwind }
attributes #8 = { alwaysinline mustprogress "min-legal-vector-width"="0" }
attributes #9 = { nobuiltin nounwind "frame-pointer"="all" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }
attributes #10 = { nounwind memory(argmem: read) }
attributes #11 = { mustprogress noinline norecurse nounwind optnone uwtable "frame-pointer"="all" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }
attributes #12 = { allocsize(0) }
attributes #13 = { noreturn nounwind }
!llvm.dbg.cu = !{!0}
!llvm.module.flags = !{!24, !25, !26, !27, !28}
!llvm.ident = !{!29}
!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !1, producer: "clang version 22.0.0git (https://github.com/llvm/llvm-project.git fe218aab737d55dfd67f4f84118744003f45958e)", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, retainedTypes: !2, splitDebugInlining: false, nameTableKind: None)
!1 = !DIFile(filename: "pseudo-probe-coro-debug-fix.cpp", directory: "/tmp", checksumkind: CSK_MD5, checksum: "601552fedf6b88dcf699ea9d066126eb")
!2 = !{!3, !6, !15, !20}
!3 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "promise_type", scope: !4, file: !1, line: 10, size: 8, flags: DIFlagTypePassByValue, elements: !5, identifier: "_ZTSN4Task12promise_typeE")
!4 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "Task", file: !1, line: 9, size: 8, flags: DIFlagTypePassByValue, elements: !5, identifier: "_ZTS4Task")
!5 = !{}
!6 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "coroutine_handle<void>", scope: !8, file: !7, line: 87, size: 64, flags: DIFlagTypePassByValue | DIFlagNonTrivial, elements: !10, templateParams: !13, identifier: "_ZTSNSt7__n486116coroutine_handleIvEE")
!7 = !DIFile(filename: "/usr/lib/gcc/x86_64-redhat-linux/11/../../../../include/c++/11/coroutine", directory: "")
!8 = !DINamespace(name: "__n4861", scope: !9, exportSymbols: true)
!9 = !DINamespace(name: "std", scope: null)
!10 = !{!11}
!11 = !DIDerivedType(tag: DW_TAG_member, name: "_M_fr_ptr", scope: !6, file: !7, line: 131, baseType: !12, size: 64, flags: DIFlagProtected)
!12 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: null, size: 64)
!13 = !{!14}
!14 = !DITemplateTypeParameter(name: "_Promise", type: null, defaulted: true)
!15 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "coroutine_handle<Task::promise_type>", scope: !8, file: !7, line: 182, size: 64, flags: DIFlagTypePassByValue | DIFlagNonTrivial, elements: !16, templateParams: !18, identifier: "_ZTSNSt7__n486116coroutine_handleIN4Task12promise_typeEEE")
!16 = !{!17}
!17 = !DIDerivedType(tag: DW_TAG_member, name: "_M_fr_ptr", scope: !15, file: !7, line: 244, baseType: !12, size: 64, flags: DIFlagPrivate)
!18 = !{!19}
!19 = !DITemplateTypeParameter(name: "_Promise", type: !3)
!20 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "co_sleep", file: !1, line: 2, size: 32, flags: DIFlagTypePassByValue | DIFlagNonTrivial, elements: !21, identifier: "_ZTS8co_sleep")
!21 = !{!22}
!22 = !DIDerivedType(tag: DW_TAG_member, name: "delay", scope: !20, file: !1, line: 7, baseType: !23, size: 32)
!23 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
!24 = !{i32 7, !"Dwarf Version", i32 5}
!25 = !{i32 2, !"Debug Info Version", i32 3}
!26 = !{i32 1, !"wchar_size", i32 4}
!27 = !{i32 7, !"uwtable", i32 2}
!28 = !{i32 7, !"frame-pointer", i32 2}
!29 = !{!"clang version 22.0.0git (https://github.com/llvm/llvm-project.git fe218aab737d55dfd67f4f84118744003f45958e)"}
!30 = distinct !DISubprogram(name: "foo", linkageName: "_Z3foov", scope: !1, file: !1, line: 18, type: !31, scopeLine: 18, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !5)
!31 = !DISubroutineType(types: !32)
!32 = !{!4}
!33 = !DILocation(line: 18, column: 21, scope: !30)
!34 = !DILocation(line: 18, column: 6, scope: !30)
!35 = !DILocalVariable(name: "__promise", scope: !30, type: !36, flags: DIFlagArtificial)
!36 = !DIDerivedType(tag: DW_TAG_typedef, name: "promise_type", scope: !37, file: !7, line: 75, baseType: !3)
!37 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "__coroutine_traits_impl<Task, void>", scope: !8, file: !7, line: 72, size: 8, flags: DIFlagTypePassByValue, elements: !5, templateParams: !38, identifier: "_ZTSNSt7__n486123__coroutine_traits_implI4TaskvEE")
!38 = !{!39, !40}
!39 = !DITemplateTypeParameter(name: "_Result", type: !4)
!40 = !DITemplateTypeParameter(type: null, defaulted: true)
!41 = !DILocation(line: 0, scope: !30)
!42 = !DILocation(line: 19, column: 12, scope: !43)
!43 = distinct !DILexicalBlock(scope: !30, file: !1, line: 18, column: 21)
!44 = !DILocation(line: 19, column: 3, scope: !43)
!45 = distinct !DISubprogram(linkageName: "_Z3foov.__await_suspend_wrapper__init", scope: !1, file: !1, type: !46, flags: DIFlagArtificial, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition, unit: !0, retainedNodes: !5)
!46 = !DISubroutineType(types: !5)
!47 = !DILocation(line: 18, column: 6, scope: !45)
!48 = distinct !DISubprogram(linkageName: "_Z3foov.__await_suspend_wrapper__await", scope: !1, file: !1, type: !46, flags: DIFlagArtificial, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition, unit: !0, retainedNodes: !5)
!49 = !DILocation(line: 19, column: 12, scope: !48)
!50 = distinct !DISubprogram(linkageName: "_Z3foov.__await_suspend_wrapper__final", scope: !1, file: !1, type: !46, flags: DIFlagArtificial, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition, unit: !0, retainedNodes: !5)
!51 = !DILocation(line: 18, column: 6, scope: !50)
!52 = distinct !DISubprogram(name: "main", scope: !1, file: !1, line: 22, type: !53, scopeLine: 22, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0)
!53 = !DISubroutineType(types: !54)
!54 = !{!23}
!55 = !DILocation(line: 23, column: 3, scope: !52)
!56 = !DILocation(line: 24, column: 1, scope: !52)