[DebugInfo][RemoveDIs] Instrument inliner for non-instr debug-info (#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.
This commit is contained in:
Jeremy Morse
2023-11-26 21:24:29 +00:00
committed by GitHub
parent f4a4e2f85d
commit c672ba7dde
19 changed files with 473 additions and 64 deletions

View File

@@ -102,10 +102,12 @@ public:
/// 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);

View File

@@ -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());

View File

@@ -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)

View File

@@ -3118,8 +3118,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()) {
@@ -3129,6 +3129,7 @@ bool JumpThreadingPass::threadGuard(BasicBlock *BB, IntrinsicInst *Guard,
NewPN->insertBefore(InsertionPoint);
Inst->replaceAllUsesWith(NewPN);
}
Inst->dropDbgValues();
Inst->eraseFromParent();
}
return true;

View File

@@ -94,6 +94,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
@@ -271,9 +272,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
@@ -331,6 +336,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();
@@ -496,6 +502,22 @@ 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;
@@ -545,6 +567,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))
@@ -602,6 +626,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) {
@@ -613,6 +640,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) {
@@ -850,12 +884,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);
@@ -944,10 +988,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
@@ -1071,6 +1120,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.

View File

@@ -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

View File

@@ -1666,48 +1666,71 @@ static void fixupLineNumbers(Function *Fn, Function::iterator FI,
// the call site location instead.
bool NoInlineLineTables = Fn->hasFnAttribute("no-inline-line-tables");
// 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)
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.
// 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>(I))
return;
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) {
// 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;
}
if (CalleeHasDebugInfo && !NoInlineLineTables)
continue;
// 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;
// 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;
BI->setDebugLoc(TheCallDL);
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,11 +1740,12 @@ static void fixupLineNumbers(Function *Fn, Function::iterator FI,
if (isa<DbgInfoIntrinsic>(BI)) {
BI = BI->eraseFromParent();
continue;
} else {
BI->dropDbgValues();
}
++BI;
}
}
}
}
@@ -2402,6 +2426,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);
}

View File

@@ -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)

View File

@@ -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--"
@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}

View File

@@ -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"

View File

@@ -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

View File

@@ -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];

View File

@@ -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

View File

@@ -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.

View File

@@ -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

View File

@@ -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.

View File

@@ -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.

View File

@@ -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)

View File

@@ -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 )