Skip to content

Commit

Permalink
Merge pull request #585 from vedantk/cherry
Browse files Browse the repository at this point in the history
Cherry-pick follow-ups to D72489 (AT_call_return_pc DWARF emission change)
  • Loading branch information
vedantk committed Jan 16, 2020
2 parents be9f5e1 + b2c99bc commit a7c1aee
Show file tree
Hide file tree
Showing 11 changed files with 326 additions and 27 deletions.
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
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
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
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
@@ -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
@@ -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)

0 comments on commit a7c1aee

Please sign in to comment.