[llvm] Add support for llvm.dbg.declare_value in the CoroSplitter pass. (PR #168134)

Shubham Sandeep Rastogi via llvm-commits llvm-commits at lists.llvm.org
Sat Nov 22 00:50:54 PST 2025


https://github.com/rastogishubham updated https://github.com/llvm/llvm-project/pull/168134

>From 92596c27015f62e1e70aefc15d1ba70ccd6c4a58 Mon Sep 17 00:00:00 2001
From: Shubham Sandeep Rastogi <srastogi22 at apple.com>
Date: Fri, 3 Oct 2025 11:46:10 -0700
Subject: [PATCH] Add support for llvm.dbg.declare_value in the CoroSplitter
 pass.

Make sure the CoroSplitter pass correctly handles llvm.dbg.declare_value
intrinsics. Also, convert them to llvm.dbg.declares so that any
subsquent passes do not need to be amended to support the
llvm.dbg.declare_value intrinsic.
---
 llvm/lib/Transforms/Coroutines/CoroFrame.cpp  | 66 ++++++++++++++++--
 .../Transforms/Coroutines/declare-value.ll    | 68 +++++++++++++++++++
 2 files changed, 129 insertions(+), 5 deletions(-)
 create mode 100644 llvm/test/Transforms/Coroutines/declare-value.ll

diff --git a/llvm/lib/Transforms/Coroutines/CoroFrame.cpp b/llvm/lib/Transforms/Coroutines/CoroFrame.cpp
index c89af688a69ca..b07fa644baa10 100644
--- a/llvm/lib/Transforms/Coroutines/CoroFrame.cpp
+++ b/llvm/lib/Transforms/Coroutines/CoroFrame.cpp
@@ -554,6 +554,7 @@ static void cacheDIVar(FrameDataInfo &FrameData,
         DIVarCache.insert({V, (*I)->getVariable()});
     };
     CacheIt(findDVRDeclares(V));
+    CacheIt(findDVRDeclareValues(V));
   }
 }
 
@@ -1142,6 +1143,47 @@ static void insertSpills(const FrameDataInfo &FrameData, coro::Shape &Shape) {
         for_each(DVRs, SalvageOne);
       }
 
+      TinyPtrVector<DbgVariableRecord *> DVRDeclareValues =
+          findDVRDeclareValues(Def);
+      // Try best to find dbg.declare_value. If the spill is a temp, there may
+      // not be a direct dbg.declare_value. Walk up the load chain to find one
+      // from an alias.
+      if (F->getSubprogram()) {
+        auto *CurDef = Def;
+        while (DVRDeclareValues.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();
+          if (!isa<AllocaInst, LoadInst>(CurDef))
+            break;
+          DVRDeclareValues = findDVRDeclareValues(CurDef);
+        }
+      }
+
+      auto SalvageOneCoro = [&](auto *DDI) {
+        // This dbg.declare_value is preserved for all coro-split function
+        // fragments. It will be unreachable in the main function, and
+        // processed by coro::salvageDebugInfo() by the Cloner. However, convert
+        // it to a dbg.declare to make sure future passes don't have to deal
+        // with a dbg.declare_value.
+        auto *VAM = ValueAsMetadata::get(CurrentReload);
+        Type *Ty = VAM->getValue()->getType();
+        // If the metadata type is not a pointer, emit a dbg.value instead.
+        DbgVariableRecord *NewDVR = new DbgVariableRecord(
+            ValueAsMetadata::get(CurrentReload), DDI->getVariable(),
+            DDI->getExpression(), DDI->getDebugLoc(),
+            Ty->isPointerTy() ? DbgVariableRecord::LocationType::Declare
+                              : DbgVariableRecord::LocationType::Value);
+        Builder.GetInsertPoint()->getParent()->insertDbgRecordBefore(
+            NewDVR, Builder.GetInsertPoint());
+        // This dbg.declare_value is for the main function entry point.  It
+        // will be deleted in all coro-split functions.
+        coro::salvageDebugInfo(ArgToAllocaMap, *DDI, false /*UseEntryValue*/);
+      };
+      for_each(DVRDeclareValues, SalvageOneCoro);
+
       // If we have a single edge PHINode, remove it and replace it with a
       // reload from the coroutine frame. (We already took care of multi edge
       // PHINodes by normalizing them in the rewritePHIs function).
