[llvm] e3cd498 - [Assignment Tracking][21/*] Account for assignment tracking in inliner

via llvm-commits llvm-commits at lists.llvm.org
Fri Nov 18 03:56:43 PST 2022


Author: OCHyams
Date: 2022-11-18T11:55:05Z
New Revision: e3cd498ff7748f967be1bdcc0a994e40ce82268f

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

LOG: [Assignment Tracking][21/*] Account for assignment tracking in inliner

The Assignment Tracking debug-info feature is outlined in this RFC:

https://discourse.llvm.org/t/
rfc-assignment-tracking-a-better-way-of-specifying-variable-locations-in-ir

The inliner requires two additions:

fixupAssignments - Update inlined instructions' DIAssignID metadata so that
inlined DIAssignID attachments are unique to the inlined instance.

trackInlinedStores - Treat inlined stores to caller-local variables
(i.e. callee stores to argument pointers that point to the caller's allocas) as
assignments. Track them using trackAssignments, which is the same method as is
used by the AssignmentTrackingPass. This means that we're able to detect stale
memory locations due to DSE after inlining. Because the stores are only tracked
_after_ inlining, any DSE or movement of stores _before_ inlining will not be
accounted for. This is an accepted limitation mentioned in the RFC.

One change is also required:

Update CloneBlock to preserve debug use-before-defs. Otherwise the assignments
will be dropped due to having the intrinsic operands replaced with empty
metadata (see use-before-def.ll in this patch and this related discourse post.

Reviewed By: jmorse

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

Added: 
    llvm/test/DebugInfo/Generic/assignment-tracking/inline/id.ll
    llvm/test/DebugInfo/Generic/assignment-tracking/inline/inline-stores.ll
    llvm/test/DebugInfo/Generic/assignment-tracking/inline/use-before-def.ll

Modified: 
    llvm/lib/Transforms/Utils/CloneFunction.cpp
    llvm/lib/Transforms/Utils/InlineFunction.cpp

Removed: 
    


################################################################################
diff  --git a/llvm/lib/Transforms/Utils/CloneFunction.cpp b/llvm/lib/Transforms/Utils/CloneFunction.cpp
index 2fde87ef7b09..78c27db35e6f 100644
--- a/llvm/lib/Transforms/Utils/CloneFunction.cpp
+++ b/llvm/lib/Transforms/Utils/CloneFunction.cpp
@@ -492,8 +492,9 @@ void PruningFunctionCloner::CloneBlock(
     }
 
     // Eagerly remap operands to the newly cloned instruction, except for PHI
-    // nodes for which we defer processing until we update the CFG.
-    if (!isa<PHINode>(NewInst)) {
+    // nodes for which we defer processing until we update the CFG. Also defer
+    // debug intrinsic processing because they may contain use-before-defs.
+    if (!isa<PHINode>(NewInst) && !isa<DbgVariableIntrinsic>(NewInst)) {
       RemapInstruction(NewInst, VMap,
                        ModuleLevelChanges ? RF_None : RF_NoModuleLevelChanges);
 
@@ -637,6 +638,15 @@ void llvm::CloneAndPruneIntoFromInst(Function *NewFunc, const Function *OldFunc,
     StartingInst = &StartingBB->front();
   }
 
+  // Collect debug intrinsics for remapping later.
+  SmallVector<const DbgVariableIntrinsic *, 8> DbgIntrinsics;
+  for (const auto &BB : *OldFunc) {
+    for (const auto &I : BB) {
+      if (const auto *DVI = dyn_cast<DbgVariableIntrinsic>(&I))
+        DbgIntrinsics.push_back(DVI);
+    }
+  }
+
   // Clone the entry block, and anything recursively reachable from it.
   std::vector<const BasicBlock *> CloneWorklist;
   PFC.CloneBlock(StartingBB, StartingInst->getIterator(), CloneWorklist);
@@ -808,6 +818,19 @@ void llvm::CloneAndPruneIntoFromInst(Function *NewFunc, const Function *OldFunc,
       VMap[OrigV] = I;
   }
 
+  // Remap debug intrinsic operands now that all values have been mapped.
+  // Doing this now (late) preserves use-before-defs in debug intrinsics. If
+  // we didn't do this, ValueAsMetadata(use-before-def) operands would be
+  // replaced by empty metadata. This would signal later cleanup passes to
+  // remove the debug intrinsics, potentially causing incorrect locations.
+  for (const auto *DVI : DbgIntrinsics) {
+    if (DbgVariableIntrinsic *NewDVI =
+            cast_or_null<DbgVariableIntrinsic>(VMap.lookup(DVI)))
+      RemapInstruction(NewDVI, 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

diff  --git a/llvm/lib/Transforms/Utils/InlineFunction.cpp b/llvm/lib/Transforms/Utils/InlineFunction.cpp
index dfb7da9b8a78..5c9511c83900 100644
--- a/llvm/lib/Transforms/Utils/InlineFunction.cpp
+++ b/llvm/lib/Transforms/Utils/InlineFunction.cpp
@@ -1767,6 +1767,94 @@ static void fixupLineNumbers(Function *Fn, Function::iterator FI,
   }
 }
 
+#undef DEBUG_TYPE
+#define DEBUG_TYPE "assignment-tracking"
+/// Find Alloca and linked DbgAssignIntrinsic for locals escaped by \p CB.
+static at::StorageToVarsMap collectEscapedLocals(const DataLayout &DL,
+                                                 const CallBase &CB) {
+  at::StorageToVarsMap EscapedLocals;
+  SmallPtrSet<const Value *, 4> SeenBases;
+
+  LLVM_DEBUG(
+      errs() << "# Finding caller local variables escaped by callee\n");
+  for (const Value *Arg : CB.args()) {
+    LLVM_DEBUG(errs() << "INSPECT: " << *Arg << "\n");
+    if (!Arg->getType()->isPointerTy()) {
+      LLVM_DEBUG(errs() << " | SKIP: Not a pointer\n");
+      continue;
+    }
+
+    const Instruction *I = dyn_cast<Instruction>(Arg);
+    if (!I) {
+      LLVM_DEBUG(errs() << " | SKIP: Not result of instruction\n");
+      continue;
+    }
+
+    // Walk back to the base storage.
+    assert(Arg->getType()->isPtrOrPtrVectorTy());
+    APInt TmpOffset(DL.getIndexTypeSizeInBits(Arg->getType()), 0, false);
+    const AllocaInst *Base = dyn_cast<AllocaInst>(
+        Arg->stripAndAccumulateConstantOffsets(DL, TmpOffset, true));
+    if (!Base) {
+      LLVM_DEBUG(errs() << " | SKIP: Couldn't walk back to base storage\n");
+      continue;
+    }
+
+    assert(Base);
+    LLVM_DEBUG(errs() << " | BASE: " << *Base << "\n");
+    // We only need to process each base address once - skip any duplicates.
+    if (!SeenBases.insert(Base).second)
+      continue;
+
+    // Find all local variables associated with the backing storage.
+    for (auto *DAI : at::getAssignmentMarkers(Base)) {
+      // Skip variables from inlined functions - they are not local variables.
+      if (DAI->getDebugLoc().getInlinedAt())
+        continue;
+      LLVM_DEBUG(errs() << " > DEF : " << *DAI << "\n");
+      EscapedLocals[Base].insert(at::VarRecord(DAI));
+    }
+  }
+  return EscapedLocals;
+}
+
+static void trackInlinedStores(Function::iterator Start, Function::iterator End,
+                               const CallBase &CB) {
+  LLVM_DEBUG(errs() << "trackInlinedStores into "
+                    << Start->getParent()->getName() << " from "
+                    << CB.getCalledFunction()->getName() << "\n");
+  std::unique_ptr<DataLayout> DL = std::make_unique<DataLayout>(CB.getModule());
+  at::trackAssignments(Start, End, collectEscapedLocals(*DL, CB), *DL);
+}
+
+/// Update inlined instructions' DIAssignID metadata. We need to do this
+/// otherwise a function inlined more than once into the same function
+/// will cause DIAssignID to be shared by many instructions.
+static void fixupAssignments(Function::iterator Start, Function::iterator End) {
+  // Map {Old, New} metadata. Not used directly - use GetNewID.
+  DenseMap<DIAssignID *, DIAssignID *> Map;
+  auto GetNewID = [&Map](Metadata *Old) {
+    DIAssignID *OldID = cast<DIAssignID>(Old);
+    if (DIAssignID *NewID = Map.lookup(OldID))
+      return NewID;
+    DIAssignID *NewID = DIAssignID::getDistinct(OldID->getContext());
+    Map[OldID] = NewID;
+    return NewID;
+  };
+  // Loop over all the inlined instructions. If we find a DIAssignID
+  // attachment or use, replace it with a new version.
+  for (auto BBI = Start; BBI != End; ++BBI) {
+    for (Instruction &I : *BBI) {
+      if (auto *ID = I.getMetadata(LLVMContext::MD_DIAssignID))
+        I.setMetadata(LLVMContext::MD_DIAssignID, GetNewID(ID));
+      else if (auto *DAI = dyn_cast<DbgAssignIntrinsic>(&I))
+        DAI->setAssignId(GetNewID(DAI->getAssignID()));
+    }
+  }
+}
+#undef DEBUG_TYPE
+#define DEBUG_TYPE "inline-function"
+
 /// Update the block frequencies of the caller after a callee has been inlined.
 ///
 /// Each block cloned into the caller has its block frequency scaled by the
@@ -2252,6 +2340,15 @@ llvm::InlineResult llvm::InlineFunction(CallBase &CB, InlineFunctionInfo &IFI,
     fixupLineNumbers(Caller, FirstNewBlock, &CB,
                      CalledFunc->getSubprogram() != nullptr);
 
+    if (getEnableAssignmentTracking()) {
+      // Interpret inlined stores to caller-local variables as assignments.
+      trackInlinedStores(FirstNewBlock, Caller->end(), CB);
+
+      // Update DIAssignID metadata attachments and uses so that they are
+      // unique to this inlined instance.
+      fixupAssignments(FirstNewBlock, Caller->end());
+    }
+
     // Now clone the inlined noalias scope metadata.
     SAMetadataCloner.clone();
     SAMetadataCloner.remap(FirstNewBlock, Caller->end());

diff  --git a/llvm/test/DebugInfo/Generic/assignment-tracking/inline/id.ll b/llvm/test/DebugInfo/Generic/assignment-tracking/inline/id.ll
new file mode 100644
index 000000000000..b6feceec5dc1
--- /dev/null
+++ b/llvm/test/DebugInfo/Generic/assignment-tracking/inline/id.ll
@@ -0,0 +1,87 @@
+; RUN: opt %s -S -passes=inline -o - -experimental-assignment-tracking \
+; RUN: | FileCheck %s
+
+;; Check that all DIAssignID metadata that are inlined are replaced with new
+;; versions. Otherwise two inlined instances of an assignment will be considered
+;; to be the same assignment.
+;;
+;; $cat test.cpp
+;; __attribute__((always_inline))
+;; int get() { int val = 5; return val; }
+;; void fun() {
+;;   get();
+;;   get();
+;; }
+
+; CHECK-LABEL: _Z3funv
+;
+; CHECK: store i32 5, ptr %val.i, align 4{{.*}}, !DIAssignID [[ID_0:![0-9]+]]
+; CHECK-NEXT: call void @llvm.dbg.assign(metadata i32 5, metadata [[val:![0-9]+]], metadata !DIExpression(), metadata [[ID_0]], metadata ptr %val.i, metadata !DIExpression()), !dbg [[dl_inline_0:![0-9]+]]
+;
+; CHECK: store i32 5, ptr %val.i1, align 4{{.*}}, !DIAssignID [[ID_1:![0-9]+]]
+; CHECK-NEXT: call void @llvm.dbg.assign(metadata i32 5, metadata [[val]], metadata !DIExpression(), metadata [[ID_1]], metadata ptr %val.i1, metadata !DIExpression()), !dbg [[dl_inline_1:![0-9]+]]
+;
+; CHECK-DAG: [[val]] = !DILocalVariable(name: "val",
+; CHECK-DAG: [[dl_inline_0]] = !DILocation({{.*}}inlinedAt
+; CHECK-DAG: [[dl_inline_1]] = !DILocation({{.*}}inlinedAt
+; CHECK-DAG: [[ID_0]] = distinct !DIAssignID()
+; CHECK-DAG: [[ID_1]] = distinct !DIAssignID()
+
+define dso_local i32 @_Z3getv() !dbg !7 {
+entry:
+  %val = alloca i32, align 4, !DIAssignID !13
+  call void @llvm.dbg.assign(metadata i1 undef, metadata !12, metadata !DIExpression(), metadata !13, metadata ptr %val, metadata !DIExpression()), !dbg !14
+  %0 = bitcast ptr %val to ptr, !dbg !15
+  call void @llvm.lifetime.start.p0i8(i64 4, ptr %0), !dbg !15
+  store i32 5, ptr %val, align 4, !dbg !16, !DIAssignID !21
+  call void @llvm.dbg.assign(metadata i32 5, metadata !12, metadata !DIExpression(), metadata !21, metadata ptr %val, metadata !DIExpression()), !dbg !14
+  %1 = load i32, ptr %val, align 4, !dbg !22
+  %2 = bitcast ptr %val to ptr, !dbg !23
+  call void @llvm.lifetime.end.p0i8(i64 4, ptr %2), !dbg !23
+  ret i32 %1, !dbg !24
+}
+
+declare void @llvm.lifetime.start.p0i8(i64 immarg, ptr nocapture)
+declare void @llvm.dbg.declare(metadata, metadata, metadata)
+declare void @llvm.lifetime.end.p0i8(i64 immarg, ptr nocapture)
+declare void @llvm.dbg.assign(metadata, metadata, metadata, metadata, metadata, metadata)
+
+; Function Attrs: nounwind uwtable mustprogress
+define dso_local void @_Z3funv() !dbg !25 {
+entry:
+  %call = call i32 @_Z3getv(), !dbg !28
+  %call1 = call i32 @_Z3getv(), !dbg !29
+  ret void, !dbg !30
+}
+
+!llvm.dbg.cu = !{!0}
+!llvm.module.flags = !{!3, !4, !5}
+!llvm.ident = !{!6}
+
+!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !1, producer: "clang version 12.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, splitDebugInlining: false, nameTableKind: None)
+!1 = !DIFile(filename: "test.cpp", directory: "")
+!2 = !{}
+!3 = !{i32 7, !"Dwarf Version", i32 4}
+!4 = !{i32 2, !"Debug Info Version", i32 3}
+!5 = !{i32 1, !"wchar_size", i32 4}
+!6 = !{!"clang version 12.0.0"}
+!7 = distinct !DISubprogram(name: "get", linkageName: "_Z3getv", scope: !1, file: !1, line: 2, type: !8, scopeLine: 2, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !11)
+!8 = !DISubroutineType(types: !9)
+!9 = !{!10}
+!10 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+!11 = !{!12}
+!12 = !DILocalVariable(name: "val", scope: !7, file: !1, line: 2, type: !10)
+!13 = distinct !DIAssignID()
+!14 = !DILocation(line: 0, scope: !7)
+!15 = !DILocation(line: 2, column: 13, scope: !7)
+!16 = !DILocation(line: 2, column: 17, scope: !7)
+!21 = distinct !DIAssignID()
+!22 = !DILocation(line: 2, column: 33, scope: !7)
+!23 = !DILocation(line: 2, column: 38, scope: !7)
+!24 = !DILocation(line: 2, column: 26, scope: !7)
+!25 = distinct !DISubprogram(name: "fun", linkageName: "_Z3funv", scope: !1, file: !1, line: 3, type: !26, scopeLine: 3, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !2)
+!26 = !DISubroutineType(types: !27)
+!27 = !{null}
+!28 = !DILocation(line: 4, column: 3, scope: !25)
+!29 = !DILocation(line: 5, column: 3, scope: !25)
+!30 = !DILocation(line: 6, column: 1, scope: !25)

diff  --git a/llvm/test/DebugInfo/Generic/assignment-tracking/inline/inline-stores.ll b/llvm/test/DebugInfo/Generic/assignment-tracking/inline/inline-stores.ll
new file mode 100644
index 000000000000..095fc1477e4e
--- /dev/null
+++ b/llvm/test/DebugInfo/Generic/assignment-tracking/inline/inline-stores.ll
@@ -0,0 +1,340 @@
+; RUN: opt -passes=inline %s -S -o - -experimental-assignment-tracking \
+; RUN: | FileCheck %s
+
+;; $ cat test.cpp
+;; __attribute__((always_inline))
+;; static void a(int* p2, int v2) { *p2 = v2; }
+;;
+;; __attribute__((always_inline))
+;; void b(int* p1, int v1) { a(p1, v1); }
+;;
+;; int f1() {
+;;   int f1_local;
+;;   a(&f1_local, 1);
+;;   return f1_local;
+;; }
+;;
+;; int f2() {
+;;   int f2_local[2];
+;;   a(f2_local, 2);
+;;   return f2_local[0];
+;; }
+;;
+;; int f3() {
+;;   int f3_local[2];
+;;   a(f3_local + 1, 3);
+;;   return f3_local[1];
+;; }
+;;
+;; int f4(int f4_param) {
+;;   a(&f4_param, 4);
+;;   return f4_param;
+;; }
+;;
+;; int f5(int f5_param) {
+;;   int &f5_alias = f5_param;
+;;   a(&f5_alias, 5);
+;;   return f5_param;
+;; }
+;;
+;; int f6() {
+;;   int f6_local;
+;;   b(&f6_local, 6);
+;;   return f6_local;
+;; }
+;;
+;; IR generated with:
+;; $ clang++ -Xclang -disable-llvm-passes test.cpp -g -O2 -o - -S -emit-llvm \
+;;   | opt -passes=declare-to-assign,sroa -o - -S
+;;
+;; Check that inlined stores are tracked as assignments. FileCheck directives
+;; inline.
+
+source_filename = "test.cpp"
+define dso_local void @_Z1bPii(ptr %p1, i32 %v1) #0 !dbg !7 {
+entry:
+  call void @llvm.dbg.assign(metadata i1 undef, metadata !13, metadata !DIExpression(), metadata !15, metadata ptr undef, metadata !DIExpression()), !dbg !16
+  call void @llvm.dbg.assign(metadata i1 undef, metadata !14, metadata !DIExpression(), metadata !17, metadata ptr undef, metadata !DIExpression()), !dbg !16
+  call void @llvm.dbg.assign(metadata ptr %p1, metadata !13, metadata !DIExpression(), metadata !18, metadata ptr undef, metadata !DIExpression()), !dbg !16
+  call void @llvm.dbg.assign(metadata i32 %v1, metadata !14, metadata !DIExpression(), metadata !19, metadata ptr undef, metadata !DIExpression()), !dbg !16
+  call void @_ZL1aPii(ptr %p1, i32 %v1), !dbg !20
+  ret void, !dbg !21
+}
+
+define internal void @_ZL1aPii(ptr %p2, i32 %v2) #2 !dbg !22 {
+entry:
+  call void @llvm.dbg.assign(metadata i1 undef, metadata !24, metadata !DIExpression(), metadata !26, metadata ptr undef, metadata !DIExpression()), !dbg !27
+  call void @llvm.dbg.assign(metadata i1 undef, metadata !25, metadata !DIExpression(), metadata !28, metadata ptr undef, metadata !DIExpression()), !dbg !27
+  call void @llvm.dbg.assign(metadata ptr %p2, metadata !24, metadata !DIExpression(), metadata !29, metadata ptr undef, metadata !DIExpression()), !dbg !27
+  call void @llvm.dbg.assign(metadata i32 %v2, metadata !25, metadata !DIExpression(), metadata !30, metadata ptr undef, metadata !DIExpression()), !dbg !27
+  store i32 %v2, ptr %p2, align 4, !dbg !31
+  ret void, !dbg !36
+}
+
+;; Store directly to local with one level of inlining. Also record f1_dbg to
+;; check that the dbg.assign gets the correct scope after inlining. This check
+;; isn't repeated for the other functions.
+;; int f1() {
+;;   int f1_local;
+;;   a(&f1_local, 1);
+;;   return f1_local;
+;; }
+;;
+; CHECK-LABEL: define dso_local i32 @_Z2f1v()
+; CHECK:       store i32 1, ptr %f1_local, align 4,{{.*}} !DIAssignID ![[ID_1:[0-9]+]]
+; CHECK-NEXT:  call void @llvm.dbg.assign(metadata i32 1, metadata ![[f1_local:[0-9]+]], metadata !DIExpression(), metadata ![[ID_1]], metadata ptr %f1_local, metadata !DIExpression()), !dbg ![[f1_dbg:[0-9]+]]
+define dso_local i32 @_Z2f1v() #3 !dbg !37 {
+entry:
+  %f1_local = alloca i32, align 4, !DIAssignID !42
+  call void @llvm.dbg.assign(metadata i1 undef, metadata !41, metadata !DIExpression(), metadata !42, metadata ptr %f1_local, metadata !DIExpression()), !dbg !43
+  %0 = bitcast ptr %f1_local to ptr, !dbg !44
+  call void @llvm.lifetime.start.p0i8(i64 4, ptr %0) #5, !dbg !44
+  call void @_ZL1aPii(ptr %f1_local, i32 1), !dbg !45
+  %1 = load i32, ptr %f1_local, align 4, !dbg !46
+  %2 = bitcast ptr %f1_local to ptr, !dbg !47
+  call void @llvm.lifetime.end.p0i8(i64 4, ptr %2) #5, !dbg !47
+  ret i32 %1, !dbg !48
+}
+
+;; Store directly to fragment of local at its base address.
+;; int f2() {
+;;   int f2_local[2];
+;;   a(f2_local, 2);
+;;   return f2_local[0];
+;; }
+;;
+; CHECK-LABEL: define dso_local i32 @_Z2f2v()
+; CHECK:       store i32 2, ptr %arraydecay, align 4,{{.*}} !DIAssignID ![[ID_2:[0-9]+]]
+; CHECK-NEXT:  call void @llvm.dbg.assign(metadata i32 2, metadata ![[f2_local:[0-9]+]], metadata !DIExpression(DW_OP_LLVM_fragment, 0, 32), metadata ![[ID_2]], metadata ptr %arraydecay, metadata !DIExpression())
+define dso_local i32 @_Z2f2v() #3 !dbg !49 {
+entry:
+  %f2_local = alloca [2 x i32], align 4, !DIAssignID !55
+  call void @llvm.dbg.assign(metadata i1 undef, metadata !51, metadata !DIExpression(), metadata !55, metadata ptr %f2_local, metadata !DIExpression()), !dbg !56
+  %0 = bitcast ptr %f2_local to ptr, !dbg !57
+  call void @llvm.lifetime.start.p0i8(i64 8, ptr %0) #5, !dbg !57
+  %arraydecay = getelementptr inbounds [2 x i32], ptr %f2_local, i64 0, i64 0, !dbg !58
+  call void @_ZL1aPii(ptr %arraydecay, i32 2), !dbg !59
+  %arrayidx = getelementptr inbounds [2 x i32], ptr %f2_local, i64 0, i64 0, !dbg !60
+  %1 = load i32, ptr %arrayidx, align 4, !dbg !60
+  %2 = bitcast ptr %f2_local to ptr, !dbg !61
+  call void @llvm.lifetime.end.p0i8(i64 8, ptr %2) #5, !dbg !61
+  ret i32 %1, !dbg !62
+}
+
+;; Store to fragment of local using rvalue offset as argument.
+;; int f3() {
+;;   int f3_local[2];
+;;   a(f3_local + 1, 3);
+;;   return f3_local[1];
+;; }
+; CHECK-LABEL: define dso_local i32 @_Z2f3v()
+; CHECK:       store i32 3, ptr %add.ptr, align 4,{{.*}} !DIAssignID ![[ID_3:[0-9]+]]
+; CHECK-NEXT:  call void @llvm.dbg.assign(metadata i32 3, metadata ![[f3_local:[0-9]+]], metadata !DIExpression(DW_OP_LLVM_fragment, 32, 32), metadata ![[ID_3]], metadata ptr %add.ptr, metadata !DIExpression())
+define dso_local i32 @_Z2f3v() #3 !dbg !63 {
+entry:
+  %f3_local = alloca [2 x i32], align 4, !DIAssignID !66
+  call void @llvm.dbg.assign(metadata i1 undef, metadata !65, metadata !DIExpression(), metadata !66, metadata ptr %f3_local, metadata !DIExpression()), !dbg !67
+  %0 = bitcast ptr %f3_local to ptr, !dbg !68
+  call void @llvm.lifetime.start.p0i8(i64 8, ptr %0) #5, !dbg !68
+  %arraydecay = getelementptr inbounds [2 x i32], ptr %f3_local, i64 0, i64 0, !dbg !69
+  %add.ptr = getelementptr inbounds i32, ptr %arraydecay, i64 1, !dbg !70
+  call void @_ZL1aPii(ptr %add.ptr, i32 3), !dbg !71
+  %arrayidx = getelementptr inbounds [2 x i32], ptr %f3_local, i64 0, i64 1, !dbg !72
+  %1 = load i32, ptr %arrayidx, align 4, !dbg !72
+  %2 = bitcast ptr %f3_local to ptr, !dbg !73
+  call void @llvm.lifetime.end.p0i8(i64 8, ptr %2) #5, !dbg !73
+  ret i32 %1, !dbg !74
+}
+
+;; Store to parameter directly.
+;; int f4(int f4_param) {
+;;   a(&f4_param, 4);
+;;   return f4_param;
+;; }
+; CHECK-LABEL: define dso_local i32 @_Z2f4i(i32 %f4_param)
+; CHECK:       store i32 4, ptr %f4_param.addr, align 4,{{.*}} !DIAssignID ![[ID_4:[0-9]+]]
+; CHECK-NEXT:  call void @llvm.dbg.assign(metadata i32 4, metadata ![[f4_param:[0-9]+]], metadata !DIExpression(), metadata ![[ID_4]], metadata ptr %f4_param.addr, metadata !DIExpression())
+define dso_local i32 @_Z2f4i(i32 %f4_param) #3 !dbg !75 {
+entry:
+  %f4_param.addr = alloca i32, align 4, !DIAssignID !80
+  call void @llvm.dbg.assign(metadata i1 undef, metadata !79, metadata !DIExpression(), metadata !80, metadata ptr %f4_param.addr, metadata !DIExpression()), !dbg !81
+  store i32 %f4_param, ptr %f4_param.addr, align 4, !DIAssignID !82
+  call void @llvm.dbg.assign(metadata i32 %f4_param, metadata !79, metadata !DIExpression(), metadata !82, metadata ptr %f4_param.addr, metadata !DIExpression()), !dbg !81
+  call void @_ZL1aPii(ptr %f4_param.addr, i32 4), !dbg !83
+  %0 = load i32, ptr %f4_param.addr, align 4, !dbg !84
+  ret i32 %0, !dbg !85
+}
+
+;; Store through an alias.
+;; int f5(int f5_param) {
+;;   int &f5_alias = f5_param;
+;;   a(&f5_alias, 5);
+;;   return f5_param;
+;; }
+; CHECK-LABEL: define dso_local i32 @_Z2f5i(i32 %f5_param)
+; CHECK:       store i32 5, ptr %f5_param.addr, align 4,{{.*}}!DIAssignID ![[ID_5:[0-9]+]]
+; CHECK-NEXT:  call void @llvm.dbg.assign(metadata i32 5, metadata ![[f5_param:[0-9]+]], metadata !DIExpression(), metadata ![[ID_5]], metadata ptr %f5_param.addr, metadata !DIExpression())
+define dso_local i32 @_Z2f5i(i32 %f5_param) #3 !dbg !86 {
+entry:
+  %f5_param.addr = alloca i32, align 4, !DIAssignID !91
+  call void @llvm.dbg.assign(metadata i1 undef, metadata !88, metadata !DIExpression(), metadata !91, metadata ptr %f5_param.addr, metadata !DIExpression()), !dbg !92
+  call void @llvm.dbg.assign(metadata i1 undef, metadata !89, metadata !DIExpression(), metadata !93, metadata ptr undef, metadata !DIExpression()), !dbg !92
+  store i32 %f5_param, ptr %f5_param.addr, align 4, !DIAssignID !94
+  call void @llvm.dbg.assign(metadata i32 %f5_param, metadata !88, metadata !DIExpression(), metadata !94, metadata ptr %f5_param.addr, metadata !DIExpression()), !dbg !92
+  call void @llvm.dbg.assign(metadata ptr %f5_param.addr, metadata !89, metadata !DIExpression(), metadata !95, metadata ptr undef, metadata !DIExpression()), !dbg !92
+  call void @_ZL1aPii(ptr %f5_param.addr, i32 5), !dbg !96
+  %0 = load i32, ptr %f5_param.addr, align 4, !dbg !97
+  ret i32 %0, !dbg !98
+}
+
+;; int f6() {
+;;   int f6_local;
+;;   b(&f6_local, 6);
+;;   return f6_local;
+;; }
+; CHECK-LABEL: define dso_local i32 @_Z2f6v()
+; CHECK:       store i32 6, ptr %f6_local, align 4,{{.*}} !DIAssignID ![[ID_6:[0-9]+]]
+; CHECK-NEXT:  call void @llvm.dbg.assign(metadata i32 6, metadata ![[f6_local:[0-9]+]], metadata !DIExpression(), metadata ![[ID_6]], metadata ptr %f6_local, metadata !DIExpression())
+define dso_local i32 @_Z2f6v() #3 !dbg !99 {
+entry:
+  %f6_local = alloca i32, align 4, !DIAssignID !102
+  call void @llvm.dbg.assign(metadata i1 undef, metadata !101, metadata !DIExpression(), metadata !102, metadata ptr %f6_local, metadata !DIExpression()), !dbg !103
+  %0 = bitcast ptr %f6_local to ptr, !dbg !104
+  call void @llvm.lifetime.start.p0i8(i64 4, ptr %0) #5, !dbg !104
+  call void @_Z1bPii(ptr %f6_local, i32 6), !dbg !105
+  %1 = load i32, ptr %f6_local, align 4, !dbg !106
+  %2 = bitcast ptr %f6_local to ptr, !dbg !107
+  call void @llvm.lifetime.end.p0i8(i64 4, ptr %2) #5, !dbg !107
+  ret i32 %1, !dbg !108
+}
+
+declare void @llvm.lifetime.start.p0i8(i64 immarg, ptr nocapture)
+declare void @llvm.lifetime.end.p0i8(i64 immarg, ptr nocapture)
+declare void @llvm.dbg.assign(metadata, metadata, metadata, metadata, metadata, metadata)
+
+; CHECK-DAG: ![[f1_local]] = !DILocalVariable(name: "f1_local",
+; CHECK-DAG: ![[f2_local]] = !DILocalVariable(name: "f2_local",
+; CHECK-DAG: ![[f3_local]] = !DILocalVariable(name: "f3_local",
+; CHECK-DAG: ![[f4_param]] = !DILocalVariable(name: "f4_param",
+; CHECK-DAG: ![[f5_param]] = !DILocalVariable(name: "f5_param",
+; CHECK-DAG: ![[f6_local]] = !DILocalVariable(name: "f6_local",
+
+; CHECK-DAG: ![[f1_dbg]] = !DILocation(line: 0, scope: ![[f1_scope:[0-9]+]])
+; CHECK-DAG: ![[f1_scope]] = distinct !DISubprogram(name: "f1",
+
+; CHECK-DAG: [[ID_1]] = distinct !DIAssignID()
+; CHECK-DAG: [[ID_2]] = distinct !DIAssignID()
+; CHECK-DAG: [[ID_3]] = distinct !DIAssignID()
+; CHECK-DAG: [[ID_4]] = distinct !DIAssignID()
+; CHECK-DAG: [[ID_5]] = distinct !DIAssignID()
+; CHECK-DAG: [[ID_6]] = distinct !DIAssignID()
+
+!llvm.dbg.cu = !{!0}
+!llvm.module.flags = !{!3, !4, !5}
+!llvm.ident = !{!6}
+
+!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !1, producer: "clang version 12.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, splitDebugInlining: false, nameTableKind: None)
+!1 = !DIFile(filename: "test.cpp", directory: "/")
+!2 = !{}
+!3 = !{i32 7, !"Dwarf Version", i32 4}
+!4 = !{i32 2, !"Debug Info Version", i32 3}
+!5 = !{i32 1, !"wchar_size", i32 4}
+!6 = !{!"clang version 12.0.0)"}
+!7 = distinct !DISubprogram(name: "b", linkageName: "_Z1bPii", scope: !1, file: !1, line: 5, type: !8, scopeLine: 5, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !12)
+!8 = !DISubroutineType(types: !9)
+!9 = !{null, !10, !11}
+!10 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !11, size: 64)
+!11 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+!12 = !{!13, !14}
+!13 = !DILocalVariable(name: "p1", arg: 1, scope: !7, file: !1, line: 5, type: !10)
+!14 = !DILocalVariable(name: "v1", arg: 2, scope: !7, file: !1, line: 5, type: !11)
+!15 = distinct !DIAssignID()
+!16 = !DILocation(line: 0, scope: !7)
+!17 = distinct !DIAssignID()
+!18 = distinct !DIAssignID()
+!19 = distinct !DIAssignID()
+!20 = !DILocation(line: 5, column: 27, scope: !7)
+!21 = !DILocation(line: 5, column: 38, scope: !7)
+!22 = distinct !DISubprogram(name: "a", linkageName: "_ZL1aPii", scope: !1, file: !1, line: 2, type: !8, scopeLine: 2, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !23)
+!23 = !{!24, !25}
+!24 = !DILocalVariable(name: "p2", arg: 1, scope: !22, file: !1, line: 2, type: !10)
+!25 = !DILocalVariable(name: "v2", arg: 2, scope: !22, file: !1, line: 2, type: !11)
+!26 = distinct !DIAssignID()
+!27 = !DILocation(line: 0, scope: !22)
+!28 = distinct !DIAssignID()
+!29 = distinct !DIAssignID()
+!30 = distinct !DIAssignID()
+!31 = !DILocation(line: 2, column: 38, scope: !22)
+!36 = !DILocation(line: 2, column: 44, scope: !22)
+!37 = distinct !DISubprogram(name: "f1", linkageName: "_Z2f1v", scope: !1, file: !1, line: 7, type: !38, scopeLine: 7, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !40)
+!38 = !DISubroutineType(types: !39)
+!39 = !{!11}
+!40 = !{!41}
+!41 = !DILocalVariable(name: "f1_local", scope: !37, file: !1, line: 8, type: !11)
+!42 = distinct !DIAssignID()
+!43 = !DILocation(line: 0, scope: !37)
+!44 = !DILocation(line: 8, column: 3, scope: !37)
+!45 = !DILocation(line: 9, column: 3, scope: !37)
+!46 = !DILocation(line: 10, column: 10, scope: !37)
+!47 = !DILocation(line: 11, column: 1, scope: !37)
+!48 = !DILocation(line: 10, column: 3, scope: !37)
+!49 = distinct !DISubprogram(name: "f2", linkageName: "_Z2f2v", scope: !1, file: !1, line: 13, type: !38, scopeLine: 13, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !50)
+!50 = !{!51}
+!51 = !DILocalVariable(name: "f2_local", scope: !49, file: !1, line: 14, type: !52)
+!52 = !DICompositeType(tag: DW_TAG_array_type, baseType: !11, size: 64, elements: !53)
+!53 = !{!54}
+!54 = !DISubrange(count: 2)
+!55 = distinct !DIAssignID()
+!56 = !DILocation(line: 0, scope: !49)
+!57 = !DILocation(line: 14, column: 3, scope: !49)
+!58 = !DILocation(line: 15, column: 5, scope: !49)
+!59 = !DILocation(line: 15, column: 3, scope: !49)
+!60 = !DILocation(line: 16, column: 10, scope: !49)
+!61 = !DILocation(line: 17, column: 1, scope: !49)
+!62 = !DILocation(line: 16, column: 3, scope: !49)
+!63 = distinct !DISubprogram(name: "f3", linkageName: "_Z2f3v", scope: !1, file: !1, line: 19, type: !38, scopeLine: 19, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !64)
+!64 = !{!65}
+!65 = !DILocalVariable(name: "f3_local", scope: !63, file: !1, line: 20, type: !52)
+!66 = distinct !DIAssignID()
+!67 = !DILocation(line: 0, scope: !63)
+!68 = !DILocation(line: 20, column: 3, scope: !63)
+!69 = !DILocation(line: 21, column: 5, scope: !63)
+!70 = !DILocation(line: 21, column: 14, scope: !63)
+!71 = !DILocation(line: 21, column: 3, scope: !63)
+!72 = !DILocation(line: 22, column: 10, scope: !63)
+!73 = !DILocation(line: 23, column: 1, scope: !63)
+!74 = !DILocation(line: 22, column: 3, scope: !63)
+!75 = distinct !DISubprogram(name: "f4", linkageName: "_Z2f4i", scope: !1, file: !1, line: 25, type: !76, scopeLine: 25, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !78)
+!76 = !DISubroutineType(types: !77)
+!77 = !{!11, !11}
+!78 = !{!79}
+!79 = !DILocalVariable(name: "f4_param", arg: 1, scope: !75, file: !1, line: 25, type: !11)
+!80 = distinct !DIAssignID()
+!81 = !DILocation(line: 0, scope: !75)
+!82 = distinct !DIAssignID()
+!83 = !DILocation(line: 26, column: 3, scope: !75)
+!84 = !DILocation(line: 27, column: 10, scope: !75)
+!85 = !DILocation(line: 27, column: 3, scope: !75)
+!86 = distinct !DISubprogram(name: "f5", linkageName: "_Z2f5i", scope: !1, file: !1, line: 30, type: !76, scopeLine: 30, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !87)
+!87 = !{!88, !89}
+!88 = !DILocalVariable(name: "f5_param", arg: 1, scope: !86, file: !1, line: 30, type: !11)
+!89 = !DILocalVariable(name: "f5_alias", scope: !86, file: !1, line: 31, type: !90)
+!90 = !DIDerivedType(tag: DW_TAG_reference_type, baseType: !11, size: 64)
+!91 = distinct !DIAssignID()
+!92 = !DILocation(line: 0, scope: !86)
+!93 = distinct !DIAssignID()
+!94 = distinct !DIAssignID()
+!95 = distinct !DIAssignID()
+!96 = !DILocation(line: 32, column: 3, scope: !86)
+!97 = !DILocation(line: 33, column: 10, scope: !86)
+!98 = !DILocation(line: 33, column: 3, scope: !86)
+!99 = distinct !DISubprogram(name: "f6", linkageName: "_Z2f6v", scope: !1, file: !1, line: 36, type: !38, scopeLine: 36, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !100)
+!100 = !{!101}
+!101 = !DILocalVariable(name: "f6_local", scope: !99, file: !1, line: 37, type: !11)
+!102 = distinct !DIAssignID()
+!103 = !DILocation(line: 0, scope: !99)
+!104 = !DILocation(line: 37, column: 3, scope: !99)
+!105 = !DILocation(line: 38, column: 3, scope: !99)
+!106 = !DILocation(line: 39, column: 10, scope: !99)
+!107 = !DILocation(line: 40, column: 1, scope: !99)
+!108 = !DILocation(line: 39, column: 3, scope: !99)

diff  --git a/llvm/test/DebugInfo/Generic/assignment-tracking/inline/use-before-def.ll b/llvm/test/DebugInfo/Generic/assignment-tracking/inline/use-before-def.ll
new file mode 100644
index 000000000000..7499a77f5c00
--- /dev/null
+++ b/llvm/test/DebugInfo/Generic/assignment-tracking/inline/use-before-def.ll
@@ -0,0 +1,79 @@
+; RUN: opt -passes=inline %s -S -o - -experimental-assignment-tracking \
+; RUN: | FileCheck %s
+
+;; Hand modified from:
+;; $ cat test.c
+;; int g = 5;
+;; static int callee() {
+;;   int local = g;
+;;   return local;
+;; }
+;;
+;; int fun() {
+;;   return callee();
+;; }
+;;
+;; IR grabbed before inlining in:
+;;     $ clang++ -O2 -g
+;; Then modified (see comment in body).
+
+;; NOTE: Although this reproducer is contrived, this has been observed in real
+;; builds with assignment tracking (dbg.assign intrinsics) - in fact, it caused
+;; verifier failures in some cases using assignment tracking (good). This is
+;; the simplest test I could write.
+
+;; use-before-defs in debug intrinsics should be preserved through inlining,
+;; or at the very least should be converted to undefs. The previous behaviour
+;; was to replace the use-before-def operand with empty metadata, which signals
+;; cleanup passes that it's okay to remove the debug intrinsic (bad). Check
+;; that this no longer happens.
+
+; CHECK: define dso_local i32 @fun()
+; CHECK-NEXT: entry
+; CHECK-NEXT: call void @llvm.dbg.value(metadata i32 %0
+
+ at g = dso_local local_unnamed_addr global i32 5, align 4, !dbg !0
+
+define dso_local i32 @fun() local_unnamed_addr #0 !dbg !11 {
+entry:
+  %call = call fastcc i32 @callee(), !dbg !14
+  ret i32 %call, !dbg !15
+}
+
+define internal fastcc i32 @callee() unnamed_addr #1 !dbg !16 {
+entry:
+  ;; dbg.value moved here from after %0 def.
+  call void @llvm.dbg.value(metadata i32 %0, metadata !18, metadata !DIExpression()), !dbg !24
+  %0 = load i32, ptr @g, align 4, !dbg !19
+  ret i32 %0, !dbg !25
+}
+
+declare void @llvm.dbg.value(metadata, metadata, metadata) #2
+
+
+!llvm.dbg.cu = !{!2}
+!llvm.module.flags = !{!7, !8, !9}
+!llvm.ident = !{!10}
+
+!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression())
+!1 = distinct !DIGlobalVariable(name: "g", scope: !2, file: !3, line: 1, type: !6, isLocal: false, isDefinition: true)
+!2 = distinct !DICompileUnit(language: DW_LANG_C99, file: !3, producer: "clang version 12.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !4, globals: !5, splitDebugInlining: false, nameTableKind: None)
+!3 = !DIFile(filename: "test.c", directory: "/")
+!4 = !{}
+!5 = !{!0}
+!6 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+!7 = !{i32 7, !"Dwarf Version", i32 4}
+!8 = !{i32 2, !"Debug Info Version", i32 3}
+!9 = !{i32 1, !"wchar_size", i32 4}
+!10 = !{!"clang version 12.0.0"}
+!11 = distinct !DISubprogram(name: "fun", scope: !3, file: !3, line: 7, type: !12, scopeLine: 7, flags: DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2, retainedNodes: !4)
+!12 = !DISubroutineType(types: !13)
+!13 = !{!6}
+!14 = !DILocation(line: 8, column: 10, scope: !11)
+!15 = !DILocation(line: 8, column: 3, scope: !11)
+!16 = distinct !DISubprogram(name: "callee", scope: !3, file: !3, line: 2, type: !12, scopeLine: 2, flags: DIFlagAllCallsDescribed, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition | DISPFlagOptimized, unit: !2, retainedNodes: !17)
+!17 = !{!18}
+!18 = !DILocalVariable(name: "local", scope: !16, file: !3, line: 3, type: !6)
+!19 = !DILocation(line: 3, column: 15, scope: !16)
+!24 = !DILocation(line: 0, scope: !16)
+!25 = !DILocation(line: 4, column: 3, scope: !16)


        


More information about the llvm-commits mailing list