diff --git a/include/swift/SIL/SILBuilder.h b/include/swift/SIL/SILBuilder.h index d548e3df19c..fc206a095ad 100644 --- a/include/swift/SIL/SILBuilder.h +++ b/include/swift/SIL/SILBuilder.h @@ -981,6 +981,10 @@ public: return insertTerminator(new (F.getModule()) AutoreleaseReturnInst(Loc, ReturnValue)); } + + ThrowInst *createThrow(SILLocation loc, SILValue errorValue) { + return insertTerminator(new (F.getModule()) ThrowInst(loc, errorValue)); + } CondBranchInst *createCondBranch(SILLocation Loc, SILValue Cond, SILBasicBlock *Target1, diff --git a/include/swift/SIL/SILCloner.h b/include/swift/SIL/SILCloner.h index d33eed33b11..a926d43c80b 100644 --- a/include/swift/SIL/SILCloner.h +++ b/include/swift/SIL/SILCloner.h @@ -1506,6 +1506,14 @@ SILCloner::visitAutoreleaseReturnInst(AutoreleaseReturnInst *Inst) { getOpValue(Inst->getOperand()))); } +template +void +SILCloner::visitThrowInst(ThrowInst *Inst) { + doPostProcess(Inst, + getBuilder().createThrow(getOpLocation(Inst->getLoc()), + getOpValue(Inst->getOperand()))); +} + template void SILCloner::visitBranchInst(BranchInst *Inst) { diff --git a/include/swift/SIL/SILInstruction.h b/include/swift/SIL/SILInstruction.h index 76fcdd9c7ab..cb10fc0cf34 100644 --- a/include/swift/SIL/SILInstruction.h +++ b/include/swift/SIL/SILInstruction.h @@ -3090,6 +3090,27 @@ public: } }; +/// ThrowInst - Throw a typed error (which, in our system, is +/// essentially just a funny kind of return). +class ThrowInst + : public UnaryInstructionBase +{ +public: + /// Constructs a ThrowInst representing a throw out of the current + /// function. + /// + /// \param loc The location of the throw. + /// \param errorValue The value to be thrown. + ThrowInst(SILLocation loc, SILValue errorValue) + : UnaryInstructionBase(loc, errorValue) {} + + SuccessorListTy getSuccessors() { + // No successors. + return SuccessorListTy(); + } +}; + /// BranchInst - An unconditional branch. class BranchInst : public TermInst { SILSuccessor DestBB; diff --git a/include/swift/SIL/SILNodes.def b/include/swift/SIL/SILNodes.def index 0d093d1f90a..22382534220 100644 --- a/include/swift/SIL/SILNodes.def +++ b/include/swift/SIL/SILNodes.def @@ -219,6 +219,7 @@ ABSTRACT_VALUE(SILInstruction, ValueBase) TERMINATOR(UnreachableInst, TermInst, None) TERMINATOR(ReturnInst, TermInst, None) TERMINATOR(AutoreleaseReturnInst, TermInst, None) + TERMINATOR(ThrowInst, TermInst, None) TERMINATOR(BranchInst, TermInst, None) TERMINATOR(CondBranchInst, TermInst, None) TERMINATOR(SwitchValueInst, TermInst, None) diff --git a/lib/IRGen/IRGenSIL.cpp b/lib/IRGen/IRGenSIL.cpp index 5021d9cda56..00dea43924e 100644 --- a/lib/IRGen/IRGenSIL.cpp +++ b/lib/IRGen/IRGenSIL.cpp @@ -691,6 +691,7 @@ public: void visitCondBranchInst(CondBranchInst *i); void visitReturnInst(ReturnInst *i); void visitAutoreleaseReturnInst(AutoreleaseReturnInst *i); + void visitThrowInst(ThrowInst *i); void visitSwitchValueInst(SwitchValueInst *i); void visitSwitchEnumInst(SwitchEnumInst *i); void visitSwitchEnumAddrInst(SwitchEnumAddrInst *i); @@ -2080,6 +2081,21 @@ void IRGenSILFunction::visitAutoreleaseReturnInst(AutoreleaseReturnInst *i) { emitReturnInst(*this, i->getOperand().getType(), temp); } +void IRGenSILFunction::visitThrowInst(swift::ThrowInst *i) { + // Store the exception to the error slot. + llvm::Value *exn = getLoweredSingletonExplosion(i->getOperand()); + Builder.CreateStore(exn, getCallerErrorResultSlot()); + + // Create a normal return, but leaving the return value undefined. + auto fnTy = CurFn->getType()->getPointerElementType(); + auto retTy = cast(fnTy)->getReturnType(); + if (retTy->isVoidTy()) { + Builder.CreateRetVoid(); + } else { + Builder.CreateRet(llvm::UndefValue::get(retTy)); + } +} + static llvm::BasicBlock *emitBBMapForSwitchValue( IRGenSILFunction &IGF, SmallVectorImpl> &dests, diff --git a/lib/Parse/ParseSIL.cpp b/lib/Parse/ParseSIL.cpp index 5a9f1e506db..77a83f84d49 100644 --- a/lib/Parse/ParseSIL.cpp +++ b/lib/Parse/ParseSIL.cpp @@ -1273,6 +1273,7 @@ bool SILParser::parseSILOpcode(ValueKind &Opcode, SourceLoc &OpcodeLoc, .Case("thick_to_objc_metatype", ValueKind::ThickToObjCMetatypeInst) .Case("thin_function_to_pointer", ValueKind::ThinFunctionToPointerInst) .Case("thin_to_thick_function", ValueKind::ThinToThickFunctionInst) + .Case("throw", ValueKind::ThrowInst) .Case("tuple", ValueKind::TupleInst) .Case("tuple_element_addr", ValueKind::TupleElementAddrInst) .Case("tuple_extract", ValueKind::TupleExtractInst) @@ -2494,6 +2495,11 @@ bool SILParser::parseSILInstruction(SILBasicBlock *BB) { ResultVal = B.createReturn(InstLoc, Val); break; } + case ValueKind::ThrowInst: { + if (parseTypedValueRef(Val)) return true; + ResultVal = B.createThrow(InstLoc, Val); + break; + } case ValueKind::BranchInst: { Identifier BBName; SourceLoc NameLoc; diff --git a/lib/SIL/SILPrinter.cpp b/lib/SIL/SILPrinter.cpp index 958ecea8530..e5720740c2d 100644 --- a/lib/SIL/SILPrinter.cpp +++ b/lib/SIL/SILPrinter.cpp @@ -1228,6 +1228,10 @@ public: OS << "autorelease_return " << getIDAndType(RI->getOperand()); } + void visitThrowInst(ThrowInst *TI) { + OS << "throw " << getIDAndType(TI->getOperand()); + } + void visitSwitchValueInst(SwitchValueInst *SII) { OS << "switch_value " << getIDAndType(SII->getOperand()); for (unsigned i = 0, e = SII->getNumCases(); i < e; ++i) { diff --git a/lib/SIL/Verifier.cpp b/lib/SIL/Verifier.cpp index 845691208f3..0bdd2115e4c 100644 --- a/lib/SIL/Verifier.cpp +++ b/lib/SIL/Verifier.cpp @@ -2126,6 +2126,24 @@ public: require(instResultType.hasRetainablePointerRepresentation(), "autoreleased return value must be a reference type"); } + + void checkThrowInst(ThrowInst *TI) { + DEBUG(TI->print(llvm::dbgs())); + + CanSILFunctionType fnType = F.getLoweredFunctionType(); + require(fnType->hasErrorResult(), + "throw in function that doesn't have an error result"); + + SILType functionResultType + = F.mapTypeIntoContext(fnType->getErrorResult().getSILType()); + SILType instResultType = TI->getOperand().getType(); + DEBUG(llvm::dbgs() << "function error result type: "; + functionResultType.dump(); + llvm::dbgs() << "throw operand type: "; + instResultType.dump();); + require(functionResultType == instResultType, + "throw operand type does not match error result type of function"); + } void checkSelectEnumCases(SelectEnumInstBase *I) { EnumDecl *eDecl = I->getEnumOperand().getType().getEnumOrBoundGenericEnum(); diff --git a/lib/SILAnalysis/ValueTracking.cpp b/lib/SILAnalysis/ValueTracking.cpp index 5d3e7a2f8ba..c2f52e90443 100644 --- a/lib/SILAnalysis/ValueTracking.cpp +++ b/lib/SILAnalysis/ValueTracking.cpp @@ -95,6 +95,7 @@ static bool isTransitiveEscapeInst(SILInstruction *Inst) { case ValueKind::DynamicMethodBranchInst: case ValueKind::ReturnInst: case ValueKind::AutoreleaseReturnInst: + case ValueKind::ThrowInst: case ValueKind::FixLifetimeInst: return false; diff --git a/lib/SILPasses/DeadCodeElimination.cpp b/lib/SILPasses/DeadCodeElimination.cpp index 227bb801418..4ade8f1b34a 100644 --- a/lib/SILPasses/DeadCodeElimination.cpp +++ b/lib/SILPasses/DeadCodeElimination.cpp @@ -282,6 +282,7 @@ void DCE::propagateLiveness(SILInstruction *I) { case ValueKind::ReturnInst: case ValueKind::AutoreleaseReturnInst: + case ValueKind::ThrowInst: case ValueKind::CondBranchInst: case ValueKind::SwitchEnumInst: case ValueKind::SwitchEnumAddrInst: diff --git a/lib/SILPasses/Utils/SILInliner.cpp b/lib/SILPasses/Utils/SILInliner.cpp index 197f51ee04e..1341512fe35 100644 --- a/lib/SILPasses/Utils/SILInliner.cpp +++ b/lib/SILPasses/Utils/SILInliner.cpp @@ -152,6 +152,13 @@ bool SILInliner::inlineFunction(ApplyInst *AI, ArrayRef Args) { continue; } + // Modify throw terminators to branch to the error-return BB, rather than + // trying to clone the ThrowInst. + if (ThrowInst *TI = dyn_cast(BI->first->getTerminator())) { + (void) TI; + llvm_unreachable("cannot remap throw in a non-try_apply call!"); + } + assert(!isa(BI->first->getTerminator()) && "Unexpected autorelease return while inlining non-Objective-C " "function?"); @@ -283,6 +290,7 @@ InlineCost swift::instructionInlineCost(SILInstruction &I) { // Return and unreachable are free. case ValueKind::UnreachableInst: case ValueKind::ReturnInst: + case ValueKind::ThrowInst: return InlineCost::Free; case ValueKind::ApplyInst: diff --git a/lib/Serialization/DeserializeSIL.cpp b/lib/Serialization/DeserializeSIL.cpp index 910af2012e0..fbdc3b319a7 100644 --- a/lib/Serialization/DeserializeSIL.cpp +++ b/lib/Serialization/DeserializeSIL.cpp @@ -1127,6 +1127,7 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, UNARY_INSTRUCTION(IsNonnull) UNARY_INSTRUCTION(Load) UNARY_INSTRUCTION(Return) + UNARY_INSTRUCTION(Throw) UNARY_INSTRUCTION(FixLifetime) UNARY_INSTRUCTION(CopyBlock) UNARY_INSTRUCTION(StrongPin) diff --git a/lib/Serialization/SerializeSIL.cpp b/lib/Serialization/SerializeSIL.cpp index c06203594f8..c965988c2da 100644 --- a/lib/Serialization/SerializeSIL.cpp +++ b/lib/Serialization/SerializeSIL.cpp @@ -859,6 +859,7 @@ void SILSerializer::writeSILInstruction(const SILInstruction &SI) { case ValueKind::UnownedRetainInst: case ValueKind::UnownedReleaseInst: case ValueKind::ReturnInst: + case ValueKind::ThrowInst: case ValueKind::DebugValueInst: case ValueKind::DebugValueAddrInst: { unsigned Attr = 0; diff --git a/test/IRGen/errors.sil b/test/IRGen/errors.sil new file mode 100644 index 00000000000..043b569fd1a --- /dev/null +++ b/test/IRGen/errors.sil @@ -0,0 +1,32 @@ +// RUN: %target-swift-frontend %s -emit-ir | FileCheck %s + +sil_stage canonical + +import Builtin +import Swift + +sil @create_error : $@thin () -> @owned _ErrorType { +entry: + unreachable +} + +// CHECK: define void @throws(%swift.refcounted*, %swift.error**) { +sil @throws : $@thin () -> @error _ErrorType { + // CHECK: [[T0:%.*]] = call %swift.error* @create_error() + %0 = function_ref @create_error : $@thin () -> @owned _ErrorType + %1 = apply %0() : $@thin () -> @owned _ErrorType + + // CHECK-NEXT: store %swift.error* [[T0]], %swift.error** %1, align 8 + // CHECK-NEXT: ret void + throw %1 : $_ErrorType +} + +// CHECK: define void @doesnt_throw(%swift.refcounted*, %swift.error**) { +sil @doesnt_throw : $@thin () -> @error _ErrorType { + // We don't have to do anything here because the caller always + // zeroes the error slot before a call. + // CHECK-NEXT: entry: + // CHECK-NEXT: ret void + %0 = tuple () + return %0 : $() +}