Skip to content

Commit

Permalink
IRGen: debug info generation for cond_fail messages.
Browse files Browse the repository at this point in the history
To display a failure message in the debugger, create a function in the debug info which has the name of the failure message.
The debug location of the trap/cond_fail is then wrapped into this function and the function is declared as "inlined".
In case the debugger stops at the trap instruction, it displays the inline function, which looks like the failure message.
For example:

* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0)
    frame #0: 0x0000000100000cbf a.out`testit3(_:) [inlined] Unexpectedly found nil while unwrapping an Optional value at test.swift:14:11 [opt]
   11
   12   @inline(never)
   13   func testit(_ a: Int?) -> Int {
-> 14     return a!
   15   }
   16

This change is currently not enabled by default, but can be enabled with the option "-Xllvm -enable-trap-debug-info".
Enabling this feature needs some changes in lldb. When the lldb part is done, this option can be removed and the feature enabled by default.
  • Loading branch information
eeckstein committed Jul 16, 2019
1 parent b40ce6b commit ffd7f75
Show file tree
Hide file tree
Showing 10 changed files with 103 additions and 29 deletions.
5 changes: 3 additions & 2 deletions lib/IRGen/GenBuiltin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,7 @@ void irgen::emitBuiltinCall(IRGenFunction &IGF, const BuiltinInfo &Builtin,

// Emit non-mergeable traps only.
if (IGF.Builder.isTrapIntrinsic(IID)) {
IGF.Builder.CreateNonMergeableTrap(IGF.IGM);
IGF.Builder.CreateNonMergeableTrap(IGF.IGM, StringRef());
return;
}

Expand Down Expand Up @@ -359,7 +359,8 @@ if (Builtin.ID == BuiltinValueKind::id) { \
// string literal. If we ever get to the point of executing this builtin
// at run time, it implies an incorrect use of the builtin and must result
// in a trap.
IGF.emitTrap(/*Unreachable=*/false);
IGF.emitTrap("invalid use of globalStringTablePointer",
/*Unreachable=*/false);
auto returnValue = llvm::UndefValue::get(IGF.IGM.Int8PtrTy);
// Consume the arguments of the builtin.
(void)args.claimAll();
Expand Down
2 changes: 1 addition & 1 deletion lib/IRGen/GenCast.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -469,7 +469,7 @@ emitExistentialScalarCastFn(IRGenModule &IGM,
}

case CheckedCastMode::Unconditional: {
IGF.emitTrap(/*EmitUnreachable=*/true);
IGF.emitTrap("type cast failed", /*EmitUnreachable=*/true);
break;
}
}
Expand Down
2 changes: 1 addition & 1 deletion lib/IRGen/IRBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -307,7 +307,7 @@ class IRBuilder : public IRBuilderBase {
/// Call the trap intrinsic. If optimizations are enabled, an inline asm
/// gadget is emitted before the trap. The gadget inhibits transforms which
/// merge trap calls together, which makes debugging crashes easier.
llvm::CallInst *CreateNonMergeableTrap(IRGenModule &IGM);
llvm::CallInst *CreateNonMergeableTrap(IRGenModule &IGM, StringRef failureMsg);

/// Split a first-class aggregate value into its component pieces.
template <unsigned N>
Expand Down
39 changes: 39 additions & 0 deletions lib/IRGen/IRGenDebugInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,9 @@ class IRGenDebugInfoImpl : public IRGenDebugInfo {

void setCurrentLoc(IRBuilder &Builder, const SILDebugScope *DS,
SILLocation Loc);

void addFailureMessageToCurrentLoc(IRBuilder &Builder, StringRef failureMsg);

void clearLoc(IRBuilder &Builder);
void pushLoc();
void popLoc();
Expand Down Expand Up @@ -1828,7 +1831,37 @@ void IRGenDebugInfoImpl::setCurrentLoc(IRBuilder &Builder,
auto DL = llvm::DebugLoc::get(L.Line, L.Column, Scope, InlinedAt);
Builder.SetCurrentDebugLocation(DL);
}

void IRGenDebugInfoImpl::addFailureMessageToCurrentLoc(IRBuilder &Builder,
StringRef failureMsg) {
auto TrapLoc = Builder.getCurrentDebugLocation();

// Create a function in the debug info which has failureMsg as name.
// TrapSc is the SIL debug scope which corresponds to TrapSP in the LLVM debug
// info.
RegularLocation ALoc = RegularLocation::getAutoGeneratedLocation();
const SILDebugScope *TrapSc = new (IGM.getSILModule()) SILDebugScope(ALoc);

llvm::DISubroutineType *DIFnTy = DBuilder.createSubroutineType(nullptr);

std::string FuncName = "Swift runtime failure: ";
FuncName += failureMsg;

llvm::DISubprogram *TrapSP = DBuilder.createFunction(
MainModule, StringRef(), FuncName, TrapLoc->getFile(), 0, DIFnTy, 0,
llvm::DINode::FlagArtificial, llvm::DISubprogram::SPFlagDefinition,
nullptr, nullptr, nullptr);

ScopeCache[TrapSc] = llvm::TrackingMDNodeRef(TrapSP);
LastScope = TrapSc;

assert(parentScopesAreSane(TrapSc) && "parent scope sanity check failed");

// Wrap the existing TrapLoc into the failure function.
auto DL = llvm::DebugLoc::get(0, 0, TrapSP, TrapLoc);
Builder.SetCurrentDebugLocation(DL);
}

void IRGenDebugInfoImpl::clearLoc(IRBuilder &Builder) {
LastDebugLoc = {};
LastScope = nullptr;
Expand Down Expand Up @@ -2320,6 +2353,12 @@ void IRGenDebugInfo::setCurrentLoc(IRBuilder &Builder, const SILDebugScope *DS,
static_cast<IRGenDebugInfoImpl *>(this)->setCurrentLoc(Builder, DS, Loc);
}

void IRGenDebugInfo::addFailureMessageToCurrentLoc(IRBuilder &Builder,
StringRef failureMsg) {
static_cast<IRGenDebugInfoImpl *>(this)->
addFailureMessageToCurrentLoc(Builder, failureMsg);
}

void IRGenDebugInfo::clearLoc(IRBuilder &Builder) {
static_cast<IRGenDebugInfoImpl *>(this)->clearLoc(Builder);
}
Expand Down
6 changes: 6 additions & 0 deletions lib/IRGen/IRGenDebugInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,12 @@ class IRGenDebugInfo {
void setCurrentLoc(IRBuilder &Builder, const SILDebugScope *DS,
SILLocation Loc);

/// Replace the current debug location in \p Builder with the same location, but contained in an
/// inlined function which is named like \p failureMsg.
///
/// This lets the debugger display the \p failureMsg as an inlined function frame.
void addFailureMessageToCurrentLoc(IRBuilder &Builder, StringRef failureMsg);

void clearLoc(IRBuilder &Builder);

/// Push the current debug location onto a stack and initialize the
Expand Down
14 changes: 11 additions & 3 deletions lib/IRGen/IRGenFunction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@
using namespace swift;
using namespace irgen;

static llvm::cl::opt<bool> EnableTrapDebugInfo(
"enable-trap-debug-info", llvm::cl::Hidden,
llvm::cl::desc("Generate failure-message functions in the debug info"));

IRGenFunction::IRGenFunction(IRGenModule &IGM, llvm::Function *Fn,
OptimizationMode OptMode,
const SILDebugScope *DbgScope,
Expand Down Expand Up @@ -432,7 +436,8 @@ Address IRGenFunction::emitAddressAtOffset(llvm::Value *base, Offset offset,
return Address(slotPtr, objectAlignment);
}

llvm::CallInst *IRBuilder::CreateNonMergeableTrap(IRGenModule &IGM) {
llvm::CallInst *IRBuilder::CreateNonMergeableTrap(IRGenModule &IGM,
StringRef failureMsg) {
if (IGM.IRGen.Opts.shouldOptimize()) {
// Emit unique side-effecting inline asm calls in order to eliminate
// the possibility that an LLVM optimization or code generation pass
Expand All @@ -452,13 +457,16 @@ llvm::CallInst *IRBuilder::CreateNonMergeableTrap(IRGenModule &IGM) {
// Emit the trap instruction.
llvm::Function *trapIntrinsic =
llvm::Intrinsic::getDeclaration(&IGM.Module, llvm::Intrinsic::ID::trap);
if (EnableTrapDebugInfo && IGM.DebugInfo && !failureMsg.empty()) {
IGM.DebugInfo->addFailureMessageToCurrentLoc(*this, failureMsg);
}
auto Call = IRBuilderBase::CreateCall(trapIntrinsic, {});
setCallingConvUsingCallee(Call);
return Call;
}

void IRGenFunction::emitTrap(bool EmitUnreachable) {
Builder.CreateNonMergeableTrap(IGM);
void IRGenFunction::emitTrap(StringRef failureMessage, bool EmitUnreachable) {
Builder.CreateNonMergeableTrap(IGM, failureMessage);
if (EmitUnreachable)
Builder.CreateUnreachable();
}
2 changes: 1 addition & 1 deletion lib/IRGen/IRGenFunction.h
Original file line number Diff line number Diff line change
Expand Up @@ -287,7 +287,7 @@ class IRGenFunction {
void setDereferenceableLoad(llvm::LoadInst *load, unsigned size);

/// Emit a non-mergeable trap call, optionally followed by a terminator.
void emitTrap(bool EmitUnreachable);
void emitTrap(StringRef failureMessage, bool EmitUnreachable);

private:
llvm::Instruction *AllocaIP;
Expand Down
6 changes: 3 additions & 3 deletions lib/IRGen/IRGenSIL.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3880,7 +3880,7 @@ static bool hasReferenceSemantics(IRGenSILFunction &IGF,
static llvm::Value *emitIsUnique(IRGenSILFunction &IGF, SILValue operand,
SourceLoc loc) {
if (!hasReferenceSemantics(IGF, operand->getType())) {
IGF.emitTrap(/*EmitUnreachable=*/false);
IGF.emitTrap("isUnique called for a non-reference", /*EmitUnreachable=*/false);
return llvm::UndefValue::get(IGF.IGM.Int1Ty);
}

Expand Down Expand Up @@ -4540,7 +4540,7 @@ static void emitTrapAndUndefValue(IRGenSILFunction &IGF,
IGF.FailBBs.push_back(failBB);

IGF.Builder.emitBlock(failBB);
IGF.emitTrap(/*EmitUnreachable=*/true);
IGF.emitTrap("mismatching type layouts", /*EmitUnreachable=*/true);

llvm::BasicBlock *contBB = llvm::BasicBlock::Create(IGF.IGM.getLLVMContext());
IGF.Builder.emitBlock(contBB);
Expand Down Expand Up @@ -5441,7 +5441,7 @@ void IRGenSILFunction::visitCondFailInst(swift::CondFailInst *i) {
// debug location. This is because zero is not an artificial line location
// in CodeView.
IGM.DebugInfo->setInlinedTrapLocation(Builder, i->getDebugScope());
emitTrap(/*EmitUnreachable=*/true);
emitTrap(i->getMessage(), /*EmitUnreachable=*/true);
Builder.emitBlock(contBB);
FailBBs.push_back(failBB);
}
Expand Down
40 changes: 22 additions & 18 deletions test/DebugInfo/linetable-codeview.swift
Original file line number Diff line number Diff line change
@@ -1,32 +1,34 @@
// RUN: %swiftc_driver %s -g -debug-info-format=codeview -emit-ir -o - | %FileCheck %s
// RUN: %swiftc_driver %s -g -debug-info-format=codeview -Xllvm -enable-trap-debug-info -emit-ir -o - | %FileCheck %s
// REQUIRES: optimized_stdlib

func markUsed<T>(_ t: T) {}
func arithmetic(_ a: Int64, _ b: Int64) {
markUsed(a + b) // line 4
markUsed(a / b) // line 5
markUsed(a + b) // line 6
markUsed(a / b) // line 7
}
struct SimpleStruct { // NOTE: Classes do not work in Windows yet.
var myVal1: Float
var myVal2: Float
func sum(myArg: Float) { // line 10
func sum(myArg: Float) { // line 12
markUsed(myVal1 + myVal2 + myArg)
}
}
func myLoop() {
for index in 0...3 { // line 15
markUsed(index) // line 16
for index in 0...3 { // line 17
markUsed(index) // line 18
}
}
func mySwitch(_ a: Int64) {
switch a {
case 0:
markUsed(a-1) // line 22
markUsed(a-1) // line 24
default: do {
markUsed(a+1)
} // line 25
} // line 27
}
}
func foo() {
var myArray: [Int64] = [] // line 29
var myArray: [Int64] = [] // line 31
}

// func arithmetic(_ a: Int64, _ b: Int64)
Expand Down Expand Up @@ -74,19 +76,21 @@ func foo() {
// CHECK-SAME: !dbg ![[ARRAY]]
// CHECK: ret void

// CHECK-DAG: ![[ADD]] = !DILocation(line: 4, scope:
// CHECK-DAG: ![[DIV]] = !DILocation(line: 5, scope:
// CHECK-DAG: ![[ADD]] = !DILocation(line: 6, scope:
// CHECK-DAG: ![[DIV]] = !DILocation(line: 7, scope:
// FIXME: The location of ``@llvm.trap`` should be in Integers.swift.gyb
// instead of being artificial.
// CHECK-DAG: ![[INLINEDADD]] = !DILocation(line: 0, scope: !{{[0-9]+}}, inlinedAt: ![[ADD]]
// CHECK: ![[INLINEDADD]] = !DILocation(line: 0, scope: ![[FAILURE_FUNC:[0-9]+]], inlinedAt: ![[INLINELOC:[0-9]+]]
// CHECK-DAG: !{{.*}} = distinct !DISubprogram(linkageName: "Swift runtime failure: arithmetic overflow", scope: {{.*}}, flags: DIFlagArtificial, spFlags: DISPFlagDefinition, {{.*}})
// CHECK-DAG: ![[INLINELOC]] = !DILocation(line: 0, scope: !{{[0-9]+}}, inlinedAt: ![[ADD]]

// NOTE: These prologue instructions are given artificial line locations for
// LLDB, but for CodeView they should have the location of the function
// to keep the linetables contiguous.
// CHECK-DAG: ![[SUM]] = distinct !DISubprogram(name: "sum", linkageName: "$s4main12SimpleStructV3sum5myArgySf_tF"
// CHECK-DAG: ![[PROLOGUE]] = !DILocation(line: 10, scope: ![[SUM]])
// CHECK-DAG: ![[FORLOOP]] = !DILocation(line: 15, scope:
// CHECK-DAG: ![[FORBODY]] = !DILocation(line: 16, scope:
// CHECK-DAG: ![[CASE]] = !DILocation(line: 22, scope:
// CHECK-DAG: ![[DEFAULTCLEANUP]] = !DILocation(line: 25, scope:
// CHECK-DAG: ![[ARRAY]] = !DILocation(line: 29, scope:
// CHECK-DAG: ![[PROLOGUE]] = !DILocation(line: 12, scope: ![[SUM]])
// CHECK-DAG: ![[FORLOOP]] = !DILocation(line: 17, scope:
// CHECK-DAG: ![[FORBODY]] = !DILocation(line: 18, scope:
// CHECK-DAG: ![[CASE]] = !DILocation(line: 24, scope:
// CHECK-DAG: ![[DEFAULTCLEANUP]] = !DILocation(line: 27, scope:
// CHECK-DAG: ![[ARRAY]] = !DILocation(line: 31, scope:
16 changes: 16 additions & 0 deletions test/IRGen/condfail_message.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// RUN: %target-swift-frontend -primary-file %s -g -Xllvm -enable-trap-debug-info -emit-ir | %FileCheck %s
// RUN: %target-swift-frontend -primary-file %s -g -Xllvm -enable-trap-debug-info -O -emit-ir | %FileCheck %s
// REQUIRES: optimized_stdlib

// CHECK-LABEL: define hidden swiftcc i8 @"$s16condfail_message6testitys4Int8VADF"(i8)
// CHECK: call void @llvm.trap(), !dbg [[LOC:![0-9]+]]

func testit(_ a: Int8) -> Int8 {
return a + 1
}

// CHECK: [[CALLER_LOC:![0-9]+]] = !DILocation(line: 9, column: 12, scope: !{{.*}})
// CHECK: [[LOC]] = !DILocation(line: 0, scope: [[FAILURE_FUNC:![0-9]+]], inlinedAt: [[CALLER_LOC]])
// CHECK: [[FAILURE_FUNC]] = distinct !DISubprogram(linkageName: "Swift runtime failure: arithmetic overflow", scope: {{.*}}, file: {{.*}}, type: [[FUNC_TYPE:![0-9]+]], flags: DIFlagArtificial, spFlags: DISPFlagDefinition, {{.*}})
// CHECK: [[FUNC_TYPE]] = !DISubroutineType(types: null)

0 comments on commit ffd7f75

Please sign in to comment.