Skip to content

Commit 617c9d5

Browse files
[Corosplit] Prepend entry_value in swift async dbg values
When the coroutine splitter splits swift coroutines, variables in the new funclets are now described in terms of the frame pointer, which is always placed at a ABI-specified register whose contents are valid upon function entry. As such, debug intrinsics must be prepended by the `entry_value` operation. Depends on D149778 Differential Revision: https://reviews.llvm.org/D149779
1 parent becfcdf commit 617c9d5

File tree

3 files changed

+194
-24
lines changed

3 files changed

+194
-24
lines changed

llvm/docs/LangRef.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6018,6 +6018,9 @@ The current supported opcode vocabulary is limited:
60186018
- ``AsmPrinter`` pass when a call site parameter value
60196019
(``DW_AT_call_site_parameter_value``) is represented as entry value of
60206020
the parameter.
6021+
- ``CoroSplit`` pass, which may move variables from allocas into a
6022+
coroutine frame. If the coroutine frame is a :ref:`_swiftasync` argument,
6023+
the variable is described with an ``DW_OP_LLVM_entry_value`` operation.
60216024

60226025
- ``DW_OP_LLVM_arg, N`` is used in debug intrinsics that refer to more than one
60236026
value, such as one that calculates the sum of two registers. This is always

llvm/lib/Transforms/Coroutines/CoroFrame.cpp

