diff --git a/include/swift/AST/Builtins.def b/include/swift/AST/Builtins.def index 5049ad631a048..5c72ed85aaf7f 100644 --- a/include/swift/AST/Builtins.def +++ b/include/swift/AST/Builtins.def @@ -318,6 +318,11 @@ BUILTIN_SIL_OPERATION(EndUnpairedAccess, "endUnpairedAccess", Special) /// Triggers a runtime failure if the condition is true. BUILTIN_SIL_OPERATION(CondFail, "condfail", Special) +/// condfail(Int1, RawPointer) -> () +/// Triggers a runtime failure if the condition is true, with a +/// reason message to encode in debug info +BUILTIN_SIL_OPERATION(CondFailMessage, "condfail_message", Special) + /// fixLifetime(T) -> () /// Fixes the lifetime of any heap references in a value. BUILTIN_SIL_OPERATION(FixLifetime, "fixLifetime", Special) diff --git a/include/swift/AST/DiagnosticsIRGen.def b/include/swift/AST/DiagnosticsIRGen.def index 9cf446576f3c2..ddbe932c6a633 100644 --- a/include/swift/AST/DiagnosticsIRGen.def +++ b/include/swift/AST/DiagnosticsIRGen.def @@ -67,6 +67,9 @@ ERROR(alignment_more_than_maximum,none, "@_alignment cannot increase alignment above maximum alignment of %0", (unsigned)) +WARNING(irgen_condfail_non_literal_message,none, + "condfail message did not evaluate to a literal string", ()) + #ifndef DIAG_NO_UNDEF # if defined(DIAG) # undef DIAG diff --git a/include/swift/SIL/SILBuilder.h b/include/swift/SIL/SILBuilder.h index 6b24c40e73e1a..f80dd702c7640 100644 --- a/include/swift/SIL/SILBuilder.h +++ b/include/swift/SIL/SILBuilder.h @@ -1774,16 +1774,21 @@ class SILBuilder { // Runtime failure //===--------------------------------------------------------------------===// - CondFailInst *createCondFail(SILLocation Loc, SILValue Operand, - bool Inverted = false) { + CondFailInst *createCondFail(SILLocation Loc, + SILValue Condition, + bool Inverted = false, + SILValue Message = SILValue(), + ArrayRef MessageArguments = {}) { if (Inverted) { - SILType Ty = Operand->getType(); + SILType Ty = Condition->getType(); SILValue True(createIntegerLiteral(Loc, Ty, 1)); - Operand = - createBuiltinBinaryFunction(Loc, "xor", Ty, Ty, {Operand, True}); + Condition = + createBuiltinBinaryFunction(Loc, "xor", Ty, Ty, {Condition, True}); } - return insert(new (getModule()) - CondFailInst(getSILDebugLocation(Loc), Operand)); + + return insert(CondFailInst::create(getModule(), + getSILDebugLocation(Loc), Condition, + Message, MessageArguments)); } BuiltinInst *createBuiltinTrap(SILLocation Loc) { diff --git a/include/swift/SIL/SILCloner.h b/include/swift/SIL/SILCloner.h index 223facefbabc5..df89e29c4f0b4 100644 --- a/include/swift/SIL/SILCloner.h +++ b/include/swift/SIL/SILCloner.h @@ -2447,7 +2447,12 @@ SILCloner::visitCondFailInst(CondFailInst *Inst) { getBuilder().setCurrentDebugScope(getOpScope(Inst->getDebugScope())); recordClonedInstruction( Inst, getBuilder().createCondFail(getOpLocation(Inst->getLoc()), - getOpValue(Inst->getOperand()))); + getOpValue(Inst->getCondition()), + /*inverted*/ false, + Inst->getMessage() + ? getOpValue(Inst->getMessage()) + : SILValue(), + getOpValueArray<8>(Inst->getMessageArguments()))); } template diff --git a/include/swift/SIL/SILInstruction.h b/include/swift/SIL/SILInstruction.h index 75668acc62a26..c87fa1e0305a7 100644 --- a/include/swift/SIL/SILInstruction.h +++ b/include/swift/SIL/SILInstruction.h @@ -6715,14 +6715,57 @@ class ProjectExistentialBoxInst //===----------------------------------------------------------------------===// /// Trigger a runtime failure if the given Int1 value is true. -class CondFailInst - : public UnaryInstructionBase { friend SILBuilder; - - CondFailInst(SILDebugLocation DebugLoc, SILValue Operand) - : UnaryInstructionBase(DebugLoc, Operand) {} + + static SmallVector + makeOperandsArray(SILValue Condition, SILValue Message, + ArrayRef MessageArguments) { + SmallVector result; + result.push_back(Condition); + if (Message) + result.push_back(Message); + assert(Message || MessageArguments.empty() + && "can only have message arguments with a message"); + result.append(MessageArguments.begin(), MessageArguments.end()); + return result; + } + + CondFailInst(SILDebugLocation DebugLoc, SILValue Condition, + SILValue Message = SILValue(), + ArrayRef MessageArguments = {}) + : InstructionBaseWithTrailingOperands( + makeOperandsArray(Condition, Message, MessageArguments), + DebugLoc) {} + + enum { Condition, Message, FirstMessageArgument }; + + static CondFailInst *create(SILModule &M, + SILDebugLocation DebugLoc, SILValue Condition, + SILValue Message = SILValue(), + ArrayRef MessageArguments = {}); + +public: + SILValue getCondition() const { return getAllOperands()[Condition].get(); } + + SILValue getMessage() const { + if (getAllOperands().size() > Message) { + return getAllOperands()[Message].get(); + } + return SILValue(); + } + + OperandValueArrayRef getMessageArguments() const { + if (getAllOperands().size() > FirstMessageArgument) { + return OperandValueArrayRef(getAllOperands().slice(FirstMessageArgument)); + } + return {}; + } }; //===----------------------------------------------------------------------===// diff --git a/include/swift/Serialization/ModuleFormat.h b/include/swift/Serialization/ModuleFormat.h index 501514650f58e..b8f2caecd8d32 100644 --- a/include/swift/Serialization/ModuleFormat.h +++ b/include/swift/Serialization/ModuleFormat.h @@ -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 message using DeclIDField = BCFixed<31>; diff --git a/lib/AST/Builtins.cpp b/lib/AST/Builtins.cpp index 546f05a6217f9..185ee9ebc2823 100644 --- a/lib/AST/Builtins.cpp +++ b/lib/AST/Builtins.cpp @@ -1035,12 +1035,20 @@ static ValueDecl *getCanBeObjCClassOperation(ASTContext &Context, } static ValueDecl *getCondFailOperation(ASTContext &C, Identifier Id) { - // Int1 -> () + // (Int1) -> () auto CondTy = BuiltinIntegerType::get(1, C); auto VoidTy = TupleType::getEmpty(C); return getBuiltinFunction(Id, {CondTy}, VoidTy); } +static ValueDecl *getCondFailMessageOperation(ASTContext &C, Identifier Id) { + // (Int1, RawPointer) -> () + auto CondTy = BuiltinIntegerType::get(1, C); + auto MsgTy = C.TheRawPointerType; + auto VoidTy = TupleType::getEmpty(C); + return getBuiltinFunction(Id, {CondTy, MsgTy}, VoidTy); +} + static ValueDecl *getAssertConfOperation(ASTContext &C, Identifier Id) { // () -> Int32 auto Int32Ty = BuiltinIntegerType::get(32, C); @@ -1895,6 +1903,9 @@ ValueDecl *swift::getBuiltinValueDecl(ASTContext &Context, Identifier Id) { case BuiltinValueKind::CondFail: return getCondFailOperation(Context, Id); + case BuiltinValueKind::CondFailMessage: + return getCondFailMessageOperation(Context, Id); + case BuiltinValueKind::AssertConf: return getAssertConfOperation(Context, Id); diff --git a/lib/IRGen/IRGenDebugInfo.cpp b/lib/IRGen/IRGenDebugInfo.cpp index c6437285892b7..55f4e558e7ba7 100644 --- a/lib/IRGen/IRGenDebugInfo.cpp +++ b/lib/IRGen/IRGenDebugInfo.cpp @@ -151,7 +151,9 @@ class IRGenDebugInfoImpl : public IRGenDebugInfo { void clearLoc(IRBuilder &Builder); void pushLoc(); void popLoc(); - void setInlinedTrapLocation(IRBuilder &Builder, const SILDebugScope *Scope); + void setInlinedTrapLocation(IRBuilder &Builder, const SILDebugScope *Scope, + SILLocation loc, + StringRef message); void setEntryPointLoc(IRBuilder &Builder); llvm::DIScope *getEntryPointFn(); llvm::DIScope *getOrCreateScope(const SILDebugScope *DS); @@ -1848,29 +1850,58 @@ void IRGenDebugInfoImpl::popLoc() { std::tie(LastDebugLoc, LastScope) = LocationStack.pop_back_val(); } -/// This is done for WinDbg to avoid having two non-contiguous sets of -/// instructions because the ``@llvm.trap`` instruction gets placed at the end -/// of the function. void IRGenDebugInfoImpl::setInlinedTrapLocation(IRBuilder &Builder, - const SILDebugScope *Scope) { - if (Opts.DebugInfoFormat != IRGenDebugInfoFormat::CodeView) - return; - - // The @llvm.trap could be inlined into a chunk of code that was also inlined. - // If this is the case then simply using the LastScope's location would - // generate debug info that claimed Function A owned Block X and Block X - // thought it was owned by Function B. Therefore, we need to find the last - // inlined scope to point to. - const SILDebugScope *TheLastScope = LastScope; - while (TheLastScope->InlinedCallSite && - TheLastScope->InlinedCallSite != TheLastScope) { - TheLastScope = TheLastScope->InlinedCallSite; - } - auto LastLocation = llvm::DebugLoc::get( - LastDebugLoc.Line, LastDebugLoc.Column, getOrCreateScope(TheLastScope)); - // FIXME: This location should point to stdlib instead of being artificial. - auto DL = llvm::DebugLoc::get(0, 0, getOrCreateScope(Scope), LastLocation); - Builder.SetCurrentDebugLocation(DL); + const SILDebugScope *Scope, + SILLocation loc, + StringRef message) { + llvm::DebugLoc DL; + // If the trap came with a message, create an artificial inline scope inside + // the scope with the message. + if (!message.empty()) { + auto realScope = getOrCreateScope(LastScope); + auto trapScope = llvm::DISubprogram::getDistinct(IGM.LLVMContext, + realScope->getFile(), + message, "", + realScope->getFile(), + LastDebugLoc.Line, + /*type*/ nullptr, + LastDebugLoc.Line, + /*containingType*/ llvm::DITypeRef(), + /*virtualIndex*/0, + /*thisAdjustment*/0, + llvm::DINode::DIFlags(), + llvm::DISubprogram::DISPFlags::SPFlagDefinition, + TheCU); + DL = llvm::DebugLoc::get(LastDebugLoc.Line, + LastDebugLoc.Column, + trapScope, + llvm::DebugLoc::get(LastDebugLoc.Line, + LastDebugLoc.Column, + getOrCreateScope(LastScope))); + } else if (Opts.DebugInfoFormat == IRGenDebugInfoFormat::CodeView) { + // This is done for WinDbg to avoid having two non-contiguous sets of + // instructions because the ``@llvm.trap`` instruction gets placed at the end + // of the function. + // + // The @llvm.trap could be inlined into a chunk of code that was also inlined. + // If this is the case then simply using the LastScope's location would + // generate debug info that claimed Function A owned Block X and Block X + // thought it was owned by Function B. Therefore, we need to find the last + // inlined scope to point to. + const SILDebugScope *TheLastScope = LastScope; + while (TheLastScope->InlinedCallSite && + TheLastScope->InlinedCallSite != TheLastScope) { + TheLastScope = TheLastScope->InlinedCallSite; + } + auto LastLocation = llvm::DebugLoc::get( + LastDebugLoc.Line, LastDebugLoc.Column, getOrCreateScope(TheLastScope)); + // FIXME: This location should point to stdlib instead of being artificial. + DL = llvm::DebugLoc::get(0, 0, getOrCreateScope(Scope), LastLocation); + } + + if (DL) { + Builder.SetCurrentDebugLocation(DL); + } } void IRGenDebugInfoImpl::setEntryPointLoc(IRBuilder &Builder) { @@ -2333,9 +2364,13 @@ void IRGenDebugInfo::popLoc() { } void IRGenDebugInfo::setInlinedTrapLocation(IRBuilder &Builder, - const SILDebugScope *Scope) { + const SILDebugScope *Scope, + SILLocation loc, + StringRef message) { static_cast(this)->setInlinedTrapLocation(Builder, - Scope); + Scope, + loc, + message); } void IRGenDebugInfo::setEntryPointLoc(IRBuilder &Builder) { diff --git a/lib/IRGen/IRGenDebugInfo.h b/lib/IRGen/IRGenDebugInfo.h index adac1ae134866..271c7f6e3f9af 100644 --- a/lib/IRGen/IRGenDebugInfo.h +++ b/lib/IRGen/IRGenDebugInfo.h @@ -73,7 +73,9 @@ class IRGenDebugInfo { /// In CodeView, since zero is not an artificial location, we emit the /// location of the unified trap block at the end of the fuction as an /// artificial inline location pointing to the user's instruction. - void setInlinedTrapLocation(IRBuilder &Builder, const SILDebugScope *Scope); + void setInlinedTrapLocation(IRBuilder &Builder, const SILDebugScope *Scope, + SILLocation loc, + StringRef message); /// Set the location for SWIFT_ENTRY_POINT_FUNCTION. void setEntryPointLoc(IRBuilder &Builder); diff --git a/lib/IRGen/IRGenSIL.cpp b/lib/IRGen/IRGenSIL.cpp index 20ffaa02cf1b9..488fcfbcdfb4c 100644 --- a/lib/IRGen/IRGenSIL.cpp +++ b/lib/IRGen/IRGenSIL.cpp @@ -35,6 +35,7 @@ #include "swift/Basic/Range.h" #include "swift/Basic/STLExtras.h" #include "swift/AST/ASTContext.h" +#include "swift/AST/DiagnosticsIRGen.h" #include "swift/AST/IRGenOptions.h" #include "swift/AST/Pattern.h" #include "swift/AST/ParameterList.h" @@ -5426,7 +5427,7 @@ void IRGenSILFunction::visitDestroyAddrInst(swift::DestroyAddrInst *i) { } void IRGenSILFunction::visitCondFailInst(swift::CondFailInst *i) { - Explosion e = getLoweredExplosion(i->getOperand()); + Explosion e = getLoweredExplosion(i->getCondition()); llvm::Value *cond = e.claimNext(); // Emit individual fail blocks so that we can map the failure back to a source @@ -5435,12 +5436,28 @@ void IRGenSILFunction::visitCondFailInst(swift::CondFailInst *i) { llvm::BasicBlock *contBB = llvm::BasicBlock::Create(IGM.getLLVMContext()); Builder.CreateCondBr(cond, failBB, contBB); Builder.emitBlock(failBB); - if (IGM.DebugInfo) - // If we are emitting DWARF, this does nothing. Otherwise the ``llvm.trap`` - // instruction emitted from ``Builtin.condfail`` should have an inlined - // debug location. This is because zero is not an artificial line location - // in CodeView. - IGM.DebugInfo->setInlinedTrapLocation(Builder, i->getDebugScope()); + if (IGM.DebugInfo) { + // Give the ``Builtin.condfail`` an inlined debug location. + + // If the failure has a literal string message associated, use it as the + // function name for the inlined location, so that backtraces show the + // message. + // TODO: Also lower the message arguments as frame variables in the inline + // scope. + SmallString<128> message; + if (auto messageValue = i->getMessage()) { + if (auto messageLiteral = dyn_cast(messageValue)) { + message = messageLiteral->getValue(); + } else { + IGM.Context.Diags.diagnose(i->getLoc().getSourceLoc(), + diag::irgen_condfail_non_literal_message); + } + } + + IGM.DebugInfo->setInlinedTrapLocation(Builder, i->getDebugScope(), + i->getLoc(), message); + } + emitTrap(/*EmitUnreachable=*/true); Builder.emitBlock(contBB); FailBBs.push_back(failBB); diff --git a/lib/ParseSIL/ParseSIL.cpp b/lib/ParseSIL/ParseSIL.cpp index 6adc7b8f2d685..e7fb6ee2ca96c 100644 --- a/lib/ParseSIL/ParseSIL.cpp +++ b/lib/ParseSIL/ParseSIL.cpp @@ -2887,7 +2887,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) @@ -2911,6 +2910,39 @@ bool SILParser::parseSILInstruction(SILBuilder &B) { #undef UNARY_INSTRUCTION #undef REFCOUNTING_INSTRUCTION + case SILInstructionKind::CondFailInst: { + SILValue condition; + if (parseTypedValueRef(condition, B)) + return true; + SILValue message; + SmallVector messageArgs; + if (P.consumeIf(tok::comma)) { + if (parseTypedValueRef(message, B)) + return true; + + if (P.consumeIf(tok::l_paren)) { + while (true) { + SILValue arg; + if (parseTypedValueRef(arg, B)) + return true; + messageArgs.push_back(arg); + if (!P.consumeIf(tok::comma)) + break; + } + if (!P.consumeIf(tok::r_paren)) + return true; + } + } + + if (parseSILDebugLocation(InstLoc, B)) + return true; + + ResultVal = B.createCondFail(InstLoc, condition, /*inverted*/ false, + message, messageArgs); + + break; + } + case SILInstructionKind::IsEscapingClosureInst: { bool IsObjcVerifcationType = false; if (parseSILOptional(IsObjcVerifcationType, *this, "objc")) diff --git a/lib/SIL/SILInstructions.cpp b/lib/SIL/SILInstructions.cpp index 8137a6468791c..31b1e23ec6fff 100644 --- a/lib/SIL/SILInstructions.cpp +++ b/lib/SIL/SILInstructions.cpp @@ -371,6 +371,18 @@ BuiltinInst::BuiltinInst(SILDebugLocation Loc, Identifier Name, Substitutions(Subs) { } +CondFailInst *CondFailInst::create(SILModule &M, + SILDebugLocation DebugLoc, + SILValue Condition, + SILValue Message, + ArrayRef MessageArguments) { + auto Size = totalSizeToAlloc(1 + (Message ? 1 : 0) + + MessageArguments.size()); + auto Buffer = M.allocateInst(Size, alignof(CondFailInst)); + return ::new (Buffer) CondFailInst( + DebugLoc, Condition, Message, MessageArguments); +} + InitBlockStorageHeaderInst * InitBlockStorageHeaderInst::create(SILFunction &F, SILDebugLocation DebugLoc, SILValue BlockStorage, diff --git a/lib/SIL/SILPrinter.cpp b/lib/SIL/SILPrinter.cpp index 95c4bad6ed891..3051086da5f24 100644 --- a/lib/SIL/SILPrinter.cpp +++ b/lib/SIL/SILPrinter.cpp @@ -1866,7 +1866,23 @@ class SILPrinter : public SILInstructionVisitor { } void visitCondFailInst(CondFailInst *FI) { - *this << getIDAndType(FI->getOperand()); + *this << getIDAndType(FI->getCondition()); + + if (auto message = FI->getMessage()) { + *this << ", " << getIDAndType(message); + + if (!FI->getMessageArguments().empty()) { + *this << '('; + interleave(FI->getMessageArguments().begin(), + FI->getMessageArguments().end(), + [&](SILValue v) { + *this << getIDAndType(v); + }, [&]{ + *this << ", "; + }); + *this << ')'; + } + } } void visitIndexAddrInst(IndexAddrInst *IAI) { diff --git a/lib/SIL/SILVerifier.cpp b/lib/SIL/SILVerifier.cpp index 084d74df3a912..d8d37ac9e0b6a 100644 --- a/lib/SIL/SILVerifier.cpp +++ b/lib/SIL/SILVerifier.cpp @@ -3861,9 +3861,11 @@ class SILVerifier : public SILVerifierBase { } void checkCondFailInst(CondFailInst *CFI) { - require(CFI->getOperand()->getType() + require(CFI->getCondition()->getType() == SILType::getBuiltinIntegerType(1, F.getASTContext()), "cond_fail operand must be a Builtin.Int1"); + +#warning "todo: message validation" } void checkReturnInst(ReturnInst *RI) { diff --git a/lib/SILGen/SILGenBuiltin.cpp b/lib/SILGen/SILGenBuiltin.cpp index cc1c9dcd866ca..9a1da5ee4d56e 100644 --- a/lib/SILGen/SILGenBuiltin.cpp +++ b/lib/SILGen/SILGenBuiltin.cpp @@ -630,6 +630,19 @@ static ManagedValue emitBuiltinCondFail(SILGenFunction &SGF, return ManagedValue::forUnmanaged(SGF.emitEmptyTuple(loc)); } +/// Specialized emitter for Builtin.condfail_message. +static ManagedValue emitBuiltinCondFailMessage(SILGenFunction &SGF, + SILLocation loc, + SubstitutionMap substitutions, + ArrayRef args, + SGFContext C) { + assert(args.size() == 2 && "condfail should be given one argument"); + + SGF.B.createCondFail(loc, args[0].getUnmanagedValue(), /*invert*/ false, + args[1].getUnmanagedValue()); + return ManagedValue::forUnmanaged(SGF.emitEmptyTuple(loc)); +} + /// Specialized emitter for Builtin.castReference. static ManagedValue emitBuiltinCastReference(SILGenFunction &SGF, diff --git a/lib/SILOptimizer/LoopTransforms/ArrayBoundsCheckOpts.cpp b/lib/SILOptimizer/LoopTransforms/ArrayBoundsCheckOpts.cpp index ccecaaa25f939..e396074286a68 100644 --- a/lib/SILOptimizer/LoopTransforms/ArrayBoundsCheckOpts.cpp +++ b/lib/SILOptimizer/LoopTransforms/ArrayBoundsCheckOpts.cpp @@ -486,7 +486,7 @@ static bool isSignedLessEqual(SILValue Start, SILValue End, SILBasicBlock &BB) { for (auto &Inst : BB) if (auto CF = dyn_cast(&Inst)) { // Try to match a cond_fail on "XOR , (SLE Start, End), 1". - if (match(CF->getOperand(), + if (match(CF->getCondition(), m_ApplyInst(BuiltinValueKind::Xor, m_ApplyInst(BuiltinValueKind::ICMP_SLE, m_Specific(Start), @@ -496,14 +496,14 @@ static bool isSignedLessEqual(SILValue Start, SILValue End, SILBasicBlock &BB) { // Inclusive ranges will have a check on the upper value (before adding // one). if (PreInclusiveEnd) { - if (match(CF->getOperand(), + if (match(CF->getCondition(), m_ApplyInst(BuiltinValueKind::Xor, m_ApplyInst(BuiltinValueKind::ICMP_SLE, m_Specific(Start), m_Specific(PreInclusiveEnd)), m_One()))) IsPreInclusiveEndLEQ = true; - if (match(CF->getOperand(), + if (match(CF->getCondition(), m_ApplyInst(BuiltinValueKind::Xor, m_ApplyInst(BuiltinValueKind::ICMP_SGT, m_Specific(End), diff --git a/lib/SILOptimizer/SILCombiner/SILCombinerMiscVisitors.cpp b/lib/SILOptimizer/SILCombiner/SILCombinerMiscVisitors.cpp index 0cf8791604477..a029431089263 100644 --- a/lib/SILOptimizer/SILCombiner/SILCombinerMiscVisitors.cpp +++ b/lib/SILOptimizer/SILCombiner/SILCombinerMiscVisitors.cpp @@ -749,7 +749,7 @@ SILInstruction *SILCombiner::visitCondFailInst(CondFailInst *CFI) { if (RemoveCondFails) return eraseInstFromFunction(*CFI); - auto *I = dyn_cast(CFI->getOperand()); + auto *I = dyn_cast(CFI->getCondition()); if (!I) return nullptr; diff --git a/lib/SILOptimizer/Transforms/CSE.cpp b/lib/SILOptimizer/Transforms/CSE.cpp index 79d57506eba23..682520c5208f2 100644 --- a/lib/SILOptimizer/Transforms/CSE.cpp +++ b/lib/SILOptimizer/Transforms/CSE.cpp @@ -194,7 +194,7 @@ class HashVisitor : public SILInstructionVisitor { } hash_code visitCondFailInst(CondFailInst *X) { - return llvm::hash_combine(X->getKind(), X->getOperand()); + return llvm::hash_combine(X->getKind(), X->getCondition()); } hash_code visitClassMethodInst(ClassMethodInst *X) { diff --git a/lib/SILOptimizer/Transforms/ConditionForwarding.cpp b/lib/SILOptimizer/Transforms/ConditionForwarding.cpp index 118f62cef7448..02374ffe537a6 100644 --- a/lib/SILOptimizer/Transforms/ConditionForwarding.cpp +++ b/lib/SILOptimizer/Transforms/ConditionForwarding.cpp @@ -134,7 +134,7 @@ static bool hasNoRelevantSideEffects(SILBasicBlock *BB) { // Even if we move the whole block across other code, it's still // guaranteed that the cond_fail is executed before the result of the // builtin is used. - auto *TEI = dyn_cast(CF->getOperand()); + auto *TEI = dyn_cast(CF->getCondition()); if (!TEI) return false; auto *BI = dyn_cast(TEI->getOperand()); diff --git a/lib/SILOptimizer/Transforms/DeadCodeElimination.cpp b/lib/SILOptimizer/Transforms/DeadCodeElimination.cpp index c8e2bee00b958..48f0097118aec 100644 --- a/lib/SILOptimizer/Transforms/DeadCodeElimination.cpp +++ b/lib/SILOptimizer/Transforms/DeadCodeElimination.cpp @@ -220,7 +220,7 @@ static SILInstruction *getProducer(CondFailInst *CFI) { // %1 = builtin "some_operation_with_overflow" // %2 = tuple_extract %1 // %3 = cond_fail %2 - SILValue FailCond = CFI->getOperand(); + SILValue FailCond = CFI->getCondition(); if (auto *TEI = dyn_cast(FailCond)) { if (auto *BI = dyn_cast(TEI->getOperand())) { return BI; diff --git a/lib/SILOptimizer/Transforms/MergeCondFail.cpp b/lib/SILOptimizer/Transforms/MergeCondFail.cpp index e2065864a226e..f6e31a7f8efbe 100644 --- a/lib/SILOptimizer/Transforms/MergeCondFail.cpp +++ b/lib/SILOptimizer/Transforms/MergeCondFail.cpp @@ -26,7 +26,7 @@ using namespace swift; /// Return true if the operand of the cond_fail instruction looks like /// the overflow bit of an arithmetic instruction. static bool hasOverflowConditionOperand(CondFailInst *CFI) { - if (auto *TEI = dyn_cast(CFI->getOperand())) + if (auto *TEI = dyn_cast(CFI->getCondition())) if (isa(TEI->getOperand())) return true; return false; @@ -101,7 +101,7 @@ class MergeCondFailInsts : public SILFunctionTransform { // Merge conditions and remove the merged cond_fail instructions. for (unsigned I = 0, E = CondFailToMerge.size(); I != E; ++I) { - auto CurCond = CondFailToMerge[I]->getOperand(); + auto CurCond = CondFailToMerge[I]->getCondition(); if (MergedCond) { CurCond = Builder.createBuiltinBinaryFunction(Loc, "or", CurCond->getType(), diff --git a/lib/SILOptimizer/Transforms/RedundantOverflowCheckRemoval.cpp b/lib/SILOptimizer/Transforms/RedundantOverflowCheckRemoval.cpp index 483ec3eab3d8c..c68752f95940d 100644 --- a/lib/SILOptimizer/Transforms/RedundantOverflowCheckRemoval.cpp +++ b/lib/SILOptimizer/Transforms/RedundantOverflowCheckRemoval.cpp @@ -499,7 +499,7 @@ class RedundantOverflowCheckRemovalPass : public SILFunctionTransform { bool tryToRemoveCondFail(CondFailInst *CFI) { // Extract the arithmetic operation from the condfail. - auto *TEI = dyn_cast(CFI->getOperand()); + auto *TEI = dyn_cast(CFI->getCondition()); if (!TEI) return false; auto *BI = dyn_cast(TEI->getOperand()); if (!BI) return false; @@ -623,7 +623,7 @@ class RedundantOverflowCheckRemovalPass : public SILFunctionTransform { void registerCondFailFormula(CondFailInst *CFI) { // Extract the arithmetic operation from the condfail. - if (auto *TEI = dyn_cast(CFI->getOperand())) { + if (auto *TEI = dyn_cast(CFI->getCondition())) { auto *BI = dyn_cast(TEI->getOperand()); if (!BI) return; @@ -655,7 +655,7 @@ class RedundantOverflowCheckRemovalPass : public SILFunctionTransform { // We can figure out that x - y will not underflow because of x >= y // %usub_underflow = tuple_extract %usub_result : $(Builtin.Int64, Builtin.Int1), 1 // cond_fail %usub_underflow : $Builtin.Int1 - if (auto *CMP = dyn_cast(CFI->getOperand())) { + if (auto *CMP = dyn_cast(CFI->getCondition())) { SILBasicBlock *TrueBB = nullptr; SILBasicBlock *FalseBB = CMP->getParent(); addComparisonRelation(CMP, TrueBB, FalseBB); diff --git a/lib/SILOptimizer/Transforms/SimplifyCFG.cpp b/lib/SILOptimizer/Transforms/SimplifyCFG.cpp index 38a0c4cbf756f..bf013dce834bd 100644 --- a/lib/SILOptimizer/Transforms/SimplifyCFG.cpp +++ b/lib/SILOptimizer/Transforms/SimplifyCFG.cpp @@ -1384,14 +1384,14 @@ static CondFailInst *getUnConditionalFail(SILBasicBlock *BB, SILValue Cond, return nullptr; // The simple case: check if it is a "cond_fail 1". - auto *IL = dyn_cast(CondFail->getOperand()); + auto *IL = dyn_cast(CondFail->getCondition()); if (IL && IL->getValue() != 0) return CondFail; // Check if the cond_fail has the same condition as the cond_br in the // predecessor block. Cond = skipInvert(Cond, Inverted, false); - SILValue CondFailCond = skipInvert(CondFail->getOperand(), Inverted, false); + SILValue CondFailCond = skipInvert(CondFail->getCondition(), Inverted, false); if (Cond == CondFailCond && !Inverted) return CondFail; return nullptr; @@ -2455,7 +2455,7 @@ static bool tryMoveCondFailToPreds(SILBasicBlock *BB) { // want to the optimization if the condition gets dead after moving the // cond_fail. bool inverted = false; - SILValue cond = skipInvert(CFI->getOperand(), inverted, true); + SILValue cond = skipInvert(CFI->getCondition(), inverted, true); if (!cond) return false; diff --git a/lib/SILOptimizer/Utils/Local.cpp b/lib/SILOptimizer/Utils/Local.cpp index 53b1e762081df..c3fc0cecf9afd 100644 --- a/lib/SILOptimizer/Utils/Local.cpp +++ b/lib/SILOptimizer/Utils/Local.cpp @@ -114,7 +114,7 @@ swift::isInstructionTriviallyDead(SILInstruction *I) { // condfail instructions that obviously can't fail are dead. if (auto *CFI = dyn_cast(I)) - if (auto *ILI = dyn_cast(CFI->getOperand())) + if (auto *ILI = dyn_cast(CFI->getCondition())) if (!ILI->getValue()) return true; diff --git a/lib/Serialization/DeserializeSIL.cpp b/lib/Serialization/DeserializeSIL.cpp index 8ec5b6bc9460f..6bc1c5aa37005 100644 --- a/lib/Serialization/DeserializeSIL.cpp +++ b/lib/Serialization/DeserializeSIL.cpp @@ -1664,7 +1664,6 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, REFCOUNTING_INSTRUCTION(StrongRetain##Name) \ UNARY_INSTRUCTION(Copy##Name##Value) #include "swift/AST/ReferenceStorage.def" - UNARY_INSTRUCTION(CondFail) REFCOUNTING_INSTRUCTION(RetainValue) REFCOUNTING_INSTRUCTION(RetainValueAddr) REFCOUNTING_INSTRUCTION(UnmanagedRetainValue) @@ -1697,6 +1696,30 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, #undef UNARY_INSTRUCTION #undef REFCOUNTING_INSTRUCTION + case SILInstructionKind::CondFailInst: { + assert(RecordKind == SIL_ONE_TYPE_VALUES + && "Layout should be OneTypeValues."); + assert((ListOfValues.size() - 2) % 3 == 0 && "wrong number of values"); + + SILValue condition = getLocalValue(ListOfValues[0], + SILType::getBuiltinIntegerType(1, SILMod.getASTContext())); + SILValue message; + SmallVector messageArgs; + if (auto messageID = ListOfValues[1]) { + message = getLocalValue(messageID, + getSILType(MF->getType(TyID), (SILValueCategory)TyCategory)); + for (unsigned i = 2; i < ListOfValues.size(); i += 3) { + SILValue arg = getLocalValue(ListOfValues[i], + getSILType(MF->getType(ListOfValues[i+1]), + (SILValueCategory)ListOfValues[i+2])); + messageArgs.push_back(arg); + } + } + + ResultVal = Builder.createCondFail(Loc, condition, /*invert*/ false, + message, messageArgs); + break; + } case SILInstructionKind::IsEscapingClosureInst: { assert(RecordKind == SIL_ONE_OPERAND && "Layout should be OneOperand."); unsigned verificationType = Attr; diff --git a/lib/Serialization/SerializeSIL.cpp b/lib/Serialization/SerializeSIL.cpp index a5cc2e677fe25..c77a6c13d8fc2 100644 --- a/lib/Serialization/SerializeSIL.cpp +++ b/lib/Serialization/SerializeSIL.cpp @@ -1230,7 +1230,6 @@ void SILSerializer::writeSILInstruction(const SILInstruction &SI) { NEVER_LOADABLE_CHECKED_REF_STORAGE(Name, "...") \ ALWAYS_LOADABLE_CHECKED_REF_STORAGE(Name, "...") #include "swift/AST/ReferenceStorage.def" - case SILInstructionKind::CondFailInst: case SILInstructionKind::RetainValueInst: case SILInstructionKind::DestructureStructInst: case SILInstructionKind::DestructureTupleInst: @@ -1286,6 +1285,35 @@ void SILSerializer::writeSILInstruction(const SILInstruction &SI) { writeOneOperandLayout(SI.getKind(), Attr, SI.getOperand(0)); break; } + case SILInstructionKind::CondFailInst: { + auto CFI = dyn_cast(&SI); + SmallVector values; + + auto conditionValue = addValueRef(CFI->getCondition()); + values.push_back(conditionValue); + + auto messageValue = addValueRef(CFI->getMessage()); + values.push_back(messageValue); + TypeID messageType = 0; + unsigned messageCategory = 0; + if (auto message = CFI->getMessage()) { + messageType = S.addTypeRef(message->getType().getASTType()); + messageCategory = (unsigned)message->getType().getCategory(); + } + + for (auto arg : CFI->getMessageArguments()) { + values.push_back(addValueRef(arg)); + values.push_back(S.addTypeRef(arg->getType().getASTType())); + values.push_back((unsigned)arg->getType().getCategory()); + } + + SILOneTypeValuesLayout::emitRecord(Out, ScratchRecord, + SILAbbrCodes[SILOneTypeValuesLayout::Code], + (unsigned)CFI->SILInstruction::getKind(), + messageType, messageCategory, + values); + break; + } case SILInstructionKind::MarkUninitializedInst: { unsigned Attr = (unsigned)cast(&SI)->getKind(); writeOneOperandExtraAttributeLayout(SI.getKind(), Attr, SI.getOperand(0)); diff --git a/test/IRGen/cond_fail_inline_scope.sil b/test/IRGen/cond_fail_inline_scope.sil new file mode 100644 index 0000000000000..48ea06cb48baa --- /dev/null +++ b/test/IRGen/cond_fail_inline_scope.sil @@ -0,0 +1,27 @@ +// RUN: %target-swift-frontend -emit-ir -gline-tables-only %s | %FileCheck %s + +import Builtin + +sil @after_trap : $() -> () + +// CHECK-LABEL: define {{.*}}@cond_fail_message(i1) +sil @cond_fail_message : $(Builtin.Int1) -> () { +entry(%z : $Builtin.Int1): + %m = string_literal utf8 "this is the way the world ends" + // CHECK: br i1 {{.*}}, label %[[TRAP:.*]], label %[[CONT:.*]], !dbg [[COND_FAIL_LOC:!.*]] + cond_fail %z : $Builtin.Int1, %m : $Builtin.RawPointer + // CHECK: ;