Skip to content

Cherry-pick follow-ups to D72489 (AT_call_return_pc DWARF emission change) #585

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Jan 16, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ def setUp(self):

@skipIf(compiler="clang", compiler_version=['<', '8.0'])
@skipIf(dwarf_version=['<', '4'])
@expectedFailureAll(oslist=["windows"], bugnumber="llvm.org/pr26265")
@skipUnlessDarwin # llvm.org/PR44561
def test_cross_dso_tail_calls(self):
self.build()
exe = self.getBuildArtifact("a.out")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ def setUp(self):

@skipIf(compiler="clang", compiler_version=['<', '8.0'])
@skipIf(dwarf_version=['<', '4'])
@expectedFailureAll(oslist=["windows"], bugnumber="llvm.org/pr26265")
@skipUnlessDarwin # llvm.org/PR44561
def test_cross_object_tail_calls(self):
self.build()
exe = self.getBuildArtifact("a.out")
Expand Down
12 changes: 7 additions & 5 deletions llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -934,6 +934,7 @@ DwarfCompileUnit::getDwarf5OrGNUAttr(dwarf::Attribute Attr) const {
case dwarf::DW_AT_call_origin:
return dwarf::DW_AT_abstract_origin;
case dwarf::DW_AT_call_pc:
case dwarf::DW_AT_call_return_pc:
return dwarf::DW_AT_low_pc;
case dwarf::DW_AT_call_value:
return dwarf::DW_AT_GNU_call_site_value;
Expand Down Expand Up @@ -982,12 +983,13 @@ DIE &DwarfCompileUnit::constructCallSiteEntryDIE(DIE &ScopeDIE,

// Attach the return PC to allow the debugger to disambiguate call paths
// from one function to another.
if (DD->getDwarfVersion() == 4 && DD->tuneForGDB()) {
assert(PCAddr && "Missing PC information for a call");
addLabelAddress(CallSiteDIE, dwarf::DW_AT_low_pc, PCAddr);
} else if (!IsTail || DD->tuneForGDB()) {
//
// The return PC is only really needed when the call /isn't/ a tail call, but
// for some reason GDB always expects it.
if (!IsTail || DD->tuneForGDB()) {
assert(PCAddr && "Missing return PC information for a call");
addLabelAddress(CallSiteDIE, dwarf::DW_AT_call_return_pc, PCAddr);
addLabelAddress(CallSiteDIE,
getDwarf5OrGNUAttr(dwarf::DW_AT_call_return_pc), PCAddr);
}

return CallSiteDIE;
Expand Down
147 changes: 127 additions & 20 deletions llvm/lib/Transforms/Utils/CodeExtractor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,14 @@
#include "llvm/IR/CFG.h"
#include "llvm/IR/Constant.h"
#include "llvm/IR/Constants.h"
#include "llvm/IR/DIBuilder.h"
#include "llvm/IR/DataLayout.h"
#include "llvm/IR/DebugInfoMetadata.h"
#include "llvm/IR/DerivedTypes.h"
#include "llvm/IR/Dominators.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/GlobalValue.h"
#include "llvm/IR/InstIterator.h"
#include "llvm/IR/InstrTypes.h"
#include "llvm/IR/Instruction.h"
#include "llvm/IR/Instructions.h"
Expand Down Expand Up @@ -1382,6 +1385,129 @@ void CodeExtractor::calculateNewCallTerminatorWeights(
MDBuilder(TI->getContext()).createBranchWeights(BranchWeights));
}

/// Erase debug info intrinsics which refer to values in \p F but aren't in
/// \p F.
static void eraseDebugIntrinsicsWithNonLocalRefs(Function &F) {
for (Instruction &I : instructions(F)) {
SmallVector<DbgVariableIntrinsic *, 4> DbgUsers;
findDbgUsers(DbgUsers, &I);
for (DbgVariableIntrinsic *DVI : DbgUsers)
if (DVI->getFunction() != &F)
DVI->eraseFromParent();
}
}

/// Fix up the debug info in the old and new functions by pointing line
/// locations and debug intrinsics to the new subprogram scope, and by deleting
/// intrinsics which point to values outside of the new function.
static void fixupDebugInfoPostExtraction(Function &OldFunc, Function &NewFunc,
CallInst &TheCall) {
DISubprogram *OldSP = OldFunc.getSubprogram();
LLVMContext &Ctx = OldFunc.getContext();

// See llvm.org/PR44560, OpenMP passes an invalid subprogram to CodeExtractor.
bool NeedWorkaroundForOpenMPIRBuilderBug =
OldSP && OldSP->getRetainedNodes()->isTemporary();

if (!OldSP || NeedWorkaroundForOpenMPIRBuilderBug) {
// Erase any debug info the new function contains.
stripDebugInfo(NewFunc);
// Make sure the old function doesn't contain any non-local metadata refs.
eraseDebugIntrinsicsWithNonLocalRefs(NewFunc);
return;
}

// Create a subprogram for the new function. Leave out a description of the
// function arguments, as the parameters don't correspond to anything at the
// source level.
assert(OldSP->getUnit() && "Missing compile unit for subprogram");
DIBuilder DIB(*OldFunc.getParent(), /*AllowUnresolvedNodes=*/false,
OldSP->getUnit());
auto SPType = DIB.createSubroutineType(DIB.getOrCreateTypeArray(None));
DISubprogram::DISPFlags SPFlags = DISubprogram::SPFlagDefinition |
DISubprogram::SPFlagOptimized |
DISubprogram::SPFlagLocalToUnit;
auto NewSP = DIB.createFunction(
OldSP->getUnit(), NewFunc.getName(), NewFunc.getName(), OldSP->getFile(),
/*LineNo=*/0, SPType, /*ScopeLine=*/0, DINode::FlagZero, SPFlags);
NewFunc.setSubprogram(NewSP);

// Debug intrinsics in the new function need to be updated in one of two
// ways:
// 1) They need to be deleted, because they describe a value in the old
// function.
// 2) They need to point to fresh metadata, e.g. because they currently
// point to a variable in the wrong scope.
SmallDenseMap<DINode *, DINode *> RemappedMetadata;
SmallVector<Instruction *, 4> DebugIntrinsicsToDelete;
for (Instruction &I : instructions(NewFunc)) {
auto *DII = dyn_cast<DbgInfoIntrinsic>(&I);
if (!DII)
continue;

// Point the intrinsic to a fresh label within the new function.
if (auto *DLI = dyn_cast<DbgLabelInst>(&I)) {
DILabel *OldLabel = DLI->getLabel();
DINode *&NewLabel = RemappedMetadata[OldLabel];
if (!NewLabel)
NewLabel = DILabel::get(Ctx, NewSP, OldLabel->getName(),
OldLabel->getFile(), OldLabel->getLine());
DLI->setArgOperand(0, MetadataAsValue::get(Ctx, NewLabel));
continue;
}

// If the location isn't a constant or an instruction, delete the
// intrinsic.
auto *DVI = cast<DbgVariableIntrinsic>(DII);
Value *Location = DVI->getVariableLocation();
if (!Location ||
(!isa<Constant>(Location) && !isa<Instruction>(Location))) {
DebugIntrinsicsToDelete.push_back(DVI);
continue;
}

// If the variable location is an instruction but isn't in the new
// function, delete the intrinsic.
Instruction *LocationInst = dyn_cast<Instruction>(Location);
if (LocationInst && LocationInst->getFunction() != &NewFunc) {
DebugIntrinsicsToDelete.push_back(DVI);
continue;
}

// Point the intrinsic to a fresh variable within the new function.
DILocalVariable *OldVar = DVI->getVariable();
DINode *&NewVar = RemappedMetadata[OldVar];
if (!NewVar)
NewVar = DIB.createAutoVariable(
NewSP, OldVar->getName(), OldVar->getFile(), OldVar->getLine(),
OldVar->getType(), /*AlwaysPreserve=*/false, DINode::FlagZero,
OldVar->getAlignInBits());
DVI->setArgOperand(1, MetadataAsValue::get(Ctx, NewVar));
}
for (auto *DII : DebugIntrinsicsToDelete)
DII->eraseFromParent();
DIB.finalizeSubprogram(NewSP);

// Fix up the scope information attached to the line locations in the new
// function.
for (Instruction &I : instructions(NewFunc)) {
if (const DebugLoc &DL = I.getDebugLoc())
I.setDebugLoc(DebugLoc::get(DL.getLine(), DL.getCol(), NewSP));

// Loop info metadata may contain line locations. Fix them up.
auto updateLoopInfoLoc = [&Ctx,
NewSP](const DILocation &Loc) -> DILocation * {
return DILocation::get(Ctx, Loc.getLine(), Loc.getColumn(), NewSP,
nullptr);
};
updateLoopMetadataDebugLocations(I, updateLoopInfoLoc);
}
if (!TheCall.getDebugLoc())
TheCall.setDebugLoc(DebugLoc::get(0, 0, OldSP));

eraseDebugIntrinsicsWithNonLocalRefs(NewFunc);
}

Function *
CodeExtractor::extractCodeRegion(const CodeExtractorAnalysisCache &CEAC) {
if (!isEligible())
Expand Down Expand Up @@ -1567,26 +1693,7 @@ CodeExtractor::extractCodeRegion(const CodeExtractorAnalysisCache &CEAC) {
}
}

// Erase debug info intrinsics. Variable updates within the new function are
// invisible to debuggers. This could be improved by defining a DISubprogram
// for the new function.
for (BasicBlock &BB : *newFunction) {
auto BlockIt = BB.begin();
// Remove debug info intrinsics from the new function.
while (BlockIt != BB.end()) {
Instruction *Inst = &*BlockIt;
++BlockIt;
if (isa<DbgInfoIntrinsic>(Inst))
Inst->eraseFromParent();
}
// Remove debug info intrinsics which refer to values in the new function
// from the old function.
SmallVector<DbgVariableIntrinsic *, 4> DbgUsers;
for (Instruction &I : BB)
findDbgUsers(DbgUsers, &I);
for (DbgVariableIntrinsic *DVI : DbgUsers)
DVI->eraseFromParent();
}
fixupDebugInfoPostExtraction(*oldFunction, *newFunction, *TheCall);

// Mark the new function `noreturn` if applicable. Terminators which resume
// exception propagation are treated as returning instructions. This is to
Expand Down
55 changes: 55 additions & 0 deletions llvm/test/Transforms/HotColdSplit/split-out-dbg-label.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
; RUN: opt -hotcoldsplit -hotcoldsplit-threshold=0 -S < %s | FileCheck %s

; When an llvm.dbg.label intrinsic is extracted into a new function, make sure
; that its metadata argument is a DILabel that points to a scope within the new
; function.
;
; In this example, the label "bye" points to the scope for @foo before
; splitting, and should point to the scope for @foo.cold.1 after splitting.

target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-apple-macosx10.14.0"

; CHECK-LABEL: define {{.*}}@foo.cold.1
; CHECK: llvm.dbg.label(metadata [[LABEL:![0-9]+]]), !dbg [[LINE:![0-9]+]]

; CHECK: [[FILE:![0-9]+]] = !DIFile
; CHECK: [[SCOPE:![0-9]+]] = distinct !DISubprogram(name: "foo.cold.1"
; CHECK: [[LINE]] = !DILocation(line: 1, column: 1, scope: [[SCOPE]]
; CHECK: [[LABEL]] = !DILabel(scope: [[SCOPE]], name: "bye", file: [[FILE]], line: 28

define void @foo(i32 %arg1) !dbg !6 {
entry:
%var = add i32 0, 0, !dbg !11
br i1 undef, label %if.then, label %if.end

if.then: ; preds = %entry
ret void

if.end: ; preds = %entry
call void @llvm.dbg.label(metadata !12), !dbg !11
call void @sink()
ret void
}

declare void @llvm.dbg.label(metadata)

declare void @sink() cold

!llvm.dbg.cu = !{!0}
!llvm.debugify = !{!3, !4}
!llvm.module.flags = !{!5}

!0 = distinct !DICompileUnit(language: DW_LANG_C, file: !1, producer: "debugify", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2)
!1 = !DIFile(filename: "<stdin>", directory: "/")
!2 = !{}
!3 = !{i32 7}
!4 = !{i32 1}
!5 = !{i32 2, !"Debug Info Version", i32 3}
!6 = distinct !DISubprogram(name: "foo", linkageName: "foo", scope: null, file: !1, line: 1, type: !7, isLocal: false, isDefinition: true, scopeLine: 1, isOptimized: true, unit: !0, retainedNodes: !8)
!7 = !DISubroutineType(types: !2)
!8 = !{!9}
!9 = !DILocalVariable(name: "1", scope: !6, file: !1, line: 1, type: !10)
!10 = !DIBasicType(name: "ty32", size: 32, encoding: DW_ATE_unsigned)
!11 = !DILocation(line: 1, column: 1, scope: !6)
!12 = !DILabel(scope: !6, name: "bye", file: !1, line: 28)
77 changes: 77 additions & 0 deletions llvm/test/Transforms/HotColdSplit/transfer-debug-info.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
; RUN: opt -hotcoldsplit -hotcoldsplit-threshold=0 -S < %s | FileCheck %s

target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-apple-macosx10.14.0"

; The block "if.end" in @foo is extracted into a new function, @foo.cold.1.
; Check the following:

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

; - The llvm.dbg.value intrinsic pointing to an argument in @foo (%arg1) is
; dropped
; CHECK-NOT: llvm.dbg.value

; - Instructions without locations in the original function have no
; location in the new function
; CHECK: [[ADD1:%.*]] = add i32 %{{.*}}, 1{{$}}

; - Ditto (see above), calls are not special
; CHECK-NEXT: call void @sink(i32 [[ADD1]])

; - Line locations are preserved
; CHECK-NEXT: call void @sink(i32 [[ADD1]]), !dbg [[LINE1:![0-9]+]]

; - llvm.dbg.value intrinsics for values local to @foo.cold.1 are preserved
; CHECK-NEXT: llvm.dbg.value(metadata i32 [[ADD1]], metadata [[VAR1:![0-9]+]], metadata !DIExpression()), !dbg [[LINE1]]

; - Expressions inside of dbg.value intrinsics are preserved
; CHECK-NEXT: llvm.dbg.value(metadata i32 [[ADD1]], metadata [[VAR1]], metadata !DIExpression(DW_OP_constu, 1, DW_OP_plus, DW_OP_stack_value)

; - The DISubprogram for @foo.cold.1 has an empty DISubroutineType
; CHECK: [[FILE:![0-9]+]] = !DIFile(filename: "<stdin>"
; CHECK: [[EMPTY_MD:![0-9]+]] = !{}
; CHECK: [[EMPTY_TYPE:![0-9]+]] = !DISubroutineType(types: [[EMPTY_MD]])
; CHECK: [[NEWSCOPE:![0-9]+]] = distinct !DISubprogram(name: "foo.cold.1", linkageName: "foo.cold.1", scope: null, file: [[FILE]], type: [[EMPTY_TYPE]], spFlags: DISPFlagLocalToUnit | DISPFlagDefinition | DISPFlagOptimized

; - Line locations in @foo.cold.1 point to the new scope for @foo.cold.1
; CHECK: [[LINE1]] = !DILocation(line: 1, column: 1, scope: [[NEWSCOPE]])

define void @foo(i32 %arg1) !dbg !6 {
entry:
%var = add i32 0, 0, !dbg !11
br i1 undef, label %if.then, label %if.end

if.then: ; preds = %entry
ret void

if.end: ; preds = %entry
call void @llvm.dbg.value(metadata i32 %arg1, metadata !9, metadata !DIExpression()), !dbg !11
%add1 = add i32 %arg1, 1
call void @sink(i32 %add1)
call void @sink(i32 %add1), !dbg !11
call void @llvm.dbg.value(metadata i32 %add1, metadata !9, metadata !DIExpression()), !dbg !11
call void @llvm.dbg.value(metadata i32 %add1, metadata !9, metadata !DIExpression(DW_OP_constu, 1, DW_OP_plus, DW_OP_stack_value)), !dbg !11
ret void
}

declare void @llvm.dbg.value(metadata, metadata, metadata)

declare void @sink(i32) cold

!llvm.dbg.cu = !{!0}
!llvm.debugify = !{!3, !4}
!llvm.module.flags = !{!5}

!0 = distinct !DICompileUnit(language: DW_LANG_C, file: !1, producer: "debugify", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2)
!1 = !DIFile(filename: "<stdin>", directory: "/")
!2 = !{}
!3 = !{i32 7}
!4 = !{i32 1}
!5 = !{i32 2, !"Debug Info Version", i32 3}
!6 = distinct !DISubprogram(name: "foo", linkageName: "foo", scope: null, file: !1, line: 1, type: !7, isLocal: false, isDefinition: true, scopeLine: 1, isOptimized: true, unit: !0, retainedNodes: !8)
!7 = !DISubroutineType(types: !2)
!8 = !{!9}
!9 = !DILocalVariable(name: "1", scope: !6, file: !1, line: 1, type: !10)
!10 = !DIBasicType(name: "ty32", size: 32, encoding: DW_ATE_unsigned)
!11 = !DILocation(line: 1, column: 1, scope: !6)
Loading