[llvm] b6eadb6 - [Coroutines] Look for dbg.declare for temp spills

Wei Wang via llvm-commits llvm-commits at lists.llvm.org
Thu Mar 30 10:13:31 PDT 2023


Author: Wei Wang
Date: 2023-03-30T10:12:23-07:00
New Revision: b6eadb6c1b509921ccbc30861c0e70b5df8b3253

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

LOG: [Coroutines] Look for dbg.declare for temp spills

A temp spill may not have direct dbg.declare attached. This can cause problem for debugger when it wants to print the value in resume/destroy/cleanup functions.

In particular, we found this happening to "this" pointer that a temp is used to store its value in entry block and spilled later.

Reviewed By: ChuanqiXu

Differential Revision: https://reviews.llvm.org/D146543

Added: 
    llvm/test/Transforms/Coroutines/coro-debug-spill-dbg.declare.ll

Modified: 
    llvm/lib/Transforms/Coroutines/CoroFrame.cpp

Removed: 
    


################################################################################
diff  --git a/llvm/lib/Transforms/Coroutines/CoroFrame.cpp b/llvm/lib/Transforms/Coroutines/CoroFrame.cpp
index 542a5a3bd6c95..d5a2275d12527 100644
--- a/llvm/lib/Transforms/Coroutines/CoroFrame.cpp
+++ b/llvm/lib/Transforms/Coroutines/CoroFrame.cpp
@@ -1709,10 +1709,11 @@ static void createFramePtr(coro::Shape &Shape) {
 static void insertSpills(const FrameDataInfo &FrameData, coro::Shape &Shape) {
   auto *CB = Shape.CoroBegin;
   LLVMContext &C = CB->getContext();
+  Function *F = CB->getFunction();
   IRBuilder<> Builder(C);
   StructType *FrameTy = Shape.FrameTy;
   Value *FramePtr = Shape.FramePtr;
-  DominatorTree DT(*CB->getFunction());
+  DominatorTree DT(*F);
   SmallDenseMap<llvm::Value *, llvm::AllocaInst *, 4> DbgPtrAllocaCache;
 
   // Create a GEP with the given index into the coroutine frame for the original
@@ -1846,6 +1847,21 @@ static void insertSpills(const FrameDataInfo &FrameData, coro::Shape &Shape) {
               SpillAlignment, E.first->getName() + Twine(".reload"));
 
         TinyPtrVector<DbgDeclareInst *> DIs = FindDbgDeclareUses(Def);
+        // Try best to find dbg.declare. If the spill is a temp, there may not
+        // be a direct dbg.declare. Walk up the load chain to find one from an
+        // alias.
+        if (F->getSubprogram()) {
+          auto *CurDef = Def;
+          while (DIs.empty() && isa<LoadInst>(CurDef)) {
+            auto *LdInst = cast<LoadInst>(CurDef);
+            // Only consider ptr to ptr same type load.
+            if (LdInst->getPointerOperandType() != LdInst->getType())
+              break;
+            CurDef = LdInst->getPointerOperand();
+            DIs = FindDbgDeclareUses(CurDef);
+          }
+        }
+
         for (DbgDeclareInst *DDI : DIs) {
           bool AllowUnresolved = false;
           // This dbg.declare is preserved for all coro-split function

diff  --git a/llvm/test/Transforms/Coroutines/coro-debug-spill-dbg.declare.ll b/llvm/test/Transforms/Coroutines/coro-debug-spill-dbg.declare.ll
new file mode 100644
index 0000000000000..d6606d7cd6ac5
--- /dev/null
+++ b/llvm/test/Transforms/Coroutines/coro-debug-spill-dbg.declare.ll
@@ -0,0 +1,152 @@
+; Test spilling a temp generates dbg.declare in resume/destroy/cleanup functions.
+;
+; RUN: opt < %s -passes='cgscc(coro-split)' -S | FileCheck %s
+;
+; The test case simulates a coroutine method in a class.
+;
+; class Container {
+;  public:
+;    Container() : field(12) {}
+;    Task foo() {
+;      co_await std::suspend_always{};
+;      auto *copy = this;
+;      co_return;
+;    }
+;    int field;
+; };
+;
+; We want to make sure that the "this" pointer is accessable in debugger before and after the suspension point.
+;
+; CHECK: define internal fastcc void @foo.resume(ptr noundef nonnull align 8 dereferenceable(32) %[[HDL:.*]])
+; CHECK-NEXT: entry.resume:
+; CHECK-NEXT:   %[[HDL]].debug = alloca ptr, align 8
+; CHECK-NEXT:   call void @llvm.dbg.declare(metadata ptr %[[HDL]].debug, metadata ![[THIS_RESUME:[0-9]+]], metadata !DIExpression(DW_OP_deref, DW_OP_plus_uconst, 24))
+;
+; CHECK: define internal fastcc void @foo.destroy(ptr noundef nonnull align 8 dereferenceable(32) %[[HDL]])
+; CHECK-NEXT: entry.destroy:
+; CHECK-NEXT:   %[[HDL]].debug = alloca ptr, align 8
+; CHECK-NEXT:   call void @llvm.dbg.declare(metadata ptr %[[HDL]].debug, metadata ![[THIS_DESTROY:[0-9]+]], metadata !DIExpression(DW_OP_deref, DW_OP_plus_uconst, 24))
+;
+; CHECK: define internal fastcc void @foo.cleanup(ptr noundef nonnull align 8 dereferenceable(32) %[[HDL]])
+; CHECK-NEXT: entry.cleanup:
+; CHECK-NEXT:   %[[HDL]].debug = alloca ptr, align 8
+; CHECK-NEXT:   call void @llvm.dbg.declare(metadata ptr %[[HDL]].debug, metadata ![[THIS_CLEANUP:[0-9]+]], metadata !DIExpression(DW_OP_deref, DW_OP_plus_uconst, 24))
+;
+; CHECK: ![[THIS_RESUME]] = !DILocalVariable(name: "this"
+; CHECK: ![[THIS_DESTROY]] = !DILocalVariable(name: "this"
+; CHECK: ![[THIS_CLEANUP]] = !DILocalVariable(name: "this"
+
+; Function Attrs: presplitcoroutine
+define ptr @foo(ptr noundef nonnull align 1 dereferenceable(1) %this) #0 !dbg !11 {
+entry:
+  %this.addr = alloca ptr, align 8
+  %__promise = alloca i8, align 1
+  store ptr %this, ptr %this.addr, align 8
+  call void @llvm.dbg.declare(metadata ptr %this.addr, metadata !20, metadata !DIExpression()), !dbg !22
+  %this1 = load ptr, ptr %this.addr, align 8
+  %0 = bitcast ptr %__promise to ptr
+  %id = call token @llvm.coro.id(i32 16, ptr %0, ptr null, ptr null)
+  %need.dyn.alloc = call i1 @llvm.coro.alloc(token %id)
+  br i1 %need.dyn.alloc, label %dyn.alloc, label %coro.begin
+
+dyn.alloc:                                        ; preds = %entry
+  %size = call i32 @llvm.coro.size.i32()
+  %alloc = call ptr @malloc(i32 %size)
+  br label %coro.begin
+
+coro.begin:                                       ; preds = %dyn.alloc, %entry
+  %phi = phi ptr [ null, %entry ], [ %alloc, %dyn.alloc ]
+  %hdl = call ptr @llvm.coro.begin(token %id, ptr %phi)
+  call void @llvm.dbg.declare(metadata ptr %__promise, metadata !23, metadata !DIExpression()), !dbg !22
+  %1 = call i8 @llvm.coro.suspend(token none, i1 false)
+  switch i8 %1, label %suspend [
+    i8 0, label %resume
+    i8 1, label %cleanup
+  ]
+
+resume:                                           ; preds = %coro.begin
+  call void @bar(ptr %this1)
+  br label %cleanup
+
+cleanup:                                          ; preds = %resume, %coro.begin
+  %mem = call ptr @llvm.coro.free(token %id, ptr %hdl)
+  call void @free(ptr %mem)
+  br label %suspend
+
+suspend:                                          ; preds = %cleanup, %coro.begin
+  %2 = call i1 @llvm.coro.end(ptr %hdl, i1 false)
+  ret ptr %hdl
+}
+
+; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
+declare void @llvm.dbg.declare(metadata, metadata, metadata) #1
+
+; Function Attrs: nounwind memory(argmem: read)
+declare ptr @llvm.coro.free(token, ptr nocapture readonly) #2
+
+; Function Attrs: nounwind memory(none)
+declare i32 @llvm.coro.size.i32() #3
+
+; Function Attrs: nounwind
+declare i8 @llvm.coro.suspend(token, i1) #4
+
+declare void @llvm.coro.resume(ptr)
+
+declare void @llvm.coro.destroy(ptr)
+
+; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: read)
+declare token @llvm.coro.id(i32, ptr readnone, ptr nocapture readonly, ptr) #5
+
+; Function Attrs: nounwind
+declare i1 @llvm.coro.alloc(token) #4
+
+; Function Attrs: nounwind
+declare ptr @llvm.coro.begin(token, ptr writeonly) #4
+
+; Function Attrs: nounwind
+declare i1 @llvm.coro.end(ptr, i1) #4
+
+declare noalias ptr @malloc(i32)
+
+declare void @free(ptr)
+
+declare void @bar(ptr)
+
+attributes #0 = { presplitcoroutine }
+attributes #1 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) }
+attributes #2 = { nounwind memory(argmem: read) }
+attributes #3 = { nounwind memory(none) }
+attributes #4 = { nounwind }
+attributes #5 = { nocallback nofree nosync nounwind willreturn memory(argmem: read) }
+
+!llvm.dbg.cu = !{!0}
+!llvm.linker.options = !{}
+!llvm.module.flags = !{!3, !4, !5, !6, !7, !8, !9}
+!llvm.ident = !{!10}
+
+!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !1, producer: "clang version 17.0.20210610", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, retainedTypes: !2, splitDebugInlining: false, nameTableKind: None)
+!1 = !DIFile(filename: "test.cpp", directory: ".")
+!2 = !{}
+!3 = !{i32 7, !"Dwarf Version", i32 5}
+!4 = !{i32 2, !"Debug Info Version", i32 3}
+!5 = !{i32 1, !"wchar_size", i32 4}
+!6 = !{i32 8, !"PIC Level", i32 2}
+!7 = !{i32 7, !"PIE Level", i32 2}
+!8 = !{i32 7, !"uwtable", i32 2}
+!9 = !{i32 7, !"frame-pointer", i32 2}
+!10 = !{!"clang version 17.0.20210610"}
+!11 = distinct !DISubprogram(name: "foo", linkageName: "_ZN9Container3fooEv", scope: !1, file: !1, line: 20, type: !12, scopeLine: 20, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0, declaration: !13, retainedNodes: !2)
+!12 = !DISubroutineType(types: !2)
+!13 = !DISubprogram(name: "foo", linkageName: "_ZN9Container3fooEv", scope: !14, file: !1, line: 20, type: !12, scopeLine: 20, flags: DIFlagPublic | DIFlagPrototyped, spFlags: 0)
+!14 = distinct !DICompositeType(tag: DW_TAG_class_type, name: "Container", file: !1, line: 17, size: 8, flags: DIFlagTypePassByValue | DIFlagNonTrivial, elements: !15, identifier: "_ZTS9Container")
+!15 = !{!16, !13}
+!16 = !DISubprogram(name: "Container", scope: !14, file: !1, line: 19, type: !17, scopeLine: 19, flags: DIFlagPublic | DIFlagPrototyped, spFlags: 0)
+!17 = !DISubroutineType(types: !18)
+!18 = !{null, !19}
+!19 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64, flags: DIFlagArtificial | DIFlagObjectPointer)
+!20 = !DILocalVariable(name: "this", arg: 1, scope: !11, type: !21, flags: DIFlagArtificial | DIFlagObjectPointer)
+!21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64)
+!22 = !DILocation(line: 0, scope: !11)
+!23 = !DILocalVariable(name: "__promise", scope: !11, type: !24, flags: DIFlagArtificial)
+!24 = !DIDerivedType(tag: DW_TAG_typedef, name: "promise_type", scope: !11, file: !1, line: 40, baseType: !25)
+!25 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "promise_type", scope: !11, file: !1, line: 6, size: 8, flags: DIFlagTypePassByValue, elements: !2, identifier: "_ZTSN4Task12promise_typeE")


        


More information about the llvm-commits mailing list