Skip to content

Commit f886945

Browse files
authored
Merge pull request #25978 from eeckstein/confail-messages
Better runtime failure messages (not yet enabled by default)
2 parents fdecd00 + bc48b3d commit f886945

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

46 files changed

+296
-137
lines changed

docs/SIL.rst

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5110,13 +5110,15 @@ cond_fail
51105110
`````````
51115111
::
51125112

5113-
sil-instruction ::= 'cond_fail' sil-operand
5113+
sil-instruction ::= 'cond_fail' sil-operand, string-literal
51145114

5115-
cond_fail %0 : $Builtin.Int1
5115+
cond_fail %0 : $Builtin.Int1, "failure reason"
51165116
// %0 must be of type $Builtin.Int1
51175117

51185118
This instruction produces a `runtime failure`_ if the operand is one.
51195119
Execution proceeds normally if the operand is zero.
5120+
The second operand is a static failure message, which is displayed by the
5121+
debugger in case the failure is triggered.
51205122

51215123
Terminators
51225124
~~~~~~~~~~~

include/swift/AST/Builtins.def

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -314,10 +314,6 @@ BUILTIN_SIL_OPERATION(BeginUnpairedModifyAccess, "beginUnpairedModifyAccess",
314314
/// be a pointer to an UnsafeValueBuffer that records an in progress access.
315315
BUILTIN_SIL_OPERATION(EndUnpairedAccess, "endUnpairedAccess", Special)
316316

317-
/// condfail(Int1) -> ()
318-
/// Triggers a runtime failure if the condition is true.
319-
BUILTIN_SIL_OPERATION(CondFail, "condfail", Special)
320-
321317
/// fixLifetime(T) -> ()
322318
/// Fixes the lifetime of any heap references in a value.
323319
BUILTIN_SIL_OPERATION(FixLifetime, "fixLifetime", Special)
@@ -404,6 +400,10 @@ BUILTIN_RUNTIME_CALL(IsOptionalType, "isOptional", "")
404400
BUILTIN(Id, Name, Attrs)
405401
#endif
406402

403+
/// condfail(Int1, RawPointer) -> ()
404+
/// Triggers a runtime failure if the condition is true.
405+
BUILTIN_MISC_OPERATION(CondFail, "condfail", "", Special)
406+
407407
/// Sizeof has type T.Type -> Int
408408
BUILTIN_MISC_OPERATION(Sizeof, "sizeof", "n", Special)
409409

include/swift/SIL/SILBuilder.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1775,15 +1775,15 @@ class SILBuilder {
17751775
//===--------------------------------------------------------------------===//
17761776

17771777
CondFailInst *createCondFail(SILLocation Loc, SILValue Operand,
1778-
bool Inverted = false) {
1778+
StringRef Message, bool Inverted = false) {
17791779
if (Inverted) {
17801780
SILType Ty = Operand->getType();
17811781
SILValue True(createIntegerLiteral(Loc, Ty, 1));
17821782
Operand =
17831783
createBuiltinBinaryFunction(Loc, "xor", Ty, Ty, {Operand, True});
17841784
}
1785-
return insert(new (getModule())
1786-
CondFailInst(getSILDebugLocation(Loc), Operand));
1785+
return insert(CondFailInst::create(getSILDebugLocation(Loc), Operand,
1786+
Message, getModule()));
17871787
}
17881788

17891789
BuiltinInst *createBuiltinTrap(SILLocation Loc) {

include/swift/SIL/SILCloner.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2447,7 +2447,8 @@ SILCloner<ImplClass>::visitCondFailInst(CondFailInst *Inst) {
24472447
getBuilder().setCurrentDebugScope(getOpScope(Inst->getDebugScope()));
24482448
recordClonedInstruction(
24492449
Inst, getBuilder().createCondFail(getOpLocation(Inst->getLoc()),
2450-
getOpValue(Inst->getOperand())));
2450+
getOpValue(Inst->getOperand()),
2451+
Inst->getMessage()));
24512452
}
24522453

24532454
template<typename ImplClass>

include/swift/SIL/SILInstruction.h

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6715,14 +6715,28 @@ class ProjectExistentialBoxInst
67156715
//===----------------------------------------------------------------------===//
67166716

67176717
/// Trigger a runtime failure if the given Int1 value is true.
6718-
class CondFailInst
6718+
///
6719+
/// Optionally cond_fail has a static failure message, which is displayed in the debugger in case the failure
6720+
/// is triggered.
6721+
class CondFailInst final
67196722
: public UnaryInstructionBase<SILInstructionKind::CondFailInst,
6720-
NonValueInstruction>
6723+
NonValueInstruction>,
6724+
private llvm::TrailingObjects<CondFailInst, char>
67216725
{
6726+
friend TrailingObjects;
67226727
friend SILBuilder;
67236728

6724-
CondFailInst(SILDebugLocation DebugLoc, SILValue Operand)
6725-
: UnaryInstructionBase(DebugLoc, Operand) {}
6729+
unsigned MessageSize;
6730+
6731+
CondFailInst(SILDebugLocation DebugLoc, SILValue Operand, StringRef Message);
6732+
6733+
static CondFailInst *create(SILDebugLocation DebugLoc, SILValue Operand,
6734+
StringRef Message, SILModule &M);
6735+
6736+
public:
6737+
StringRef getMessage() const {
6738+
return {getTrailingObjects<char>(), MessageSize};
6739+
}
67266740
};
67276741

67286742
//===----------------------------------------------------------------------===//

include/swift/Serialization/ModuleFormat.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ const uint16_t SWIFTMODULE_VERSION_MAJOR = 0;
5252
/// describe what change you made. The content of this comment isn't important;
5353
/// it just ensures a conflict if two people change the module format.
5454
/// Don't worry about adhering to the 80-column limit for this line.
55-
const uint16_t SWIFTMODULE_VERSION_MINOR = 500; // dependency types for protocols
55+
const uint16_t SWIFTMODULE_VERSION_MINOR = 501; // cond_fail messages
5656

5757
using DeclIDField = BCFixed<31>;
5858

lib/AST/Builtins.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1037,8 +1037,9 @@ static ValueDecl *getCanBeObjCClassOperation(ASTContext &Context,
10371037
static ValueDecl *getCondFailOperation(ASTContext &C, Identifier Id) {
10381038
// Int1 -> ()
10391039
auto CondTy = BuiltinIntegerType::get(1, C);
1040+
auto MsgTy = C.TheRawPointerType;
10401041
auto VoidTy = TupleType::getEmpty(C);
1041-
return getBuiltinFunction(Id, {CondTy}, VoidTy);
1042+
return getBuiltinFunction(Id, {CondTy, MsgTy}, VoidTy);
10421043
}
10431044

10441045
static ValueDecl *getAssertConfOperation(ASTContext &C, Identifier Id) {

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
}

lib/ParseSIL/ParseSIL.cpp

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2635,6 +2635,33 @@ bool SILParser::parseSILInstruction(SILBuilder &B) {
26352635
break;
26362636
}
26372637

2638+
case SILInstructionKind::CondFailInst: {
2639+
2640+
if (parseTypedValueRef(Val, B))
2641+
return true;
2642+
2643+
SmallVector<char, 128> stringBuffer;
2644+
StringRef message;
2645+
if (P.consumeIf(tok::comma)) {
2646+
// Parse the string.
2647+
if (P.Tok.getKind() != tok::string_literal) {
2648+
P.diagnose(P.Tok, diag::expected_tok_in_sil_instr, "string");
2649+
return true;
2650+
}
2651+
SmallVector<Lexer::StringSegment, 1> segments;
2652+
P.L->getStringLiteralSegments(P.Tok, segments);
2653+
assert(segments.size() == 1);
2654+
2655+
P.consumeToken(tok::string_literal);
2656+
message = P.L->getEncodedStringSegment(segments.front(), stringBuffer);
2657+
}
2658+
if (parseSILDebugLocation(InstLoc, B))
2659+
return true;
2660+
2661+
ResultVal = B.createCondFail(InstLoc, Val, message);
2662+
break;
2663+
}
2664+
26382665
case SILInstructionKind::AllocValueBufferInst: {
26392666
SILType Ty;
26402667
if (parseSILType(Ty) ||
@@ -2887,7 +2914,6 @@ bool SILParser::parseSILInstruction(SILBuilder &B) {
28872914
UNARY_INSTRUCTION(DestroyAddr)
28882915
UNARY_INSTRUCTION(CopyValue)
28892916
UNARY_INSTRUCTION(DestroyValue)
2890-
UNARY_INSTRUCTION(CondFail)
28912917
UNARY_INSTRUCTION(EndBorrow)
28922918
UNARY_INSTRUCTION(DestructureStruct)
28932919
UNARY_INSTRUCTION(DestructureTuple)

lib/SIL/OperandOwnership.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -953,6 +953,7 @@ ANY_OWNERSHIP_BUILTIN(AtomicRMW)
953953
ANY_OWNERSHIP_BUILTIN(AtomicStore)
954954
ANY_OWNERSHIP_BUILTIN(BitCast)
955955
ANY_OWNERSHIP_BUILTIN(CanBeObjCClass)
956+
ANY_OWNERSHIP_BUILTIN(CondFail)
956957
ANY_OWNERSHIP_BUILTIN(CmpXChg)
957958
ANY_OWNERSHIP_BUILTIN(CondUnreachable)
958959
ANY_OWNERSHIP_BUILTIN(CopyArray)

lib/SIL/SILInstructions.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -773,6 +773,21 @@ StringLiteralInst *StringLiteralInst::create(SILDebugLocation Loc,
773773
return ::new (buf) StringLiteralInst(Loc, text, encoding, Ty);
774774
}
775775

776+
CondFailInst::CondFailInst(SILDebugLocation DebugLoc, SILValue Operand,
777+
StringRef Message)
778+
: UnaryInstructionBase(DebugLoc, Operand),
779+
MessageSize(Message.size()) {
780+
memcpy(getTrailingObjects<char>(), Message.data(), Message.size());
781+
}
782+
783+
CondFailInst *CondFailInst::create(SILDebugLocation DebugLoc, SILValue Operand,
784+
StringRef Message, SILModule &M) {
785+
786+
auto Size = totalSizeToAlloc<char>(Message.size());
787+
auto Buffer = M.allocateInst(Size, alignof(CondFailInst));
788+
return ::new (Buffer) CondFailInst(DebugLoc, Operand, Message);
789+
}
790+
776791
uint64_t StringLiteralInst::getCodeUnitCount() {
777792
auto E = unsigned(Encoding::UTF16);
778793
if (SILInstruction::Bits.StringLiteralInst.TheEncoding == E)

lib/SIL/SILPrinter.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1866,7 +1866,8 @@ class SILPrinter : public SILInstructionVisitor<SILPrinter> {
18661866
}
18671867

18681868
void visitCondFailInst(CondFailInst *FI) {
1869-
*this << getIDAndType(FI->getOperand());
1869+
*this << getIDAndType(FI->getOperand()) << ", "
1870+
<< QuotedString(FI->getMessage());
18701871
}
18711872

18721873
void visitIndexAddrInst(IndexAddrInst *IAI) {

lib/SIL/ValueOwnership.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -378,6 +378,7 @@ CONSTANT_OWNERSHIP_BUILTIN(Any, And)
378378
CONSTANT_OWNERSHIP_BUILTIN(Any, AssumeNonNegative)
379379
CONSTANT_OWNERSHIP_BUILTIN(Any, AssumeTrue)
380380
CONSTANT_OWNERSHIP_BUILTIN(Any, BitCast)
381+
CONSTANT_OWNERSHIP_BUILTIN(Any, CondFail)
381382
CONSTANT_OWNERSHIP_BUILTIN(Any, ExactSDiv)
382383
CONSTANT_OWNERSHIP_BUILTIN(Any, ExactUDiv)
383384
CONSTANT_OWNERSHIP_BUILTIN(Any, FAdd)

0 commit comments

Comments
 (0)