Skip to content

Commit 5843069

Browse files
abidhtgymnichMeinersbur
authored
[CodeExtractor] Improve debug info for input values. (#136016)
If we use `CodeExtractor` to extract the block1 into a new function, ``` define void @foo() !dbg !2 { entry: %1 = alloca i32, i64 1, align 4 %2 = alloca i32, i64 1, align 4 #dbg_declare(ptr %1, !8, !DIExpression(), !1) br label %block1 block1: store i32 1, ptr %1, align 4 store i32 2, ptr %2, align 4 #dbg_declare(ptr %2, !10, !DIExpression(), !1) ret void } ``` it will look like the extracted function shown below (with some irrelevent details removed). ``` define internal void @Extracted(ptr %arg0, ptr %arg1) { newFuncRoot: br label %block1 block1: store i32 1, ptr %arg0, align 4 store i32 2, ptr %arg1, align 4 ret void } ``` You will notice that it has replaced the usage of values that were in the parent function (%1 and %2) with the arguments to the new function. But it did not do the same thing with `#dbg_declare` which was simply dropped because its location pointed to a value outside of the new function. Similarly arg0 is without any debug record, although the value that it replaced had one and we could materialize one for it based on that. This is not just a theoretical limitations. `CodeExtractor` is used to create functions that implement many of the `OpenMP` constructs in `OMPIRBuilder`. As a result of these limitations, the debug information is missing from the created functions. This PR tries to address this problem. It iterates over the input to the extracted function and looks at their debug uses. If they were present in the new function, it updates their location. Otherwise it materialize a similar usage in the new function. Most of these changes are localized in `fixupDebugInfoPostExtraction`. Only other change is to propagate function inputs and the replacement values to it. --------- Co-authored-by: Tim Gymnich <[email protected]> Co-authored-by: Michael Kruse <[email protected]>
1 parent 571e024 commit 5843069

File tree

6 files changed

+200
-19
lines changed

6 files changed

+200
-19
lines changed

llvm/include/llvm/Transforms/Utils/CodeExtractor.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -290,7 +290,8 @@ class CodeExtractorAnalysisCache {
290290
void emitFunctionBody(const ValueSet &inputs, const ValueSet &outputs,
291291
const ValueSet &StructValues, Function *newFunction,
292292
StructType *StructArgTy, BasicBlock *header,
293-
const ValueSet &SinkingCands);
293+
const ValueSet &SinkingCands,
294+
SmallVectorImpl<Value *> &NewValues);
294295

295296
/// Generates a Basic Block that calls the extracted function.
296297
CallInst *emitReplacerCall(const ValueSet &inputs, const ValueSet &outputs,

llvm/lib/Transforms/Utils/CodeExtractor.cpp

Lines changed: 59 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1239,11 +1239,19 @@ static void eraseDebugIntrinsicsWithNonLocalRefs(Function &F) {
12391239
}
12401240
}
12411241

1242-
/// Fix up the debug info in the old and new functions by pointing line
1243-
/// locations and debug intrinsics to the new subprogram scope, and by deleting
1244-
/// intrinsics which point to values outside of the new function.
1242+
/// Fix up the debug info in the old and new functions. Following changes are
1243+
/// done.
1244+
/// 1. If a debug record points to a value that has been replaced, update the
1245+
/// record to use the new value.
1246+
/// 2. If an Input value that has been replaced was used as a location of a
1247+
/// debug record in the Parent function, then materealize a similar record in
1248+
/// the new function.
1249+
/// 3. Point line locations and debug intrinsics to the new subprogram scope
1250+
/// 4. Remove intrinsics which point to values outside of the new function.
12451251
static void fixupDebugInfoPostExtraction(Function &OldFunc, Function &NewFunc,
1246-
CallInst &TheCall) {
1252+
CallInst &TheCall,
1253+
const SetVector<Value *> &Inputs,
1254+
ArrayRef<Value *> NewValues) {
12471255
DISubprogram *OldSP = OldFunc.getSubprogram();
12481256
LLVMContext &Ctx = OldFunc.getContext();
12491257

@@ -1270,14 +1278,49 @@ static void fixupDebugInfoPostExtraction(Function &OldFunc, Function &NewFunc,
12701278
/*LineNo=*/0, SPType, /*ScopeLine=*/0, DINode::FlagZero, SPFlags);
12711279
NewFunc.setSubprogram(NewSP);
12721280

1281+
auto UpdateOrInsertDebugRecord = [&](auto *DR, Value *OldLoc, Value *NewLoc,
1282+
DIExpression *Expr, bool Declare) {
1283+
if (DR->getParent()->getParent() == &NewFunc) {
1284+
DR->replaceVariableLocationOp(OldLoc, NewLoc);
1285+
return;
1286+
}
1287+
if (Declare) {
1288+
DIB.insertDeclare(NewLoc, DR->getVariable(), Expr, DR->getDebugLoc(),
1289+
&NewFunc.getEntryBlock());
1290+
return;
1291+
}
1292+
DIB.insertDbgValueIntrinsic(
1293+
NewLoc, DR->getVariable(), Expr, DR->getDebugLoc(),
1294+
NewFunc.getEntryBlock().getTerminator()->getIterator());
1295+
};
1296+
for (auto [Input, NewVal] : zip_equal(Inputs, NewValues)) {
1297+
SmallVector<DbgVariableIntrinsic *, 1> DbgUsers;
1298+
SmallVector<DbgVariableRecord *, 1> DPUsers;
1299+
findDbgUsers(DbgUsers, Input, &DPUsers);
1300+
DIExpression *Expr = DIB.createExpression();
1301+
1302+
// Iterate the debud users of the Input values. If they are in the extracted
1303+
// function then update their location with the new value. If they are in
1304+
// the parent function then create a similar debug record.
1305+
for (auto *DVI : DbgUsers)
1306+
UpdateOrInsertDebugRecord(DVI, Input, NewVal, Expr,
1307+
isa<DbgDeclareInst>(DVI));
1308+
for (auto *DVR : DPUsers)
1309+
UpdateOrInsertDebugRecord(DVR, Input, NewVal, Expr, DVR->isDbgDeclare());
1310+
}
1311+
12731312
auto IsInvalidLocation = [&NewFunc](Value *Location) {
1274-
// Location is invalid if it isn't a constant or an instruction, or is an
1275-
// instruction but isn't in the new function.
1276-
if (!Location ||
1277-
(!isa<Constant>(Location) && !isa<Instruction>(Location)))
1313+
// Location is invalid if it isn't a constant, an instruction or an
1314+
// argument, or is an instruction/argument but isn't in the new function.
1315+
if (!Location || (!isa<Constant>(Location) && !isa<Argument>(Location) &&
1316+
!isa<Instruction>(Location)))
12781317
return true;
1279-
Instruction *LocationInst = dyn_cast<Instruction>(Location);
1280-
return LocationInst && LocationInst->getFunction() != &NewFunc;
1318+
1319+
if (Argument *Arg = dyn_cast<Argument>(Location))
1320+
return Arg->getParent() != &NewFunc;
1321+
if (Instruction *LocationInst = dyn_cast<Instruction>(Location))
1322+
return LocationInst->getFunction() != &NewFunc;
1323+
return false;
12811324
};
12821325

12831326
// Debug intrinsics in the new function need to be updated in one of two
@@ -1506,9 +1549,10 @@ CodeExtractor::extractCodeRegion(const CodeExtractorAnalysisCache &CEAC,
15061549
inputs, outputs, EntryFreq, oldFunction->getName() + "." + SuffixToUse,
15071550
StructValues, StructTy);
15081551
newFunction->IsNewDbgInfoFormat = oldFunction->IsNewDbgInfoFormat;
1552+
SmallVector<Value *> NewValues;
15091553

15101554
emitFunctionBody(inputs, outputs, StructValues, newFunction, StructTy, header,
1511-
SinkingCands);
1555+
SinkingCands, NewValues);
15121556

15131557
std::vector<Value *> Reloads;
15141558
CallInst *TheCall = emitReplacerCall(
@@ -1518,7 +1562,8 @@ CodeExtractor::extractCodeRegion(const CodeExtractorAnalysisCache &CEAC,
15181562
insertReplacerCall(oldFunction, header, TheCall->getParent(), outputs,
15191563
Reloads, ExitWeights);
15201564

1521-
fixupDebugInfoPostExtraction(*oldFunction, *newFunction, *TheCall);
1565+
fixupDebugInfoPostExtraction(*oldFunction, *newFunction, *TheCall, inputs,
1566+
NewValues);
15221567

15231568
LLVM_DEBUG(if (verifyFunction(*newFunction, &errs())) {
15241569
newFunction->dump();
@@ -1583,7 +1628,8 @@ Type *CodeExtractor::getSwitchType() {
15831628
void CodeExtractor::emitFunctionBody(
15841629
const ValueSet &inputs, const ValueSet &outputs,
15851630
const ValueSet &StructValues, Function *newFunction,
1586-
StructType *StructArgTy, BasicBlock *header, const ValueSet &SinkingCands) {
1631+
StructType *StructArgTy, BasicBlock *header, const ValueSet &SinkingCands,
1632+
SmallVectorImpl<Value *> &NewValues) {
15871633
Function *oldFunction = header->getParent();
15881634
LLVMContext &Context = oldFunction->getContext();
15891635

@@ -1615,7 +1661,6 @@ void CodeExtractor::emitFunctionBody(
16151661

16161662
// Rewrite all users of the inputs in the extracted region to use the
16171663
// arguments (or appropriate addressing into struct) instead.
1618-
SmallVector<Value *> NewValues;
16191664
for (unsigned i = 0, e = inputs.size(), aggIdx = 0; i != e; ++i) {
16201665
Value *RewriteVal;
16211666
if (StructValues.contains(inputs[i])) {

llvm/test/Transforms/CodeExtractor/LoopExtractor_alloca.ll

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
; CHECK-LABEL: define internal void @test.loop1(ptr %v1)
2121
; CHECK-NEXT: newFuncRoot:
22+
; CHECK-NEXT: #dbg_value
2223
; CHECK-NEXT: br
2324

2425
define void @test() {
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
; RUN: opt -passes=hotcoldsplit -hotcoldsplit-threshold=0 -S < %s | FileCheck %s
2+
3+
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128"
4+
target triple = "x86_64-unknown-linux-gnu"
5+
6+
define void @foo(i32 %a, i32 %b) !dbg !2 {
7+
entry:
8+
%1 = alloca i32, i64 1, align 4
9+
%2 = alloca i32, i64 1, align 4
10+
store i32 %a, ptr %1, align 4
11+
#dbg_declare(ptr %1, !8, !DIExpression(), !1)
12+
#dbg_value(i32 %b, !9, !DIExpression(), !1)
13+
%tobool = icmp eq i32 %a, 0
14+
br i1 %tobool, label %if.then, label %if.end
15+
if.then: ; preds = %entry
16+
ret void
17+
18+
if.end: ; preds = %entry
19+
store i32 10, ptr %1, align 4
20+
%3 = add i32 %b, 1
21+
store i32 1, ptr %2, align 4
22+
call void @sink(i32 %3)
23+
#dbg_declare(ptr %2, !10, !DIExpression(), !1)
24+
ret void
25+
}
26+
27+
declare void @sink(i32) cold
28+
29+
!llvm.dbg.cu = !{!6}
30+
!llvm.module.flags = !{!0}
31+
!0 = !{i32 2, !"Debug Info Version", i32 3}
32+
!1 = !DILocation(line: 11, column: 7, scope: !2)
33+
!2 = distinct !DISubprogram(name: "foo", scope: !3, file: !3, type: !4, spFlags: DISPFlagDefinition, unit: !6)
34+
!3 = !DIFile(filename: "test.c", directory: "")
35+
!4 = !DISubroutineType(cc: DW_CC_program, types: !5)
36+
!5 = !{}
37+
!6 = distinct !DICompileUnit(language: DW_LANG_C, file: !3)
38+
!7 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
39+
!8 = !DILocalVariable(name: "a", scope: !2, file: !3, type: !7)
40+
!9 = !DILocalVariable(name: "b", scope: !2, file: !3, type: !7)
41+
!10 = !DILocalVariable(name: "c", scope: !2, file: !3, type: !7)
42+
43+
; CHECK: define {{.*}}@foo.cold.1(ptr %[[ARG0:.*]], i32 %[[ARG1:.*]], ptr %[[ARG2:.*]]){{.*}} !dbg ![[FN:.*]] {
44+
; CHECK-NEXT: newFuncRoot:
45+
; CHECK-NEXT: #dbg_declare(ptr %[[ARG0]], ![[V1:[0-9]+]], {{.*}})
46+
; CHECK-NEXT: #dbg_value(i32 %[[ARG1]], ![[V2:[0-9]+]], {{.*}})
47+
; CHECK-NEXT: br
48+
; CHECK: if.end:
49+
; CHECK: #dbg_declare(ptr %[[ARG2]], ![[V3:[0-9]+]], {{.*}})
50+
; CHECK: }
51+
52+
; CHECK: ![[V1]] = !DILocalVariable(name: "a", scope: ![[FN]]{{.*}})
53+
; CHECK: ![[V2]] = !DILocalVariable(name: "b", scope: ![[FN]]{{.*}})
54+
; CHECK: ![[V3]] = !DILocalVariable(name: "c", scope: ![[FN]]{{.*}})

llvm/test/Transforms/HotColdSplit/transfer-debug-info.ll

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,6 @@ target triple = "x86_64-apple-macosx10.14.0"
88

99
; CHECK-LABEL: define {{.*}}@foo.cold.1
1010

11-
; - The llvm.dbg.value intrinsic pointing to an argument in @foo (%arg1) is
12-
; dropped
13-
; CHECK-NOT: #dbg_value
14-
1511
; - Instructions without locations in the original function have no
1612
; location in the new function
1713
; CHECK: [[ADD1:%.*]] = add i32 %{{.*}}, 1{{$}}

llvm/unittests/Transforms/Utils/CodeExtractorTest.cpp

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include "llvm/AsmParser/Parser.h"
1212
#include "llvm/IR/BasicBlock.h"
1313
#include "llvm/IR/Constants.h"
14+
#include "llvm/IR/DebugInfoMetadata.h"
1415
#include "llvm/IR/Dominators.h"
1516
#include "llvm/IR/InstIterator.h"
1617
#include "llvm/IR/Instructions.h"
@@ -731,4 +732,87 @@ TEST(CodeExtractor, OpenMPAggregateArgs) {
731732
EXPECT_FALSE(verifyFunction(*Outlined));
732733
EXPECT_FALSE(verifyFunction(*Func));
733734
}
735+
736+
TEST(CodeExtractor, ArgsDebugInfo) {
737+
LLVMContext Ctx;
738+
SMDiagnostic Err;
739+
std::unique_ptr<Module> M(parseAssemblyString(R"ir(
740+
741+
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128"
742+
target triple = "x86_64-unknown-linux-gnu"
743+
744+
define void @foo(i32 %a, i32 %b) !dbg !2 {
745+
%1 = alloca i32, i64 1, align 4, !dbg !1
746+
store i32 %a, ptr %1, align 4, !dbg !1
747+
#dbg_declare(ptr %1, !8, !DIExpression(), !1)
748+
#dbg_value(i32 %b, !9, !DIExpression(), !1)
749+
br label %entry
750+
751+
entry:
752+
br label %extract
753+
754+
extract:
755+
store i32 10, ptr %1, align 4, !dbg !1
756+
%2 = add i32 %b, 1, !dbg !1
757+
br label %exit
758+
759+
exit:
760+
ret void
761+
}
762+
!llvm.dbg.cu = !{!6}
763+
!llvm.module.flags = !{!0}
764+
!0 = !{i32 2, !"Debug Info Version", i32 3}
765+
!1 = !DILocation(line: 11, column: 7, scope: !2)
766+
!2 = distinct !DISubprogram(name: "foo", scope: !3, file: !3, type: !4, spFlags: DISPFlagDefinition, unit: !6)
767+
!3 = !DIFile(filename: "test.f90", directory: "")
768+
!4 = !DISubroutineType(cc: DW_CC_program, types: !5)
769+
!5 = !{null}
770+
!6 = distinct !DICompileUnit(language: DW_LANG_Fortran95, file: !3)
771+
!7 = !DIBasicType(name: "integer", size: 32, encoding: DW_ATE_signed)
772+
!8 = !DILocalVariable(name: "a", scope: !2, file: !3, type: !7)
773+
!9 = !DILocalVariable(name: "b", scope: !2, file: !3, type: !7)
774+
775+
)ir",
776+
Err, Ctx));
777+
778+
Function *Func = M->getFunction("foo");
779+
SmallVector<BasicBlock *, 1> Blocks{getBlockByName(Func, "extract")};
780+
781+
auto TestExtracted = [&](bool AggregateArgs) {
782+
CodeExtractor CE(Blocks, /* DominatorTree */ nullptr, AggregateArgs);
783+
EXPECT_TRUE(CE.isEligible());
784+
CodeExtractorAnalysisCache CEAC(*Func);
785+
SetVector<Value *> Inputs, Outputs, SinkingCands, HoistingCands;
786+
BasicBlock *CommonExit = nullptr;
787+
CE.findAllocas(CEAC, SinkingCands, HoistingCands, CommonExit);
788+
CE.findInputsOutputs(Inputs, Outputs, SinkingCands);
789+
Function *Outlined = CE.extractCodeRegion(CEAC, Inputs, Outputs);
790+
Outlined->dump();
791+
EXPECT_TRUE(Outlined);
792+
BasicBlock &EB = Outlined->getEntryBlock();
793+
Instruction *Term = EB.getTerminator();
794+
EXPECT_TRUE(Term);
795+
EXPECT_TRUE(Term->hasDbgRecords());
796+
for (DbgVariableRecord &DVR : filterDbgVars(Term->getDbgRecordRange())) {
797+
DILocalVariable *Var = DVR.getVariable();
798+
EXPECT_TRUE(Var);
799+
if (DVR.isDbgDeclare())
800+
EXPECT_TRUE(Var->getName() == "a");
801+
else
802+
EXPECT_TRUE(Var->getName() == "b");
803+
for (Value *Loc : DVR.location_ops()) {
804+
if (Instruction *I = dyn_cast<Instruction>(Loc))
805+
EXPECT_TRUE(I->getParent() == &EB);
806+
else if (Argument *A = dyn_cast<Argument>(Loc))
807+
EXPECT_TRUE(A->getParent() == Outlined);
808+
}
809+
}
810+
EXPECT_FALSE(verifyFunction(*Outlined));
811+
};
812+
// Test with both true and false for AggregateArgs.
813+
TestExtracted(true);
814+
TestExtracted(false);
815+
EXPECT_FALSE(verifyFunction(*Func));
816+
}
817+
734818
} // end anonymous namespace

0 commit comments

Comments
 (0)