Skip to content

Commit e915300

Browse files
committed
IRGen: debug info generation for cond_fail messages.
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.
1 parent 3fd7eea commit e915300

File tree

10 files changed

+84
-13
lines changed

10 files changed

+84
-13
lines changed

lib/IRGen/GenBuiltin.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,7 @@ void irgen::emitBuiltinCall(IRGenFunction &IGF, const BuiltinInfo &Builtin,
204204

205205
// Emit non-mergeable traps only.
206206
if (IGF.Builder.isTrapIntrinsic(IID)) {
207-
IGF.Builder.CreateNonMergeableTrap(IGF.IGM);
207+
IGF.Builder.CreateNonMergeableTrap(IGF.IGM, StringRef());
208208
return;
209209
}
210210

@@ -359,7 +359,8 @@ if (Builtin.ID == BuiltinValueKind::id) { \
359359
// string literal. If we ever get to the point of executing this builtin
360360
// at run time, it implies an incorrect use of the builtin and must result
361361
// in a trap.
362-
IGF.emitTrap(/*Unreachable=*/false);
362+
IGF.emitTrap("invalid use of globalStringTablePointer",
363+
/*Unreachable=*/false);
363364
auto returnValue = llvm::UndefValue::get(IGF.IGM.Int8PtrTy);
364365
// Consume the arguments of the builtin.
365366
(void)args.claimAll();

lib/IRGen/GenCast.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -469,7 +469,7 @@ emitExistentialScalarCastFn(IRGenModule &IGM,
469469
}
470470

471471
case CheckedCastMode::Unconditional: {
472-
IGF.emitTrap(/*EmitUnreachable=*/true);
472+
IGF.emitTrap("type cast failed", /*EmitUnreachable=*/true);
473473
break;
474474
}
475475
}

lib/IRGen/IRBuilder.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -307,7 +307,7 @@ class IRBuilder : public IRBuilderBase {
307307
/// Call the trap intrinsic. If optimizations are enabled, an inline asm
308308
/// gadget is emitted before the trap. The gadget inhibits transforms which
309309
/// merge trap calls together, which makes debugging crashes easier.
310-
llvm::CallInst *CreateNonMergeableTrap(IRGenModule &IGM);
310+
llvm::CallInst *CreateNonMergeableTrap(IRGenModule &IGM, StringRef failureMsg);
311311

312312
/// Split a first-class aggregate value into its component pieces.
313313
template <unsigned N>

lib/IRGen/IRGenDebugInfo.cpp

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,9 @@ class IRGenDebugInfoImpl : public IRGenDebugInfo {
148148

149149
void setCurrentLoc(IRBuilder &Builder, const SILDebugScope *DS,
150150
SILLocation Loc);
151+
152+
void addFailureMessageToCurrentLoc(IRBuilder &Builder, StringRef failureMsg);
153+
151154
void clearLoc(IRBuilder &Builder);
152155
void pushLoc();
153156
void popLoc();
@@ -1828,7 +1831,37 @@ void IRGenDebugInfoImpl::setCurrentLoc(IRBuilder &Builder,
18281831
auto DL = llvm::DebugLoc::get(L.Line, L.Column, Scope, InlinedAt);
18291832
Builder.SetCurrentDebugLocation(DL);
18301833
}
1834+
1835+
void IRGenDebugInfoImpl::addFailureMessageToCurrentLoc(IRBuilder &Builder,
1836+
StringRef failureMsg) {
1837+
auto TrapLoc = Builder.getCurrentDebugLocation();
1838+
1839+
// Create a function in the debug info which has failureMsg as name.
1840+
// TrapSc is the SIL debug scope which corresponds to TrapSP in the LLVM debug
1841+
// info.
1842+
RegularLocation ALoc = RegularLocation::getAutoGeneratedLocation();
1843+
const SILDebugScope *TrapSc = new (IGM.getSILModule()) SILDebugScope(ALoc);
1844+
1845+
llvm::DISubroutineType *DIFnTy = DBuilder.createSubroutineType(nullptr);
1846+
1847+
std::string FuncName = "Swift runtime failure: ";
1848+
FuncName += failureMsg;
1849+
1850+
llvm::DISubprogram *TrapSP = DBuilder.createFunction(
1851+
MainModule, StringRef(), FuncName, TrapLoc->getFile(), 0, DIFnTy, 0,
1852+
llvm::DINode::FlagArtificial, llvm::DISubprogram::SPFlagDefinition,
1853+
nullptr, nullptr, nullptr);
18311854

1855+
ScopeCache[TrapSc] = llvm::TrackingMDNodeRef(TrapSP);
1856+
LastScope = TrapSc;
1857+
1858+
assert(parentScopesAreSane(TrapSc) && "parent scope sanity check failed");
1859+
1860+
// Wrap the existing TrapLoc into the failure function.
1861+
auto DL = llvm::DebugLoc::get(0, 0, TrapSP, TrapLoc);
1862+
Builder.SetCurrentDebugLocation(DL);
1863+
}
1864+
18321865
void IRGenDebugInfoImpl::clearLoc(IRBuilder &Builder) {
18331866
LastDebugLoc = {};
18341867
LastScope = nullptr;
@@ -2320,6 +2353,12 @@ void IRGenDebugInfo::setCurrentLoc(IRBuilder &Builder, const SILDebugScope *DS,
23202353
static_cast<IRGenDebugInfoImpl *>(this)->setCurrentLoc(Builder, DS, Loc);
23212354
}
23222355

2356+
void IRGenDebugInfo::addFailureMessageToCurrentLoc(IRBuilder &Builder,
2357+
StringRef failureMsg) {
2358+
static_cast<IRGenDebugInfoImpl *>(this)->
2359+
addFailureMessageToCurrentLoc(Builder, failureMsg);
2360+
}
2361+
23232362
void IRGenDebugInfo::clearLoc(IRBuilder &Builder) {
23242363
static_cast<IRGenDebugInfoImpl *>(this)->clearLoc(Builder);
23252364
}

lib/IRGen/IRGenDebugInfo.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,12 @@ class IRGenDebugInfo {
5959
void setCurrentLoc(IRBuilder &Builder, const SILDebugScope *DS,
6060
SILLocation Loc);
6161

62+
/// Replace the current debug location in \p Builder with the same location, but contained in an
63+
/// inlined function which is named like \p failureMsg.
64+
///
65+
/// This lets the debugger display the \p failureMsg as an inlined function frame.
66+
void addFailureMessageToCurrentLoc(IRBuilder &Builder, StringRef failureMsg);
67+
6268
void clearLoc(IRBuilder &Builder);
6369

6470
/// Push the current debug location onto a stack and initialize the

lib/IRGen/IRGenFunction.cpp

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,10 @@
3131
using namespace swift;
3232
using namespace irgen;
3333

34+
static llvm::cl::opt<bool> EnableTrapDebugInfo(
35+
"enable-trap-debug-info", llvm::cl::Hidden,
36+
llvm::cl::desc("Generate failure-message functions in the debug info"));
37+
3438
IRGenFunction::IRGenFunction(IRGenModule &IGM, llvm::Function *Fn,
3539
OptimizationMode OptMode,
3640
const SILDebugScope *DbgScope,
@@ -432,7 +436,8 @@ Address IRGenFunction::emitAddressAtOffset(llvm::Value *base, Offset offset,
432436
return Address(slotPtr, objectAlignment);
433437
}
434438

435-
llvm::CallInst *IRBuilder::CreateNonMergeableTrap(IRGenModule &IGM) {
439+
llvm::CallInst *IRBuilder::CreateNonMergeableTrap(IRGenModule &IGM,
440+
StringRef failureMsg) {
436441
if (IGM.IRGen.Opts.shouldOptimize()) {
437442
// Emit unique side-effecting inline asm calls in order to eliminate
438443
// the possibility that an LLVM optimization or code generation pass
@@ -452,13 +457,16 @@ llvm::CallInst *IRBuilder::CreateNonMergeableTrap(IRGenModule &IGM) {
452457
// Emit the trap instruction.
453458
llvm::Function *trapIntrinsic =
454459
llvm::Intrinsic::getDeclaration(&IGM.Module, llvm::Intrinsic::ID::trap);
460+
if (EnableTrapDebugInfo && IGM.DebugInfo && !failureMsg.empty()) {
461+
IGM.DebugInfo->addFailureMessageToCurrentLoc(*this, failureMsg);
462+
}
455463
auto Call = IRBuilderBase::CreateCall(trapIntrinsic, {});
456464
setCallingConvUsingCallee(Call);
457465
return Call;
458466
}
459467

460-
void IRGenFunction::emitTrap(bool EmitUnreachable) {
461-
Builder.CreateNonMergeableTrap(IGM);
468+
void IRGenFunction::emitTrap(StringRef failureMessage, bool EmitUnreachable) {
469+
Builder.CreateNonMergeableTrap(IGM, failureMessage);
462470
if (EmitUnreachable)
463471
Builder.CreateUnreachable();
464472
}

lib/IRGen/IRGenFunction.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -287,7 +287,7 @@ class IRGenFunction {
287287
void setDereferenceableLoad(llvm::LoadInst *load, unsigned size);
288288

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

292292
private:
293293
llvm::Instruction *AllocaIP;

lib/IRGen/IRGenSIL.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3880,7 +3880,7 @@ static bool hasReferenceSemantics(IRGenSILFunction &IGF,
38803880
static llvm::Value *emitIsUnique(IRGenSILFunction &IGF, SILValue operand,
38813881
SourceLoc loc) {
38823882
if (!hasReferenceSemantics(IGF, operand->getType())) {
3883-
IGF.emitTrap(/*EmitUnreachable=*/false);
3883+
IGF.emitTrap("isUnique called for a non-reference", /*EmitUnreachable=*/false);
38843884
return llvm::UndefValue::get(IGF.IGM.Int1Ty);
38853885
}
38863886

@@ -4540,7 +4540,7 @@ static void emitTrapAndUndefValue(IRGenSILFunction &IGF,
45404540
IGF.FailBBs.push_back(failBB);
45414541

45424542
IGF.Builder.emitBlock(failBB);
4543-
IGF.emitTrap(/*EmitUnreachable=*/true);
4543+
IGF.emitTrap("mismatching type layouts", /*EmitUnreachable=*/true);
45444544

45454545
llvm::BasicBlock *contBB = llvm::BasicBlock::Create(IGF.IGM.getLLVMContext());
45464546
IGF.Builder.emitBlock(contBB);
@@ -5441,7 +5441,7 @@ void IRGenSILFunction::visitCondFailInst(swift::CondFailInst *i) {
54415441
// debug location. This is because zero is not an artificial line location
54425442
// in CodeView.
54435443
IGM.DebugInfo->setInlinedTrapLocation(Builder, i->getDebugScope());
5444-
emitTrap(/*EmitUnreachable=*/true);
5444+
emitTrap(i->getMessage(), /*EmitUnreachable=*/true);
54455445
Builder.emitBlock(contBB);
54465446
FailBBs.push_back(failBB);
54475447
}

test/DebugInfo/linetable-codeview.swift

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// RUN: %swiftc_driver %s -g -debug-info-format=codeview -emit-ir -o - | %FileCheck %s
1+
// RUN: %swiftc_driver %s -g -debug-info-format=codeview -Xllvm -enable-trap-debug-info -emit-ir -o - | %FileCheck %s
22
func markUsed<T>(_ t: T) {}
33
func arithmetic(_ a: Int64, _ b: Int64) {
44
markUsed(a + b) // line 4
@@ -78,7 +78,9 @@ func foo() {
7878
// CHECK-DAG: ![[DIV]] = !DILocation(line: 5, scope:
7979
// FIXME: The location of ``@llvm.trap`` should be in Integers.swift.gyb
8080
// instead of being artificial.
81-
// CHECK-DAG: ![[INLINEDADD]] = !DILocation(line: 0, scope: !{{[0-9]+}}, inlinedAt: ![[ADD]]
81+
// CHECK: ![[INLINEDADD]] = !DILocation(line: 0, scope: ![[FAILURE_FUNC:[0-9]+]], inlinedAt: ![[INLINELOC:[0-9]+]]
82+
// CHECK-DAG: !{{.*}} = distinct !DISubprogram(linkageName: "Swift runtime failure: arithmetic overflow", scope: {{.*}}, flags: DIFlagArtificial, spFlags: DISPFlagDefinition, {{.*}})
83+
// CHECK-DAG: ![[INLINELOC]] = !DILocation(line: 0, scope: !{{[0-9]+}}, inlinedAt: ![[ADD]]
8284

8385
// NOTE: These prologue instructions are given artificial line locations for
8486
// LLDB, but for CodeView they should have the location of the function

test/IRGen/condfail_message.swift

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// RUN: %target-swift-frontend -primary-file %s -g -Xllvm -enable-trap-debug-info -emit-ir | %FileCheck %s
2+
// RUN: %target-swift-frontend -primary-file %s -g -Xllvm -enable-trap-debug-info -O -emit-ir | %FileCheck %s
3+
4+
// CHECK-LABEL: define hidden swiftcc i8 @"$s16condfail_message6testitys4Int8VADF"(i8)
5+
// CHECK: call void @llvm.trap(), !dbg [[LOC:![0-9]+]]
6+
7+
func testit(_ a: Int8) -> Int8 {
8+
return a + 1
9+
}
10+
11+
// CHECK: [[CALLER_LOC:![0-9]+]] = !DILocation(line: 8, column: 12, scope: !{{.*}})
12+
// CHECK: [[LOC]] = !DILocation(line: 0, scope: [[FAILURE_FUNC:![0-9]+]], inlinedAt: [[CALLER_LOC]])
13+
// CHECK: [[FAILURE_FUNC]] = distinct !DISubprogram(linkageName: "Swift runtime failure: arithmetic overflow", scope: {{.*}}, file: {{.*}}, type: [[FUNC_TYPE:![0-9]+]], flags: DIFlagArtificial, spFlags: DISPFlagDefinition, {{.*}})
14+
// CHECK: [[FUNC_TYPE]] = !DISubroutineType(types: null)
15+

0 commit comments

Comments
 (0)