Skip to content

Better runtime failure messages (not yet enabled by default) #25978

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 5 commits into from
Jul 12, 2019
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
6 changes: 4 additions & 2 deletions docs/SIL.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5110,13 +5110,15 @@ cond_fail
`````````
::

sil-instruction ::= 'cond_fail' sil-operand
sil-instruction ::= 'cond_fail' sil-operand, string-literal

cond_fail %0 : $Builtin.Int1
cond_fail %0 : $Builtin.Int1, "failure reason"
// %0 must be of type $Builtin.Int1

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

Terminators
~~~~~~~~~~~
Expand Down
8 changes: 4 additions & 4 deletions include/swift/AST/Builtins.def
Original file line number Diff line number Diff line change
Expand Up @@ -314,10 +314,6 @@ BUILTIN_SIL_OPERATION(BeginUnpairedModifyAccess, "beginUnpairedModifyAccess",
/// be a pointer to an UnsafeValueBuffer that records an in progress access.
BUILTIN_SIL_OPERATION(EndUnpairedAccess, "endUnpairedAccess", Special)

/// condfail(Int1) -> ()
/// Triggers a runtime failure if the condition is true.
BUILTIN_SIL_OPERATION(CondFail, "condfail", Special)

/// fixLifetime(T) -> ()
/// Fixes the lifetime of any heap references in a value.
BUILTIN_SIL_OPERATION(FixLifetime, "fixLifetime", Special)
Expand Down Expand Up @@ -404,6 +400,10 @@ BUILTIN_RUNTIME_CALL(IsOptionalType, "isOptional", "")
BUILTIN(Id, Name, Attrs)
#endif

/// condfail(Int1, RawPointer) -> ()
/// Triggers a runtime failure if the condition is true.
BUILTIN_MISC_OPERATION(CondFail, "condfail", "", Special)

/// Sizeof has type T.Type -> Int
BUILTIN_MISC_OPERATION(Sizeof, "sizeof", "n", Special)

Expand Down
6 changes: 3 additions & 3 deletions include/swift/SIL/SILBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -1775,15 +1775,15 @@ class SILBuilder {
//===--------------------------------------------------------------------===//

CondFailInst *createCondFail(SILLocation Loc, SILValue Operand,
bool Inverted = false) {
StringRef Message, bool Inverted = false) {
if (Inverted) {
SILType Ty = Operand->getType();
SILValue True(createIntegerLiteral(Loc, Ty, 1));
Operand =
createBuiltinBinaryFunction(Loc, "xor", Ty, Ty, {Operand, True});
}
return insert(new (getModule())
CondFailInst(getSILDebugLocation(Loc), Operand));
return insert(CondFailInst::create(getSILDebugLocation(Loc), Operand,
Message, getModule()));
}

BuiltinInst *createBuiltinTrap(SILLocation Loc) {
Expand Down
3 changes: 2 additions & 1 deletion include/swift/SIL/SILCloner.h
Original file line number Diff line number Diff line change
Expand Up @@ -2447,7 +2447,8 @@ SILCloner<ImplClass>::visitCondFailInst(CondFailInst *Inst) {
getBuilder().setCurrentDebugScope(getOpScope(Inst->getDebugScope()));
recordClonedInstruction(
Inst, getBuilder().createCondFail(getOpLocation(Inst->getLoc()),
getOpValue(Inst->getOperand())));
getOpValue(Inst->getOperand()),
Inst->getMessage()));
}

template<typename ImplClass>
Expand Down
22 changes: 18 additions & 4 deletions include/swift/SIL/SILInstruction.h
Original file line number Diff line number Diff line change
Expand Up @@ -6715,14 +6715,28 @@ class ProjectExistentialBoxInst
//===----------------------------------------------------------------------===//

/// Trigger a runtime failure if the given Int1 value is true.
class CondFailInst
///
/// Optionally cond_fail has a static failure message, which is displayed in the debugger in case the failure
/// is triggered.
class CondFailInst final
: public UnaryInstructionBase<SILInstructionKind::CondFailInst,
NonValueInstruction>
NonValueInstruction>,
private llvm::TrailingObjects<CondFailInst, char>
{
friend TrailingObjects;
friend SILBuilder;

CondFailInst(SILDebugLocation DebugLoc, SILValue Operand)
: UnaryInstructionBase(DebugLoc, Operand) {}
unsigned MessageSize;

CondFailInst(SILDebugLocation DebugLoc, SILValue Operand, StringRef Message);

static CondFailInst *create(SILDebugLocation DebugLoc, SILValue Operand,
StringRef Message, SILModule &M);

public:
StringRef getMessage() const {
return {getTrailingObjects<char>(), MessageSize};
}
};

//===----------------------------------------------------------------------===//
Expand Down
2 changes: 1 addition & 1 deletion include/swift/Serialization/ModuleFormat.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ const uint16_t SWIFTMODULE_VERSION_MAJOR = 0;
/// describe what change you made. The content of this comment isn't important;
/// it just ensures a conflict if two people change the module format.
/// Don't worry about adhering to the 80-column limit for this line.
const uint16_t SWIFTMODULE_VERSION_MINOR = 500; // dependency types for protocols
const uint16_t SWIFTMODULE_VERSION_MINOR = 501; // cond_fail messages

using DeclIDField = BCFixed<31>;

Expand Down
3 changes: 2 additions & 1 deletion lib/AST/Builtins.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1037,8 +1037,9 @@ static ValueDecl *getCanBeObjCClassOperation(ASTContext &Context,
static ValueDecl *getCondFailOperation(ASTContext &C, Identifier Id) {
// Int1 -> ()
auto CondTy = BuiltinIntegerType::get(1, C);
auto MsgTy = C.TheRawPointerType;
auto VoidTy = TupleType::getEmpty(C);
return getBuiltinFunction(Id, {CondTy}, VoidTy);
return getBuiltinFunction(Id, {CondTy, MsgTy}, VoidTy);
}

static ValueDecl *getAssertConfOperation(ASTContext &C, Identifier Id) {
Expand Down
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
28 changes: 27 additions & 1 deletion lib/ParseSIL/ParseSIL.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2635,6 +2635,33 @@ bool SILParser::parseSILInstruction(SILBuilder &B) {
break;
}

case SILInstructionKind::CondFailInst: {

if (parseTypedValueRef(Val, B))
return true;

SmallVector<char, 128> stringBuffer;
StringRef message;
if (P.consumeIf(tok::comma)) {
// Parse the string.
if (P.Tok.getKind() != tok::string_literal) {
P.diagnose(P.Tok, diag::expected_tok_in_sil_instr, "string");
return true;
}
SmallVector<Lexer::StringSegment, 1> segments;
P.L->getStringLiteralSegments(P.Tok, segments);
assert(segments.size() == 1);

P.consumeToken(tok::string_literal);
message = P.L->getEncodedStringSegment(segments.front(), stringBuffer);
}
if (parseSILDebugLocation(InstLoc, B))
return true;

ResultVal = B.createCondFail(InstLoc, Val, message);
break;
}

case SILInstructionKind::AllocValueBufferInst: {
SILType Ty;
if (parseSILType(Ty) ||
Expand Down Expand Up @@ -2887,7 +2914,6 @@ bool SILParser::parseSILInstruction(SILBuilder &B) {
UNARY_INSTRUCTION(DestroyAddr)
UNARY_INSTRUCTION(CopyValue)
UNARY_INSTRUCTION(DestroyValue)
UNARY_INSTRUCTION(CondFail)
UNARY_INSTRUCTION(EndBorrow)
UNARY_INSTRUCTION(DestructureStruct)
UNARY_INSTRUCTION(DestructureTuple)
Expand Down
1 change: 1 addition & 0 deletions lib/SIL/OperandOwnership.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -953,6 +953,7 @@ ANY_OWNERSHIP_BUILTIN(AtomicRMW)
ANY_OWNERSHIP_BUILTIN(AtomicStore)
ANY_OWNERSHIP_BUILTIN(BitCast)
ANY_OWNERSHIP_BUILTIN(CanBeObjCClass)
ANY_OWNERSHIP_BUILTIN(CondFail)
ANY_OWNERSHIP_BUILTIN(CmpXChg)
ANY_OWNERSHIP_BUILTIN(CondUnreachable)
ANY_OWNERSHIP_BUILTIN(CopyArray)
Expand Down
15 changes: 15 additions & 0 deletions lib/SIL/SILInstructions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -773,6 +773,21 @@ StringLiteralInst *StringLiteralInst::create(SILDebugLocation Loc,
return ::new (buf) StringLiteralInst(Loc, text, encoding, Ty);
}

CondFailInst::CondFailInst(SILDebugLocation DebugLoc, SILValue Operand,
StringRef Message)
: UnaryInstructionBase(DebugLoc, Operand),
MessageSize(Message.size()) {
memcpy(getTrailingObjects<char>(), Message.data(), Message.size());
}

CondFailInst *CondFailInst::create(SILDebugLocation DebugLoc, SILValue Operand,
StringRef Message, SILModule &M) {

auto Size = totalSizeToAlloc<char>(Message.size());
auto Buffer = M.allocateInst(Size, alignof(CondFailInst));
return ::new (Buffer) CondFailInst(DebugLoc, Operand, Message);
}

uint64_t StringLiteralInst::getCodeUnitCount() {
auto E = unsigned(Encoding::UTF16);
if (SILInstruction::Bits.StringLiteralInst.TheEncoding == E)
Expand Down
3 changes: 2 additions & 1 deletion lib/SIL/SILPrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1866,7 +1866,8 @@ class SILPrinter : public SILInstructionVisitor<SILPrinter> {
}

void visitCondFailInst(CondFailInst *FI) {
*this << getIDAndType(FI->getOperand());
*this << getIDAndType(FI->getOperand()) << ", "
<< QuotedString(FI->getMessage());
}

void visitIndexAddrInst(IndexAddrInst *IAI) {
Expand Down
1 change: 1 addition & 0 deletions lib/SIL/ValueOwnership.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -378,6 +378,7 @@ CONSTANT_OWNERSHIP_BUILTIN(Any, And)
CONSTANT_OWNERSHIP_BUILTIN(Any, AssumeNonNegative)
CONSTANT_OWNERSHIP_BUILTIN(Any, AssumeTrue)
CONSTANT_OWNERSHIP_BUILTIN(Any, BitCast)
CONSTANT_OWNERSHIP_BUILTIN(Any, CondFail)
CONSTANT_OWNERSHIP_BUILTIN(Any, ExactSDiv)
CONSTANT_OWNERSHIP_BUILTIN(Any, ExactUDiv)
CONSTANT_OWNERSHIP_BUILTIN(Any, FAdd)
Expand Down
Loading