[llvm] 617c9d5 - [Corosplit] Prepend entry_value in swift async dbg values

Felipe de Azevedo Piovezan via llvm-commits llvm-commits at lists.llvm.org
Wed May 10 11:39:22 PDT 2023


Author: Felipe de Azevedo Piovezan
Date: 2023-05-10T14:38:19-04:00
New Revision: 617c9d59df0ce4543d5252740b3f77854b912808

URL: https://github.com/llvm/llvm-project/commit/617c9d59df0ce4543d5252740b3f77854b912808
DIFF: https://github.com/llvm/llvm-project/commit/617c9d59df0ce4543d5252740b3f77854b912808.diff

LOG: [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

Added: 
    llvm/test/Transforms/Coroutines/swift-async-dbg.ll

Modified: 
    llvm/docs/LangRef.rst
    llvm/lib/Transforms/Coroutines/CoroFrame.cpp

Removed: 
    


################################################################################
diff  --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst
index c00fea32eff41..1eccc30877327 100644
--- a/llvm/docs/LangRef.rst
+++ b/llvm/docs/LangRef.rst
@@ -6018,6 +6018,9 @@ The current supported opcode vocabulary is limited:
     - ``AsmPrinter`` pass when a call site parameter value
       (``DW_AT_call_site_parameter_value``) is represented as entry value of
       the parameter.
+    - ``CoroSplit`` pass, which may move variables from allocas into a
+      coroutine frame. If the coroutine frame is a :ref:`_swiftasync` argument,
+      the variable is described with an ``DW_OP_LLVM_entry_value`` operation.
 
 - ``DW_OP_LLVM_arg, N`` is used in debug intrinsics that refer to more than one
   value, such as one that calculates the sum of two registers. This is always

diff  --git a/llvm/lib/Transforms/Coroutines/CoroFrame.cpp b/llvm/lib/Transforms/Coroutines/CoroFrame.cpp
index c81f1e7f21fc8..ebbfd5acc7237 100644
--- a/llvm/lib/Transforms/Coroutines/CoroFrame.cpp
+++ b/llvm/lib/Transforms/Coroutines/CoroFrame.cpp
@@ -2877,31 +2877,37 @@ void coro::salvageDebugInfo(
   if (!Storage)
     return;
 
-  // Store a pointer to the coroutine frame object in an alloca so it
-  // is available throughout the function when producing unoptimized
-  // code. Extending the lifetime this way is correct because the
-  // variable has been declared by a dbg.declare intrinsic.
-  //
-  // Avoid to create the alloca would be eliminated by optimization
-  // passes and the corresponding dbg.declares would be invalid.
-  if (!OptimizeFrame)
-    if (auto *Arg = dyn_cast<Argument>(Storage)) {
-      auto &Cached = ArgToAllocaMap[Arg];
-      if (!Cached) {
-        Cached = Builder.CreateAlloca(Storage->getType(), 0, nullptr,
-                                      Arg->getName() + ".debug");
-        Builder.CreateStore(Storage, Cached);
-      }
-      Storage = Cached;
-      // FIXME: LLVM lacks nuanced semantics to 
diff erentiate between
-      // memory and direct locations at the IR level. The backend will
-      // turn a dbg.declare(alloca, ..., DIExpression()) into a memory
-      // location. Thus, if there are deref and offset operations in the
-      // expression, we need to add a DW_OP_deref at the *start* of the
-      // expression to first load the contents of the alloca before
-      // adjusting it with the expression.
-      Expr = DIExpression::prepend(Expr, DIExpression::DerefBefore);
+  auto *StorageAsArg = dyn_cast<Argument>(Storage);
+  const bool IsSwiftAsyncArg =
+      StorageAsArg && StorageAsArg->hasAttribute(Attribute::SwiftAsync);
+
+  // Swift async arguments are described by an entry value of the ABI-defined
+  // register containing the coroutine context.
+  if (IsSwiftAsyncArg && !Expr->isEntryValue())
+    Expr = DIExpression::prepend(Expr, DIExpression::EntryValue);
+
+  // If the coroutine frame is an Argument, store it in an alloca to improve
+  // its availability (e.g. registers may be clobbered).
+  // Avoid this if optimizations are enabled (they would remove the alloca) or
+  // if the value is guaranteed to be available through other means (e.g. swift
+  // ABI guarantees).
+  if (StorageAsArg && !OptimizeFrame && !IsSwiftAsyncArg) {
+    auto &Cached = ArgToAllocaMap[StorageAsArg];
+    if (!Cached) {
+      Cached = Builder.CreateAlloca(Storage->getType(), 0, nullptr,
+                                    Storage->getName() + ".debug");
+      Builder.CreateStore(Storage, Cached);
     }
+    Storage = Cached;
+    // FIXME: LLVM lacks nuanced semantics to 
diff erentiate between
+    // memory and direct locations at the IR level. The backend will
+    // turn a dbg.declare(alloca, ..., DIExpression()) into a memory
+    // location. Thus, if there are deref and offset operations in the
+    // expression, we need to add a DW_OP_deref at the *start* of the
+    // expression to first load the contents of the alloca before
+    // adjusting it with the expression.
+    Expr = DIExpression::prepend(Expr, DIExpression::DerefBefore);
+  }
 
   DVI->replaceVariableLocationOp(OriginalStorage, Storage);
   DVI->setExpression(Expr);

diff  --git a/llvm/test/Transforms/Coroutines/swift-async-dbg.ll b/llvm/test/Transforms/Coroutines/swift-async-dbg.ll
new file mode 100644
index 0000000000000..2f93d3c24dec3
--- /dev/null
+++ b/llvm/test/Transforms/Coroutines/swift-async-dbg.ll
@@ -0,0 +1,161 @@
+; RUN: opt %s -S -passes='module(coro-early),cgscc(coro-split,simplifycfg)' -o - | FileCheck %s
+target datalayout = "e-m:o-i64:64-i128:128-n32:64-S128"
+target triple = "arm64-apple-macosx13.0.0"
+
+; This coroutine has one split point and two variables defined by:
+;   %var_with_dbg_value,   which has multiple dbg.value intrinsics associated with
+;                          it, one per split point.
+;   %var_with_dbg_declare, which has a single dbg.declare intrinsic associated
+;                          with it at the coroutine entry.
+; We check that, for each funclet, the debug intrinsics are propagated properly AND that
+; an `entry_value` operation is created.
+define swifttailcc void @coroutineA(ptr swiftasync %arg) !dbg !48 {
+  %var_with_dbg_value = alloca ptr, align 8
+  %var_with_dbg_declare = alloca ptr, align 8
+  call void @llvm.dbg.declare(metadata ptr %var_with_dbg_declare, metadata !500, metadata !DIExpression()), !dbg !54
+  call void @llvm.dbg.value(metadata ptr %var_with_dbg_value, metadata !50, metadata !DIExpression(DW_OP_deref)), !dbg !54
+  %i2 = call token @llvm.coro.id.async(i32 16, i32 16, i32 0, ptr nonnull @coroutineATu)
+  %i3 = call ptr @llvm.coro.begin(token %i2, ptr null)
+; CHECK-LABEL: define {{.*}} @coroutineA(
+; CHECK-SAME:    ptr swiftasync %[[frame_ptr:.*]])
+; CHECK:      @llvm.dbg.declare(metadata ptr %[[frame_ptr]], {{.*}} !DIExpression(
+; CHECK-SAME:                   DW_OP_LLVM_entry_value, 1, DW_OP_plus_uconst, 16, DW_OP_plus_uconst, 8)
+; CHECK:      @llvm.dbg.value(metadata ptr %[[frame_ptr]], {{.*}} !DIExpression(
+; CHECK-SAME:                 DW_OP_LLVM_entry_value, 1, DW_OP_plus_uconst, 16, DW_OP_deref)
+; CHECK:      call {{.*}} @swift_task_switch
+
+  %i7 = call ptr @llvm.coro.async.resume(), !dbg !54
+  %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
+  %i11 = extractvalue { ptr } %i10, 0, !dbg !54
+  %i12 = call ptr @__swift_async_resume_get_context(ptr %i11), !dbg !54
+  call void @dont_optimize(ptr %var_with_dbg_value, ptr %var_with_dbg_declare)
+  call void @llvm.dbg.value(metadata ptr %var_with_dbg_value, metadata !50, metadata !DIExpression(DW_OP_deref)), !dbg !54
+  %i17 = load i32, ptr getelementptr inbounds (<{i32, i32}>, ptr @coroutineBTu, i64 0, i32 1), align 8, !dbg !54
+  %i18 = zext i32 %i17 to i64, !dbg !54
+  %i19 = call swiftcc ptr @swift_task_alloc(i64 %i18), !dbg !54
+; CHECK-NOT: define
+; CHECK-LABEL: define {{.*}} @coroutineATY0_(
+; CHECK-SAME:    ptr swiftasync %[[frame_ptr:.*]])
+; CHECK:      @llvm.dbg.declare(metadata ptr %[[frame_ptr]], {{.*}} !DIExpression(
+; CHECK-SAME:                   DW_OP_LLVM_entry_value, 1, DW_OP_plus_uconst, 16, DW_OP_plus_uconst, 8)
+; CHECK:      @llvm.dbg.value(metadata ptr %[[frame_ptr]], {{.*}} !DIExpression(
+; CHECK-SAME:                 DW_OP_LLVM_entry_value, 1, DW_OP_plus_uconst, 16, DW_OP_deref)
+; CHECK:      call {{.*}} @coroutineB
+
+  %i23 = call ptr @llvm.coro.async.resume(), !dbg !54
+  %i25 = getelementptr inbounds <{ ptr, ptr }>, ptr %i19, i64 0, i32 1, !dbg !54
+  store ptr %i23, ptr %i25, align 8, !dbg !54
+  %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
+  %i28 = extractvalue { ptr } %i27, 0, !dbg !54
+  %i29 = call ptr @__swift_async_resume_project_context(ptr %i28), !dbg !54
+  call swiftcc void @swift_task_dealloc(ptr nonnull %i19), !dbg !54
+  call void @dont_optimize(ptr %var_with_dbg_value, ptr %var_with_dbg_declare)
+  call void @llvm.dbg.value(metadata ptr %var_with_dbg_value, metadata !50, metadata !DIExpression(DW_OP_deref)), !dbg !54
+; CHECK-NOT: define
+; CHECK-LABEL: define {{.*}} @coroutineATQ1_(
+; CHECK-SAME:    ptr swiftasync %[[frame_ptr:.*]])
+; Note the extra level of indirection that shows up here!
+; CHECK:      @llvm.dbg.declare(metadata ptr %[[frame_ptr]], {{.*}} !DIExpression(
+; CHECK-SAME:                   DW_OP_LLVM_entry_value, 1, DW_OP_deref, DW_OP_plus_uconst, 16, DW_OP_plus_uconst, 8)
+; CHECK:      @llvm.dbg.value(metadata ptr %[[frame_ptr]], {{.*}} !DIExpression(
+; CHECK-SAME:                 DW_OP_LLVM_entry_value, 1, DW_OP_deref, DW_OP_plus_uconst, 16, DW_OP_deref)
+; CHECK:      call {{.*}} @swift_task_switch
+
+  %i31 = call ptr @llvm.coro.async.resume(), !dbg !54
+  %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
+  %i34 = extractvalue { ptr } %i33, 0, !dbg !54
+  %i35 = call ptr @__swift_async_resume_get_context(ptr %i34), !dbg !54
+  %i45 = call i1 (ptr, i1, ...) @llvm.coro.end.async(ptr %i3, i1 false, ptr nonnull @coroutineA.0.1, ptr undef, ptr undef), !dbg !54
+  unreachable, !dbg !54
+; CHECK-NOT: define
+; CHECK-LABEL: define {{.*}} @coroutineATY2_(
+; CHECK-SAME:    ptr swiftasync %[[frame_ptr:.*]])
+; CHECK:      @llvm.dbg.declare(metadata ptr %[[frame_ptr]], {{.*}} !DIExpression(
+; CHECK-SAME:                   DW_OP_LLVM_entry_value, 1, DW_OP_plus_uconst, 16, DW_OP_plus_uconst, 8)
+}
+
+; Everything from here on is just support code for the coroutines.
+
+ at 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
+ at 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
+
+define weak_odr hidden ptr @__swift_async_resume_get_context(ptr %arg) !dbg !64 {
+  ret ptr %arg, !dbg !65
+}
+define hidden swifttailcc void @coroutineA.1(ptr %arg, i64 %arg1, i64 %arg2, ptr %arg3) !dbg !66 {
+  musttail call swifttailcc void @swift_task_switch(ptr swiftasync %arg3, ptr %arg, i64 %arg1, i64 %arg2), !dbg !67
+  ret void, !dbg !67
+}
+
+define weak_odr hidden ptr @__swift_async_resume_project_context(ptr %arg) !dbg !68 {
+  %i1 = load ptr, ptr %arg, align 8, !dbg !69
+  %i2 = call ptr @llvm.swift.async.context.addr(), !dbg !69
+  store ptr %i1, ptr %i2, align 8, !dbg !69
+  ret ptr %i1, !dbg !69
+}
+define hidden swifttailcc void @coroutineA.0(ptr %arg, ptr %arg1) !dbg !70 {
+  musttail call swifttailcc void %arg(ptr swiftasync %arg1), !dbg !71
+  ret void, !dbg !71
+}
+define hidden swifttailcc void @coroutineA.0.1(ptr %arg, ptr %arg1) !dbg !72 {
+  musttail call swifttailcc void %arg(ptr swiftasync %arg1), !dbg !73
+  ret void, !dbg !73
+}
+define swifttailcc void @coroutineB(ptr swiftasync %arg) !dbg !37 {
+  %i2 = call token @llvm.coro.id.async(i32 16, i32 16, i32 0, ptr nonnull @coroutineBTu)
+  %i3 = call ptr @llvm.coro.begin(token %i2, ptr null)
+  %i6 = getelementptr inbounds <{ ptr, ptr }>, ptr %arg, i64 0, i32 1, !dbg !42
+  %i712 = load ptr, ptr %i6, align 8, !dbg !42
+  %i10 = call i1 (ptr, i1, ...) @llvm.coro.end.async(ptr %i3, i1 false, ptr nonnull @coroutineB.0, ptr %i712, ptr %arg), !dbg !42
+  unreachable, !dbg !42
+}
+define hidden swifttailcc void @coroutineB.0(ptr %arg, ptr %arg1) !dbg !44 {
+  musttail call swifttailcc void %arg(ptr swiftasync %arg1), !dbg !47
+  ret void, !dbg !47
+}
+
+declare i1 @llvm.coro.end.async(ptr, i1, ...)
+declare ptr @llvm.coro.async.resume()
+declare ptr @llvm.coro.begin(token, ptr writeonly)
+declare ptr @llvm.swift.async.context.addr()
+declare swiftcc ptr @swift_task_alloc(i64)
+declare swiftcc void @swift_task_dealloc(ptr)
+declare swifttailcc void @swift_task_switch(ptr, ptr, i64, i64)
+declare token @llvm.coro.id.async(i32, i32, i32, ptr)
+declare void @dont_optimize(ptr, ptr)
+declare void @llvm.dbg.declare(metadata, metadata, metadata)
+declare void @llvm.dbg.value(metadata, metadata, metadata)
+declare { ptr } @llvm.coro.suspend.async.sl_p0s(i32, ptr, ptr, ...)
+
+!llvm.module.flags = !{!6, !7}
+!llvm.dbg.cu = !{!16}
+
+!6 = !{i32 7, !"Dwarf Version", i32 4}
+!7 = !{i32 2, !"Debug Info Version", i32 3}
+
+!50 = !DILocalVariable(name: "k1", scope: !48, file: !17, line: 7, type: !53)
+!500 = !DILocalVariable(name: "k2", scope: !48, file: !17, line: 7, type: !53)
+!49 = !{!50, !500}
+
+!16 = distinct !DICompileUnit(language: DW_LANG_Swift, file: !17, producer: "", emissionKind: FullDebug)
+!17 = !DIFile(filename: "blah", directory: "")
+
+!53 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "Klass")
+!46 = !DISubroutineType(types: null)
+
+!64 = distinct !DISubprogram(linkageName: "blah", file: !17, type: !46, unit: !16)
+!68 = distinct !DISubprogram(linkageName: "blah", file: !17, type: !46, unit: !16)
+!66 = distinct !DISubprogram(linkageName: "coroutineA", file: !17, type: !46, unit: !16)
+!70 = distinct !DISubprogram(linkageName: "coroutineA", file: !17, type: !46, unit: !16)
+!72 = distinct !DISubprogram(linkageName: "coroutineA", file: !17, type: !46, unit: !16)
+!48 = distinct !DISubprogram(linkageName: "coroutineA", file: !17, type: !46, unit: !16, retainedNodes: !49)
+!37 = distinct !DISubprogram(linkageName: "coroutineB", file: !17, type: !46, unit: !16)
+!44 = distinct !DISubprogram(linkageName: "coroutineB", file: !17, type: !46, unit: !16)
+!65 = !DILocation(line: 0, scope: !64)
+!67 = !DILocation(line: 0, scope: !66)
+!69 = !DILocation(line: 0, scope: !68)
+!71 = !DILocation(line: 0, scope: !70)
+!73 = !DILocation(line: 0, scope: !72)
+!54 = !DILocation(line: 6, scope: !48)
+!42 = !DILocation(line: 3, scope: !37)
+!47 = !DILocation(line: 0, scope: !44)


        


More information about the llvm-commits mailing list