From 237a3ef77f45edff357d91d47ba6d358b8b12b87 Mon Sep 17 00:00:00 2001 From: Erik Eckstein Date: Fri, 5 Jul 2019 20:02:49 +0200 Subject: [PATCH 1/5] SIL: Extend cond_fail by a second operand, which is a static string literal, indicating the failure reason. --- docs/SIL.rst | 6 ++-- include/swift/SIL/SILBuilder.h | 6 ++-- include/swift/SIL/SILCloner.h | 3 +- include/swift/SIL/SILInstruction.h | 22 ++++++++++++--- include/swift/Serialization/ModuleFormat.h | 2 +- lib/ParseSIL/ParseSIL.cpp | 28 ++++++++++++++++++- lib/SIL/SILInstructions.cpp | 15 ++++++++++ lib/SIL/SILPrinter.cpp | 3 +- lib/SILGen/SILGenBuiltin.cpp | 2 +- lib/SILGen/SILGenExpr.cpp | 4 +-- .../LoopTransforms/ArrayBoundsCheckOpts.cpp | 2 +- .../Mandatory/ClosureLifetimeFixup.cpp | 6 ++-- lib/SILOptimizer/Transforms/MergeCondFail.cpp | 11 ++++++-- lib/SILOptimizer/Transforms/SimplifyCFG.cpp | 12 ++++---- lib/Serialization/DeserializeSIL.cpp | 8 +++++- lib/Serialization/SerializeSIL.cpp | 13 ++++++++- test/SIL/Parser/basic.sil | 4 ++- test/Serialization/Inputs/def_basic.sil | 4 +-- 18 files changed, 117 insertions(+), 34 deletions(-) diff --git a/docs/SIL.rst b/docs/SIL.rst index b5f73114541d1..69c2d54002984 100644 --- a/docs/SIL.rst +++ b/docs/SIL.rst @@ -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 ~~~~~~~~~~~ diff --git a/include/swift/SIL/SILBuilder.h b/include/swift/SIL/SILBuilder.h index 6b24c40e73e1a..9ee2d5c7c70d5 100644 --- a/include/swift/SIL/SILBuilder.h +++ b/include/swift/SIL/SILBuilder.h @@ -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) { diff --git a/include/swift/SIL/SILCloner.h b/include/swift/SIL/SILCloner.h index 223facefbabc5..42859fbc52ab3 100644 --- a/include/swift/SIL/SILCloner.h +++ b/include/swift/SIL/SILCloner.h @@ -2447,7 +2447,8 @@ SILCloner::visitCondFailInst(CondFailInst *Inst) { getBuilder().setCurrentDebugScope(getOpScope(Inst->getDebugScope())); recordClonedInstruction( Inst, getBuilder().createCondFail(getOpLocation(Inst->getLoc()), - getOpValue(Inst->getOperand()))); + getOpValue(Inst->getOperand()), + Inst->getMessage())); } template diff --git a/include/swift/SIL/SILInstruction.h b/include/swift/SIL/SILInstruction.h index 75668acc62a26..a4dddb5b52c3b 100644 --- a/include/swift/SIL/SILInstruction.h +++ b/include/swift/SIL/SILInstruction.h @@ -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 + NonValueInstruction>, + private llvm::TrailingObjects { + 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(), MessageSize}; + } }; //===----------------------------------------------------------------------===// diff --git a/include/swift/Serialization/ModuleFormat.h b/include/swift/Serialization/ModuleFormat.h index 501514650f58e..361119b98207e 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 messages using DeclIDField = BCFixed<31>; diff --git a/lib/ParseSIL/ParseSIL.cpp b/lib/ParseSIL/ParseSIL.cpp index 6adc7b8f2d685..c608c8c843f86 100644 --- a/lib/ParseSIL/ParseSIL.cpp +++ b/lib/ParseSIL/ParseSIL.cpp @@ -2635,6 +2635,33 @@ bool SILParser::parseSILInstruction(SILBuilder &B) { break; } + case SILInstructionKind::CondFailInst: { + + if (parseTypedValueRef(Val, B)) + return true; + + SmallVector 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 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) || @@ -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) diff --git a/lib/SIL/SILInstructions.cpp b/lib/SIL/SILInstructions.cpp index 8137a6468791c..3043735cf9939 100644 --- a/lib/SIL/SILInstructions.cpp +++ b/lib/SIL/SILInstructions.cpp @@ -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(), Message.data(), Message.size()); +} + +CondFailInst *CondFailInst::create(SILDebugLocation DebugLoc, SILValue Operand, + StringRef Message, SILModule &M) { + + auto Size = totalSizeToAlloc(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) diff --git a/lib/SIL/SILPrinter.cpp b/lib/SIL/SILPrinter.cpp index 95c4bad6ed891..d0f2f30f9a00d 100644 --- a/lib/SIL/SILPrinter.cpp +++ b/lib/SIL/SILPrinter.cpp @@ -1866,7 +1866,8 @@ class SILPrinter : public SILInstructionVisitor { } void visitCondFailInst(CondFailInst *FI) { - *this << getIDAndType(FI->getOperand()); + *this << getIDAndType(FI->getOperand()) << ", " + << QuotedString(FI->getMessage()); } void visitIndexAddrInst(IndexAddrInst *IAI) { diff --git a/lib/SILGen/SILGenBuiltin.cpp b/lib/SILGen/SILGenBuiltin.cpp index cc1c9dcd866ca..d93d4d9289071 100644 --- a/lib/SILGen/SILGenBuiltin.cpp +++ b/lib/SILGen/SILGenBuiltin.cpp @@ -626,7 +626,7 @@ static ManagedValue emitBuiltinCondFail(SILGenFunction &SGF, SGFContext C) { assert(args.size() == 1 && "condfail should be given one argument"); - SGF.B.createCondFail(loc, args[0].getUnmanagedValue()); + SGF.B.createCondFail(loc, args[0].getUnmanagedValue(), "runtime failure"); return ManagedValue::forUnmanaged(SGF.emitEmptyTuple(loc)); } diff --git a/lib/SILGen/SILGenExpr.cpp b/lib/SILGen/SILGenExpr.cpp index 742b8dc107070..21b7779f720b9 100644 --- a/lib/SILGen/SILGenExpr.cpp +++ b/lib/SILGen/SILGenExpr.cpp @@ -4851,7 +4851,7 @@ RValue RValueEmitter::emitForceValue(ForceValueExpr *loc, Expr *E, failureBuilder.setTrackingList(SGF.getBuilder().getTrackingList()); auto boolTy = SILType::getBuiltinIntegerType(1, SGF.getASTContext()); auto trueV = failureBuilder.createIntegerLiteral(loc, boolTy, 1); - failureBuilder.createCondFail(loc, trueV); + failureBuilder.createCondFail(loc, trueV, "force unwrapped a nil value"); failureBuilder.createUnreachable(loc); } } @@ -4990,7 +4990,7 @@ RValue RValueEmitter::visitMakeTemporarilyEscapableExpr( auto isEscaping = SGF.B.createIsEscapingClosure( loc, borrowedClosure.getValue(), IsEscapingClosureInst::WithoutActuallyEscaping); - SGF.B.createCondFail(loc, isEscaping); + SGF.B.createCondFail(loc, isEscaping, "non-escaping closure has escaped"); return rvalue; } diff --git a/lib/SILOptimizer/LoopTransforms/ArrayBoundsCheckOpts.cpp b/lib/SILOptimizer/LoopTransforms/ArrayBoundsCheckOpts.cpp index ccecaaa25f939..218cfdd2e3bd9 100644 --- a/lib/SILOptimizer/LoopTransforms/ArrayBoundsCheckOpts.cpp +++ b/lib/SILOptimizer/LoopTransforms/ArrayBoundsCheckOpts.cpp @@ -712,7 +712,7 @@ struct InductionInfo { auto ResultTy = SILType::getBuiltinIntegerType(1, Builder.getASTContext()); auto *CmpSGE = Builder.createBuiltinBinaryFunction( Loc, "cmp_sge", Start->getType(), ResultTy, {Start, End}); - Builder.createCondFail(Loc, CmpSGE); + Builder.createCondFail(Loc, CmpSGE, "loop induction variable overflowed"); IsOverflowCheckInserted = true; // We can now remove the cond fail on the increment the above comparison diff --git a/lib/SILOptimizer/Mandatory/ClosureLifetimeFixup.cpp b/lib/SILOptimizer/Mandatory/ClosureLifetimeFixup.cpp index cc00c8c79b2c1..126b67ae87190 100644 --- a/lib/SILOptimizer/Mandatory/ClosureLifetimeFixup.cpp +++ b/lib/SILOptimizer/Mandatory/ClosureLifetimeFixup.cpp @@ -691,7 +691,7 @@ static bool fixupCopyBlockWithoutEscaping(CopyBlockWithoutEscapingInst *cb, SILValue v = sentinelClosure; SILValue isEscaping = b.createIsEscapingClosure( loc, v, IsEscapingClosureInst::ObjCEscaping); - b.createCondFail(loc, isEscaping); + b.createCondFail(loc, isEscaping, "non-escaping closure has escaped"); b.createDestroyValue(loc, v); return true; } @@ -705,7 +705,7 @@ static bool fixupCopyBlockWithoutEscaping(CopyBlockWithoutEscapingInst *cb, SILValue V = sentinelClosure; SILValue isEscaping = B.createIsEscapingClosure( loc, V, IsEscapingClosureInst::ObjCEscaping); - B.createCondFail(loc, isEscaping); + B.createCondFail(loc, isEscaping, "non-escaping closure has escaped"); B.createDestroyValue(loc, V); } @@ -773,7 +773,7 @@ static bool fixupCopyBlockWithoutEscaping(CopyBlockWithoutEscapingInst *cb, SILValue v = updater.GetValueInMiddleOfBlock(singleDestroy->getParent()); SILValue isEscaping = b.createIsEscapingClosure(loc, v, IsEscapingClosureInst::ObjCEscaping); - b.createCondFail(loc, isEscaping); + b.createCondFail(loc, isEscaping, "non-escaping closure has escaped"); b.createDestroyValue(loc, v); } diff --git a/lib/SILOptimizer/Transforms/MergeCondFail.cpp b/lib/SILOptimizer/Transforms/MergeCondFail.cpp index e2065864a226e..e8ad4f8fc5c51 100644 --- a/lib/SILOptimizer/Transforms/MergeCondFail.cpp +++ b/lib/SILOptimizer/Transforms/MergeCondFail.cpp @@ -69,7 +69,9 @@ class MergeCondFailInsts : public SILFunctionTransform { // Do not process arithmetic overflow checks. We typically generate more // efficient code with separate jump-on-overflow. - if (CFI && !hasOverflowConditionOperand(CFI)) + if (CFI && !hasOverflowConditionOperand(CFI) && + (CondFailToMerge.empty() || + CFI->getMessage() == CondFailToMerge.front()->getMessage())) CondFailToMerge.push_back(CFI); } @@ -109,12 +111,15 @@ class MergeCondFailInsts : public SILFunctionTransform { {MergedCond, CurCond}); } - CondFailToMerge[I]->eraseFromParent(); MergedCond = CurCond; } // Create a new cond_fail using the merged condition. - Builder.createCondFail(Loc, MergedCond); + Builder.createCondFail(Loc, MergedCond, LastCFI->getMessage()); + + for (CondFailInst *CFI : CondFailToMerge) { + CFI->eraseFromParent(); + } return true; } }; diff --git a/lib/SILOptimizer/Transforms/SimplifyCFG.cpp b/lib/SILOptimizer/Transforms/SimplifyCFG.cpp index 38a0c4cbf756f..1e37a33c7424b 100644 --- a/lib/SILOptimizer/Transforms/SimplifyCFG.cpp +++ b/lib/SILOptimizer/Transforms/SimplifyCFG.cpp @@ -1399,9 +1399,9 @@ static CondFailInst *getUnConditionalFail(SILBasicBlock *BB, SILValue Cond, /// Creates a new cond_fail instruction, optionally with an xor inverted /// condition. -static void createCondFail(CondFailInst *Orig, SILValue Cond, bool inverted, - SILBuilder &Builder) { - Builder.createCondFail(Orig->getLoc(), Cond, inverted); +static void createCondFail(CondFailInst *Orig, SILValue Cond, StringRef Message, + bool inverted, SILBuilder &Builder) { + Builder.createCondFail(Orig->getLoc(), Cond, Message, inverted); } /// Inverts the expected value of 'PotentialExpect' (if it is an expect @@ -1643,7 +1643,7 @@ bool SimplifyCFG::simplifyCondBrBlock(CondBranchInst *BI) { if (auto *TrueCFI = getUnConditionalFail(TrueSide, CFCondition, false)) { LLVM_DEBUG(llvm::dbgs() << "replace with cond_fail:" << *BI); SILBuilderWithScope Builder(BI); - createCondFail(TrueCFI, CFCondition, false, Builder); + createCondFail(TrueCFI, CFCondition, TrueCFI->getMessage(), false, Builder); SILBuilderWithScope(BI).createBranch(BI->getLoc(), FalseSide, FalseArgs); BI->eraseFromParent(); @@ -1655,7 +1655,7 @@ bool SimplifyCFG::simplifyCondBrBlock(CondBranchInst *BI) { if (auto *FalseCFI = getUnConditionalFail(FalseSide, CFCondition, true)) { LLVM_DEBUG(llvm::dbgs() << "replace with inverted cond_fail:" << *BI); SILBuilderWithScope Builder(BI); - createCondFail(FalseCFI, CFCondition, true, Builder); + createCondFail(FalseCFI, CFCondition, FalseCFI->getMessage(), true, Builder); SILBuilderWithScope(BI).createBranch(BI->getLoc(), TrueSide, TrueArgs); BI->eraseFromParent(); @@ -2497,7 +2497,7 @@ static bool tryMoveCondFailToPreds(SILBasicBlock *BB) { SILValue incoming = condArg->getIncomingPhiValue(Pred); SILBuilderWithScope Builder(Pred->getTerminator()); - createCondFail(CFI, incoming, inverted, Builder); + createCondFail(CFI, incoming, CFI->getMessage(), inverted, Builder); } CFI->eraseFromParent(); return true; diff --git a/lib/Serialization/DeserializeSIL.cpp b/lib/Serialization/DeserializeSIL.cpp index 8ec5b6bc9460f..b258bf597d770 100644 --- a/lib/Serialization/DeserializeSIL.cpp +++ b/lib/Serialization/DeserializeSIL.cpp @@ -1617,6 +1617,13 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, encoding.getValue()); break; } + case SILInstructionKind::CondFailInst: { + SILValue Op = getLocalValue(ValID, getSILType(MF->getType(TyID), \ + (SILValueCategory)TyCategory)); + StringRef StringVal = MF->getIdentifierText(ValID2); + ResultVal = Builder.createCondFail(Loc, Op, StringVal); + break; + } case SILInstructionKind::MarkFunctionEscapeInst: { // Format: a list of typed values. A typed value is expressed by 4 IDs: // TypeID, TypeCategory, ValueID, ValueResultNumber. @@ -1664,7 +1671,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) diff --git a/lib/Serialization/SerializeSIL.cpp b/lib/Serialization/SerializeSIL.cpp index a5cc2e677fe25..82649e966d799 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: @@ -1399,6 +1398,18 @@ void SILSerializer::writeSILInstruction(const SILInstruction &SI) { S.addTypeRef(TAI->getTailType().getASTType())); break; } + case SILInstructionKind::CondFailInst: { + auto *CFI = cast(&SI); + SILValue operand = CFI->getOperand(); + SILTwoOperandsLayout::emitRecord(Out, ScratchRecord, + SILAbbrCodes[SILTwoOperandsLayout::Code], + (unsigned)SI.getKind(), /*attributes*/ 0, + S.addTypeRef(operand->getType().getASTType()), + (unsigned)operand->getType().getCategory(), + addValueRef(operand), + 0, 0, S.addUniquedStringRef(CFI->getMessage())); + break; + } case SILInstructionKind::StringLiteralInst: { auto SLI = cast(&SI); StringRef Str = SLI->getValue(); diff --git a/test/SIL/Parser/basic.sil b/test/SIL/Parser/basic.sil index d6ac39c106135..49c310e2e75e0 100644 --- a/test/SIL/Parser/basic.sil +++ b/test/SIL/Parser/basic.sil @@ -1183,8 +1183,10 @@ bb0(%0 : $Int32, %1 : $Int32, %2 : $Int32, %3 : $Foo): // CHECK-LABEL: sil @cond_fail_test : $@convention(thin) (Builtin.Int1) -> () { sil @cond_fail_test : $(Builtin.Int1) -> () { entry(%0 : $Builtin.Int1): - // CHECK: cond_fail %0 : $Builtin.Int1 + // CHECK: cond_fail %0 : $Builtin.Int1, "" cond_fail %0 : $Builtin.Int1 + // CHECK: cond_fail %0 : $Builtin.Int1, "message" + cond_fail %0 : $Builtin.Int1, "message" %1 = tuple () return %1 : $() } diff --git a/test/Serialization/Inputs/def_basic.sil b/test/Serialization/Inputs/def_basic.sil index 02ffd2850a6f7..d41da79b1048c 100644 --- a/test/Serialization/Inputs/def_basic.sil +++ b/test/Serialization/Inputs/def_basic.sil @@ -1036,8 +1036,8 @@ bb0(%0 : $Builtin.RawPointer, %1 : $Builtin.Word): // CHECK: sil public_external [transparent] [serialized] @cond_fail_test : $@convention(thin) (Builtin.Int1) -> () { sil [transparent] [serialized] @cond_fail_test : $@convention(thin) (Builtin.Int1) -> () { entry(%0 : $Builtin.Int1): - // CHECK: cond_fail %0 : $Builtin.Int1 - cond_fail %0 : $Builtin.Int1 + // CHECK: cond_fail %0 : $Builtin.Int1, "message" + cond_fail %0 : $Builtin.Int1, "message" %1 = tuple () return %1 : $() } From b40ce6b34fa863538620c6c57514bbdb49e830cc Mon Sep 17 00:00:00 2001 From: Erik Eckstein Date: Fri, 12 Jul 2019 13:30:53 +0200 Subject: [PATCH 2/5] SIL: add a failure message operand to Builtin.condfail The SIL generation for this builtin also changes: instead of generating the cond_fail instructions upfront, let the optimizer generate it, if the operand is a static string literal. In worst case, if the second operand is not a static string literal, the Builtin.condfail is lowered at the end of the optimization pipeline with a default message: "unknown program error". --- include/swift/AST/Builtins.def | 8 +++--- lib/AST/Builtins.cpp | 3 ++- lib/SIL/OperandOwnership.cpp | 1 + lib/SIL/ValueOwnership.cpp | 1 + lib/SILGen/SILGenBuiltin.cpp | 12 --------- .../Analysis/SimplifyInstruction.cpp | 13 ++++++++++ lib/SILOptimizer/Mandatory/IRGenPrepare.cpp | 25 ++++++++++++------- .../SILCombinerBuiltinVisitors.cpp | 9 +++++++ .../AccessEnforcementReleaseSinking.cpp | 1 + stdlib/public/core/Assert.swift | 10 ++++---- stdlib/public/core/IntegerTypes.swift.gyb | 2 +- stdlib/public/core/StaticString.swift | 5 ++++ test/DebugInfo/doubleinlines.swift | 10 ++++---- test/DebugInfo/linetable-codeview.swift | 2 +- test/IRGen/builtins.swift | 6 ++--- test/SILGen/builtins.swift | 8 +++--- .../castoptimizer-wrongscope.swift | 2 +- test/SILOptimizer/existential_transform.swift | 1 + test/SILOptimizer/pound_assert.swift | 1 + .../specialize_checked_cast_branch.swift | 16 ++++++------ 20 files changed, 82 insertions(+), 54 deletions(-) diff --git a/include/swift/AST/Builtins.def b/include/swift/AST/Builtins.def index 5049ad631a048..06e3e4832adf0 100644 --- a/include/swift/AST/Builtins.def +++ b/include/swift/AST/Builtins.def @@ -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) @@ -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) diff --git a/lib/AST/Builtins.cpp b/lib/AST/Builtins.cpp index 546f05a6217f9..71479b1df13e4 100644 --- a/lib/AST/Builtins.cpp +++ b/lib/AST/Builtins.cpp @@ -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) { diff --git a/lib/SIL/OperandOwnership.cpp b/lib/SIL/OperandOwnership.cpp index 9012751e73f31..966485f8328a3 100644 --- a/lib/SIL/OperandOwnership.cpp +++ b/lib/SIL/OperandOwnership.cpp @@ -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) diff --git a/lib/SIL/ValueOwnership.cpp b/lib/SIL/ValueOwnership.cpp index 379ee41eb67d0..6949c69ea0533 100644 --- a/lib/SIL/ValueOwnership.cpp +++ b/lib/SIL/ValueOwnership.cpp @@ -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) diff --git a/lib/SILGen/SILGenBuiltin.cpp b/lib/SILGen/SILGenBuiltin.cpp index d93d4d9289071..bc8ef800fe7ae 100644 --- a/lib/SILGen/SILGenBuiltin.cpp +++ b/lib/SILGen/SILGenBuiltin.cpp @@ -618,18 +618,6 @@ static ManagedValue emitBuiltinEndUnpairedAccess(SILGenFunction &SGF, return ManagedValue::forUnmanaged(SGF.emitEmptyTuple(loc)); } -/// Specialized emitter for Builtin.condfail. -static ManagedValue emitBuiltinCondFail(SILGenFunction &SGF, - SILLocation loc, - SubstitutionMap substitutions, - ArrayRef args, - SGFContext C) { - assert(args.size() == 1 && "condfail should be given one argument"); - - SGF.B.createCondFail(loc, args[0].getUnmanagedValue(), "runtime failure"); - return ManagedValue::forUnmanaged(SGF.emitEmptyTuple(loc)); -} - /// Specialized emitter for Builtin.castReference. static ManagedValue emitBuiltinCastReference(SILGenFunction &SGF, diff --git a/lib/SILOptimizer/Analysis/SimplifyInstruction.cpp b/lib/SILOptimizer/Analysis/SimplifyInstruction.cpp index 17fe07bcd2193..914650a6bda91 100644 --- a/lib/SILOptimizer/Analysis/SimplifyInstruction.cpp +++ b/lib/SILOptimizer/Analysis/SimplifyInstruction.cpp @@ -454,6 +454,19 @@ SILValue InstSimplifier::visitBeginAccessInst(BeginAccessInst *BAI) { } static SILValue simplifyBuiltin(BuiltinInst *BI) { + + switch (BI->getBuiltinInfo().ID) { + case BuiltinValueKind::IntToPtr: + if (auto *OpBI = dyn_cast(BI->getOperand(0))) { + if (OpBI->getBuiltinInfo().ID == BuiltinValueKind::PtrToInt) { + return OpBI->getOperand(0); + } + } + return SILValue(); + default: + break; + } + const IntrinsicInfo &Intrinsic = BI->getIntrinsicInfo(); switch (Intrinsic.ID) { diff --git a/lib/SILOptimizer/Mandatory/IRGenPrepare.cpp b/lib/SILOptimizer/Mandatory/IRGenPrepare.cpp index c87b54135f8dc..3269d4c0c7289 100644 --- a/lib/SILOptimizer/Mandatory/IRGenPrepare.cpp +++ b/lib/SILOptimizer/Mandatory/IRGenPrepare.cpp @@ -48,16 +48,23 @@ static bool cleanFunction(SILFunction &fn) { continue; } - const BuiltinInfo &bInfo = bi->getBuiltinInfo(); - if (bInfo.ID != BuiltinValueKind::PoundAssert && - bInfo.ID != BuiltinValueKind::StaticReport) { - continue; + switch (bi->getBuiltinInfo().ID) { + case BuiltinValueKind::CondFail: { + SILBuilderWithScope Builder(bi); + Builder.createCondFail(bi->getLoc(), bi->getOperand(0), + "unknown program error"); + LLVM_FALLTHROUGH; + } + case BuiltinValueKind::PoundAssert: + case BuiltinValueKind::StaticReport: + // The call to the builtin should get removed before we reach + // IRGen. + recursivelyDeleteTriviallyDeadInstructions(bi, /* Force */ true); + madeChange = true; + break; + default: + break; } - - // The call to the builtin should get removed before we reach - // IRGen. - recursivelyDeleteTriviallyDeadInstructions(bi, /* Force */ true); - madeChange = true; } } diff --git a/lib/SILOptimizer/SILCombiner/SILCombinerBuiltinVisitors.cpp b/lib/SILOptimizer/SILCombiner/SILCombinerBuiltinVisitors.cpp index 1c538ffdcdc5f..6f5edb7cf6e90 100644 --- a/lib/SILOptimizer/SILCombiner/SILCombinerBuiltinVisitors.cpp +++ b/lib/SILOptimizer/SILCombiner/SILCombinerBuiltinVisitors.cpp @@ -622,6 +622,15 @@ SILInstruction *SILCombiner::visitBuiltinInst(BuiltinInst *I) { } break; } + case BuiltinValueKind::CondFail: + if (auto *SLI = dyn_cast(I->getOperand(1))) { + if (SLI->getEncoding() == StringLiteralInst::Encoding::UTF8) { + Builder.createCondFail(I->getLoc(), I->getOperand(0), SLI->getValue()); + eraseInstFromFunction(*I); + return nullptr; + } + } + break; default: break; } diff --git a/lib/SILOptimizer/Transforms/AccessEnforcementReleaseSinking.cpp b/lib/SILOptimizer/Transforms/AccessEnforcementReleaseSinking.cpp index 049a258ac77c9..ae2150c5edb42 100644 --- a/lib/SILOptimizer/Transforms/AccessEnforcementReleaseSinking.cpp +++ b/lib/SILOptimizer/Transforms/AccessEnforcementReleaseSinking.cpp @@ -129,6 +129,7 @@ static bool isBarrier(SILInstruction *inst) { case BuiltinValueKind::GetObjCTypeEncoding: case BuiltinValueKind::Swift3ImplicitObjCEntrypoint: case BuiltinValueKind::WillThrow: + case BuiltinValueKind::CondFail: case BuiltinValueKind::PoundAssert: case BuiltinValueKind::GlobalStringTablePointer: return false; diff --git a/stdlib/public/core/Assert.swift b/stdlib/public/core/Assert.swift index d5b594b24c741..4b1c847e76acd 100644 --- a/stdlib/public/core/Assert.swift +++ b/stdlib/public/core/Assert.swift @@ -93,7 +93,7 @@ public func precondition( } } else if _isReleaseAssertConfiguration() { let error = !condition() - Builtin.condfail(error._value) + Builtin.condfail(error._value, StaticString("precondition failure").unsafeRawPointer) } } @@ -171,7 +171,7 @@ public func preconditionFailure( _assertionFailure("Fatal error", message(), file: file, line: line, flags: _fatalErrorFlags()) } else if _isReleaseAssertConfiguration() { - Builtin.int_trap() + Builtin.condfail(true._value, StaticString("precondition failure").unsafeRawPointer) } _conditionallyUnreachable() } @@ -207,12 +207,12 @@ internal func _precondition( // Only check in debug and release mode. In release mode just trap. if _isDebugAssertConfiguration() { if !_fastPath(condition()) { - _fatalErrorMessage("Fatal error", message, file: file, line: line, + _assertionFailure("Fatal error", message, file: file, line: line, flags: _fatalErrorFlags()) } } else if _isReleaseAssertConfiguration() { let error = !condition() - Builtin.condfail(error._value) + Builtin.condfail(error._value, message.unsafeRawPointer) } } @@ -240,7 +240,7 @@ public func _overflowChecked( file: file, line: line, flags: _fatalErrorFlags()) } } else { - Builtin.condfail(error._value) + Builtin.condfail(error._value, StaticString("_overflowChecked failure").unsafeRawPointer) } return result } diff --git a/stdlib/public/core/IntegerTypes.swift.gyb b/stdlib/public/core/IntegerTypes.swift.gyb index 794199d2e5b54..ed0630408ad1c 100644 --- a/stdlib/public/core/IntegerTypes.swift.gyb +++ b/stdlib/public/core/IntegerTypes.swift.gyb @@ -1234,7 +1234,7 @@ ${assignmentOperatorComment(x.operator, True)} Builtin.${u}${x.llvmName}_with_overflow_Int${bits}( lhs._value, rhs._value, true._value) % end - Builtin.condfail(overflow) + Builtin.condfail(overflow, StaticString("arithmetic overflow").unsafeRawPointer) lhs = ${Self}(result) } % end diff --git a/stdlib/public/core/StaticString.swift b/stdlib/public/core/StaticString.swift index 10709dec53d70..fc3c0f287a033 100644 --- a/stdlib/public/core/StaticString.swift +++ b/stdlib/public/core/StaticString.swift @@ -94,6 +94,11 @@ public struct StaticString return Int(_utf8CodeUnitCount) } + @_alwaysEmitIntoClient @_transparent + internal var unsafeRawPointer: Builtin.RawPointer { + return Builtin.inttoptr_Word(_startPtrOrData) + } + /// A Boolean value indicating whether the static string stores a pointer to /// ASCII or UTF-8 code units. @_transparent diff --git a/test/DebugInfo/doubleinlines.swift b/test/DebugInfo/doubleinlines.swift index 00d80d86f312f..4be4cf10291e0 100644 --- a/test/DebugInfo/doubleinlines.swift +++ b/test/DebugInfo/doubleinlines.swift @@ -2,15 +2,15 @@ // RUN: -debug-info-format=codeview -O -parse-as-library \ // RUN: -module-name DoubleInlines -o - | %FileCheck %s -func condFail(arg: Builtin.Int1 ) { - Builtin.condfail(arg) +func condFail(arg: Builtin.Int1, msg: Builtin.RawPointer) { + Builtin.condfail(arg, msg) } -func callCondFail(arg: Builtin.Int1) { - condFail(arg: arg) +func callCondFail(arg: Builtin.Int1, msg: Builtin.RawPointer) { + condFail(arg: arg, msg: msg) } -// CHECK: define hidden swiftcc void @"$s13DoubleInlines12callCondFail3argyBi1__tF"{{.*}} !dbg ![[FUNCSCOPE:.*]] { +// CHECK: define hidden swiftcc void @"$s13DoubleInlines12callCondFail3arg3msgyBi1__BptF"{{.*}} !dbg ![[FUNCSCOPE:.*]] { // CHECK: tail call void asm sideeffect "", "n"(i32 0) #3, !dbg ![[SCOPEONE:.*]] // CHECK: ![[FUNCSCOPEOTHER:.*]] = distinct !DISubprogram(name: "condFail",{{.*}} // CHECK: ![[SCOPEFIVE:.*]] = distinct !DILexicalBlock(scope: ![[FUNCSCOPE]], file: ![[FILE:.*]], line: 9) diff --git a/test/DebugInfo/linetable-codeview.swift b/test/DebugInfo/linetable-codeview.swift index 778421dc1bbbd..fe2f6d858f487 100644 --- a/test/DebugInfo/linetable-codeview.swift +++ b/test/DebugInfo/linetable-codeview.swift @@ -36,7 +36,7 @@ func foo() { // between other instructions for the division. We want to make sure // all instructions from the division have the same debug location and // are contiguous. - // CHECK: call {{.*}} @"$ss18_fatalErrorMessage__4file4line5flagss5NeverOs12StaticStringV_A2HSus6UInt32VtF"{{.*}}, !dbg ![[DIV:[0-9]+]] + // CHECK: call {{.*}} @"$ss17_assertionFailure__4file4line5flagss5NeverOs12StaticStringV_A2HSus6UInt32VtF"{{.*}}, !dbg ![[DIV:[0-9]+]] // CHECK-NEXT: unreachable, !dbg ![[DIV]] // CHECK: sdiv i64 %0, %1, !dbg ![[DIV]] // CHECK: call void @llvm.trap(), !dbg ![[INLINEDADD:[0-9]+]] diff --git a/test/IRGen/builtins.swift b/test/IRGen/builtins.swift index 85455ab1a2c6d..d5a1dda8b0461 100644 --- a/test/IRGen/builtins.swift +++ b/test/IRGen/builtins.swift @@ -1,5 +1,5 @@ -// RUN: %target-swift-frontend -module-name builtins -parse-stdlib -primary-file %s -emit-ir -o - -disable-objc-attr-requires-foundation-module | %FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-%target-runtime +// RUN: %target-swift-frontend -module-name builtins -parse-stdlib -disable-access-control -primary-file %s -emit-ir -o - -disable-objc-attr-requires-foundation-module | %FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-%target-runtime // REQUIRES: CPU=x86_64 @@ -315,10 +315,10 @@ func testStaticReport(_ b: Bool, ptr: Builtin.RawPointer) -> () { // CHECK-LABEL: define hidden {{.*}}void @"$s8builtins12testCondFail{{[_0-9a-zA-Z]*}}F"(i1, i1) func testCondFail(_ b: Bool, c: Bool) { // CHECK: br i1 %0, label %[[FAIL:.*]], label %[[CONT:.*]] - Builtin.condfail(b) + Builtin.condfail(b, StaticString("message").unsafeRawPointer) // CHECK: