[llvm] [DebugInfo][RemoveDIs] Instrument inliner for non-instr debug-info (PR #72884)

Jeremy Morse via llvm-commits llvm-commits at lists.llvm.org
Mon Nov 20 07:30:08 PST 2023


https://github.com/jmorse created https://github.com/llvm/llvm-project/pull/72884

With intrinsics representing debug-info, we just clone all the intrinsics when inlining a function and don't think about it any further. With non-instruction debug-info however we need to be a bit more careful and manually move the debug-info from one place to another. For the most part, this means keeping a "cursor" during block cloning of where we last copied debug-info from, and performing debug-info copying whenever we successfully clone another instruction.

There are several utilities in LLVM for doing this, all of which now need to manually call cloneDebugInfo. The testing story for this is not well covered as we could rely on normal instruction-cloning mechanisms to do all the hard stuff. Thus, I've added a few tests to explicitly test dbg.value behaviours, ahead of them becoming not-instructions.

Here's a mapping of what's-tested-by-what:
  * `DebugInfoFinder`: tested by all the inlining tests, it's what maintains CU relationships for example,
  * `PruningFunctionCloner::CloneBlock` the new inline-dbg-values.ll test
  * `CloneAndPruneIntoFromInst`: above ^^^
  * `fixupLineNumbers`: above^^^
  * dropDbgValues call in the same: Transforms/Inline/no-inline-line-tables.ll
  * `remapInstructionsIntoBlocks`: Transforms/LoopUnroll/debug-info.ll
  * `DuplicateInstructionsInSplitBetween`: the added jump-threading test and the modified callsitesplitting test. Both passes needed a new call to dropDbgValues too, which it tested.
  * setTailBit: the new inline-alloca-ordering.ll test.

The latter is the weirdest part of RemoveDIs/DDD: when we splice ranges of instructions, normally it's not inclusive of preceeding dbg.values and inclusive of trailing dbg.values. Quite often we do actually intend on including preceeding dbg.values, and that's where the "head" bit of an iterator gets set. However it's technically possible to not want to move the _trailing_ dbg.values on an instruction sequence... and this is the only place in LLVM that I've found which requires that behaviour. It's purely because the inliner steps through all allocas until it finds a non-alloca, and then it moves that range. Every other part of LLVM identifies two real instructions and then moves a range, just not this part.

If we had to scatter this all around LLVM it would probably be unreasonably hard, but IMO this is limited enough of an edge case for us to just work around it.

>From beab8550614fd00af8fdd039d0bdd97f6cf271da Mon Sep 17 00:00:00 2001
From: Jeremy Morse <jeremy.morse at sony.com>
Date: Wed, 7 Jun 2023 16:03:08 +0100
Subject: [PATCH] [DebugInfo][RemoveDIs] Instrument inliner for non-instr
 debug-info

With intrinsics representing debug-info, we just clone all the intrinsics
when inlining a function and don't think about it any further. With
non-instruction debug-info however we need to be a bit more careful and
manually move the debug-info from one place to another. For the most part,
this means keeping a "cursor" during block cloning of where we last copied
debug-info from, and performing debug-info copying whenever we successfully
clone another instruction.

There are several utilities in LLVM for doing this, all of which now need
to manually call cloneDebugInfo. The testing story for this is not well
covered as we could rely on normal instruction-cloning mechanisms to do al
the hard stuff. Thus, I've added a few tests to explicitly test dbg.value
behaviours, ahead of them becoming not-instructions.
---
 llvm/include/llvm/IR/DebugInfo.h              |   6 +-
 llvm/lib/IR/DebugInfo.cpp                     |  20 +--
 .../Transforms/Scalar/CallSiteSplitting.cpp   |   1 +
 llvm/lib/Transforms/Scalar/JumpThreading.cpp  |   5 +-
 llvm/lib/Transforms/Utils/CloneFunction.cpp   |  59 ++++++-
 llvm/lib/Transforms/Utils/CloneModule.cpp     |   1 +
 llvm/lib/Transforms/Utils/InlineFunction.cpp  |  99 ++++++-----
 .../Generic/inline-alloca-ordering.ll         |  64 ++++++++
 .../DebugInfo/Generic/inline-dbg-values.ll    | 154 ++++++++++++++++++
 .../DebugInfo/Generic/inline-debug-loc.ll     |   1 +
 .../callsite-split-preserve-debug.ll          |  23 ++-
 .../Transforms/Inline/alloca-dbgdeclare.ll    |   3 +
 .../delete-function-with-metadata-use.ll      |   1 +
 .../Transforms/Inline/ignore-debug-info.ll    |   3 +
 .../Inline/inline-skip-use-empty-alloca.ll    |   2 +
 .../local-as-metadata-undominated-use.ll      |   2 +
 .../Inline/no-inline-line-tables.ll           |   1 +
 .../JumpThreading/guard-split-debuginfo.ll    |  80 +++++++++
 llvm/test/Transforms/LoopUnroll/debug-info.ll |   1 +
 19 files changed, 467 insertions(+), 59 deletions(-)
 create mode 100644 llvm/test/DebugInfo/Generic/inline-alloca-ordering.ll
 create mode 100644 llvm/test/DebugInfo/Generic/inline-dbg-values.ll
 create mode 100644 llvm/test/Transforms/JumpThreading/guard-split-debuginfo.ll

diff --git a/llvm/include/llvm/IR/DebugInfo.h b/llvm/include/llvm/IR/DebugInfo.h
index 5d6e8c2fd28da619..2a581eb5f09d9f80 100644
--- a/llvm/include/llvm/IR/DebugInfo.h
+++ b/llvm/include/llvm/IR/DebugInfo.h
@@ -102,10 +102,12 @@ class DebugInfoFinder {
   /// Process a single instruction and collect debug info anchors.
   void processInstruction(const Module &M, const Instruction &I);
 
-  /// Process DbgVariableIntrinsic.
-  void processVariable(const Module &M, const DbgVariableIntrinsic &DVI);
+  /// Process a DILocalVariable.
+  void processVariable(const Module &M, const DILocalVariable *DVI);
   /// Process debug info location.
   void processLocation(const Module &M, const DILocation *Loc);
+  // Process a DPValue, much like a DbgVariableIntrinsic.
+  void processDPValue(const Module &M, const DPValue &DPV);
 
   /// Process subprogram.
   void processSubprogram(DISubprogram *SP);
diff --git a/llvm/lib/IR/DebugInfo.cpp b/llvm/lib/IR/DebugInfo.cpp
index 600643704522fdcc..17fdb669f672b244 100644
--- a/llvm/lib/IR/DebugInfo.cpp
+++ b/llvm/lib/IR/DebugInfo.cpp
@@ -205,10 +205,13 @@ void DebugInfoFinder::processCompileUnit(DICompileUnit *CU) {
 void DebugInfoFinder::processInstruction(const Module &M,
                                          const Instruction &I) {
   if (auto *DVI = dyn_cast<DbgVariableIntrinsic>(&I))
-    processVariable(M, *DVI);
+    processVariable(M, DVI->getVariable());
 
   if (auto DbgLoc = I.getDebugLoc())
     processLocation(M, DbgLoc.get());
+
+  for (const DPValue &DPV : I.getDbgValueRange())
+    processDPValue(M, DPV);
 }
 
 void DebugInfoFinder::processLocation(const Module &M, const DILocation *Loc) {
@@ -218,6 +221,11 @@ void DebugInfoFinder::processLocation(const Module &M, const DILocation *Loc) {
   processLocation(M, Loc->getInlinedAt());
 }
 
+void DebugInfoFinder::processDPValue(const Module &M, const DPValue &DPV) {
+  processVariable(M, DPV.getVariable());
+  processLocation(M, DPV.getDebugLoc().get());
+}
+
 void DebugInfoFinder::processType(DIType *DT) {
   if (!addType(DT))
     return;
@@ -292,15 +300,7 @@ void DebugInfoFinder::processSubprogram(DISubprogram *SP) {
 }
 
 void DebugInfoFinder::processVariable(const Module &M,
-                                      const DbgVariableIntrinsic &DVI) {
-  auto *N = dyn_cast<MDNode>(DVI.getVariable());
-  if (!N)
-    return;
-
-  auto *DV = dyn_cast<DILocalVariable>(N);
-  if (!DV)
-    return;
-
+                                      const DILocalVariable *DV) {
   if (!NodesSeen.insert(DV).second)
     return;
   processScope(DV->getScope());
diff --git a/llvm/lib/Transforms/Scalar/CallSiteSplitting.cpp b/llvm/lib/Transforms/Scalar/CallSiteSplitting.cpp
index a0ee6d89131635af..47af299dbd473dd5 100644
--- a/llvm/lib/Transforms/Scalar/CallSiteSplitting.cpp
+++ b/llvm/lib/Transforms/Scalar/CallSiteSplitting.cpp
@@ -402,6 +402,7 @@ static void splitCallSite(CallBase &CB,
       NewPN->insertBefore(&*TailBB->begin());
       CurrentI->replaceAllUsesWith(NewPN);
     }
+    CurrentI->dropDbgValues();
     CurrentI->eraseFromParent();
     // We are done once we handled the first original instruction in TailBB.
     if (CurrentI == OriginalBegin)
diff --git a/llvm/lib/Transforms/Scalar/JumpThreading.cpp b/llvm/lib/Transforms/Scalar/JumpThreading.cpp
index 2d899f100f8154d6..c61e30ad6007b003 100644
--- a/llvm/lib/Transforms/Scalar/JumpThreading.cpp
+++ b/llvm/lib/Transforms/Scalar/JumpThreading.cpp
@@ -3064,8 +3064,8 @@ bool JumpThreadingPass::threadGuard(BasicBlock *BB, IntrinsicInst *Guard,
     if (!isa<PHINode>(&*BI))
       ToRemove.push_back(&*BI);
 
-  Instruction *InsertionPoint = &*BB->getFirstInsertionPt();
-  assert(InsertionPoint && "Empty block?");
+  BasicBlock::iterator InsertionPoint = BB->getFirstInsertionPt();
+  assert(InsertionPoint != BB->end() && "Empty block?");
   // Substitute with Phis & remove.
   for (auto *Inst : reverse(ToRemove)) {
     if (!Inst->use_empty()) {
@@ -3075,6 +3075,7 @@ bool JumpThreadingPass::threadGuard(BasicBlock *BB, IntrinsicInst *Guard,
       NewPN->insertBefore(InsertionPoint);
       Inst->replaceAllUsesWith(NewPN);
     }
+    Inst->dropDbgValues();
     Inst->eraseFromParent();
   }
   return true;
diff --git a/llvm/lib/Transforms/Utils/CloneFunction.cpp b/llvm/lib/Transforms/Utils/CloneFunction.cpp
index 94c6abcf7b4e1ac4..5ef95543e8a036e5 100644
--- a/llvm/lib/Transforms/Utils/CloneFunction.cpp
+++ b/llvm/lib/Transforms/Utils/CloneFunction.cpp
@@ -91,6 +91,7 @@ void llvm::CloneFunctionInto(Function *NewFunc, const Function *OldFunc,
                              const char *NameSuffix, ClonedCodeInfo *CodeInfo,
                              ValueMapTypeRemapper *TypeMapper,
                              ValueMaterializer *Materializer) {
+  NewFunc->setIsNewDbgInfoFormat(OldFunc->IsNewDbgInfoFormat);
   assert(NameSuffix && "NameSuffix cannot be null!");
 
 #ifndef NDEBUG
@@ -268,9 +269,13 @@ void llvm::CloneFunctionInto(Function *NewFunc, const Function *OldFunc,
            BB = cast<BasicBlock>(VMap[&OldFunc->front()])->getIterator(),
            BE = NewFunc->end();
        BB != BE; ++BB)
-    // Loop over all instructions, fixing each one as we find it...
-    for (Instruction &II : *BB)
+    // Loop over all instructions, fixing each one as we find it, and any
+    // attached debug-info records.
+    for (Instruction &II : *BB) {
       RemapInstruction(&II, VMap, RemapFlag, TypeMapper, Materializer);
+      RemapDPValueRange(II.getModule(), II.getDbgValueRange(), VMap,
+                         RemapFlag, TypeMapper, Materializer);
+    }
 
   // Only update !llvm.dbg.cu for DifferentModule (not CloneModule). In the
   // same module, the compile unit will already be listed (or not). When
@@ -328,6 +333,7 @@ Function *llvm::CloneFunction(Function *F, ValueToValueMapTy &VMap,
   // Create the new function...
   Function *NewF = Function::Create(FTy, F->getLinkage(), F->getAddressSpace(),
                                     F->getName(), F->getParent());
+  NewF->setIsNewDbgInfoFormat(F->IsNewDbgInfoFormat);
 
   // Loop over the arguments, copying the names of the mapped arguments over...
   Function::arg_iterator DestI = NewF->arg_begin();
@@ -493,6 +499,21 @@ void PruningFunctionCloner::CloneBlock(
   bool hasCalls = false, hasDynamicAllocas = false, hasStaticAllocas = false;
   bool hasMemProfMetadata = false;
 
+  // Keep a cursor pointing at the last place we cloned debug-info records from.
+  BasicBlock::const_iterator DbgCursor = StartingInst;
+  auto CloneDbgRecordsToHere = [NewBB,&DbgCursor](Instruction *NewInst, BasicBlock::const_iterator II) {
+    if (!NewBB->IsNewDbgInfoFormat)
+      return;
+
+    // Clone debug-info records onto this instruction. Iterate through any
+    // source-instructions we've cloned and then subsequently optimised away,
+    // so that their debug-info doesn't go missing.
+    for (; DbgCursor != II; ++DbgCursor)
+      NewInst->cloneDebugInfoFrom(&*DbgCursor, std::nullopt, false);
+    NewInst->cloneDebugInfoFrom(&*II);
+    DbgCursor = std::next(II);
+  };
+
   // Loop over all instructions, and copy them over, DCE'ing as we go.  This
   // loop doesn't include the terminator.
   for (BasicBlock::const_iterator II = StartingInst, IE = --BB->end(); II != IE;
@@ -542,6 +563,8 @@ void PruningFunctionCloner::CloneBlock(
       hasMemProfMetadata |= II->hasMetadata(LLVMContext::MD_memprof);
     }
 
+    CloneDbgRecordsToHere(NewInst, II);
+
     if (CodeInfo) {
       CodeInfo->OrigVMap[&*II] = NewInst;
       if (auto *CB = dyn_cast<CallBase>(&*II))
@@ -599,6 +622,9 @@ void PruningFunctionCloner::CloneBlock(
     if (OldTI->hasName())
       NewInst->setName(OldTI->getName() + NameSuffix);
     NewInst->insertInto(NewBB, NewBB->end());
+
+    CloneDbgRecordsToHere(NewInst, OldTI->getIterator());
+
     VMap[OldTI] = NewInst; // Add instruction map to value.
 
     if (CodeInfo) {
@@ -610,6 +636,13 @@ void PruningFunctionCloner::CloneBlock(
 
     // Recursively clone any reachable successor blocks.
     append_range(ToClone, successors(BB->getTerminator()));
+  } else {
+    // If we didn't create a new terminator, clone DPValues from the old
+    // terminator onto the new terminator.
+    Instruction *NewInst = NewBB->getTerminator();
+    assert(NewInst);
+
+    CloneDbgRecordsToHere(NewInst, OldTI->getIterator());
   }
 
   if (CodeInfo) {
@@ -847,12 +880,22 @@ void llvm::CloneAndPruneIntoFromInst(Function *NewFunc, const Function *OldFunc,
                        TypeMapper, Materializer);
   }
 
+  // Do the same for DPValues, touching all the instructions in the cloned
+  // range of blocks.
+  Function::iterator Begin = cast<BasicBlock>(VMap[StartingBB])->getIterator();
+  for (BasicBlock &BB : make_range(Begin, NewFunc->end())) {
+    for (Instruction &I : BB) {
+      RemapDPValueRange(I.getModule(), I.getDbgValueRange(), VMap,
+                       ModuleLevelChanges ? RF_None : RF_NoModuleLevelChanges,
+                       TypeMapper, Materializer);
+    }
+  }
+
   // Simplify conditional branches and switches with a constant operand. We try
   // to prune these out when cloning, but if the simplification required
   // looking through PHI nodes, those are only available after forming the full
   // basic block. That may leave some here, and we still want to prune the dead
   // code as early as possible.
-  Function::iterator Begin = cast<BasicBlock>(VMap[StartingBB])->getIterator();
   for (BasicBlock &BB : make_range(Begin, NewFunc->end()))
     ConstantFoldTerminator(&BB);
 
@@ -941,10 +984,15 @@ void llvm::CloneAndPruneFunctionInto(
 void llvm::remapInstructionsInBlocks(ArrayRef<BasicBlock *> Blocks,
                                      ValueToValueMapTy &VMap) {
   // Rewrite the code to refer to itself.
-  for (auto *BB : Blocks)
-    for (auto &Inst : *BB)
+  for (auto *BB : Blocks) {
+    Module *M = BB->getModule();
+    for (auto &Inst : *BB) {
+      RemapDPValueRange(Inst.getModule(), Inst.getDbgValueRange(), VMap,
+                       RF_NoModuleLevelChanges | RF_IgnoreMissingLocals);
       RemapInstruction(&Inst, VMap,
                        RF_NoModuleLevelChanges | RF_IgnoreMissingLocals);
+    }
+  }
 }
 
 /// Clones a loop \p OrigLoop.  Returns the loop and the blocks in \p
@@ -1068,6 +1116,7 @@ BasicBlock *llvm::DuplicateInstructionsInSplitBetween(
     Instruction *New = BI->clone();
     New->setName(BI->getName());
     New->insertBefore(NewTerm);
+    New->cloneDebugInfoFrom(&*BI);
     ValueMapping[&*BI] = New;
 
     // Remap operands to patch up intra-block references.
diff --git a/llvm/lib/Transforms/Utils/CloneModule.cpp b/llvm/lib/Transforms/Utils/CloneModule.cpp
index aae0a280afeb5c71..00e40fe73d90b63b 100644
--- a/llvm/lib/Transforms/Utils/CloneModule.cpp
+++ b/llvm/lib/Transforms/Utils/CloneModule.cpp
@@ -61,6 +61,7 @@ std::unique_ptr<Module> llvm::CloneModule(
   New->setDataLayout(M.getDataLayout());
   New->setTargetTriple(M.getTargetTriple());
   New->setModuleInlineAsm(M.getModuleInlineAsm());
+  New->IsNewDbgInfoFormat = M.IsNewDbgInfoFormat;
 
   // Loop over all of the global variables, making corresponding globals in the
   // new module.  Here we add them to the VMap and to the new Module.  We
diff --git a/llvm/lib/Transforms/Utils/InlineFunction.cpp b/llvm/lib/Transforms/Utils/InlineFunction.cpp
index 4749b213ee56c195..5d17c008f4837812 100644
--- a/llvm/lib/Transforms/Utils/InlineFunction.cpp
+++ b/llvm/lib/Transforms/Utils/InlineFunction.cpp
@@ -1666,48 +1666,70 @@ static void fixupLineNumbers(Function *Fn, Function::iterator FI,
   // the call site location instead.
   bool NoInlineLineTables = Fn->hasFnAttribute("no-inline-line-tables");
 
-  for (; FI != Fn->end(); ++FI) {
-    for (BasicBlock::iterator BI = FI->begin(), BE = FI->end();
-         BI != BE; ++BI) {
-      // Loop metadata needs to be updated so that the start and end locs
-      // reference inlined-at locations.
-      auto updateLoopInfoLoc = [&Ctx, &InlinedAtNode,
-                                &IANodes](Metadata *MD) -> Metadata * {
-        if (auto *Loc = dyn_cast_or_null<DILocation>(MD))
-          return inlineDebugLoc(Loc, InlinedAtNode, Ctx, IANodes).get();
-        return MD;
-      };
-      updateLoopMetadataDebugLocations(*BI, updateLoopInfoLoc);
-
-      if (!NoInlineLineTables)
-        if (DebugLoc DL = BI->getDebugLoc()) {
-          DebugLoc IDL =
-              inlineDebugLoc(DL, InlinedAtNode, BI->getContext(), IANodes);
-          BI->setDebugLoc(IDL);
-          continue;
-        }
+  // Helper-util for updating the metadata attached to an instruction.
+  auto UpdateInst = [&](Instruction &I) {
+    // Loop metadata needs to be updated so that the start and end locs
+    // reference inlined-at locations.
+    auto updateLoopInfoLoc = [&Ctx, &InlinedAtNode,
+                              &IANodes](Metadata *MD) -> Metadata * {
+      if (auto *Loc = dyn_cast_or_null<DILocation>(MD))
+        return inlineDebugLoc(Loc, InlinedAtNode, Ctx, IANodes).get();
+      return MD;
+    };
+    updateLoopMetadataDebugLocations(I, updateLoopInfoLoc);
+
+    if (!NoInlineLineTables)
+      if (DebugLoc DL = I.getDebugLoc()) {
+        DebugLoc IDL =
+            inlineDebugLoc(DL, InlinedAtNode, I.getContext(), IANodes);
+        I.setDebugLoc(IDL);
+        return;
+      }
 
-      if (CalleeHasDebugInfo && !NoInlineLineTables)
-        continue;
+    if (CalleeHasDebugInfo && !NoInlineLineTables)
+      return;
 
-      // If the inlined instruction has no line number, or if inline info
-      // is not being generated, make it look as if it originates from the call
-      // location. This is important for ((__always_inline, __nodebug__))
-      // functions which must use caller location for all instructions in their
-      // function body.
+    // If the inlined instruction has no line number, or if inline info
+    // is not being generated, make it look as if it originates from the call
+    // location. This is important for ((__always_inline, __nodebug__))
+    // functions which must use caller location for all instructions in their
+    // function body.
 
-      // Don't update static allocas, as they may get moved later.
-      if (auto *AI = dyn_cast<AllocaInst>(BI))
-        if (allocaWouldBeStaticInEntry(AI))
-          continue;
+    // Don't update static allocas, as they may get moved later.
+    if (auto *AI = dyn_cast<AllocaInst>(&I))
+      if (allocaWouldBeStaticInEntry(AI))
+        return;
 
-      // Do not force a debug loc for pseudo probes, since they do not need to
-      // be debuggable, and also they are expected to have a zero/null dwarf
-      // discriminator at this point which could be violated otherwise.
-      if (isa<PseudoProbeInst>(BI))
-        continue;
+    // Do not force a debug loc for pseudo probes, since they do not need to
+    // be debuggable, and also they are expected to have a zero/null dwarf
+    // discriminator at this point which could be violated otherwise.
+    if (isa<PseudoProbeInst>(I))
+      return;
 
-      BI->setDebugLoc(TheCallDL);
+    I.setDebugLoc(TheCallDL);
+  };
+
+  // Helper-util for updating debug-info records attached to instructions.
+  auto UpdateDPV = [&](DPValue *DPV) {
+    assert(DPV->getDebugLoc() && "Debug Value must have debug loc");
+    if (NoInlineLineTables) {
+      DPV->setDebugLoc(TheCallDL);
+      return;
+    }
+    DebugLoc DL = DPV->getDebugLoc();
+    DebugLoc IDL =
+        inlineDebugLoc(DL, InlinedAtNode, DPV->getMarker()->getParent()->getContext(), IANodes);
+    DPV->setDebugLoc(IDL);
+  };
+
+  // Iterate over all instructions, updating metadata and debug-info records.
+  for (; FI != Fn->end(); ++FI) {
+    for (BasicBlock::iterator BI = FI->begin(), BE = FI->end();
+         BI != BE; ++BI) {
+      UpdateInst(*BI);
+      for (DPValue &DPV : BI->getDbgValueRange()) {
+        UpdateDPV(&DPV);
+      }
     }
 
     // Remove debug info intrinsics if we're not keeping inline info.
@@ -1717,6 +1739,8 @@ static void fixupLineNumbers(Function *Fn, Function::iterator FI,
         if (isa<DbgInfoIntrinsic>(BI)) {
           BI = BI->eraseFromParent();
           continue;
+        } else {
+          BI->dropDbgValues();
         }
         ++BI;
       }
@@ -2404,6 +2428,7 @@ llvm::InlineResult llvm::InlineFunction(CallBase &CB, InlineFunctionInfo &IFI,
       // Transfer all of the allocas over in a block.  Using splice means
       // that the instructions aren't removed from the symbol table, then
       // reinserted.
+      I.setTailBit(true);
       Caller->getEntryBlock().splice(InsertPoint, &*FirstNewBlock,
                                      AI->getIterator(), I);
     }
diff --git a/llvm/test/DebugInfo/Generic/inline-alloca-ordering.ll b/llvm/test/DebugInfo/Generic/inline-alloca-ordering.ll
new file mode 100644
index 0000000000000000..9ff3d80af80d1ef8
--- /dev/null
+++ b/llvm/test/DebugInfo/Generic/inline-alloca-ordering.ll
@@ -0,0 +1,64 @@
+; RUN: opt %s --passes=inline -o - -S | FileCheck %s --implicit-check-not=dbg.value
+; RUN: opt %s --passes=inline -o - -S --try-experimental-debuginfo-iterators | FileCheck %s --implicit-check-not=dbg.value
+
+;; The inliner, specially, hoists all alloca instructions into the entry block
+;; of the calling function. Ensure that it doesn't accidentally transfer the
+;; dbg.value intrinsic from after the alloca to somewhere else. There should be
+;; one dbg.value in the resulting block after the call to ext, and before the
+;; call to init.
+;;
+;; This becomes significant in the context of non-instruction debug-info. When
+;; splicing segments of instructions around, it's typically the range from one
+;; "real" instruction to another, implicitly including all the dbg.values that
+;; come before the ending instruction. The inliner is a (unique!) location in
+;; LLVM that builds a range of only a single instruction kind (allocas) and thus
+;; doesn't transfer the dbg.value to the entry block. This needs Special
+;; Handling once we get rid of debug-intrinsics.
+
+; CHECK: declare void @llvm.dbg.value(metadata,
+
+; CHECK:    define i32 @bar()
+; CHECK-NEXT: %1 = alloca [65 x i32], align 16
+; CHECK-NEXT: call void @ext()
+; CHECK-NEXT: call void @llvm.lifetime.start.p0(
+; CHECK-NEXT: call void @llvm.dbg.value(metadata i32 0, metadata !10, metadata !DIExpression()), !dbg !12
+; CHECK-NEXT: call void @init(ptr %1)
+
+declare void @ext()
+declare void @init(ptr)
+declare void @llvm.dbg.value(metadata, metadata, metadata)
+
+define internal i32 @foo() !dbg !4 {
+  %1 = alloca [65 x i32], align 16
+  call void @llvm.dbg.value(metadata i32 0, metadata !11, metadata !DIExpression()), !dbg !14
+  call void @init(ptr %1)
+  %2 = load i32, ptr %1, align 4
+  ret i32 %2
+}
+
+define i32 @bar() !dbg !16 {
+  call void @ext()
+  %1 = call i32 @foo(), !dbg !17
+  ret i32 %1
+}
+
+!llvm.dbg.cu = !{!0}
+!llvm.module.flags = !{!8, !9}
+!llvm.ident = !{!10}
+
+!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, file: !1, producer: "clang", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !2)
+!1 = !DIFile(filename: "a.cc", directory: "/tmp")
+!2 = !{}
+!4 = distinct !DISubprogram(name: "foo", linkageName: "_Z3fooi", scope: !1, file: !1, line: 3, type: !5, isLocal: false, isDefinition: true, scopeLine: 3, flags: DIFlagPrototyped, isOptimized: false, unit: !0, retainedNodes: !2)
+!5 = !DISubroutineType(types: !6)
+!6 = !{null, !7}
+!7 = !DIBasicType(name: "int", size: 32, align: 32, encoding: DW_ATE_signed)
+!8 = !{i32 2, !"Dwarf Version", i32 4}
+!9 = !{i32 2, !"Debug Info Version", i32 3}
+!10 = !{!"clang"}
+!11 = !DILocalVariable(name: "i", arg: 1, scope: !4, file: !1, line: 3, type: !7)
+!12 = !DIExpression()
+!14 = !DILocation(line: 4, column: 7, scope: !15)
+!15 = distinct !DILexicalBlock(scope: !4, file: !1, line: 4, column: 7)
+!16 = distinct !DISubprogram(name: "bar", linkageName: "bar", scope: !1, file: !1, line: 3, type: !5, isLocal: false, isDefinition: true, scopeLine: 3, flags: DIFlagPrototyped, isOptimized: false, unit: !0, retainedNodes: !2)
+!17 = !DILocation(line: 4, column: 7, scope: !16)
diff --git a/llvm/test/DebugInfo/Generic/inline-dbg-values.ll b/llvm/test/DebugInfo/Generic/inline-dbg-values.ll
new file mode 100644
index 0000000000000000..7d580d6fba37a94f
--- /dev/null
+++ b/llvm/test/DebugInfo/Generic/inline-dbg-values.ll
@@ -0,0 +1,154 @@
+; RUN: opt -passes='cgscc(inline)' -S %s -o - -S | FileCheck %s --implicit-check-not=dbg.value
+; RUN: opt -passes='cgscc(inline)' -S %s -o - -S --try-experimental-debuginfo-iterators | FileCheck %s --implicit-check-not=dbg.value
+
+;; Test that dbg.value intrinsics are inlined, remapped, and have their
+;; dilocation updated just like normal instructions. This becomes
+;; important when debug-info records case to be instructions.
+;;
+;; test should be inlined into test2
+
+; CHECK: declare void @llvm.dbg.value(metadata,
+
+; CHECK: define i32 @test2
+; CHECK-NEXT: entry:
+; CHECK:      %k.addr.i = alloca i32, align 4
+; CHECK:      %k2.i = alloca i32, align 4
+; CHECK:      %0 = load i32, ptr @global_var, align 4, !dbg !9
+; CHECK:      store i32 %0, ptr %k.addr.i, align 4
+; CHECK-NEXT: call void @llvm.dbg.value(metadata ptr %k.addr.i, metadata ![[KVAR:[0-9]+]], metadata !DIExpression()), !dbg ![[KLINE:[0-9]+]]
+; CHECK-NEXT: call void @llvm.dbg.value(metadata ptr %k2.i, metadata ![[K2VAR:[0-9]+]], metadata !DIExpression()), !dbg ![[GLINE:[0-9]+]]
+; CHECK-NEXT: %1 = load i32, ptr %k.addr.i, align 4,
+;;
+;; dbg.values in this block should be remapped to the local load, but also
+;; the Argument to the calling test2 function.
+;;
+; CHECK: if.then.i:
+; CHECK-NEXT: %3 = load i32, ptr %k2.i,
+; CHECK-NEXT: call void @llvm.dbg.value(metadata i32 %3, metadata ![[KVAR]], metadata !DIExpression()), !dbg ![[KLINE]]
+; CHECK-NEXT: call void @llvm.dbg.value(metadata i32 %foo, metadata ![[K2VAR]], metadata !DIExpression()), !dbg ![[GLINE]]
+;
+;; Similarly, the end block should retain remapped dbg.values, with the second
+;; referring to the @global_var load in the entry block. Check that we clone
+;; from the terminator correctly.
+;
+; CHECK: if.end.i:
+; CHECK-NEXT:  store i32 0, ptr %retval.i, align 4,
+; CHECK-NEXT:  call void @llvm.dbg.value(metadata i32 0, metadata ![[KVAR]], metadata !DIExpression()), !dbg ![[KLINE]]
+; CHECK-NEXT:  call void @llvm.dbg.value(metadata i32 %0, metadata ![[K2VAR]], metadata !DIExpression()), !dbg ![[GLINE]]
+; CHECK-NEXT:  br label %test.exit,
+;
+;; More or less the same checks again in the exit block, this time at the head
+;; of the block, and on a terminator that gets elided.
+;
+; CHECK: test.exit:
+; CHECK-NEXT: call void @llvm.dbg.value(metadata i32 %0, metadata ![[KVAR]], metadata !DIExpression()), !dbg ![[KLINE]]
+; CHECK-NEXT: call void @llvm.dbg.value(metadata i32 %bar, metadata ![[K2VAR]], metadata !DIExpression()), !dbg ![[GLINE]]
+; CHECK-NEXT: %4 = load i32, ptr %retval.i, align 4,
+; CHECK-NEXT: call void @llvm.dbg.value(metadata i32 1, metadata ![[KVAR]], metadata !DIExpression()), !dbg ![[KLINE]]
+; CHECK-NEXT: call void @llvm.dbg.value(metadata i32 1, metadata ![[K2VAR]], metadata !DIExpression()), !dbg ![[GLINE]]
+;
+;; Test that the metadata maps onto the correct things, and that the DILocations
+;; attached to the intrinsics have been inlined.
+;
+; CHECK-DAG: ![[TEST2SP:[0-9]+]] = distinct !DISubprogram(name: "test2",
+; CHECK-DAG: ![[INLINESITEBLOCK:[0-9]+]] = distinct !DILexicalBlock(scope: ![[TEST2SP]],
+; CHECK-DAG: ![[TESTSP:[0-9]+]] = distinct !DISubprogram(name: "test",
+; CHECK-DAG: ![[KVAR]] = !DILocalVariable(name: "k",
+; CHECK-DAG: ![[K2VAR]] = !DILocalVariable(name: "k2",
+; CHECK-DAG: ![[KLINE]] = !DILocation(line: 4, scope: ![[TESTSP]], inlinedAt: ![[INLINESITE:[0-9]+]])
+; CHECK-DAG: ![[INLINESITE]] = distinct !DILocation(line: 14, scope: ![[INLINESITEBLOCK]])
+; CHECK-DAG: ![[GLINE]] = !DILocation(line: 5, scope: ![[TESTSP]], inlinedAt: ![[INLINESITE:[0-9]+]])
+
+target triple = "x86_64--"
+
+ at global_var = external global i32
+
+define internal i32 @test(i32 %k, i32 %foo, i32 %bar)  !dbg !4 {
+entry:
+  %retval = alloca i32, align 4
+  %k.addr = alloca i32, align 4
+  %k2 = alloca i32, align 4
+  store i32 %k, ptr %k.addr, align 4
+  call void @llvm.dbg.value(metadata ptr %k.addr, metadata !13, metadata !DIExpression()), !dbg !14
+  call void @llvm.dbg.value(metadata ptr %k2, metadata !15, metadata !DIExpression()), !dbg !16
+  %0 = load i32, ptr %k.addr, align 4, !dbg !16
+  %call = call i32 @_Z8test_exti(i32 %0), !dbg !16
+  store i32 %call, ptr %k2, align 4, !dbg !16
+  %1 = load i32, ptr %k2, align 4, !dbg !17
+  %cmp = icmp sgt i32 %1, 100, !dbg !17
+  br i1 %cmp, label %if.then, label %if.end, !dbg !17
+
+if.then:                                          ; preds = %entry
+  %2 = load i32, ptr %k2, align 4, !dbg !18
+  call void @llvm.dbg.value(metadata i32 %2, metadata !13, metadata !DIExpression()), !dbg !14
+  call void @llvm.dbg.value(metadata i32 %foo, metadata !15, metadata !DIExpression()), !dbg !16
+  store i32 %2, ptr %retval, !dbg !18
+  br label %return, !dbg !18
+
+if.end:                                           ; preds = %entry
+  store i32 0, ptr %retval, !dbg !19
+  call void @llvm.dbg.value(metadata i32 0, metadata !13, metadata !DIExpression()), !dbg !14
+  call void @llvm.dbg.value(metadata i32 %k, metadata !15, metadata !DIExpression()), !dbg !16
+  br label %return, !dbg !19
+
+return:                                           ; preds = %if.end, %if.then
+  call void @llvm.dbg.value(metadata i32 %k, metadata !13, metadata !DIExpression()), !dbg !14
+  call void @llvm.dbg.value(metadata i32 %bar, metadata !15, metadata !DIExpression()), !dbg !16
+  %3 = load i32, ptr %retval, !dbg !20
+  call void @llvm.dbg.value(metadata i32 1, metadata !13, metadata !DIExpression()), !dbg !14
+  call void @llvm.dbg.value(metadata i32 1, metadata !15, metadata !DIExpression()), !dbg !16
+  ret i32 %3, !dbg !20
+}
+
+declare void @llvm.dbg.value(metadata, metadata, metadata) #1
+
+declare i32 @_Z8test_exti(i32)
+
+define i32 @test2(i32 %foo, i32 %bar) !dbg !10 {
+entry:
+  %exn.slot = alloca ptr
+  %ehselector.slot = alloca i32
+  %e = alloca i32, align 4
+  %0 = load i32, ptr @global_var, align 4, !dbg !21
+  %call = call i32 @test(i32 %0, i32 %foo, i32 %bar), !dbg !21
+  br label %try.cont, !dbg !23
+
+try.cont:                                         ; preds = %catch, %invoke.cont
+  store i32 1, ptr @global_var, align 4, !dbg !29
+  ret i32 0, !dbg !30
+}
+
+!llvm.dbg.cu = !{!0}
+!llvm.module.flags = !{!31}
+
+!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, producer: "clang version 3.3 ", isOptimized: false, emissionKind: FullDebug, file: !1, enums: !2, retainedTypes: !2, globals: !2, imports: !2)
+!1 = !DIFile(filename: "<unknown>", directory: "")
+!2 = !{}
+!4 = distinct !DISubprogram(name: "test", linkageName: "_Z4testi", line: 4, isLocal: false, isDefinition: true, virtualIndex: 6, flags: DIFlagPrototyped, isOptimized: false, unit: !0, scopeLine: 4, file: !5, scope: !6, type: !7, retainedNodes: !2)
+!5 = !DIFile(filename: "test.cpp", directory: "")
+!6 = !DIFile(filename: "test.cpp", directory: "")
+!7 = !DISubroutineType(types: !8)
+!8 = !{!9, !9}
+!9 = !DIBasicType(tag: DW_TAG_base_type, name: "int", size: 32, align: 32, encoding: DW_ATE_signed)
+!10 = distinct !DISubprogram(name: "test2", linkageName: "_Z5test2v", line: 11, isLocal: false, isDefinition: true, virtualIndex: 6, flags: DIFlagPrototyped, isOptimized: false, unit: !0, scopeLine: 11, file: !5, scope: !6, type: !11, retainedNodes: !2)
+!11 = !DISubroutineType(types: !12)
+!12 = !{!9}
+!13 = !DILocalVariable(name: "k", line: 4, arg: 1, scope: !4, file: !6, type: !9)
+!14 = !DILocation(line: 4, scope: !4)
+!15 = !DILocalVariable(name: "k2", line: 5, scope: !4, file: !6, type: !9)
+!16 = !DILocation(line: 5, scope: !4)
+!17 = !DILocation(line: 6, scope: !4)
+!18 = !DILocation(line: 7, scope: !4)
+!19 = !DILocation(line: 8, scope: !4)
+!20 = !DILocation(line: 9, scope: !4)
+!21 = !DILocation(line: 14, scope: !22)
+!22 = distinct !DILexicalBlock(line: 13, column: 0, file: !5, scope: !10)
+!23 = !DILocation(line: 15, scope: !22)
+!24 = !DILocalVariable(name: "e", line: 16, scope: !10, file: !6, type: !9)
+!25 = !DILocation(line: 16, scope: !10)
+!26 = !DILocation(line: 17, scope: !27)
+!27 = distinct !DILexicalBlock(line: 16, column: 0, file: !5, scope: !10)
+!28 = !DILocation(line: 18, scope: !27)
+!29 = !DILocation(line: 19, scope: !10)
+!30 = !DILocation(line: 20, scope: !10)
+!31 = !{i32 1, !"Debug Info Version", i32 3}
diff --git a/llvm/test/DebugInfo/Generic/inline-debug-loc.ll b/llvm/test/DebugInfo/Generic/inline-debug-loc.ll
index 37e124b537b4d6cb..0a704bade1e38134 100644
--- a/llvm/test/DebugInfo/Generic/inline-debug-loc.ll
+++ b/llvm/test/DebugInfo/Generic/inline-debug-loc.ll
@@ -1,4 +1,5 @@
 ; RUN: opt -passes='cgscc(inline)' -S < %s | FileCheck %s
+; RUN: opt -passes='cgscc(inline)' -S < %s --try-experimental-debuginfo-iterators | FileCheck %s
 
 target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
 
diff --git a/llvm/test/Transforms/CallSiteSplitting/callsite-split-preserve-debug.ll b/llvm/test/Transforms/CallSiteSplitting/callsite-split-preserve-debug.ll
index d14896cf9d0b5ca2..e185286304a686f0 100644
--- a/llvm/test/Transforms/CallSiteSplitting/callsite-split-preserve-debug.ll
+++ b/llvm/test/Transforms/CallSiteSplitting/callsite-split-preserve-debug.ll
@@ -1,10 +1,20 @@
-; RUN: opt -passes=callsite-splitting -S < %s | FileCheck %s
+; RUN: opt -passes=callsite-splitting -S < %s | FileCheck %s --implicit-check-not=dbg.value
+; RUN: opt -passes=callsite-splitting -S < %s --try-experimental-debuginfo-iterators | FileCheck %s --implicit-check-not=dbg.value
+
+;; Test that DebugLocs are preserved, and that dbg.values are duplicated.
+
+; CHECK: declare void @llvm.dbg.value(metadata,
 
 ; CHECK-LABEL: @test1
-; CHECK:         [[R1:%.+]] = call i32 @callee(i32 0, i32 %dd), !dbg [[DBG1:!.*]]
-; CHECK:         [[R2:%.+]] = call i32 @callee(i32 1, i32 %dd), !dbg [[DBG1]]
+; CHECK:         call void @llvm.dbg.value(metadata i32 0,
+; CHECK-NEXT:    [[R1:%.+]] = call i32 @callee(i32 0, i32 %dd), !dbg [[DBG1:!.*]]
+; CHECK:         call void @llvm.dbg.value(metadata i32 0,
+; CHECK-NEXT:    [[R2:%.+]] = call i32 @callee(i32 1, i32 %dd), !dbg [[DBG1]]
 ; CHECK-LABEL: CallSite:
 ; CHECK-NEXT:    phi i32 [ [[R2]], %land.rhs.split ], [ [[R1]], %entry.split ], !dbg [[DBG1]]
+; CHECK-NEXT:    call void @llvm.dbg.value(metadata i32 1,
+
+declare void @llvm.dbg.value(metadata, metadata, metadata)
 
 define i32 @test1(ptr dereferenceable(4) %cc, i32 %dd) !dbg !6 {
 entry:
@@ -15,18 +25,23 @@ land.rhs:                                         ; preds = %entry
 
 CallSite:                                         ; preds = %land.rhs, %entry
   %pv = phi i32 [ 0, %entry ], [ 1, %land.rhs ]
+  call void @llvm.dbg.value(metadata i32 0, metadata !9, metadata !DIExpression()), !dbg !18
   %call = call i32 @callee(i32 %pv, i32 %dd), !dbg !18
+  call void @llvm.dbg.value(metadata i32 1, metadata !9, metadata !DIExpression()), !dbg !18
   ret i32 %call
 }
 
 ; CHECK-LABEL: @test2
 ; CHECK:         [[LV1:%.*]] = load i32, ptr %ptr, align 4, !dbg [[DBG_LV:!.*]]
+; CHECK-NEXT:    call void @llvm.dbg.value(metadata i32 0,
 ; CHECK-NEXT:    [[R1:%.+]] = call i32 @callee(i32 0, i32 10), !dbg [[DBG_CALL:!.*]]
 ; CHECK:         [[LV2:%.*]] = load i32, ptr %ptr, align 4, !dbg [[DBG_LV]]
+; CHECK-NEXT:    call void @llvm.dbg.value(metadata i32 0,
 ; CHECK-NEXT:    [[R2:%.+]] = call i32 @callee(i32 0, i32 %i), !dbg [[DBG_CALL]]
 ; CHECK-LABEL: CallSite:
 ; CHECK-NEXT:    phi i32 [ [[LV1]], %Header.split ], [ [[LV2]], %TBB.split ], !dbg [[DBG_LV]]
 ; CHECK-NEXT:    phi i32 [ [[R1]], %Header.split ], [ [[R2]], %TBB.split ], !dbg [[DBG_CALL]]
+; CHECK-NEXT:    call void @llvm.dbg.value(metadata i32 1,
 
 define void @test2(ptr %ptr, i32 %i) !dbg !19 {
 Header:
@@ -38,7 +53,9 @@ TBB:                                              ; preds = %Header
 
 CallSite:                                         ; preds = %TBB, %Header
   %lv = load i32, ptr %ptr, align 4, !dbg !25
+  call void @llvm.dbg.value(metadata i32 0, metadata !21, metadata !DIExpression()), !dbg !26
   %cv = call i32 @callee(i32 0, i32 %i), !dbg !26
+  call void @llvm.dbg.value(metadata i32 1, metadata !21, metadata !DIExpression()), !dbg !26
   %sub = sub nsw i32 %lv, %cv
   br label %End
 
diff --git a/llvm/test/Transforms/Inline/alloca-dbgdeclare.ll b/llvm/test/Transforms/Inline/alloca-dbgdeclare.ll
index 7fc1bdd8ed68db48..9592333aeb5420d6 100644
--- a/llvm/test/Transforms/Inline/alloca-dbgdeclare.ll
+++ b/llvm/test/Transforms/Inline/alloca-dbgdeclare.ll
@@ -1,5 +1,8 @@
 ; RUN: opt -passes=inline -S < %s | FileCheck %s
 ; RUN: opt -passes='cgscc(inline)' -S < %s | FileCheck %s
+
+; RUN: opt -passes=inline -S < %s --try-experimental-debuginfo-iterators | FileCheck %s
+; RUN: opt -passes='cgscc(inline)' -S < %s --try-experimental-debuginfo-iterators | FileCheck %s
 ; struct A {
 ;   int arg0;
 ;   double arg1[2];
diff --git a/llvm/test/Transforms/Inline/delete-function-with-metadata-use.ll b/llvm/test/Transforms/Inline/delete-function-with-metadata-use.ll
index 8176135a5b538d58..8399cd63847ed7e8 100644
--- a/llvm/test/Transforms/Inline/delete-function-with-metadata-use.ll
+++ b/llvm/test/Transforms/Inline/delete-function-with-metadata-use.ll
@@ -1,4 +1,5 @@
 ; RUN: opt -passes=inline < %s -S | FileCheck %s
+; RUN: opt -passes=inline < %s -S --try-experimental-debuginfo-iterators | FileCheck %s
 
 ; CHECK: define {{.*}}@f1
 ; CHECK-NOT: define
diff --git a/llvm/test/Transforms/Inline/ignore-debug-info.ll b/llvm/test/Transforms/Inline/ignore-debug-info.ll
index 7ccf7e86bac2ad81..ff697b5c8ab32d91 100644
--- a/llvm/test/Transforms/Inline/ignore-debug-info.ll
+++ b/llvm/test/Transforms/Inline/ignore-debug-info.ll
@@ -3,6 +3,9 @@
 ; RUN: opt < %s -S -passes='cgscc(inline)' -inline-threshold=2 | FileCheck %s
 ; RUN: opt < %s -S -strip-debug -passes='cgscc(inline)' -inline-threshold=2 | FileCheck %s
 ;
+; RUN: opt < %s -S -passes=inline -inline-threshold=2 --try-experimental-debuginfo-iterators | FileCheck %s
+; RUN: opt < %s -S -passes='cgscc(inline)' -inline-threshold=2 --try-experimental-debuginfo-iterators | FileCheck %s
+;
 ; The purpose of this test is to check that debug info doesn't influence
 ; inlining decisions.
 
diff --git a/llvm/test/Transforms/Inline/inline-skip-use-empty-alloca.ll b/llvm/test/Transforms/Inline/inline-skip-use-empty-alloca.ll
index e2660e2648b7637a..ea298a4556f43022 100644
--- a/llvm/test/Transforms/Inline/inline-skip-use-empty-alloca.ll
+++ b/llvm/test/Transforms/Inline/inline-skip-use-empty-alloca.ll
@@ -1,5 +1,7 @@
 ; RUN: opt < %s -S -passes=inline | FileCheck %s
 ; RUN: opt < %s -S -strip-debug -passes=inline | FileCheck %s
+;
+; RUN: opt < %s -S -passes=inline --try-experimental-debuginfo-iterators | FileCheck %s
 
 ; https://bugs.llvm.org/show_bug.cgi?id=43291
 ; The purpose of this test is to check if there is use_empty in the inner loop when scanning
diff --git a/llvm/test/Transforms/Inline/local-as-metadata-undominated-use.ll b/llvm/test/Transforms/Inline/local-as-metadata-undominated-use.ll
index 2a9e55a83e51476d..83f9a3a778d7f58f 100644
--- a/llvm/test/Transforms/Inline/local-as-metadata-undominated-use.ll
+++ b/llvm/test/Transforms/Inline/local-as-metadata-undominated-use.ll
@@ -1,5 +1,7 @@
 ; RUN: opt -passes=inline -S < %s | FileCheck %s
 ; RUN: opt -passes='cgscc(inline)' -S < %s | FileCheck %s
+;
+; RUN: opt -passes=inline -S < %s --try-experimental-debuginfo-iterators | FileCheck %s
 
 ; Make sure the inliner doesn't crash when a metadata-bridged SSA operand is an
 ; undominated use.
diff --git a/llvm/test/Transforms/Inline/no-inline-line-tables.ll b/llvm/test/Transforms/Inline/no-inline-line-tables.ll
index e298088d1fce5947..4aeb94da776e9733 100644
--- a/llvm/test/Transforms/Inline/no-inline-line-tables.ll
+++ b/llvm/test/Transforms/Inline/no-inline-line-tables.ll
@@ -1,4 +1,5 @@
 ; RUN: opt < %s -passes=inline -S | FileCheck %s
+; RUN: opt < %s -passes=inline -S --try-experimental-debuginfo-iterators | FileCheck %s
 
 ; This tests that functions with the attribute `no-inline-line-tables` have the
 ; correct debug information when they are inlined.
diff --git a/llvm/test/Transforms/JumpThreading/guard-split-debuginfo.ll b/llvm/test/Transforms/JumpThreading/guard-split-debuginfo.ll
new file mode 100644
index 0000000000000000..05ff74939449cd5d
--- /dev/null
+++ b/llvm/test/Transforms/JumpThreading/guard-split-debuginfo.ll
@@ -0,0 +1,80 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
+; RUN: opt -S -passes=jump-threading %s -o - -S | FileCheck %s
+; RUN: opt -S -passes=jump-threading %s -o - -S --try-experimental-debuginfo-iterators | FileCheck %s
+
+; Test that debug-info records in the Merge block, to be copied by
+; DuplicateInstructionsInSplitBetween, get duplicated into the relevant
+; parent blocks. And that ino jump-threading, the old dbg.value gets
+; deleted.
+
+declare void @llvm.experimental.guard(i1, ...)
+
+declare i32 @f1()
+declare i32 @f2()
+
+declare void @llvm.dbg.value(metadata, metadata, metadata)
+
+define i32 @branch_implies_guard(i32 %a) !dbg !7 {
+; CHECK-LABEL: @branch_implies_guard(
+; CHECK-NEXT:    [[COND:%.*]] = icmp slt i32 [[A:%.*]], 10
+; CHECK-NEXT:    br i1 [[COND]], label [[T1_SPLIT:%.*]], label [[F1_SPLIT:%.*]], !dbg [[DBG12:![0-9]+]]
+; CHECK:       T1.split:
+; CHECK-NEXT:    [[V1:%.*]] = call i32 @f1(), !dbg [[DBG12]]
+; CHECK-NEXT:    call void @llvm.dbg.value(metadata i32 0, metadata [[META13:![0-9]+]], metadata !DIExpression()), !dbg [[DBG14:![0-9]+]]
+; CHECK-NEXT:    [[RETVAL3:%.*]] = add i32 [[V1]], 10, !dbg [[DBG12]]
+; CHECK-NEXT:    [[CONDGUARD4:%.*]] = icmp slt i32 [[A]], 20, !dbg [[DBG12]]
+; CHECK-NEXT:    br label [[MERGE:%.*]]
+; CHECK:       F1.split:
+; CHECK-NEXT:    [[V2:%.*]] = call i32 @f2(), !dbg [[DBG12]]
+; CHECK-NEXT:    call void @llvm.dbg.value(metadata i32 0, metadata [[META13]], metadata !DIExpression()), !dbg [[DBG14]]
+; CHECK-NEXT:    [[RETVAL1:%.*]] = add i32 [[V2]], 10, !dbg [[DBG12]]
+; CHECK-NEXT:    [[CONDGUARD2:%.*]] = icmp slt i32 [[A]], 20, !dbg [[DBG12]]
+; CHECK-NEXT:    call void (i1, ...) @llvm.experimental.guard(i1 [[CONDGUARD2]]) [ "deopt"() ]
+; CHECK-NEXT:    br label [[MERGE]]
+; CHECK:       Merge:
+; CHECK-NEXT:    [[RETPHI:%.*]] = phi i32 [ [[V1]], [[T1_SPLIT]] ], [ [[V2]], [[F1_SPLIT]] ]
+; CHECK-NEXT:    [[TMP1:%.*]] = phi i32 [ [[RETVAL3]], [[T1_SPLIT]] ], [ [[RETVAL1]], [[F1_SPLIT]] ]
+; CHECK-NEXT:    ret i32 [[TMP1]], !dbg [[DBG12]]
+;
+  %cond = icmp slt i32 %a, 10
+  br i1 %cond, label %T1, label %F1, !dbg !26
+
+T1:
+  %v1 = call i32 @f1(), !dbg !26
+  br label %Merge
+
+F1:
+  %v2 = call i32 @f2(), !dbg !26
+  br label %Merge
+
+Merge:
+  %retPhi = phi i32 [ %v1, %T1 ], [ %v2, %F1 ]
+  call void @llvm.dbg.value(metadata i32 0, metadata !12, metadata !DIExpression()), !dbg !13
+  %retVal = add i32 %retPhi, 10, !dbg !26
+  %condGuard = icmp slt i32 %a, 20, !dbg !26
+  call void(i1, ...) @llvm.experimental.guard(i1 %condGuard) [ "deopt"() ]
+  ret i32 %retVal, !dbg !26
+}
+
+!llvm.dbg.cu = !{!0}
+!llvm.module.flags = !{!3, !4, !5}
+!llvm.ident = !{!6}
+
+!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, splitDebugInlining: false, nameTableKind: None)
+!1 = !DIFile(filename: "test.c", directory: "/tmp/out.c")
+!2 = !{}
+!3 = !{i32 7, !"Dwarf Version", i32 4}
+!4 = !{i32 2, !"Debug Info Version", i32 3}
+!5 = !{i32 1, !"wchar_size", i32 4}
+!6 = !{!""}
+!7 = distinct !DISubprogram(name: "foo", scope: !1, file: !1, line: 3, type: !8, scopeLine: 3, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !2)
+!8 = !DISubroutineType(types: !9)
+!9 = !{!10, !11, !11}
+!10 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+!11 = !DIBasicType(name: "long int", size: 64, encoding: DW_ATE_signed)
+!12 = !DILocalVariable(name: "bar", arg: 1, scope: !7, file: !1, line: 3, type: !11)
+!13 = !DILocation(line: 0, scope: !7)
+!14 = !DILocalVariable(name: "baz", arg: 2, scope: !7, file: !1, line: 3, type: !11)
+!19 = distinct !DILexicalBlock(scope: !7, file: !1, line: 8, column: 7)
+!26 = !DILocation(line: 13, column: 3, scope: !7)
+
diff --git a/llvm/test/Transforms/LoopUnroll/debug-info.ll b/llvm/test/Transforms/LoopUnroll/debug-info.ll
index 65553ee3af5427f1..2188cea3b881148a 100644
--- a/llvm/test/Transforms/LoopUnroll/debug-info.ll
+++ b/llvm/test/Transforms/LoopUnroll/debug-info.ll
@@ -1,4 +1,5 @@
 ; RUN: opt %s -S -o - -passes=loop-unroll | FileCheck %s
+; RUN: opt %s -S -o - -passes=loop-unroll --try-experimental-debuginfo-iterators | FileCheck %s
 ; generated at -O3 from:
 ; void f() {
 ;   for (int i = 1; i <=32; i <<=2 )



More information about the llvm-commits mailing list