From 05ccd9734c49b6c45d4e3bbd34db4b42e2279829 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Mon, 30 Oct 2023 22:53:34 -0400 Subject: [PATCH 1/3] SIL: Introduce ThrowAddrInst --- .../Sources/SIL/Instruction.swift | 4 + .../Sources/SIL/Registration.swift | 1 + docs/SIL.rst | 26 +++++- include/swift/SIL/AddressWalker.h | 1 + include/swift/SIL/SILBuilder.h | 5 ++ include/swift/SIL/SILCloner.h | 8 ++ include/swift/SIL/SILInstruction.h | 28 ++++++- include/swift/SIL/SILNodes.def | 2 + include/swift/SILOptimizer/Utils/SCCVisitor.h | 1 + lib/IRGen/IRGenSIL.cpp | 80 ++++++++++++++----- lib/SIL/IR/OperandOwnership.cpp | 1 + lib/SIL/IR/SILArgument.cpp | 1 + lib/SIL/IR/SILInstructions.cpp | 3 + lib/SIL/IR/SILPrinter.cpp | 4 + lib/SIL/Parser/ParseSIL.cpp | 6 ++ lib/SIL/Utils/BasicBlockUtils.cpp | 1 + lib/SIL/Utils/InstructionUtils.cpp | 1 + lib/SIL/Verifier/SILVerifier.cpp | 3 +- lib/SILOptimizer/ARC/ARCSequenceOptUtils.cpp | 1 + .../Mandatory/PredictableMemOpt.cpp | 1 + .../Transforms/DeadCodeElimination.cpp | 3 +- lib/SILOptimizer/Transforms/SimplifyCFG.cpp | 1 + .../UtilityPasses/SerializeSILPass.cpp | 1 + lib/SILOptimizer/Utils/CFGOptUtils.cpp | 1 + lib/SILOptimizer/Utils/SILInliner.cpp | 1 + lib/Serialization/DeserializeSIL.cpp | 4 + lib/Serialization/ModuleFormat.h | 2 +- lib/Serialization/SerializeSIL.cpp | 1 + test/IRGen/typed_throws.sil | 4 +- 29 files changed, 165 insertions(+), 31 deletions(-) diff --git a/SwiftCompilerSources/Sources/SIL/Instruction.swift b/SwiftCompilerSources/Sources/SIL/Instruction.swift index 4801d820508cf..2607e45002806 100644 --- a/SwiftCompilerSources/Sources/SIL/Instruction.swift +++ b/SwiftCompilerSources/Sources/SIL/Instruction.swift @@ -1094,6 +1094,10 @@ final public class ThrowInst : TermInst, UnaryInstruction { public override var isFunctionExiting: Bool { true } } +final public class ThrowAddrInst : TermInst { + public override var isFunctionExiting: Bool { true } +} + final public class YieldInst : TermInst { } diff --git a/SwiftCompilerSources/Sources/SIL/Registration.swift b/SwiftCompilerSources/Sources/SIL/Registration.swift index f87774bab9b16..58ef686fac7f4 100644 --- a/SwiftCompilerSources/Sources/SIL/Registration.swift +++ b/SwiftCompilerSources/Sources/SIL/Registration.swift @@ -172,6 +172,7 @@ public func registerSILClasses() { register(UnreachableInst.self) register(ReturnInst.self) register(ThrowInst.self) + register(ThrowAddrInst.self) register(YieldInst.self) register(UnwindInst.self) register(TryApplyInst.self) diff --git a/docs/SIL.rst b/docs/SIL.rst index 50e3968b86f9a..ffae6ebdd28ff 100644 --- a/docs/SIL.rst +++ b/docs/SIL.rst @@ -613,8 +613,8 @@ the caller. A non-autoreleased ``apply`` of a function that is defined with an autoreleased result has the effect of performing an autorelease in the callee. -- SIL function types may provide an optional error result, written by - placing ``@error`` on a result. An error result is always +- SIL function types may provide an optional direct error result, written by + placing ``@error`` on a result. A direct error result is always implicitly ``@owned``. Only functions with a native calling convention may have an error result. @@ -8006,6 +8006,28 @@ the basic block argument will be the operand of the ``throw``. A function must not contain more than one ``throw`` instruction. +throw_addr +`````````` +:: + + sil-terminator ::= 'throw_addr' + + throw_addr + // indirect error result must be initialized at this point + +Exits the current function and returns control to the calling +function. The current function must have an indirect error result, +and so the function must have been invoked with a ``try_apply`` +instruction. Control will resume in the error destination of +that instruction. + +The function is responsible for initializing its error result +before the ``throw_addr``. + +``throw_addr`` does not retain or release any values. + +A function must not contain more than one ``throw_addr`` instruction. + yield ````` :: diff --git a/include/swift/SIL/AddressWalker.h b/include/swift/SIL/AddressWalker.h index c34e873c580f7..171d3ca9f3354 100644 --- a/include/swift/SIL/AddressWalker.h +++ b/include/swift/SIL/AddressWalker.h @@ -150,6 +150,7 @@ TransitiveAddressWalker::walk(SILValue projectedAddress) && { case TermKind::UnreachableInst: case TermKind::UnwindInst: + case TermKind::ThrowAddrInst: llvm_unreachable("Should never be used"); case TermKind::SwitchEnumInst: case TermKind::SwitchValueInst: diff --git a/include/swift/SIL/SILBuilder.h b/include/swift/SIL/SILBuilder.h index 2481926ec9151..28f63cd7041bc 100644 --- a/include/swift/SIL/SILBuilder.h +++ b/include/swift/SIL/SILBuilder.h @@ -2484,6 +2484,11 @@ class SILBuilder { new (getModule()) ThrowInst(getSILDebugLocation(Loc), errorValue)); } + ThrowAddrInst *createThrowAddr(SILLocation Loc) { + return insertTerminator( + new (getModule()) ThrowAddrInst(getSILDebugLocation(Loc))); + } + UnwindInst *createUnwind(SILLocation loc) { return insertTerminator( new (getModule()) UnwindInst(getSILDebugLocation(loc))); diff --git a/include/swift/SIL/SILCloner.h b/include/swift/SIL/SILCloner.h index 282074dfec557..ffd1279fe77f4 100644 --- a/include/swift/SIL/SILCloner.h +++ b/include/swift/SIL/SILCloner.h @@ -3134,6 +3134,14 @@ SILCloner::visitThrowInst(ThrowInst *Inst) { getOpValue(Inst->getOperand()))); } +template +void +SILCloner::visitThrowAddrInst(ThrowAddrInst *Inst) { + getBuilder().setCurrentDebugScope(getOpScope(Inst->getDebugScope())); + recordClonedInstruction( + Inst, getBuilder().createThrowAddr(getOpLocation(Inst->getLoc()))); +} + template void SILCloner::visitUnwindInst(UnwindInst *Inst) { diff --git a/include/swift/SIL/SILInstruction.h b/include/swift/SIL/SILInstruction.h index 9ef3e5d005245..80c55ca9ce79a 100644 --- a/include/swift/SIL/SILInstruction.h +++ b/include/swift/SIL/SILInstruction.h @@ -9168,6 +9168,7 @@ class TermInst : public NonValueInstruction { case TermKind::UnreachableInst: case TermKind::ReturnInst: case TermKind::ThrowInst: + case TermKind::ThrowAddrInst: case TermKind::YieldInst: case TermKind::CondBranchInst: case TermKind::BranchInst: @@ -9295,8 +9296,7 @@ class ReturnInst } }; -/// ThrowInst - Throw a typed error (which, in our system, is -/// essentially just a funny kind of return). +/// ThrowInst - Throw a typed error, returning it via the direct error result. class ThrowInst : public UnaryInstructionBase { @@ -9317,6 +9317,30 @@ class ThrowInst } }; +/// ThrowAddrInst - Throw a typed error, previously stored in the indirect +/// error result. +class ThrowAddrInst + : public InstructionBase +{ + friend SILBuilder; + + /// Constructs a ThrowAddrInst representing a throw out of the current + /// function. + /// + /// \param DebugLoc The location of the throw. + ThrowAddrInst(SILDebugLocation DebugLoc) + : InstructionBase(DebugLoc) {} + +public: + SuccessorListTy getSuccessors() { + // No successors. + return SuccessorListTy(); + } + + ArrayRef getAllOperands() const { return {}; } + MutableArrayRef getAllOperands() { return {}; } +}; + /// UnwindInst - Continue unwinding out of this function. Currently this is /// only used in coroutines as the eventual terminator of the unwind edge /// out of a 'yield'. diff --git a/include/swift/SIL/SILNodes.def b/include/swift/SIL/SILNodes.def index dcb0c15d13d72..555d7f74ea55f 100644 --- a/include/swift/SIL/SILNodes.def +++ b/include/swift/SIL/SILNodes.def @@ -722,6 +722,8 @@ ABSTRACT_INST(TermInst, SILInstruction) TermInst, None, DoesNotRelease) TERMINATOR(ThrowInst, throw, TermInst, None, DoesNotRelease) + TERMINATOR(ThrowAddrInst, throw_addr, + TermInst, None, DoesNotRelease) TERMINATOR(YieldInst, yield, TermInst, MayHaveSideEffects, MayRelease) TERMINATOR(UnwindInst, unwind, diff --git a/include/swift/SILOptimizer/Utils/SCCVisitor.h b/include/swift/SILOptimizer/Utils/SCCVisitor.h index e2b4417429cdd..0d8c03c584e43 100644 --- a/include/swift/SILOptimizer/Utils/SCCVisitor.h +++ b/include/swift/SILOptimizer/Utils/SCCVisitor.h @@ -132,6 +132,7 @@ class SCCVisitor { case TermKind::ReturnInst: case TermKind::SwitchValueInst: case TermKind::ThrowInst: + case TermKind::ThrowAddrInst: case TermKind::UnwindInst: llvm_unreachable("Did not expect terminator that does not have args!"); diff --git a/lib/IRGen/IRGenSIL.cpp b/lib/IRGen/IRGenSIL.cpp index deecf9878ca13..61a1e34f079f8 100644 --- a/lib/IRGen/IRGenSIL.cpp +++ b/lib/IRGen/IRGenSIL.cpp @@ -1469,6 +1469,7 @@ class IRGenSILFunction : void visitCondBranchInst(CondBranchInst *i); void visitReturnInst(ReturnInst *i); void visitThrowInst(ThrowInst *i); + void visitThrowAddrInst(ThrowAddrInst *i); void visitUnwindInst(UnwindInst *i); void visitYieldInst(YieldInst *i); void visitSwitchValueInst(SwitchValueInst *i); @@ -4251,23 +4252,34 @@ void IRGenSILFunction::visitReturnInst(swift::ReturnInst *i) { void IRGenSILFunction::visitThrowInst(swift::ThrowInst *i) { SILFunctionConventions conv(CurSILFn->getLoweredFunctionType(), getSILModule()); + assert(!conv.hasIndirectSILErrorResults()); + if (!isAsync()) { if (conv.isTypedError()) { llvm::Constant *flag = llvm::ConstantInt::get(IGM.IntPtrTy, 1); flag = llvm::ConstantExpr::getIntToPtr(flag, IGM.Int8PtrTy); - if (!conv.hasIndirectSILErrorResults()) { - Explosion errorResult = getLoweredExplosion(i->getOperand()); - auto &ti = cast(IGM.getTypeInfo(conv.getSILErrorType( - IGM.getMaximalTypeExpansionContext()))); - ti.initialize(*this, errorResult, getCallerTypedErrorResultSlot(), false); - } + Explosion errorResult = getLoweredExplosion(i->getOperand()); + auto &ti = cast(IGM.getTypeInfo(conv.getSILErrorType( + IGM.getMaximalTypeExpansionContext()))); + ti.initialize(*this, errorResult, getCallerTypedErrorResultSlot(), false); + Builder.CreateStore(flag, getCallerErrorResultSlot()); } else { Explosion errorResult = getLoweredExplosion(i->getOperand()); Builder.CreateStore(errorResult.claimNext(), getCallerErrorResultSlot()); } - // Async functions just return to the continuation. - } else if (isAsync()) { + + // Create a normal return, but leaving the return value undefined. + auto fnTy = CurFn->getFunctionType(); + auto retTy = fnTy->getReturnType(); + if (retTy->isVoidTy()) { + Builder.CreateRetVoid(); + } else { + Builder.CreateRet(llvm::UndefValue::get(retTy)); + } + + // Async functions just return to the continuation. + } else { // Store the exception to the error slot. auto exn = getLoweredExplosion(i->getOperand()); @@ -4276,13 +4288,9 @@ void IRGenSILFunction::visitThrowInst(swift::ThrowInst *i) { conv.getSILResultType(IGM.getMaximalTypeExpansionContext())); if (conv.isTypedError()) { - if (conv.hasIndirectSILErrorResults()) { - (void)exn.claimAll(); - } else { - auto &ti = cast(IGM.getTypeInfo(conv.getSILErrorType( - IGM.getMaximalTypeExpansionContext()))); - ti.initialize(*this, exn, getCallerTypedErrorResultSlot(), false); - } + auto &ti = cast(IGM.getTypeInfo(conv.getSILErrorType( + IGM.getMaximalTypeExpansionContext()))); + ti.initialize(*this, exn, getCallerTypedErrorResultSlot(), false); llvm::Constant *flag = llvm::ConstantInt::get(IGM.IntPtrTy, 1); flag = llvm::ConstantExpr::getIntToPtr(flag, IGM.Int8PtrTy); assert(exn.empty() && "Unclaimed typed error results"); @@ -4293,16 +4301,44 @@ void IRGenSILFunction::visitThrowInst(swift::ThrowInst *i) { Explosion empty; emitAsyncReturn(*this, layout, funcResultType, i->getFunction()->getLoweredFunctionType(), empty, exn); - return; } +} - // Create a normal return, but leaving the return value undefined. - auto fnTy = CurFn->getFunctionType(); - auto retTy = fnTy->getReturnType(); - if (retTy->isVoidTy()) { - Builder.CreateRetVoid(); +void IRGenSILFunction::visitThrowAddrInst(swift::ThrowAddrInst *i) { + SILFunctionConventions conv(CurSILFn->getLoweredFunctionType(), + getSILModule()); + assert(conv.isTypedError()); + assert(conv.hasIndirectSILErrorResults()); + + if (!isAsync()) { + llvm::Constant *flag = llvm::ConstantInt::get(IGM.IntPtrTy, 1); + flag = llvm::ConstantExpr::getIntToPtr(flag, IGM.Int8PtrTy); + Builder.CreateStore(flag, getCallerErrorResultSlot()); + + // Create a normal return, but leaving the return value undefined. + auto fnTy = CurFn->getFunctionType(); + auto retTy = fnTy->getReturnType(); + if (retTy->isVoidTy()) { + Builder.CreateRetVoid(); + } else { + Builder.CreateRet(llvm::UndefValue::get(retTy)); + } + + // Async functions just return to the continuation. } else { - Builder.CreateRet(llvm::UndefValue::get(retTy)); + auto layout = getAsyncContextLayout(*this); + auto funcResultType = CurSILFn->mapTypeIntoContext( + conv.getSILResultType(IGM.getMaximalTypeExpansionContext())); + + llvm::Constant *flag = llvm::ConstantInt::get(IGM.IntPtrTy, 1); + flag = llvm::ConstantExpr::getIntToPtr(flag, IGM.Int8PtrTy); + + Explosion exn; + exn.add(flag); + + Explosion empty; + emitAsyncReturn(*this, layout, funcResultType, + i->getFunction()->getLoweredFunctionType(), empty, exn); } } diff --git a/lib/SIL/IR/OperandOwnership.cpp b/lib/SIL/IR/OperandOwnership.cpp index ba5e775bc9ff9..d5a00270f6652 100644 --- a/lib/SIL/IR/OperandOwnership.cpp +++ b/lib/SIL/IR/OperandOwnership.cpp @@ -120,6 +120,7 @@ SHOULD_NEVER_VISIT_INST(StringLiteral) SHOULD_NEVER_VISIT_INST(StrongRetain) SHOULD_NEVER_VISIT_INST(Unreachable) SHOULD_NEVER_VISIT_INST(Unwind) +SHOULD_NEVER_VISIT_INST(ThrowAddr) SHOULD_NEVER_VISIT_INST(ReleaseValue) SHOULD_NEVER_VISIT_INST(ReleaseValueAddr) SHOULD_NEVER_VISIT_INST(StrongRelease) diff --git a/lib/SIL/IR/SILArgument.cpp b/lib/SIL/IR/SILArgument.cpp index 30ec6a195c05a..11f362c533a0b 100644 --- a/lib/SIL/IR/SILArgument.cpp +++ b/lib/SIL/IR/SILArgument.cpp @@ -312,6 +312,7 @@ getSingleTerminatorOperandForPred(const SILBasicBlock *parentBlock, case TermKind::UnreachableInst: case TermKind::ReturnInst: case TermKind::ThrowInst: + case TermKind::ThrowAddrInst: case TermKind::UnwindInst: llvm_unreachable("Have terminator that implies no successors?!"); case TermKind::TryApplyInst: diff --git a/lib/SIL/IR/SILInstructions.cpp b/lib/SIL/IR/SILInstructions.cpp index 45e6b3e54da94..091f06c910276 100644 --- a/lib/SIL/IR/SILInstructions.cpp +++ b/lib/SIL/IR/SILInstructions.cpp @@ -1699,6 +1699,7 @@ bool TermInst::isFunctionExiting() const { return false; case TermKind::ReturnInst: case TermKind::ThrowInst: + case TermKind::ThrowAddrInst: case TermKind::UnwindInst: return true; } @@ -1719,6 +1720,7 @@ bool TermInst::isProgramTerminating() const { case TermKind::CheckedCastAddrBranchInst: case TermKind::ReturnInst: case TermKind::ThrowInst: + case TermKind::ThrowAddrInst: case TermKind::UnwindInst: case TermKind::TryApplyInst: case TermKind::YieldInst: @@ -1745,6 +1747,7 @@ const Operand *TermInst::forwardedOperand() const { case TermKind::UnreachableInst: case TermKind::ReturnInst: case TermKind::ThrowInst: + case TermKind::ThrowAddrInst: case TermKind::YieldInst: case TermKind::TryApplyInst: case TermKind::CondBranchInst: diff --git a/lib/SIL/IR/SILPrinter.cpp b/lib/SIL/IR/SILPrinter.cpp index 2ccbfd9c8e882..63822a40583e4 100644 --- a/lib/SIL/IR/SILPrinter.cpp +++ b/lib/SIL/IR/SILPrinter.cpp @@ -2625,6 +2625,10 @@ class SILPrinter : public SILInstructionVisitor { *this << getIDAndType(TI->getOperand()); } + void visitThrowAddrInst(ThrowAddrInst *TAI) { + // no operands + } + void visitUnwindInst(UnwindInst *UI) { // no operands } diff --git a/lib/SIL/Parser/ParseSIL.cpp b/lib/SIL/Parser/ParseSIL.cpp index dd2d382e063ca..8129552c44908 100644 --- a/lib/SIL/Parser/ParseSIL.cpp +++ b/lib/SIL/Parser/ParseSIL.cpp @@ -5457,6 +5457,12 @@ bool SILParser::parseSpecificSILInstruction(SILBuilder &B, ResultVal = B.createThrow(InstLoc, Val); break; } + case SILInstructionKind::ThrowAddrInst: { + if (parseSILDebugLocation(InstLoc, B)) + return true; + ResultVal = B.createThrowAddr(InstLoc); + break; + } case SILInstructionKind::UnwindInst: { if (parseSILDebugLocation(InstLoc, B)) return true; diff --git a/lib/SIL/Utils/BasicBlockUtils.cpp b/lib/SIL/Utils/BasicBlockUtils.cpp index a0a522270cb6a..dd49ff790d1ad 100644 --- a/lib/SIL/Utils/BasicBlockUtils.cpp +++ b/lib/SIL/Utils/BasicBlockUtils.cpp @@ -232,6 +232,7 @@ void swift::getEdgeArgs(TermInst *T, unsigned edgeIdx, SILBasicBlock *newEdgeBB, case SILInstructionKind::ReturnInst: case SILInstructionKind::ThrowInst: + case SILInstructionKind::ThrowAddrInst: case SILInstructionKind::UnwindInst: case SILInstructionKind::UnreachableInst: llvm_unreachable("terminator never has successors"); diff --git a/lib/SIL/Utils/InstructionUtils.cpp b/lib/SIL/Utils/InstructionUtils.cpp index 157a222ddb9ef..f4da211239e90 100644 --- a/lib/SIL/Utils/InstructionUtils.cpp +++ b/lib/SIL/Utils/InstructionUtils.cpp @@ -524,6 +524,7 @@ RuntimeEffect swift::getRuntimeEffect(SILInstruction *inst, SILType &impactType) case SILInstructionKind::UnreachableInst: case SILInstructionKind::ReturnInst: case SILInstructionKind::ThrowInst: + case SILInstructionKind::ThrowAddrInst: case SILInstructionKind::YieldInst: case SILInstructionKind::UnwindInst: case SILInstructionKind::BranchInst: diff --git a/lib/SIL/Verifier/SILVerifier.cpp b/lib/SIL/Verifier/SILVerifier.cpp index e6192c1036060..b6e34ad32c99c 100644 --- a/lib/SIL/Verifier/SILVerifier.cpp +++ b/lib/SIL/Verifier/SILVerifier.cpp @@ -6244,7 +6244,8 @@ class SILVerifier : public SILVerifierBase { require(!FoundReturnBlock, "more than one return block in function"); FoundReturnBlock = true; - } else if (isa(BB.getTerminator())) { + } else if (isa(BB.getTerminator()) || + isa(BB.getTerminator())) { require(!FoundThrowBlock, "more than one throw block in function"); FoundThrowBlock = true; diff --git a/lib/SILOptimizer/ARC/ARCSequenceOptUtils.cpp b/lib/SILOptimizer/ARC/ARCSequenceOptUtils.cpp index e29e1ea47ac16..064a915c5b72b 100644 --- a/lib/SILOptimizer/ARC/ARCSequenceOptUtils.cpp +++ b/lib/SILOptimizer/ARC/ARCSequenceOptUtils.cpp @@ -28,6 +28,7 @@ bool isARCSignificantTerminator(TermInst *TI) { // Be conservative for now. These actually perform some sort of operation // against the operand or can use the value in some way. case TermKind::ThrowInst: + case TermKind::ThrowAddrInst: case TermKind::ReturnInst: case TermKind::UnwindInst: case TermKind::YieldInst: diff --git a/lib/SILOptimizer/Mandatory/PredictableMemOpt.cpp b/lib/SILOptimizer/Mandatory/PredictableMemOpt.cpp index 4dcd1aeaa19e7..b4d955f250099 100644 --- a/lib/SILOptimizer/Mandatory/PredictableMemOpt.cpp +++ b/lib/SILOptimizer/Mandatory/PredictableMemOpt.cpp @@ -2754,6 +2754,7 @@ bool AllocOptimize::tryToRemoveDeadAllocation() { LLVM_FALLTHROUGH; case TermKind::ReturnInst: case TermKind::ThrowInst: + case TermKind::ThrowAddrInst: case TermKind::UnwindInst: case TermKind::YieldInst: { // These terminators can never be non-consuming uses of an owned diff --git a/lib/SILOptimizer/Transforms/DeadCodeElimination.cpp b/lib/SILOptimizer/Transforms/DeadCodeElimination.cpp index e33eab4631726..1b2bcec8897a5 100644 --- a/lib/SILOptimizer/Transforms/DeadCodeElimination.cpp +++ b/lib/SILOptimizer/Transforms/DeadCodeElimination.cpp @@ -384,9 +384,9 @@ void DCE::markTerminatorArgsLive(SILBasicBlock *Pred, switch (Term->getTermKind()) { case TermKind::ReturnInst: case TermKind::ThrowInst: + case TermKind::ThrowAddrInst: case TermKind::UnwindInst: case TermKind::YieldInst: - case TermKind::UnreachableInst: case TermKind::SwitchValueInst: case TermKind::SwitchEnumAddrInst: @@ -490,6 +490,7 @@ void DCE::propagateLiveness(SILInstruction *I) { case TermKind::BranchInst: case TermKind::UnreachableInst: case TermKind::UnwindInst: + case TermKind::ThrowAddrInst: return; case TermKind::ReturnInst: diff --git a/lib/SILOptimizer/Transforms/SimplifyCFG.cpp b/lib/SILOptimizer/Transforms/SimplifyCFG.cpp index e37f506939907..7ccf0dc8ad997 100644 --- a/lib/SILOptimizer/Transforms/SimplifyCFG.cpp +++ b/lib/SILOptimizer/Transforms/SimplifyCFG.cpp @@ -2809,6 +2809,7 @@ bool SimplifyCFG::simplifyBlocks() { Changed |= simplifyTermWithIdenticalDestBlocks(BB); break; case TermKind::ThrowInst: + case TermKind::ThrowAddrInst: case TermKind::DynamicMethodBranchInst: case TermKind::ReturnInst: case TermKind::UnwindInst: diff --git a/lib/SILOptimizer/UtilityPasses/SerializeSILPass.cpp b/lib/SILOptimizer/UtilityPasses/SerializeSILPass.cpp index 08ccd3840b434..bacedb1f9ec82 100644 --- a/lib/SILOptimizer/UtilityPasses/SerializeSILPass.cpp +++ b/lib/SILOptimizer/UtilityPasses/SerializeSILPass.cpp @@ -270,6 +270,7 @@ static bool hasOpaqueArchetype(TypeExpansionContext context, case SILInstructionKind::UnreachableInst: case SILInstructionKind::ReturnInst: case SILInstructionKind::ThrowInst: + case SILInstructionKind::ThrowAddrInst: case SILInstructionKind::YieldInst: case SILInstructionKind::UnwindInst: case SILInstructionKind::BranchInst: diff --git a/lib/SILOptimizer/Utils/CFGOptUtils.cpp b/lib/SILOptimizer/Utils/CFGOptUtils.cpp index 3bf32b15fa565..58b21d1c5409d 100644 --- a/lib/SILOptimizer/Utils/CFGOptUtils.cpp +++ b/lib/SILOptimizer/Utils/CFGOptUtils.cpp @@ -510,6 +510,7 @@ static bool isSafeNonExitTerminator(TermInst *ti) { case TermKind::UnreachableInst: case TermKind::ReturnInst: case TermKind::ThrowInst: + case TermKind::ThrowAddrInst: case TermKind::UnwindInst: return false; // yield is special because it can do arbitrary, diff --git a/lib/SILOptimizer/Utils/SILInliner.cpp b/lib/SILOptimizer/Utils/SILInliner.cpp index d846bd0b94ded..16f944428f2c0 100644 --- a/lib/SILOptimizer/Utils/SILInliner.cpp +++ b/lib/SILOptimizer/Utils/SILInliner.cpp @@ -1000,6 +1000,7 @@ InlineCost swift::instructionInlineCost(SILInstruction &I) { case SILInstructionKind::UnreachableInst: case SILInstructionKind::ReturnInst: case SILInstructionKind::ThrowInst: + case SILInstructionKind::ThrowAddrInst: case SILInstructionKind::UnwindInst: case SILInstructionKind::YieldInst: case SILInstructionKind::EndCOWMutationInst: diff --git a/lib/Serialization/DeserializeSIL.cpp b/lib/Serialization/DeserializeSIL.cpp index 0e5b63faeca92..f9a3fd6fbfc15 100644 --- a/lib/Serialization/DeserializeSIL.cpp +++ b/lib/Serialization/DeserializeSIL.cpp @@ -2999,6 +2999,10 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, ResultInst = Builder.createUnwind(Loc); break; } + case SILInstructionKind::ThrowAddrInst: { + ResultInst = Builder.createThrowAddr(Loc); + break; + } case SILInstructionKind::YieldInst: { SILBasicBlock *unwindBB = getBBForReference(Fn, ListOfValues.back()); ListOfValues = ListOfValues.drop_back(); diff --git a/lib/Serialization/ModuleFormat.h b/lib/Serialization/ModuleFormat.h index ef23709cf9255..6770d43beb402 100644 --- a/lib/Serialization/ModuleFormat.h +++ b/lib/Serialization/ModuleFormat.h @@ -58,7 +58,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 = 815; // nonisolated(unsafe) +const uint16_t SWIFTMODULE_VERSION_MINOR = 816; // throw_addr /// A standard hash seed used for all string hashes in a serialized module. /// diff --git a/lib/Serialization/SerializeSIL.cpp b/lib/Serialization/SerializeSIL.cpp index 98e40a2fb4e03..17bb7cc2ae48b 100644 --- a/lib/Serialization/SerializeSIL.cpp +++ b/lib/Serialization/SerializeSIL.cpp @@ -932,6 +932,7 @@ void SILSerializer::writeSILInstruction(const SILInstruction &SI) { return; case SILInstructionKind::UnwindInst: + case SILInstructionKind::ThrowAddrInst: case SILInstructionKind::UnreachableInst: { writeNoOperandLayout(&SI); break; diff --git a/test/IRGen/typed_throws.sil b/test/IRGen/typed_throws.sil index 24cfe6021981e..0e96996db43e2 100644 --- a/test/IRGen/typed_throws.sil +++ b/test/IRGen/typed_throws.sil @@ -302,7 +302,7 @@ bb0(%0: $*τ_0_1, %1: $*τ_0_0, %2 : $Builtin.Int64, %3 : $*τ_0_0): sil @throwError : $@convention(thin) <τ_0_0> (Builtin.Int64, @in τ_0_0) -> @error_indirect τ_0_0 { bb0(%0: $*τ_0_0, %1 : $Builtin.Int64, %2 : $*τ_0_0): copy_addr [take] %2 to [init] %0 : $*τ_0_0 - throw %0 : $*τ_0_0 + throw_addr } sil @throwError2 : $@convention(thin) (Builtin.Int64, @in T) -> (@error_indirect T, @out V) @@ -352,7 +352,7 @@ bb0(%0: $*τ_0_0, %1 : $Builtin.Int64, %2 : $*τ_0_0): sil @throwErrorAsync : $@convention(thin) @async <τ_0_0> (Builtin.Int64, @in τ_0_0) -> @error_indirect τ_0_0 { bb0(%0: $*τ_0_0, %1 : $Builtin.Int64, %2 : $*τ_0_0): copy_addr [take] %2 to [init] %0 : $*τ_0_0 - throw %0 : $*τ_0_0 + throw_addr } sil @throwError2Async : $@convention(thin) @async (Builtin.Int64, @in T) -> (@error_indirect T, @out V) From 863604e3930e9bff05e93d663713c205fa9f39a5 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Tue, 31 Oct 2023 15:29:42 -0400 Subject: [PATCH 2/3] SIL: Fix MemoryLifetimeVerifier for try_apply with indirect error result --- include/swift/SIL/ApplySite.h | 8 +++++++ lib/SIL/Verifier/MemoryLifetimeVerifier.cpp | 26 ++++++++++++++------- 2 files changed, 25 insertions(+), 9 deletions(-) diff --git a/include/swift/SIL/ApplySite.h b/include/swift/SIL/ApplySite.h index 24f2948e35bca..49be6db22352f 100644 --- a/include/swift/SIL/ApplySite.h +++ b/include/swift/SIL/ApplySite.h @@ -741,6 +741,14 @@ class FullApplySite : public ApplySite { && (getCalleeArgIndex(op) < getNumIndirectSILResults()); } + /// Returns true if \p op is an operand that passes an indirect + /// result argument to the apply site. + bool isIndirectErrorResultOperand(const Operand &op) const { + return isArgumentOperand(op) + && (getCalleeArgIndex(op) >= getNumIndirectSILResults()) + && (getCalleeArgIndex(op) < getNumIndirectSILErrorResults()); + } + static FullApplySite getFromOpaqueValue(void *p) { return FullApplySite(p); } static bool classof(const SILInstruction *inst) { diff --git a/lib/SIL/Verifier/MemoryLifetimeVerifier.cpp b/lib/SIL/Verifier/MemoryLifetimeVerifier.cpp index 11866ad54277c..c928c9157f675 100644 --- a/lib/SIL/Verifier/MemoryLifetimeVerifier.cpp +++ b/lib/SIL/Verifier/MemoryLifetimeVerifier.cpp @@ -490,16 +490,24 @@ void MemoryLifetimeVerifier::setBitsOfPredecessor(Bits &getSet, Bits &killSet, TermInst *term = pred->getTerminator(); if (auto *tai = dyn_cast(term)) { - // @out results of try_apply are only valid in the normal-block, but not in - // the throw-block. - if (tai->getNormalBB() != block) - return; - FullApplySite FAS(tai); - for (Operand &op : tai->getAllOperands()) { - if (FAS.isArgumentOperand(op) && - FAS.getArgumentConvention(op) == SILArgumentConvention::Indirect_Out) { - locations.genBits(getSet, killSet, op.get()); + + if (block == tai->getNormalBB()) { + // @out results of try_apply are only valid in the normal-block. + for (Operand &op : tai->getAllOperands()) { + if (FAS.isArgumentOperand(op) && + FAS.isIndirectResultOperand(op)) { + locations.genBits(getSet, killSet, op.get()); + } + } + } else { + // @error_indirect results of try_apply are only valid in the error-block. + assert(block == tai->getErrorBB()); + for (Operand &op : tai->getAllOperands()) { + if (FAS.isArgumentOperand(op) && + FAS.isIndirectErrorResultOperand(op)) { + locations.genBits(getSet, killSet, op.get()); + } } } } else if (auto *castInst = dyn_cast(term)) { From 079c5c4a1c3c36fde4ac1b710f679e44fc26764c Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Tue, 31 Oct 2023 15:30:27 -0400 Subject: [PATCH 3/3] SILGen: Support for 'throw' and 'try_apply' with indirect error result --- lib/SILGen/JumpDest.h | 24 ++++- lib/SILGen/SILGenApply.cpp | 40 +++++++-- lib/SILGen/SILGenDistributed.cpp | 2 + lib/SILGen/SILGenEpilog.cpp | 16 +++- lib/SILGen/SILGenExpr.cpp | 28 ++++-- lib/SILGen/SILGenFunction.h | 6 +- lib/SILGen/SILGenStmt.cpp | 120 ++++++++++++++++++------- test/SILGen/typed_throws_generic.swift | 40 ++++++++- 8 files changed, 219 insertions(+), 57 deletions(-) diff --git a/lib/SILGen/JumpDest.h b/lib/SILGen/JumpDest.h index 0168c21edd807..ea25bbaf21ddb 100644 --- a/lib/SILGen/JumpDest.h +++ b/lib/SILGen/JumpDest.h @@ -27,6 +27,13 @@ namespace swift { namespace Lowering { +struct LLVM_LIBRARY_VISIBILITY ThrownErrorInfo { + SILValue IndirectErrorResult; + + explicit ThrownErrorInfo(SILValue IndirectErrorResult) + : IndirectErrorResult(IndirectErrorResult) {} +}; + /// The destination of a direct jump. Swift currently does not /// support indirect branches or goto, so the jump mechanism only /// needs to worry about branches out of scopes, not into them. @@ -34,11 +41,17 @@ class LLVM_LIBRARY_VISIBILITY JumpDest { SILBasicBlock *Block = nullptr; CleanupsDepth Depth = CleanupsDepth::invalid(); CleanupLocation CleanupLoc; + llvm::Optional ThrownError; + public: JumpDest(CleanupLocation L) : CleanupLoc(L) {} - JumpDest(SILBasicBlock *block, CleanupsDepth depth, CleanupLocation l) - : Block(block), Depth(depth), CleanupLoc(l) {} + JumpDest(SILBasicBlock *block, + CleanupsDepth depth, + CleanupLocation l, + llvm::Optional ThrownError = llvm::None) + : Block(block), Depth(depth), CleanupLoc(l), + ThrownError(ThrownError) {} SILBasicBlock *getBlock() const { return Block; } SILBasicBlock *takeBlock() { @@ -49,7 +62,14 @@ class LLVM_LIBRARY_VISIBILITY JumpDest { CleanupsDepth getDepth() const { return Depth; } CleanupLocation getCleanupLocation() const { return CleanupLoc; } + ThrownErrorInfo getThrownError() const { + assert(ThrownError); + return *ThrownError; + } + JumpDest translate(CleanupsDepth NewDepth) && { + assert(!ThrownError); + JumpDest NewValue(Block, NewDepth, CleanupLoc); Block = nullptr; Depth = CleanupsDepth::invalid(); diff --git a/lib/SILGen/SILGenApply.cpp b/lib/SILGen/SILGenApply.cpp index 82f964b63129e..5f9d7e529151f 100644 --- a/lib/SILGen/SILGenApply.cpp +++ b/lib/SILGen/SILGenApply.cpp @@ -1856,6 +1856,7 @@ static void emitRawApply(SILGenFunction &SGF, CanSILFunctionType substFnType, ApplyOptions options, ArrayRef indirectResultAddrs, + SILValue indirectErrorAddr, SmallVectorImpl &rawResults, ExecutorBreadcrumb prevExecutor) { SILFunctionConventions substFnConv(substFnType, SGF.SGM.M); @@ -1880,6 +1881,10 @@ static void emitRawApply(SILGenFunction &SGF, #endif argValues.append(indirectResultAddrs.begin(), indirectResultAddrs.end()); + assert(!!indirectErrorAddr == substFnConv.hasIndirectSILErrorResults()); + if (indirectErrorAddr) + argValues.push_back(indirectErrorAddr); + auto inputParams = substFnType->getParameters(); assert(inputParams.size() == args.size()); @@ -1978,6 +1983,7 @@ static void emitRawApply(SILGenFunction &SGF, SILBasicBlock *errorBB = SGF.getTryApplyErrorDest(loc, substFnType, prevExecutor, substFnType->getErrorResult(), + indirectErrorAddr, options.contains(ApplyFlags::DoesNotThrow)); options -= ApplyFlags::DoesNotThrow; @@ -4876,7 +4882,8 @@ SILGenFunction::emitBeginApply(SILLocation loc, ManagedValue fn, // Emit the call. SmallVector rawResults; emitRawApply(*this, loc, fn, subs, args, substFnType, options, - /*indirect results*/ {}, rawResults, ExecutorBreadcrumb()); + /*indirect results*/ {}, /*indirect errors*/ {}, + rawResults, ExecutorBreadcrumb()); auto token = rawResults.pop_back_val(); auto yieldValues = llvm::makeArrayRef(rawResults); @@ -5411,6 +5418,16 @@ RValue SILGenFunction::emitApply( SmallVector indirectResultAddrs; resultPlan->gatherIndirectResultAddrs(*this, loc, indirectResultAddrs); + SILValue indirectErrorAddr; + if (substFnType->hasErrorResult()) { + auto errorResult = substFnType->getErrorResult(); + if (errorResult.getConvention() == ResultConvention::Indirect) { + auto loweredErrorResultType = getSILType(errorResult, substFnType); + indirectErrorAddr = B.createAllocStack(loc, loweredErrorResultType); + enterDeallocStackCleanup(indirectErrorAddr); + } + } + // If the function returns an inner pointer, we'll need to lifetime-extend // the 'self' parameter. SILValue lifetimeExtendedSelf; @@ -5540,7 +5557,8 @@ RValue SILGenFunction::emitApply( { SmallVector rawDirectResults; emitRawApply(*this, loc, fn, subs, args, substFnType, options, - indirectResultAddrs, rawDirectResults, breadcrumb); + indirectResultAddrs, indirectErrorAddr, + rawDirectResults, breadcrumb); assert(rawDirectResults.size() == 1); rawDirectResult = rawDirectResults[0]; } @@ -5709,12 +5727,22 @@ SILValue SILGenFunction::emitApplyWithRethrow(SILLocation loc, SILValue fn, // Emit the rethrow logic. { B.emitBlock(errorBB); - SILValue error = errorBB->createPhiArgument( - fnConv.getSILErrorType(getTypeExpansionContext()), - OwnershipKind::Owned); + + SILValue error; + bool indirectError = fnConv.hasIndirectSILErrorResults(); + + if (!indirectError) { + error = errorBB->createPhiArgument( + fnConv.getSILErrorType(getTypeExpansionContext()), + OwnershipKind::Owned); + } Cleanups.emitCleanupsForReturn(CleanupLocation(loc), IsForUnwind); - B.createThrow(loc, error); + + if (!indirectError) + B.createThrow(loc, error); + else + B.createThrowAddr(loc); } // Enter the normal path. diff --git a/lib/SILGen/SILGenDistributed.cpp b/lib/SILGen/SILGenDistributed.cpp index 9d79696bb89a7..03ce2484f7cd4 100644 --- a/lib/SILGen/SILGenDistributed.cpp +++ b/lib/SILGen/SILGenDistributed.cpp @@ -479,6 +479,8 @@ void SILGenFunction::emitDistributedActorFactory(FuncDecl *fd) { // TODO(distrib OwnershipKind::Owned); Cleanups.emitCleanupsForReturn(CleanupLocation(loc), IsForUnwind); + + // FIXME: typed throws B.createThrow(loc, error); } } diff --git a/lib/SILGen/SILGenEpilog.cpp b/lib/SILGen/SILGenEpilog.cpp index 0520c19af569a..6527bb8ba001d 100644 --- a/lib/SILGen/SILGenEpilog.cpp +++ b/lib/SILGen/SILGenEpilog.cpp @@ -90,7 +90,8 @@ void SILGenFunction::prepareRethrowEpilog( rethrowBB->createPhiArgument(loweredErrorType, OwnershipKind::Owned); } - ThrowDest = JumpDest(rethrowBB, getCleanupsDepth(), cleanupLoc); + ThrowDest = JumpDest(rethrowBB, getCleanupsDepth(), cleanupLoc, + ThrownErrorInfo(IndirectErrorResult)); } void SILGenFunction::prepareCoroutineUnwindEpilog(CleanupLocation cleanupLoc) { @@ -403,12 +404,21 @@ static bool prepareExtraEpilog(SILGenFunction &SGF, JumpDest &dest, void SILGenFunction::emitRethrowEpilog(SILLocation topLevel) { SILValue exn; SILLocation throwLoc = topLevel; - if (!prepareExtraEpilog(*this, ThrowDest, throwLoc, &exn)) + + if (!prepareExtraEpilog(*this, ThrowDest, throwLoc, + !IndirectErrorResult ? &exn : nullptr)) { return; + } Cleanups.emitCleanupsForReturn(ThrowDest.getCleanupLocation(), IsForUnwind); - B.createThrow(CleanupLocation(throwLoc), exn); + // FIXME: opaque values + if (!IndirectErrorResult) { + B.createThrow(CleanupLocation(throwLoc), exn); + } else { + assert(IndirectErrorResult); + B.createThrowAddr(CleanupLocation(throwLoc)); + } ThrowDest = JumpDest::invalid(); } diff --git a/lib/SILGen/SILGenExpr.cpp b/lib/SILGen/SILGenExpr.cpp index 580dac67f7c42..2f2ddc6ed81f4 100644 --- a/lib/SILGen/SILGenExpr.cpp +++ b/lib/SILGen/SILGenExpr.cpp @@ -1147,8 +1147,15 @@ SILGenFunction::ForceTryEmission::ForceTryEmission(SILGenFunction &SGF, // Set up a "catch" block for when an error occurs. SILBasicBlock *catchBB = SGF.createBasicBlock(FunctionSection::Postmatter); + + // FIXME: typed throws + ASTContext &ctx = SGF.getASTContext(); + (void) catchBB->createPhiArgument(SILType::getExceptionType(ctx), + OwnershipKind::Owned); + SGF.ThrowDest = JumpDest(catchBB, SGF.Cleanups.getCleanupsDepth(), - CleanupLocation(loc)); + CleanupLocation(loc), + ThrownErrorInfo(/*indirectErrorAddr=*/nullptr)); } void SILGenFunction::ForceTryEmission::finish() { @@ -1164,10 +1171,11 @@ void SILGenFunction::ForceTryEmission::finish() { // Otherwise, we need to emit it. SILGenSavedInsertionPoint scope(SGF, catchBB, FunctionSection::Postmatter); - if (auto diagnoseError = SGF.getASTContext().getDiagnoseUnexpectedError()) { - ASTContext &ctx = SGF.getASTContext(); - auto error = SGF.B.createTermResult(SILType::getExceptionType(ctx), - OwnershipKind::Owned); + // FIXME: typed throws + auto error = ManagedValue::forForwardedRValue(SGF, catchBB->getArgument(0)); + + ASTContext &ctx = SGF.getASTContext(); + if (auto diagnoseError = ctx.getDiagnoseUnexpectedError()) { auto args = SGF.emitSourceLocationArgs(Loc->getExclaimLoc(), Loc); SGF.emitApplyOfLibraryIntrinsic( @@ -1237,9 +1245,15 @@ RValue RValueEmitter::visitOptionalTryExpr(OptionalTryExpr *E, SGFContext C) { // Set up a "catch" block for when an error occurs. SILBasicBlock *catchBB = SGF.createBasicBlock(FunctionSection::Postmatter); + + // FIXME: typed throws + auto *errorArg = catchBB->createPhiArgument( + SILType::getExceptionType(SGF.getASTContext()), OwnershipKind::Owned); + llvm::SaveAndRestore throwDest{ SGF.ThrowDest, - JumpDest(catchBB, SGF.Cleanups.getCleanupsDepth(), E)}; + JumpDest(catchBB, SGF.Cleanups.getCleanupsDepth(), E, + ThrownErrorInfo(/*indirectErrorAddr=*/nullptr))}; SILValue branchArg; if (shouldWrapInOptional) { @@ -1298,8 +1312,6 @@ RValue RValueEmitter::visitOptionalTryExpr(OptionalTryExpr *E, SGFContext C) { // result type. SGF.B.emitBlock(catchBB); FullExpr catchCleanups(SGF.Cleanups, E); - auto *errorArg = catchBB->createPhiArgument( - SILType::getExceptionType(SGF.getASTContext()), OwnershipKind::Owned); (void) SGF.emitManagedRValueWithCleanup(errorArg); catchCleanups.pop(); diff --git a/lib/SILGen/SILGenFunction.h b/lib/SILGen/SILGenFunction.h index d0d829e23f149..2dfb569a67d93 100644 --- a/lib/SILGen/SILGenFunction.h +++ b/lib/SILGen/SILGenFunction.h @@ -389,7 +389,8 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction JumpDest FailDest = JumpDest::invalid(); /// The destination for throws. The block will always be in the - /// postmatter and takes a BB argument of the exception type. + /// postmatter. For a direct error return, it takes a BB argument + /// of the exception type. JumpDest ThrowDest = JumpDest::invalid(); /// Support for typed throws. @@ -1989,7 +1990,8 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction SILBasicBlock *getTryApplyErrorDest(SILLocation loc, CanSILFunctionType fnTy, ExecutorBreadcrumb prevExecutor, - SILResultInfo exnResult, + SILResultInfo errorResult, + SILValue indirectErrorAddr, bool isSuppressed); /// Emit a dynamic member reference. diff --git a/lib/SILGen/SILGenStmt.cpp b/lib/SILGen/SILGenStmt.cpp index 6c3aa6426ad04..0209ce134936d 100644 --- a/lib/SILGen/SILGenStmt.cpp +++ b/lib/SILGen/SILGenStmt.cpp @@ -194,12 +194,17 @@ namespace { SILBasicBlock *createBasicBlock() { return SGF.createBasicBlock(); } - template - JumpDest createJumpDest(Stmt *cleanupLoc, Args... args) { - return JumpDest(SGF.createBasicBlock(args...), + JumpDest createJumpDest(Stmt *cleanupLoc) { + return JumpDest(SGF.createBasicBlock(), SGF.getCleanupsDepth(), CleanupLocation(cleanupLoc)); } + JumpDest createThrowDest(Stmt *cleanupLoc, ThrownErrorInfo errorInfo) { + return JumpDest(SGF.createBasicBlock(FunctionSection::Postmatter), + SGF.getCleanupsDepth(), + CleanupLocation(cleanupLoc), + errorInfo); + } }; } // end anonymous namespace @@ -1114,11 +1119,24 @@ void StmtEmitter::visitDoCatchStmt(DoCatchStmt *S) { Type formalExnType = S->getCaughtErrorType(); auto &exnTL = SGF.getTypeLowering(formalExnType); + SILValue exnArg; + + // FIXME: opaque values + if (exnTL.isAddressOnly()) { + exnArg = SGF.B.createAllocStack( + S, exnTL.getLoweredType()); + SGF.enterDeallocStackCleanup(exnArg); + } + // Create the throw destination at the end of the function. - JumpDest throwDest = createJumpDest(S->getBody(), - FunctionSection::Postmatter); - SILArgument *exnArg = throwDest.getBlock()->createPhiArgument( - exnTL.getLoweredType(), OwnershipKind::Owned); + JumpDest throwDest = createThrowDest(S->getBody(), + ThrownErrorInfo(exnArg)); + + // FIXME: opaque values + if (!exnTL.isAddressOnly()) { + exnArg = throwDest.getBlock()->createPhiArgument( + exnTL.getLoweredType(), OwnershipKind::Owned); + } // We always need a continuation block because we might fall out of // a catch block. But we don't need a loop block unless the 'do' @@ -1452,15 +1470,20 @@ SILBasicBlock * SILGenFunction::getTryApplyErrorDest(SILLocation loc, CanSILFunctionType fnTy, ExecutorBreadcrumb prevExecutor, - SILResultInfo exnResult, + SILResultInfo errorResult, + SILValue indirectErrorAddr, bool suppressErrorPath) { - assert(exnResult.getConvention() == ResultConvention::Owned); - // For now, don't try to re-use destination blocks for multiple // failure sites. SILBasicBlock *destBB = createBasicBlock(FunctionSection::Postmatter); - SILValue exn = destBB->createPhiArgument(getSILType(exnResult, fnTy), + + SILValue errorValue; + if (errorResult.getConvention() == ResultConvention::Owned) { + errorValue = destBB->createPhiArgument(getSILType(errorResult, fnTy), OwnershipKind::Owned); + } else { + errorValue = indirectErrorAddr; + } assert(B.hasValidInsertionPoint() && B.insertingAtEndOfBlock()); SILGenSavedInsertionPoint savedIP(*this, destBB, FunctionSection::Postmatter); @@ -1477,7 +1500,7 @@ SILGenFunction::getTryApplyErrorDest(SILLocation loc, // We don't want to exit here with a dead cleanup on the stack, // so push the scope first. FullExpr scope(Cleanups, CleanupLocation(loc)); - emitThrow(loc, emitManagedRValueWithCleanup(exn)); + emitThrow(loc, emitManagedRValueWithCleanup(errorValue)); return destBB; } @@ -1493,33 +1516,49 @@ void SILGenFunction::emitThrow(SILLocation loc, ManagedValue exnMV, return; } - // Claim the exception value. If we need to handle throwing - // cleanups, the correct thing to do here is to recreate the - // exception's cleanup when emitting each cleanup we branch through. - // But for now we aren't bothering. - SILValue exn = exnMV.forward(*this); - - // Whether the thrown exception is already an Error existential box. - SILType existentialBoxType = SILType::getExceptionType(getASTContext()); - bool isExistentialBox = exn->getType() == existentialBoxType; - - // FIXME: Right now, we suppress emission of the willThrow builtin if the - // error isn't already the error existential, because swift_willThrow expects - // the existential box. - if (emitWillThrow && isExistentialBox) { - // Generate a call to the 'swift_willThrow' runtime function to allow the - // debugger to catch the throw event. - B.createBuiltin(loc, SGM.getASTContext().getIdentifier("willThrow"), - SGM.Types.getEmptyTupleType(), {}, {exn}); + SmallVector args; + + auto indirectErrorAddr = ThrowDest.getThrownError().IndirectErrorResult; + + // If exnMV was not provided by the caller, we must have an indirect + // error result that already stores the thrown error. + assert(!exnMV.isInContext() || indirectErrorAddr); + + SILValue exn; + if (!exnMV.isInContext()) { + // Claim the exception value. If we need to handle throwing + // cleanups, the correct thing to do here is to recreate the + // exception's cleanup when emitting each cleanup we branch through. + // But for now we aren't bothering. + exn = exnMV.forward(*this); + + // Whether the thrown exception is already an Error existential box. + SILType existentialBoxType = SILType::getExceptionType(getASTContext()); + bool isExistentialBox = exn->getType() == existentialBoxType; + + // FIXME: Right now, we suppress emission of the willThrow builtin if the + // error isn't already the error existential, because swift_willThrow expects + // the existential box. + if (emitWillThrow && isExistentialBox) { + // Generate a call to the 'swift_willThrow' runtime function to allow the + // debugger to catch the throw event. + B.createBuiltin(loc, SGM.getASTContext().getIdentifier("willThrow"), + SGM.Types.getEmptyTupleType(), {}, {exn}); + } } - // If we don't have an existential box, create one to jump to the throw - // destination. SILBasicBlock &throwBB = *ThrowDest.getBlock(); if (!throwBB.getArguments().empty()) { + assert(exn); + assert(!indirectErrorAddr); + auto errorArg = throwBB.getArguments()[0]; + + // If we don't have an existential box, create one to jump to the throw + // destination. SILType errorArgType = errorArg->getType(); if (exn->getType() != errorArgType) { + SILType existentialBoxType = SILType::getExceptionType(getASTContext()); assert(errorArgType == existentialBoxType); // FIXME: Callers should provide this conformance from places recorded in @@ -1541,8 +1580,23 @@ void SILGenFunction::emitThrow(SILLocation loc, ManagedValue exnMV, exn = exnMV.forward(*this); } + + // A direct error value is passed to the epilog block as a BB argument. + args.push_back(exn); + } else { + assert(indirectErrorAddr); + + // FIXME: opaque values + + // If an error value was provided by the caller, copy it into the + // indirect error result. Otherwise we assume the indirect error + // result has been initialized. + if (exn) { + B.createCopyAddr(loc, exn, indirectErrorAddr, + IsTake, IsInitialization); + } } // Branch to the cleanup destination. - Cleanups.emitBranchAndCleanups(ThrowDest, loc, exn, IsForUnwind); + Cleanups.emitBranchAndCleanups(ThrowDest, loc, args, IsForUnwind); } diff --git a/test/SILGen/typed_throws_generic.swift b/test/SILGen/typed_throws_generic.swift index d14e32c1cdf8b..f7ebfa431e79e 100644 --- a/test/SILGen/typed_throws_generic.swift +++ b/test/SILGen/typed_throws_generic.swift @@ -1,6 +1,40 @@ -// RUN: %target-swift-emit-silgen %s -enable-experimental-feature TypedThrows | %FileCheck %s +// RUN: %target-swift-emit-silgen %s -enable-experimental-feature TypedThrows -enable-experimental-feature FullTypedThrows | %FileCheck %s -public func genericThrows(_: () throws(E) -> ()) throws(E) -> () {} +public func genericThrow(e: E) throws(E) -> () { + throw e +} + +// CHECK-LABEL: sil [ossa] @$s20typed_throws_generic0C5Throw1eyx_txYKs5ErrorRzlF : $@convention(thin) (@in_guaranteed E) -> @error_indirect E { + +// CHECK: bb0(%0 : $*E, %1 : $*E): +// CHECK: [[TMP:%.*]] = alloc_stack $E +// CHECK: copy_addr %1 to [init] [[TMP]] : $*E +// CHECK: copy_addr [take] [[TMP]] to [init] %0 : $*E +// CHECK: dealloc_stack [[TMP]] : $*E +// CHECK: throw_addr + + +public func genericTryApply(fn: () throws(E) -> ()) throws(E) -> () { + try fn() +} + +// CHECK-LABEL: sil [ossa] @$s20typed_throws_generic0C8TryApply2fnyyyxYKXE_txYKs5ErrorRzlF : $@convention(thin) (@guaranteed @noescape @callee_guaranteed @substituted <τ_0_0> () -> @error_indirect τ_0_0 for ) -> @error_indirect E { -// CHECK-LABEL: sil [ossa] @$s20typed_throws_generic0C6ThrowsyyyyxYKXExYKs5ErrorRzlF : $@convention(thin) (@guaranteed @noescape @callee_guaranteed @substituted <τ_0_0> () -> @error_indirect τ_0_0 for ) -> @error_indirect E { // CHECK: bb0(%0 : $*E, %1 : @guaranteed $@noescape @callee_guaranteed @substituted <τ_0_0> () -> @error_indirect τ_0_0 for ): +// CHECK: [[FN:%.*]] = copy_value %1 : $@noescape @callee_guaranteed @substituted <τ_0_0> () -> @error_indirect τ_0_0 for +// CHECK: [[ERROR:%.*]] = alloc_stack $E +// CHECK: [[FN_BORROW:%.*]] = begin_borrow [[FN]] : $@noescape @callee_guaranteed @substituted <τ_0_0> () -> @error_indirect τ_0_0 for +// CHECK: try_apply [[FN_BORROW]]([[ERROR]]) : $@noescape @callee_guaranteed @substituted <τ_0_0> () -> @error_indirect τ_0_0 for , normal bb1, error bb2 + +// CHECK: bb1(%7 : $()): +// CHECK: end_borrow [[FN_BORROW]] +// CHECK: dealloc_stack [[ERROR]] +// CHECK: destroy_value [[FN]] +// CHECK: return + +// CHECK: bb2: +// CHECK: copy_addr [take] [[ERROR]] to [init] %0 : $*E +// CHECK: end_borrow [[FN_BORROW]] +// CHECK: dealloc_stack [[ERROR]] +// CHECK: destroy_value [[FN]] +// CHECK: throw_addr