Lines changed: 30 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -2877,31 +2877,37 @@ void coro::salvageDebugInfo(
28772877
if (!Storage)
28782878
return;
28792879

2880-
// Store a pointer to the coroutine frame object in an alloca so it
2881-
// is available throughout the function when producing unoptimized
2882-
// code. Extending the lifetime this way is correct because the
2883-
// variable has been declared by a dbg.declare intrinsic.
2884-
//
2885-
// Avoid to create the alloca would be eliminated by optimization
2886-
// passes and the corresponding dbg.declares would be invalid.
2887-
if (!OptimizeFrame)
2888-
if (auto *Arg = dyn_cast<Argument>(Storage)) {
2889-
auto &Cached = ArgToAllocaMap[Arg];
2890-
if (!Cached) {
2891-
Cached = Builder.CreateAlloca(Storage->getType(), 0, nullptr,
2892-
Arg->getName() + ".debug");
2893-
Builder.CreateStore(Storage, Cached);
2894-
}
2895-
Storage = Cached;
2896-
// FIXME: LLVM lacks nuanced semantics to differentiate between
2897-
// memory and direct locations at the IR level. The backend will
2898-
// turn a dbg.declare(alloca, ..., DIExpression()) into a memory
2899-
// location. Thus, if there are deref and offset operations in the
2900-
// expression, we need to add a DW_OP_deref at the *start* of the
2901-
// expression to first load the contents of the alloca before
2902-
// adjusting it with the expression.
2903-
Expr = DIExpression::prepend(Expr, DIExpression::DerefBefore);
2880+
auto *StorageAsArg = dyn_cast<Argument>(Storage);
2881+
const bool IsSwiftAsyncArg =
2882+
StorageAsArg && StorageAsArg->hasAttribute(Attribute::SwiftAsync);
2883+
2884+
// Swift async arguments are described by an entry value of the ABI-defined
2885+
// register containing the coroutine context.
2886+
if (IsSwiftAsyncArg && !Expr->isEntryValue())
2887+
Expr = DIExpression::prepend(Expr, DIExpression::EntryValue);
2888+
2889+
// If the coroutine frame is an Argument, store it in an alloca to improve
2890+
// its availability (e.g. registers may be clobbered).
2891+
// Avoid this if optimizations are enabled (they would remove the alloca) or
2892+
// if the value is guaranteed to be available through other means (e.g. swift
2893+
// ABI guarantees).
2894+
if (StorageAsArg && !OptimizeFrame && !IsSwiftAsyncArg) {
2895+
auto &Cached = ArgToAllocaMap[StorageAsArg];
2896+
if (!Cached) {
2897+
Cached = Builder.CreateAlloca(Storage->getType(), 0, nullptr,
2898+
Storage->getName() + ".debug");
2899+
Builder.CreateStore(Storage, Cached);
29042900
}
2901+
Storage = Cached;
2902+
// FIXME: LLVM lacks nuanced semantics to differentiate between
2903+
// memory and direct locations at the IR level. The backend will
2904+
// turn a dbg.declare(alloca, ..., DIExpression()) into a memory
2905+
// location. Thus, if there are deref and offset operations in the
2906+
// expression, we need to add a DW_OP_deref at the *start* of the
2907+
// expression to first load the contents of the alloca before
2908+
// adjusting it with the expression.
2909+
Expr = DIExpression::prepend(Expr, DIExpression::DerefBefore);
2910+
}
29052911

29062912
DVI->replaceVariableLocationOp(OriginalStorage, Storage);
29072913
DVI->setExpression(Expr);
Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
; RUN: opt %s -S -passes='module(coro-early),cgscc(coro-split,simplifycfg)' -o - | FileCheck %s
2+
target datalayout = "e-m:o-i64:64-i128:128-n32:64-S128"
3+
target triple = "arm64-apple-macosx13.0.0"
4+
5+
; This coroutine has one split point and two variables defined by:
6+
; %var_with_dbg_value, which has multiple dbg.value intrinsics associated with
7+
; it, one per split point.
8+
; %var_with_dbg_declare, which has a single dbg.declare intrinsic associated
9+
; with it at the coroutine entry.
10+
; We check that, for each funclet, the debug intrinsics are propagated properly AND that
11+
; an `entry_value` operation is created.
12+
define swifttailcc void @coroutineA(ptr swiftasync %arg) !dbg !48 {
13+
%var_with_dbg_value = alloca ptr, align 8
14+
%var_with_dbg_declare = alloca ptr, align 8
15+
call void @llvm.dbg.declare(metadata ptr %var_with_dbg_declare, metadata !500, metadata !DIExpression()), !dbg !54
16+
call void @llvm.dbg.value(metadata ptr %var_with_dbg_value, metadata !50, metadata !DIExpression(DW_OP_deref)), !dbg !54
17+
%i2 = call token @llvm.coro.id.async(i32 16, i32 16, i32 0, ptr nonnull @coroutineATu)
18+
%i3 = call ptr @llvm.coro.begin(token %i2, ptr null)
19+
; CHECK-LABEL: define {{.*}} @coroutineA(
20+
; CHECK-SAME: ptr swiftasync %[[frame_ptr:.*]])
21+
; CHECK: @llvm.dbg.declare(metadata ptr %[[frame_ptr]], {{.*}} !DIExpression(
22+
; CHECK-SAME: DW_OP_LLVM_entry_value, 1, DW_OP_plus_uconst, 16, DW_OP_plus_uconst, 8)
23+
; CHECK: @llvm.dbg.value(metadata ptr %[[frame_ptr]], {{.*}} !DIExpression(
24+
; CHECK-SAME: DW_OP_LLVM_entry_value, 1, DW_OP_plus_uconst, 16, DW_OP_deref)
25+
; CHECK: call {{.*}} @swift_task_switch
26+
27+
%i7 = call ptr @llvm.coro.async.resume(), !dbg !54
28+
%i10 = call { ptr } (i32, ptr, ptr, ...) @llvm.coro.suspend.async.sl_p0s(i32 0, ptr %i7, ptr nonnull @__swift_async_resume_get_context, ptr nonnull @coroutineA.1, ptr %i7, i64 0, i64 0, ptr %arg), !dbg !54
29+
%i11 = extractvalue { ptr } %i10, 0, !dbg !54
30+
%i12 = call ptr @__swift_async_resume_get_context(ptr %i11), !dbg !54
31+
call void @dont_optimize(ptr %var_with_dbg_value, ptr %var_with_dbg_declare)
32+
call void @llvm.dbg.value(metadata ptr %var_with_dbg_value, metadata !50, metadata !DIExpression(DW_OP_deref)), !dbg !54
33+
%i17 = load i32, ptr getelementptr inbounds (<{i32, i32}>, ptr @coroutineBTu, i64 0, i32 1), align 8, !dbg !54
34+
%i18 = zext i32 %i17 to i64, !dbg !54
35+
%i19 = call swiftcc ptr @swift_task_alloc(i64 %i18), !dbg !54
36+
; CHECK-NOT: define
37+
; CHECK-LABEL: define {{.*}} @coroutineATY0_(
38+
; CHECK-SAME: ptr swiftasync %[[frame_ptr:.*]])
39+
; CHECK: @llvm.dbg.declare(metadata ptr %[[frame_ptr]], {{.*}} !DIExpression(
40+
; CHECK-SAME: DW_OP_LLVM_entry_value, 1, DW_OP_plus_uconst, 16, DW_OP_plus_uconst, 8)
41+
; CHECK: @llvm.dbg.value(metadata ptr %[[frame_ptr]], {{.*}} !DIExpression(
42+
; CHECK-SAME: DW_OP_LLVM_entry_value, 1, DW_OP_plus_uconst, 16, DW_OP_deref)
43+
; CHECK: call {{.*}} @coroutineB
44+
45+
%i23 = call ptr @llvm.coro.async.resume(), !dbg !54
46+
%i25 = getelementptr inbounds <{ ptr, ptr }>, ptr %i19, i64 0, i32 1, !dbg !54
47+
store ptr %i23, ptr %i25, align 8, !dbg !54
48+
%i27 = call { ptr } (i32, ptr, ptr, ...) @llvm.coro.suspend.async.sl_p0s(i32 0, ptr %i23, ptr nonnull @__swift_async_resume_project_context, ptr nonnull @coroutineA.0, ptr nonnull @coroutineB, ptr nonnull %i19), !dbg !54
49+
%i28 = extractvalue { ptr } %i27, 0, !dbg !54
50+
%i29 = call ptr @__swift_async_resume_project_context(ptr %i28), !dbg !54
51+
call swiftcc void @swift_task_dealloc(ptr nonnull %i19), !dbg !54
52+
call void @dont_optimize(ptr %var_with_dbg_value, ptr %var_with_dbg_declare)
53+
call void @llvm.dbg.value(metadata ptr %var_with_dbg_value, metadata !50, metadata !DIExpression(DW_OP_deref)), !dbg !54
54+
; CHECK-NOT: define
55+
; CHECK-LABEL: define {{.*}} @coroutineATQ1_(
56+
; CHECK-SAME: ptr swiftasync %[[frame_ptr:.*]])
57+
; Note the extra level of indirection that shows up here!
58+
; CHECK: @llvm.dbg.declare(metadata ptr %[[frame_ptr]], {{.*}} !DIExpression(
59+
; CHECK-SAME: DW_OP_LLVM_entry_value, 1, DW_OP_deref, DW_OP_plus_uconst, 16, DW_OP_plus_uconst, 8)
60+
; CHECK: @llvm.dbg.value(metadata ptr %[[frame_ptr]], {{.*}} !DIExpression(
61+
; CHECK-SAME: DW_OP_LLVM_entry_value, 1, DW_OP_deref, DW_OP_plus_uconst, 16, DW_OP_deref)
62+
; CHECK: call {{.*}} @swift_task_switch
63+
64+
%i31 = call ptr @llvm.coro.async.resume(), !dbg !54
65+
%i33 = call { ptr } (i32, ptr, ptr, ...) @llvm.coro.suspend.async.sl_p0s(i32 0, ptr %i31, ptr nonnull @__swift_async_resume_get_context, ptr nonnull @coroutineA.1, ptr %i31, i64 0, i64 0, ptr %i29), !dbg !54
66+
%i34 = extractvalue { ptr } %i33, 0, !dbg !54
67+
%i35 = call ptr @__swift_async_resume_get_context(ptr %i34), !dbg !54
68+
%i45 = call i1 (ptr, i1, ...) @llvm.coro.end.async(ptr %i3, i1 false, ptr nonnull @coroutineA.0.1, ptr undef, ptr undef), !dbg !54
69+
unreachable, !dbg !54
70+
; CHECK-NOT: define
71+
; CHECK-LABEL: define {{.*}} @coroutineATY2_(
72+
; CHECK-SAME: ptr swiftasync %[[frame_ptr:.*]])
73+
; CHECK: @llvm.dbg.declare(metadata ptr %[[frame_ptr]], {{.*}} !DIExpression(
74+
; CHECK-SAME: DW_OP_LLVM_entry_value, 1, DW_OP_plus_uconst, 16, DW_OP_plus_uconst, 8)
75+
}
76+
77+
; Everything from here on is just support code for the coroutines.
78+
79+
@coroutineBTu = global <{i32, i32}> <{ i32 trunc (i64 sub (i64 ptrtoint (ptr @"coroutineB" to i64), i64 ptrtoint (ptr @"coroutineBTu" to i64)) to i32), i32 16 }>, align 8
80+
@coroutineATu = global <{i32, i32}> <{ i32 trunc (i64 sub (i64 ptrtoint (ptr @"coroutineA" to i64), i64 ptrtoint (ptr @"coroutineATu" to i64)) to i32), i32 16 }>, align 8
81+
82+
define weak_odr hidden ptr @__swift_async_resume_get_context(ptr %arg) !dbg !64 {
83+
ret ptr %arg, !dbg !65
84+
}
85+
define hidden swifttailcc void @coroutineA.1(ptr %arg, i64 %arg1, i64 %arg2, ptr %arg3) !dbg !66 {
86+
musttail call swifttailcc void @swift_task_switch(ptr swiftasync %arg3, ptr %arg, i64 %arg1, i64 %arg2), !dbg !67
87+
ret void, !dbg !67
88+
}
89+
90+
define weak_odr hidden ptr @__swift_async_resume_project_context(ptr %arg) !dbg !68 {
91+
%i1 = load ptr, ptr %arg, align 8, !dbg !69
92+
%i2 = call ptr @llvm.swift.async.context.addr(), !dbg !69
93+
store ptr %i1, ptr %i2, align 8, !dbg !69
94+
ret ptr %i1, !dbg !69
95+
}
96+
define hidden swifttailcc void @coroutineA.0(ptr %arg, ptr %arg1) !dbg !70 {
97+
musttail call swifttailcc void %arg(ptr swiftasync %arg1), !dbg !71
98+
ret void, !dbg !71
99+
}
100+
define hidden swifttailcc void @coroutineA.0.1(ptr %arg, ptr %arg1) !dbg !72 {
101+
musttail call swifttailcc void %arg(ptr swiftasync %arg1), !dbg !73
102+
ret void, !dbg !73
103+
}
104+
define swifttailcc void @coroutineB(ptr swiftasync %arg) !dbg !37 {
105+
%i2 = call token @llvm.coro.id.async(i32 16, i32 16, i32 0, ptr nonnull @coroutineBTu)
106+
%i3 = call ptr @llvm.coro.begin(token %i2, ptr null)
107+
%i6 = getelementptr inbounds <{ ptr, ptr }>, ptr %arg, i64 0, i32 1, !dbg !42
108+
%i712 = load ptr, ptr %i6, align 8, !dbg !42
109+
%i10 = call i1 (ptr, i1, ...) @llvm.coro.end.async(ptr %i3, i1 false, ptr nonnull @coroutineB.0, ptr %i712, ptr %arg), !dbg !42
110+
unreachable, !dbg !42
111+
}
112+
define hidden swifttailcc void @coroutineB.0(ptr %arg, ptr %arg1) !dbg !44 {
113+
musttail call swifttailcc void %arg(ptr swiftasync %arg1), !dbg !47
114+
ret void, !dbg !47
115+
}
116+
117+
declare i1 @llvm.coro.end.async(ptr, i1, ...)
118+
declare ptr @llvm.coro.async.resume()
119+
declare ptr @llvm.coro.begin(token, ptr writeonly)
120+
declare ptr @llvm.swift.async.context.addr()
121+
declare swiftcc ptr @swift_task_alloc(i64)
122+
declare swiftcc void @swift_task_dealloc(ptr)
123+
declare swifttailcc void @swift_task_switch(ptr, ptr, i64, i64)
124+
declare token @llvm.coro.id.async(i32, i32, i32, ptr)
125+
declare void @dont_optimize(ptr, ptr)
126+
declare void @llvm.dbg.declare(metadata, metadata, metadata)
127+
declare void @llvm.dbg.value(metadata, metadata, metadata)
128+
declare { ptr } @llvm.coro.suspend.async.sl_p0s(i32, ptr, ptr, ...)
129+
130+
!llvm.module.flags = !{!6, !7}
131+
!llvm.dbg.cu = !{!16}
132+
133+
!6 = !{i32 7, !"Dwarf Version", i32 4}
134+
!7 = !{i32 2, !"Debug Info Version", i32 3}
135+
136+
!50 = !DILocalVariable(name: "k1", scope: !48, file: !17, line: 7, type: !53)
137+
!500 = !DILocalVariable(name: "k2", scope: !48, file: !17, line: 7, type: !53)
138+
!49 = !{!50, !500}
139+
140+
!16 = distinct !DICompileUnit(language: DW_LANG_Swift, file: !17, producer: "", emissionKind: FullDebug)
141+
!17 = !DIFile(filename: "blah", directory: "")
142+
143+
!53 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "Klass")
144+
!46 = !DISubroutineType(types: null)
145+
146+
!64 = distinct !DISubprogram(linkageName: "blah", file: !17, type: !46, unit: !16)
147+
!68 = distinct !DISubprogram(linkageName: "blah", file: !17, type: !46, unit: !16)
148+
!66 = distinct !DISubprogram(linkageName: "coroutineA", file: !17, type: !46, unit: !16)
149+
!70 = distinct !DISubprogram(linkageName: "coroutineA", file: !17, type: !46, unit: !16)
150+
!72 = distinct !DISubprogram(linkageName: "coroutineA", file: !17, type: !46, unit: !16)
151+
!48 = distinct !DISubprogram(linkageName: "coroutineA", file: !17, type: !46, unit: !16, retainedNodes: !49)
152+
!37 = distinct !DISubprogram(linkageName: "coroutineB", file: !17, type: !46, unit: !16)
153+
!44 = distinct !DISubprogram(linkageName: "coroutineB", file: !17, type: !46, unit: !16)
154+
!65 = !DILocation(line: 0, scope: !64)
155+
!67 = !DILocation(line: 0, scope: !66)
156+
!69 = !DILocation(line: 0, scope: !68)
157+
!71 = !DILocation(line: 0, scope: !70)
158+
!73 = !DILocation(line: 0, scope: !72)
159+
!54 = !DILocation(line: 6, scope: !48)
160+
!42 = !DILocation(line: 3, scope: !37)
161+
!47 = !DILocation(line: 0, scope: !44)

0 commit comments

Comments
 (0)