@@ -1925,7 +1967,7 @@ void coro::salvageDebugInfo(
   Function *F = DVR.getFunction();
   // Follow the pointer arithmetic all the way to the incoming
   // function argument and convert into a DIExpression.
-  bool SkipOutermostLoad = DVR.isDbgDeclare();
+  bool SkipOutermostLoad = DVR.isDbgDeclare() || DVR.isDbgDeclareValue();
   Value *OriginalStorage = DVR.getVariableLocationOp(0);
 
   auto SalvagedInfo =
@@ -1939,10 +1981,11 @@ void coro::salvageDebugInfo(
 
   DVR.replaceVariableLocationOp(OriginalStorage, Storage);
   DVR.setExpression(Expr);
-  // We only hoist dbg.declare today since it doesn't make sense to hoist
-  // dbg.value since it does not have the same function wide guarantees that
-  // dbg.declare does.
-  if (DVR.getType() == DbgVariableRecord::LocationType::Declare) {
+  // We only hoist dbg.declare and dbg.declare_value today since it doesn't make
+  // sense to hoist dbg.value since it does not have the same function wide
+  // guarantees that dbg.declare does.
+  if (DVR.getType() == DbgVariableRecord::LocationType::Declare ||
+      DVR.getType() == DbgVariableRecord::LocationType::DeclareValue) {
     std::optional<BasicBlock::iterator> InsertPt;
     if (auto *I = dyn_cast<Instruction>(Storage)) {
       InsertPt = I->getInsertionPointAfterDef();
@@ -1957,6 +2000,19 @@ void coro::salvageDebugInfo(
       InsertPt = F->getEntryBlock().begin();
     if (InsertPt) {
       DVR.removeFromParent();
+      // If there is a dbg.declare_value being reinserted, insert it as a
+      // dbg.declare instead, so that subsequent passes don't have to deal with
+      // a dbg.declare_value.
+      if (DVR.getType() == DbgVariableRecord::LocationType::DeclareValue) {
+        auto *MD = DVR.getRawLocation();
+        if (auto *VAM = dyn_cast<ValueAsMetadata>(MD)) {
+          Type *Ty = VAM->getValue()->getType();
+          if (Ty->isPointerTy())
+            DVR.Type = DbgVariableRecord::LocationType::Declare;
+          else
+            DVR.Type = DbgVariableRecord::LocationType::Value;
+        }
+      }
       (*InsertPt)->getParent()->insertDbgRecordBefore(&DVR, *InsertPt);
     }
   }
diff --git a/llvm/test/Transforms/Coroutines/declare-value.ll b/llvm/test/Transforms/Coroutines/declare-value.ll
new file mode 100644
index 0000000000000..94049c28169b9
--- /dev/null
+++ b/llvm/test/Transforms/Coroutines/declare-value.ll
@@ -0,0 +1,68 @@
+;RUN: opt -mtriple='arm64-' %s -S -passes='module(coro-early),cgscc(coro-split,simplifycfg)' -o - | FileCheck %s
+
+; CHECK:  %.debug = alloca double, align 8
+; CHECK-NEXT:    #dbg_declare(ptr %{{.*}}, !{{[0-9]+}}, !DIExpression(DW_OP_deref), !{{[0-9]+}})
+; CHECK-NEXT:  store double %{{[0-9]+}}, ptr %{{.*}}, align 8
+; CHECK-NEXT:    #dbg_declare(ptr %arg, !{{[0-9]+}}, !DIExpression(DW_OP_plus_uconst, 24), !{{[0-9]+}})
+
+; ModuleID = '/Users/srastogi/Development/llvm-project-2/llvm/test/Transforms/Coroutines/declare-value.ll'
+source_filename = "/Users/srastogi/Development/llvm-project-2/llvm/test/Transforms/Coroutines/declare-value.ll"
+target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-Fn32"
+target triple = "arm64-unknown"
+
+ 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
+
+; Function Attrs: presplitcoroutine
+define swifttailcc void @coroutineA(ptr swiftasync %arg, double %0) #0 !dbg !1 {
+  %var_with_dbg_value = alloca ptr, align 8
+  %var_with_dbg_declare = alloca ptr, align 8
+    #dbg_declare(ptr %var_with_dbg_declare, !5, !DIExpression(), !7)
+    #dbg_declare_value(double %0, !5, !DIExpression(), !7)
+  %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)
+  %i7 = call ptr @llvm.coro.async.resume(), !dbg !7
+  %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 !7
+  call void @dont_optimize(ptr %var_with_dbg_value, ptr %var_with_dbg_declare), !dbg !7
+  unreachable, !dbg !7
+}
+
+define weak_odr hidden ptr @__swift_async_resume_get_context(ptr %arg) !dbg !8 {
+  ret ptr %arg, !dbg !9
+}
+
+define hidden swifttailcc void @coroutineA.1(ptr %arg, i64 %arg1, i64 %arg2, ptr %arg3) !dbg !10 {
+  ret void, !dbg !11
+}
+
+declare void @dont_optimize(ptr, ptr)
+
+; Function Attrs: nomerge nounwind
+declare ptr @llvm.coro.async.resume() #1
+
+; Function Attrs: nounwind
+declare ptr @llvm.coro.begin(token, ptr writeonly) #2
+
+; Function Attrs: nounwind
+declare token @llvm.coro.id.async(i32, i32, i32, ptr) #2
+
+; Function Attrs: nomerge nounwind
+declare { ptr } @llvm.coro.suspend.async.sl_p0s(i32, ptr, ptr, ...) #1
+
+attributes #0 = { presplitcoroutine }
+attributes #1 = { nomerge nounwind }
+attributes #2 = { nounwind }
+
+!llvm.module.flags = !{!0}
+
+!0 = !{i32 2, !"Debug Info Version", i32 3}
+!1 = distinct !DISubprogram(scope: null, spFlags: DISPFlagDefinition, unit: !2, retainedNodes: !4)
+!2 = distinct !DICompileUnit(language: DW_LANG_Swift, file: !3, isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug)
+!3 = !DIFile(filename: "blah", directory: "")
+!4 = !{}
+!5 = !DILocalVariable(scope: !1, type: !6)
+!6 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "Klass")
+!7 = !DILocation(line: 0, scope: !1)
+!8 = distinct !DISubprogram(scope: null, spFlags: DISPFlagDefinition, unit: !2)
+!9 = !DILocation(line: 0, scope: !8)
+!10 = distinct !DISubprogram(scope: null, spFlags: DISPFlagDefinition, unit: !2)
+!11 = !DILocation(line: 0, scope: !10)
\ No newline at end of file



More information about the llvm-commits mailing list