/// Return a basic block suitable to be the destination block of a /// try_apply instruction. The block is implicitly emitted and filled in. SILBasicBlock * SILGenFunction::getTryApplyErrorDest(SILLocation loc, SILResultInfo exnResult, 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(exnResult.getSILType(), ValueOwnershipKind::Owned); assert(B.hasValidInsertionPoint() && B.insertingAtEndOfBlock()); SavedInsertionPoint savedIP(*this, destBB, FunctionSection::Postmatter); // If we're suppressing error paths, just wrap it up as unreachable // and return. if (suppressErrorPath) { B.createUnreachable(loc); return destBB; } // We don't want to exit here with a dead cleanup on the stack, // so push the scope first. FullExpr scope(Cleanups, CleanupLocation::get(loc)); emitThrow(loc, emitManagedRValueWithCleanup(exn)); return destBB; }
/// Perform a foreign error check by testing whether the error was nil. static void emitErrorIsNonNilErrorCheck(SILGenFunction &SGF, SILLocation loc, ManagedValue errorSlot, bool suppressErrorCheck) { // If we're suppressing the check, just don't check. if (suppressErrorCheck) return; SILValue optionalError = SGF.B.emitLoadValueOperation( loc, errorSlot.forward(SGF), LoadOwnershipQualifier::Take); ASTContext &ctx = SGF.getASTContext(); // Switch on the optional error. SILBasicBlock *errorBB = SGF.createBasicBlock(FunctionSection::Postmatter); errorBB->createPHIArgument(optionalError->getType().unwrapOptionalType(), ValueOwnershipKind::Owned); SILBasicBlock *contBB = SGF.createBasicBlock(); SGF.B.createSwitchEnum(loc, optionalError, /*default*/ nullptr, { { ctx.getOptionalSomeDecl(), errorBB }, { ctx.getOptionalNoneDecl(), contBB } }); // Emit the error block. Pass in none for the errorSlot since we have passed // in the errorSlot as our BB argument so we can pass ownership correctly. In // emitForeignErrorBlock, we will create the appropriate cleanup for the // argument. SGF.emitForeignErrorBlock(loc, errorBB, None); // Return the result. SGF.B.emitBlock(contBB); return; }
Condition SILGenFunction::emitCondition(SILValue V, SILLocation Loc, bool hasFalseCode, bool invertValue, ArrayRef<SILType> contArgs) { assert(B.hasValidInsertionPoint() && "emitting condition at unreachable point"); SILBasicBlock *ContBB = createBasicBlock(); for (SILType argTy : contArgs) { ContBB->createPHIArgument(argTy, ValueOwnershipKind::Owned); } SILBasicBlock *FalseBB, *FalseDestBB; if (hasFalseCode) { FalseBB = FalseDestBB = createBasicBlock(); } else { FalseBB = nullptr; FalseDestBB = ContBB; } SILBasicBlock *TrueBB = createBasicBlock(); if (invertValue) B.createCondBranch(Loc, V, FalseDestBB, TrueBB); else B.createCondBranch(Loc, V, TrueBB, FalseDestBB); return Condition(TrueBB, FalseBB, ContBB, Loc); }
/// Carry out the operations required for an indirect conditional cast /// using a scalar cast operation. void swift::emitIndirectConditionalCastWithScalar( SILBuilder &B, ModuleDecl *M, SILLocation loc, CastConsumptionKind consumption, SILValue src, CanType sourceType, SILValue dest, CanType targetType, SILBasicBlock *indirectSuccBB, SILBasicBlock *indirectFailBB, ProfileCounter TrueCount, ProfileCounter FalseCount) { assert(canUseScalarCheckedCastInstructions(B.getModule(), sourceType, targetType)); // We only need a different failure block if the cast consumption // requires us to destroy the source value. SILBasicBlock *scalarFailBB; if (!shouldDestroyOnFailure(consumption)) { scalarFailBB = indirectFailBB; } else { scalarFailBB = B.splitBlockForFallthrough(); } // We always need a different success block. SILBasicBlock *scalarSuccBB = B.splitBlockForFallthrough(); auto &srcTL = B.getModule().Types.getTypeLowering(src->getType()); // Always take; this works under an assumption that retaining the // result is equivalent to retaining the source. That means that // these casts would not be appropriate for bridging-like conversions. SILValue srcValue = srcTL.emitLoadOfCopy(B, loc, src, IsTake); SILType targetValueType = dest->getType().getObjectType(); B.createCheckedCastBranch(loc, /*exact*/ false, srcValue, targetValueType, scalarSuccBB, scalarFailBB, TrueCount, FalseCount); // Emit the success block. B.setInsertionPoint(scalarSuccBB); { auto &targetTL = B.getModule().Types.getTypeLowering(targetValueType); SILValue succValue = scalarSuccBB->createPHIArgument( targetValueType, ValueOwnershipKind::Owned); if (!shouldTakeOnSuccess(consumption)) targetTL.emitCopyValue(B, loc, succValue); targetTL.emitStoreOfCopy(B, loc, succValue, dest, IsInitialization); B.createBranch(loc, indirectSuccBB); } // Emit the failure block. if (shouldDestroyOnFailure(consumption)) { B.setInsertionPoint(scalarFailBB); srcTL.emitDestroyValue(B, loc, srcValue); B.createBranch(loc, indirectFailBB); } }
static TryApplyInst *replaceTryApplyInst(SILBuilder &B, SILLocation Loc, TryApplyInst *OldTAI, SILValue NewFn, SubstitutionMap NewSubs, ArrayRef<SILValue> NewArgs, SILFunctionConventions Conv) { SILBasicBlock *NormalBB = OldTAI->getNormalBB(); SILBasicBlock *ResultBB = nullptr; SILType NewResultTy = Conv.getSILResultType(); // Does the result value need to be casted? auto OldResultTy = NormalBB->getArgument(0)->getType(); bool ResultCastRequired = NewResultTy != OldResultTy; // Create a new normal BB only if the result of the new apply differs // in type from the argument of the original normal BB. if (!ResultCastRequired) { ResultBB = NormalBB; } else { ResultBB = B.getFunction().createBasicBlockBefore(NormalBB); ResultBB->createPHIArgument(NewResultTy, ValueOwnershipKind::Owned); } // We can always just use the original error BB because we'll be // deleting the edge to it from the old TAI. SILBasicBlock *ErrorBB = OldTAI->getErrorBB(); // Insert a try_apply here. // Note that this makes this block temporarily double-terminated! // We won't fix that until deleteDevirtualizedApply. auto NewTAI = B.createTryApply(Loc, NewFn, NewSubs, NewArgs, ResultBB, ErrorBB); if (ResultCastRequired) { B.setInsertionPoint(ResultBB); SILValue ResultValue = ResultBB->getArgument(0); ResultValue = castValueToABICompatibleType(&B, Loc, ResultValue, NewResultTy, OldResultTy); B.createBranch(Loc, NormalBB, { ResultValue }); } B.setInsertionPoint(NormalBB->begin()); return NewTAI; }
/// Perform a foreign error check by testing whether the call result is nil. static ManagedValue emitResultIsNilErrorCheck(SILGenFunction &SGF, SILLocation loc, ManagedValue origResult, ManagedValue errorSlot, bool suppressErrorCheck) { // Take local ownership of the optional result value. SILValue optionalResult = origResult.forward(SGF); SILType resultObjectType = optionalResult->getType().getAnyOptionalObjectType(); ASTContext &ctx = SGF.getASTContext(); // If we're suppressing the check, just do an unchecked take. if (suppressErrorCheck) { SILValue objectResult = SGF.B.createUncheckedEnumData(loc, optionalResult, ctx.getOptionalSomeDecl()); return SGF.emitManagedRValueWithCleanup(objectResult); } // Switch on the optional result. SILBasicBlock *errorBB = SGF.createBasicBlock(FunctionSection::Postmatter); SILBasicBlock *contBB = SGF.createBasicBlock(); SGF.B.createSwitchEnum(loc, optionalResult, /*default*/ nullptr, { { ctx.getOptionalSomeDecl(), contBB }, { ctx.getOptionalNoneDecl(), errorBB } }); // Emit the error block. SGF.emitForeignErrorBlock(loc, errorBB, errorSlot); // In the continuation block, take ownership of the now non-optional // result value. SGF.B.emitBlock(contBB); SILValue objectResult = contBB->createPHIArgument(resultObjectType, ValueOwnershipKind::Owned); return SGF.emitManagedRValueWithCleanup(objectResult); }
void FunctionSignatureTransform::createFunctionSignatureOptimizedFunction() { // Create the optimized function ! SILModule &M = F->getModule(); std::string Name = createOptimizedSILFunctionName(); SILLinkage linkage = F->getLinkage(); if (isAvailableExternally(linkage)) linkage = SILLinkage::Shared; DEBUG(llvm::dbgs() << " -> create specialized function " << Name << "\n"); NewF = M.createFunction(linkage, Name, createOptimizedSILFunctionType(), F->getGenericEnvironment(), F->getLocation(), F->isBare(), F->isTransparent(), F->isSerialized(), F->isThunk(), F->getClassVisibility(), F->getInlineStrategy(), F->getEffectsKind(), nullptr, F->getDebugScope()); if (F->hasUnqualifiedOwnership()) { NewF->setUnqualifiedOwnership(); } // Then we transfer the body of F to NewF. NewF->spliceBody(F); // Array semantic clients rely on the signature being as in the original // version. for (auto &Attr : F->getSemanticsAttrs()) { if (!StringRef(Attr).startswith("array.")) NewF->addSemanticsAttr(Attr); } // Do the last bit of work to the newly created optimized function. ArgumentExplosionFinalizeOptimizedFunction(); DeadArgumentFinalizeOptimizedFunction(); // Create the thunk body ! F->setThunk(IsThunk); // The thunk now carries the information on how the signature is // optimized. If we inline the thunk, we will get the benefit of calling // the signature optimized function without additional setup on the // caller side. F->setInlineStrategy(AlwaysInline); SILBasicBlock *ThunkBody = F->createBasicBlock(); for (auto &ArgDesc : ArgumentDescList) { ThunkBody->createFunctionArgument(ArgDesc.Arg->getType(), ArgDesc.Decl); } SILLocation Loc = ThunkBody->getParent()->getLocation(); SILBuilder Builder(ThunkBody); Builder.setCurrentDebugScope(ThunkBody->getParent()->getDebugScope()); FunctionRefInst *FRI = Builder.createFunctionRef(Loc, NewF); // Create the args for the thunk's apply, ignoring any dead arguments. llvm::SmallVector<SILValue, 8> ThunkArgs; for (auto &ArgDesc : ArgumentDescList) { addThunkArgument(ArgDesc, Builder, ThunkBody, ThunkArgs); } // We are ignoring generic functions and functions with out parameters for // now. SILValue ReturnValue; SILType LoweredType = NewF->getLoweredType(); SILType ResultType = NewF->getConventions().getSILResultType(); auto FunctionTy = LoweredType.castTo<SILFunctionType>(); if (FunctionTy->hasErrorResult()) { // We need a try_apply to call a function with an error result. SILFunction *Thunk = ThunkBody->getParent(); SILBasicBlock *NormalBlock = Thunk->createBasicBlock(); ReturnValue = NormalBlock->createPHIArgument(ResultType, ValueOwnershipKind::Owned); SILBasicBlock *ErrorBlock = Thunk->createBasicBlock(); SILType Error = SILType::getPrimitiveObjectType(FunctionTy->getErrorResult().getType()); auto *ErrorArg = ErrorBlock->createPHIArgument(Error, ValueOwnershipKind::Owned); Builder.createTryApply(Loc, FRI, LoweredType, SubstitutionList(), ThunkArgs, NormalBlock, ErrorBlock); Builder.setInsertionPoint(ErrorBlock); Builder.createThrow(Loc, ErrorArg); Builder.setInsertionPoint(NormalBlock); } else { ReturnValue = Builder.createApply(Loc, FRI, LoweredType, ResultType, SubstitutionList(), ThunkArgs, false); } // Set up the return results. if (NewF->isNoReturnFunction()) { Builder.createUnreachable(Loc); } else { Builder.createReturn(Loc, ReturnValue); } // Do the last bit work to finalize the thunk. OwnedToGuaranteedFinalizeThunkFunction(Builder, F); assert(F->getDebugScope()->Parent != NewF->getDebugScope()->Parent); }
void SILGenFunction::emitClassConstructorInitializer(ConstructorDecl *ctor) { MagicFunctionName = SILGenModule::getMagicFunctionName(ctor); assert(ctor->getBody() && "Class constructor without a body?"); // True if this constructor delegates to a peer constructor with self.init(). bool isDelegating = false; if (!ctor->hasStubImplementation()) { isDelegating = ctor->getDelegatingOrChainedInitKind(nullptr) == ConstructorDecl::BodyInitKind::Delegating; } // Set up the 'self' argument. If this class has a superclass, we set up // self as a box. This allows "self reassignment" to happen in super init // method chains, which is important for interoperating with Objective-C // classes. We also use a box for delegating constructors, since the // delegated-to initializer may also replace self. // // TODO: If we could require Objective-C classes to have an attribute to get // this behavior, we could avoid runtime overhead here. VarDecl *selfDecl = ctor->getImplicitSelfDecl(); auto *dc = ctor->getDeclContext(); auto selfClassDecl = dc->getAsClassOrClassExtensionContext(); bool NeedsBoxForSelf = isDelegating || (selfClassDecl->hasSuperclass() && !ctor->hasStubImplementation()); bool usesObjCAllocator = Lowering::usesObjCAllocator(selfClassDecl); // If needed, mark 'self' as uninitialized so that DI knows to // enforce its DI properties on stored properties. MarkUninitializedInst::Kind MUKind; if (isDelegating) MUKind = MarkUninitializedInst::DelegatingSelf; else if (selfClassDecl->requiresStoredPropertyInits() && usesObjCAllocator) { // Stored properties will be initialized in a separate // .cxx_construct method called by the Objective-C runtime. assert(selfClassDecl->hasSuperclass() && "Cannot use ObjC allocation without a superclass"); MUKind = MarkUninitializedInst::DerivedSelfOnly; } else if (selfClassDecl->hasSuperclass()) MUKind = MarkUninitializedInst::DerivedSelf; else MUKind = MarkUninitializedInst::RootSelf; if (NeedsBoxForSelf) { // Allocate the local variable for 'self'. emitLocalVariableWithCleanup(selfDecl, MUKind)->finishInitialization(*this); } // Emit the prolog for the non-self arguments. // FIXME: Handle self along with the other body patterns. uint16_t ArgNo = emitProlog(ctor->getParameterList(1), TupleType::getEmpty(F.getASTContext()), ctor, ctor->hasThrows()); SILType selfTy = getLoweredLoadableType(selfDecl->getType()); ManagedValue selfArg = B.createInputFunctionArgument(selfTy, selfDecl); if (!NeedsBoxForSelf) { SILLocation PrologueLoc(selfDecl); PrologueLoc.markAsPrologue(); SILDebugVariable DbgVar(selfDecl->isLet(), ++ArgNo); B.createDebugValue(PrologueLoc, selfArg.getValue(), DbgVar); } if (!ctor->hasStubImplementation()) { assert(selfTy.hasReferenceSemantics() && "can't emit a value type ctor here"); if (NeedsBoxForSelf) { SILLocation prologueLoc = RegularLocation(ctor); prologueLoc.markAsPrologue(); // SEMANTIC ARC TODO: When the verifier is complete, review this. B.emitStoreValueOperation(prologueLoc, selfArg.forward(*this), VarLocs[selfDecl].value, StoreOwnershipQualifier::Init); } else { selfArg = B.createMarkUninitialized(selfDecl, selfArg, MUKind); VarLocs[selfDecl] = VarLoc::get(selfArg.getValue()); } } // Prepare the end of initializer location. SILLocation endOfInitLoc = RegularLocation(ctor); endOfInitLoc.pointToEnd(); // Create a basic block to jump to for the implicit 'self' return. // We won't emit the block until after we've emitted the body. prepareEpilog(Type(), ctor->hasThrows(), CleanupLocation::get(endOfInitLoc)); auto resultType = ctor->mapTypeIntoContext(ctor->getResultInterfaceType()); // If the constructor can fail, set up an alternative epilog for constructor // failure. SILBasicBlock *failureExitBB = nullptr; SILArgument *failureExitArg = nullptr; auto &resultLowering = getTypeLowering(resultType); if (ctor->getFailability() != OTK_None) { SILBasicBlock *failureBB = createBasicBlock(FunctionSection::Postmatter); RegularLocation loc(ctor); loc.markAutoGenerated(); // On failure, we'll clean up everything and return nil instead. SILGenSavedInsertionPoint savedIP(*this, failureBB, FunctionSection::Postmatter); failureExitBB = createBasicBlock(); failureExitArg = failureExitBB->createPHIArgument( resultLowering.getLoweredType(), ValueOwnershipKind::Owned); Cleanups.emitCleanupsForReturn(ctor); SILValue nilResult = B.createEnum(loc, SILValue(), getASTContext().getOptionalNoneDecl(), resultLowering.getLoweredType()); B.createBranch(loc, failureExitBB, nilResult); B.setInsertionPoint(failureExitBB); B.createReturn(loc, failureExitArg); FailDest = JumpDest(failureBB, Cleanups.getCleanupsDepth(), ctor); } // Handle member initializers. if (isDelegating) { // A delegating initializer does not initialize instance // variables. } else if (ctor->hasStubImplementation()) { // Nor does a stub implementation. } else if (selfClassDecl->requiresStoredPropertyInits() && usesObjCAllocator) { // When the class requires all stored properties to have initial // values and we're using Objective-C's allocation, stored // properties are initialized via the .cxx_construct method, which // will be called by the runtime. // Note that 'self' has been fully initialized at this point. } else { // Emit the member initializers. emitMemberInitializers(ctor, selfDecl, selfClassDecl); } emitProfilerIncrement(ctor->getBody()); // Emit the constructor body. emitStmt(ctor->getBody()); // Emit the call to super.init() right before exiting from the initializer. if (NeedsBoxForSelf) { if (auto *SI = ctor->getSuperInitCall()) { B.setInsertionPoint(ReturnDest.getBlock()); emitRValue(SI); B.emitBlock(B.splitBlockForFallthrough(), ctor); ReturnDest = JumpDest(B.getInsertionBB(), ReturnDest.getDepth(), ReturnDest.getCleanupLocation()); B.clearInsertionPoint(); } } CleanupStateRestorationScope SelfCleanupSave(Cleanups); // Build a custom epilog block, since the AST representation of the // constructor decl (which has no self in the return type) doesn't match the // SIL representation. { // Ensure that before we add additional cleanups, that we have emitted all // cleanups at this point. assert(!Cleanups.hasAnyActiveCleanups(getCleanupsDepth(), ReturnDest.getDepth()) && "emitting epilog in wrong scope"); SILGenSavedInsertionPoint savedIP(*this, ReturnDest.getBlock()); auto cleanupLoc = CleanupLocation(ctor); // If we're using a box for self, reload the value at the end of the init // method. if (NeedsBoxForSelf) { ManagedValue storedSelf = ManagedValue::forUnmanaged(VarLocs[selfDecl].value); selfArg = B.createLoadCopy(cleanupLoc, storedSelf); } else { // We have to do a retain because we are returning the pointer +1. // // SEMANTIC ARC TODO: When the verifier is complete, we will need to // change this to selfArg = B.emitCopyValueOperation(...). Currently due // to the way that SILGen performs folding of copy_value, destroy_value, // the returned selfArg may be deleted causing us to have a // dead-pointer. Instead just use the old self value since we have a // class. selfArg = B.createCopyValue(cleanupLoc, selfArg); } // Inject the self value into an optional if the constructor is failable. if (ctor->getFailability() != OTK_None) { RegularLocation loc(ctor); loc.markAutoGenerated(); selfArg = B.createEnum(loc, selfArg, getASTContext().getOptionalSomeDecl(), getLoweredLoadableType(resultType)); } // Save our cleanup state. We want all other potential cleanups to fire, but // not this one. if (selfArg.hasCleanup()) SelfCleanupSave.pushCleanupState(selfArg.getCleanup(), CleanupState::Dormant); // Translate our cleanup to the new top cleanup. // // This is needed to preserve the invariant in getEpilogBB that when // cleanups are emitted, everything above ReturnDest.getDepth() has been // emitted. This is not true if we use ManagedValue and friends in the // epilogBB, thus the translation. We perform the same check above that // getEpilogBB performs to ensure that we still do not have the same // problem. ReturnDest = std::move(ReturnDest).translate(getTopCleanup()); } // Emit the epilog and post-matter. auto returnLoc = emitEpilog(ctor, /*UsesCustomEpilog*/true); // Unpop our selfArg cleanup, so we can forward. std::move(SelfCleanupSave).pop(); // Finish off the epilog by returning. If this is a failable ctor, then we // actually jump to the failure epilog to keep the invariant that there is // only one SIL return instruction per SIL function. if (B.hasValidInsertionPoint()) { if (failureExitBB) B.createBranch(returnLoc, failureExitBB, selfArg.forward(*this)); else B.createReturn(returnLoc, selfArg.forward(*this)); } }
void SILGenFunction::emitValueConstructor(ConstructorDecl *ctor) { MagicFunctionName = SILGenModule::getMagicFunctionName(ctor); if (ctor->isMemberwiseInitializer()) return emitImplicitValueConstructor(*this, ctor); // True if this constructor delegates to a peer constructor with self.init(). bool isDelegating = ctor->getDelegatingOrChainedInitKind(nullptr) == ConstructorDecl::BodyInitKind::Delegating; // Get the 'self' decl and type. VarDecl *selfDecl = ctor->getImplicitSelfDecl(); auto &lowering = getTypeLowering(selfDecl->getType()->getInOutObjectType()); SILType selfTy = lowering.getLoweredType(); (void)selfTy; assert(!selfTy.getClassOrBoundGenericClass() && "can't emit a class ctor here"); // Decide if we need to do extra work to warn on unsafe behavior in pre-Swift-5 // modes. MarkUninitializedInst::Kind MUIKind; if (isDelegating) { MUIKind = MarkUninitializedInst::DelegatingSelf; } else if (getASTContext().isSwiftVersionAtLeast(5)) { MUIKind = MarkUninitializedInst::RootSelf; } else { auto *dc = ctor->getParent(); if (isa<ExtensionDecl>(dc) && dc->getAsStructOrStructExtensionContext()->getParentModule() != dc->getParentModule()) { MUIKind = MarkUninitializedInst::CrossModuleRootSelf; } else { MUIKind = MarkUninitializedInst::RootSelf; } } // Allocate the local variable for 'self'. emitLocalVariableWithCleanup(selfDecl, MUIKind)->finishInitialization(*this); SILValue selfLV = VarLocs[selfDecl].value; // Emit the prolog. emitProlog(ctor->getParameterList(1), ctor->getResultInterfaceType(), ctor, ctor->hasThrows()); emitConstructorMetatypeArg(*this, ctor); // Create a basic block to jump to for the implicit 'self' return. // We won't emit this until after we've emitted the body. // The epilog takes a void return because the return of 'self' is implicit. prepareEpilog(Type(), ctor->hasThrows(), CleanupLocation(ctor)); // If the constructor can fail, set up an alternative epilog for constructor // failure. SILBasicBlock *failureExitBB = nullptr; SILArgument *failureExitArg = nullptr; auto resultType = ctor->mapTypeIntoContext(ctor->getResultInterfaceType()); auto &resultLowering = getTypeLowering(resultType); if (ctor->getFailability() != OTK_None) { SILBasicBlock *failureBB = createBasicBlock(FunctionSection::Postmatter); // On failure, we'll clean up everything (except self, which should have // been cleaned up before jumping here) and return nil instead. SILGenSavedInsertionPoint savedIP(*this, failureBB, FunctionSection::Postmatter); failureExitBB = createBasicBlock(); Cleanups.emitCleanupsForReturn(ctor); // Return nil. if (F.getConventions().hasIndirectSILResults()) { // Inject 'nil' into the indirect return. assert(F.getIndirectResults().size() == 1); B.createInjectEnumAddr(ctor, F.getIndirectResults()[0], getASTContext().getOptionalNoneDecl()); B.createBranch(ctor, failureExitBB); B.setInsertionPoint(failureExitBB); B.createReturn(ctor, emitEmptyTuple(ctor)); } else { // Pass 'nil' as the return value to the exit BB. failureExitArg = failureExitBB->createPHIArgument( resultLowering.getLoweredType(), ValueOwnershipKind::Owned); SILValue nilResult = B.createEnum(ctor, SILValue(), getASTContext().getOptionalNoneDecl(), resultLowering.getLoweredType()); B.createBranch(ctor, failureExitBB, nilResult); B.setInsertionPoint(failureExitBB); B.createReturn(ctor, failureExitArg); } FailDest = JumpDest(failureBB, Cleanups.getCleanupsDepth(), ctor); } // If this is not a delegating constructor, emit member initializers. if (!isDelegating) { auto *typeDC = ctor->getDeclContext(); auto *nominal = typeDC->getAsNominalTypeOrNominalTypeExtensionContext(); emitMemberInitializers(ctor, selfDecl, nominal); } emitProfilerIncrement(ctor->getBody()); // Emit the constructor body. emitStmt(ctor->getBody()); // Build a custom epilog block, since the AST representation of the // constructor decl (which has no self in the return type) doesn't match the // SIL representation. SILValue selfValue; { SILGenSavedInsertionPoint savedIP(*this, ReturnDest.getBlock()); assert(B.getInsertionBB()->empty() && "Epilog already set up?"); auto cleanupLoc = CleanupLocation::get(ctor); if (!F.getConventions().hasIndirectSILResults()) { // Otherwise, load and return the final 'self' value. selfValue = lowering.emitLoad(B, cleanupLoc, selfLV, LoadOwnershipQualifier::Copy); // Inject the self value into an optional if the constructor is failable. if (ctor->getFailability() != OTK_None) { selfValue = B.createEnum(cleanupLoc, selfValue, getASTContext().getOptionalSomeDecl(), getLoweredLoadableType(resultType)); } } else { // If 'self' is address-only, copy 'self' into the indirect return slot. assert(F.getConventions().getNumIndirectSILResults() == 1 && "no indirect return for address-only ctor?!"); // Get the address to which to store the result. SILValue completeReturnAddress = F.getIndirectResults()[0]; SILValue returnAddress; switch (ctor->getFailability()) { // For non-failable initializers, store to the return address directly. case OTK_None: returnAddress = completeReturnAddress; break; // If this is a failable initializer, project out the payload. case OTK_Optional: case OTK_ImplicitlyUnwrappedOptional: returnAddress = B.createInitEnumDataAddr(cleanupLoc, completeReturnAddress, getASTContext().getOptionalSomeDecl(), selfLV->getType()); break; } // We have to do a non-take copy because someone else may be using the // box (e.g. someone could have closed over it). B.createCopyAddr(cleanupLoc, selfLV, returnAddress, IsNotTake, IsInitialization); // Inject the enum tag if the result is optional because of failability. if (ctor->getFailability() != OTK_None) { // Inject the 'Some' tag. B.createInjectEnumAddr(cleanupLoc, completeReturnAddress, getASTContext().getOptionalSomeDecl()); } } } // Finally, emit the epilog and post-matter. auto returnLoc = emitEpilog(ctor, /*UsesCustomEpilog*/true); // Finish off the epilog by returning. If this is a failable ctor, then we // actually jump to the failure epilog to keep the invariant that there is // only one SIL return instruction per SIL function. if (B.hasValidInsertionPoint()) { if (!failureExitBB) { // If we're not returning self, then return () since we're returning Void. if (!selfValue) { SILLocation loc(ctor); loc.markAutoGenerated(); selfValue = emitEmptyTuple(loc); } B.createReturn(returnLoc, selfValue); } else { if (selfValue) B.createBranch(returnLoc, failureExitBB, selfValue); else B.createBranch(returnLoc, failureExitBB); } } }
/// Insert monomorphic inline caches for a specific class or metatype /// type \p SubClassTy. static FullApplySite speculateMonomorphicTarget(FullApplySite AI, SILType SubType, CheckedCastBranchInst *&CCBI) { CCBI = nullptr; // Bail if this class_method cannot be devirtualized. if (!canDevirtualizeClassMethod(AI, SubType)) return FullApplySite(); if (SubType.getSwiftRValueType()->hasDynamicSelfType()) return FullApplySite(); // Create a diamond shaped control flow and a checked_cast_branch // instruction that checks the exact type of the object. // This cast selects between two paths: one that calls the slow dynamic // dispatch and one that calls the specific method. auto It = AI.getInstruction()->getIterator(); SILFunction *F = AI.getFunction(); SILBasicBlock *Entry = AI.getParent(); // Iden is the basic block containing the direct call. SILBasicBlock *Iden = F->createBasicBlock(); // Virt is the block containing the slow virtual call. SILBasicBlock *Virt = F->createBasicBlock(); Iden->createPHIArgument(SubType, ValueOwnershipKind::Owned); SILBasicBlock *Continue = Entry->split(It); SILBuilderWithScope Builder(Entry, AI.getInstruction()); // Create the checked_cast_branch instruction that checks at runtime if the // class instance is identical to the SILType. ClassMethodInst *CMI = cast<ClassMethodInst>(AI.getCallee()); CCBI = Builder.createCheckedCastBranch(AI.getLoc(), /*exact*/ true, CMI->getOperand(), SubType, Iden, Virt); It = CCBI->getIterator(); SILBuilderWithScope VirtBuilder(Virt, AI.getInstruction()); SILBuilderWithScope IdenBuilder(Iden, AI.getInstruction()); // This is the class reference downcasted into subclass SubType. SILValue DownCastedClassInstance = Iden->getArgument(0); // Copy the two apply instructions into the two blocks. FullApplySite IdenAI = CloneApply(AI, IdenBuilder); FullApplySite VirtAI = CloneApply(AI, VirtBuilder); // See if Continue has a release on self as the instruction right after the // apply. If it exists, move it into position in the diamond. SILBasicBlock::iterator next = next_or_end(Continue->begin(), Continue->end()); auto *Release = (next == Continue->end()) ? nullptr : dyn_cast<StrongReleaseInst>(next); if (Release && Release->getOperand() == CMI->getOperand()) { VirtBuilder.createStrongRelease(Release->getLoc(), CMI->getOperand(), Release->getAtomicity()); IdenBuilder.createStrongRelease(Release->getLoc(), DownCastedClassInstance, Release->getAtomicity()); Release->eraseFromParent(); } // Create a PHInode for returning the return value from both apply // instructions. SILArgument *Arg = Continue->createPHIArgument(AI.getType(), ValueOwnershipKind::Owned); if (!isa<TryApplyInst>(AI)) { if (AI.getSubstCalleeType()->isNoReturnFunction()) { IdenBuilder.createUnreachable(AI.getLoc()); VirtBuilder.createUnreachable(AI.getLoc()); } else { IdenBuilder.createBranch(AI.getLoc(), Continue, { cast<ApplyInst>(IdenAI) }); VirtBuilder.createBranch(AI.getLoc(), Continue, { cast<ApplyInst>(VirtAI) }); } } // Remove the old Apply instruction. assert(AI.getInstruction() == &Continue->front() && "AI should be the first instruction in the split Continue block"); if (isa<TryApplyInst>(AI)) { AI.getInstruction()->eraseFromParent(); assert(Continue->empty() && "There should not be an instruction after try_apply"); Continue->eraseFromParent(); } else { auto apply = cast<ApplyInst>(AI); apply->replaceAllUsesWith(Arg); apply->eraseFromParent(); assert(!Continue->empty() && "There should be at least a terminator after AI"); } // Update the stats. NumTargetsPredicted++; // Devirtualize the apply instruction on the identical path. auto NewInstPair = devirtualizeClassMethod(IdenAI, DownCastedClassInstance); assert(NewInstPair.first && "Expected to be able to devirtualize apply!"); replaceDeadApply(IdenAI, NewInstPair.first); // Split critical edges resulting from VirtAI. if (auto *TAI = dyn_cast<TryApplyInst>(VirtAI)) { auto *ErrorBB = TAI->getFunction()->createBasicBlock(); ErrorBB->createPHIArgument(TAI->getErrorBB()->getArgument(0)->getType(), ValueOwnershipKind::Owned); Builder.setInsertionPoint(ErrorBB); Builder.createBranch(TAI->getLoc(), TAI->getErrorBB(), {ErrorBB->getArgument(0)}); auto *NormalBB = TAI->getFunction()->createBasicBlock(); NormalBB->createPHIArgument(TAI->getNormalBB()->getArgument(0)->getType(), ValueOwnershipKind::Owned); Builder.setInsertionPoint(NormalBB); Builder.createBranch(TAI->getLoc(), TAI->getNormalBB(), {NormalBB->getArgument(0)}); Builder.setInsertionPoint(VirtAI.getInstruction()); SmallVector<SILValue, 4> Args; for (auto Arg : VirtAI.getArguments()) { Args.push_back(Arg); } FullApplySite NewVirtAI = Builder.createTryApply(VirtAI.getLoc(), VirtAI.getCallee(), VirtAI.getSubstitutions(), Args, NormalBB, ErrorBB); VirtAI.getInstruction()->eraseFromParent(); VirtAI = NewVirtAI; } return VirtAI; }
void SILGenFunction::prepareRethrowEpilog(CleanupLocation cleanupLoc) { auto exnType = SILType::getExceptionType(getASTContext()); SILBasicBlock *rethrowBB = createBasicBlock(FunctionSection::Postmatter); rethrowBB->createPHIArgument(exnType, ValueOwnershipKind::Owned); ThrowDest = JumpDest(rethrowBB, getCleanupsDepth(), cleanupLoc); }
/// \brief Devirtualize an apply of a class method. /// /// \p AI is the apply to devirtualize. /// \p ClassOrMetatype is a class value or metatype value that is the /// self argument of the apply we will devirtualize. /// return the result value of the new ApplyInst if created one or null. DevirtualizationResult swift::devirtualizeClassMethod(FullApplySite AI, SILValue ClassOrMetatype) { DEBUG(llvm::dbgs() << " Trying to devirtualize : " << *AI.getInstruction()); SILModule &Mod = AI.getModule(); auto *MI = cast<MethodInst>(AI.getCallee()); auto ClassOrMetatypeType = ClassOrMetatype->getType(); auto *F = getTargetClassMethod(Mod, ClassOrMetatypeType, MI); CanSILFunctionType GenCalleeType = F->getLoweredFunctionType(); SmallVector<Substitution, 4> Subs; getSubstitutionsForCallee(Mod, GenCalleeType, ClassOrMetatypeType.getSwiftRValueType(), AI, Subs); CanSILFunctionType SubstCalleeType = GenCalleeType; if (GenCalleeType->isPolymorphic()) SubstCalleeType = GenCalleeType->substGenericArgs(Mod, Subs); SILFunctionConventions substConv(SubstCalleeType, Mod); SILBuilderWithScope B(AI.getInstruction()); FunctionRefInst *FRI = B.createFunctionRef(AI.getLoc(), F); // Create the argument list for the new apply, casting when needed // in order to handle covariant indirect return types and // contravariant argument types. llvm::SmallVector<SILValue, 8> NewArgs; auto IndirectResultArgIter = AI.getIndirectSILResults().begin(); for (auto ResultTy : substConv.getIndirectSILResultTypes()) { NewArgs.push_back( castValueToABICompatibleType(&B, AI.getLoc(), *IndirectResultArgIter, IndirectResultArgIter->getType(), ResultTy)); ++IndirectResultArgIter; } auto ParamArgIter = AI.getArgumentsWithoutIndirectResults().begin(); // Skip the last parameter, which is `self`. Add it below. for (auto param : substConv.getParameters().drop_back()) { auto paramType = substConv.getSILType(param); NewArgs.push_back( castValueToABICompatibleType(&B, AI.getLoc(), *ParamArgIter, ParamArgIter->getType(), paramType)); ++ParamArgIter; } // Add the self argument, upcasting if required because we're // calling a base class's method. auto SelfParamTy = substConv.getSILType(SubstCalleeType->getSelfParameter()); NewArgs.push_back(castValueToABICompatibleType(&B, AI.getLoc(), ClassOrMetatype, ClassOrMetatypeType, SelfParamTy)); SILType ResultTy = substConv.getSILResultType(); SILType SubstCalleeSILType = SILType::getPrimitiveObjectType(SubstCalleeType); FullApplySite NewAI; SILBasicBlock *ResultBB = nullptr; SILBasicBlock *NormalBB = nullptr; SILValue ResultValue; bool ResultCastRequired = false; SmallVector<Operand *, 4> OriginalResultUses; if (!isa<TryApplyInst>(AI)) { NewAI = B.createApply(AI.getLoc(), FRI, SubstCalleeSILType, ResultTy, Subs, NewArgs, cast<ApplyInst>(AI)->isNonThrowing()); ResultValue = NewAI.getInstruction(); } else { auto *TAI = cast<TryApplyInst>(AI); // Create new normal and error BBs only if: // - re-using a BB would create a critical edge // - or, the result of the new apply would be of different // type than the argument of the original normal BB. if (TAI->getNormalBB()->getSinglePredecessorBlock()) ResultBB = TAI->getNormalBB(); else { ResultBB = B.getFunction().createBasicBlock(); ResultBB->createPHIArgument(ResultTy, ValueOwnershipKind::Owned); } NormalBB = TAI->getNormalBB(); SILBasicBlock *ErrorBB = nullptr; if (TAI->getErrorBB()->getSinglePredecessorBlock()) ErrorBB = TAI->getErrorBB(); else { ErrorBB = B.getFunction().createBasicBlock(); ErrorBB->createPHIArgument(TAI->getErrorBB()->getArgument(0)->getType(), ValueOwnershipKind::Owned); } NewAI = B.createTryApply(AI.getLoc(), FRI, SubstCalleeSILType, Subs, NewArgs, ResultBB, ErrorBB); if (ErrorBB != TAI->getErrorBB()) { B.setInsertionPoint(ErrorBB); B.createBranch(TAI->getLoc(), TAI->getErrorBB(), {ErrorBB->getArgument(0)}); } // Does the result value need to be casted? ResultCastRequired = ResultTy != NormalBB->getArgument(0)->getType(); if (ResultBB != NormalBB) B.setInsertionPoint(ResultBB); else if (ResultCastRequired) { B.setInsertionPoint(NormalBB->begin()); // Collect all uses, before casting. for (auto *Use : NormalBB->getArgument(0)->getUses()) { OriginalResultUses.push_back(Use); } NormalBB->getArgument(0)->replaceAllUsesWith( SILUndef::get(AI.getType(), Mod)); NormalBB->replacePHIArgument(0, ResultTy, ValueOwnershipKind::Owned); } // The result value is passed as a parameter to the normal block. ResultValue = ResultBB->getArgument(0); } // Check if any casting is required for the return value. ResultValue = castValueToABICompatibleType(&B, NewAI.getLoc(), ResultValue, ResultTy, AI.getType()); DEBUG(llvm::dbgs() << " SUCCESS: " << F->getName() << "\n"); NumClassDevirt++; if (NormalBB) { if (NormalBB != ResultBB) { // If artificial normal BB was introduced, branch // to the original normal BB. B.createBranch(NewAI.getLoc(), NormalBB, { ResultValue }); } else if (ResultCastRequired) { // Update all original uses by the new value. for (auto *Use: OriginalResultUses) { Use->set(ResultValue); } } return std::make_pair(NewAI.getInstruction(), NewAI); } // We need to return a pair of values here: // - the first one is the actual result of the devirtualized call, possibly // casted into an appropriate type. This SILValue may be a BB arg, if it // was a cast between optional types. // - the second one is the new apply site. return std::make_pair(ResultValue, NewAI); }
/// \brief Inlines the callee of a given ApplyInst (which must be the value of a /// FunctionRefInst referencing a function with a known body), into the caller /// containing the ApplyInst, which must be the same function as provided to the /// constructor of SILInliner. It only performs one step of inlining: it does /// not recursively inline functions called by the callee. /// /// It is the responsibility of the caller of this function to delete /// the given ApplyInst when inlining is successful. /// /// \returns true on success or false if it is unable to inline the function /// (for any reason). void SILInliner::inlineFunction(FullApplySite AI, ArrayRef<SILValue> Args) { assert(canInlineFunction(AI) && "Asked to inline function that is unable to be inlined?!"); SILFunction &F = getBuilder().getFunction(); assert(AI.getFunction() && AI.getFunction() == &F && "Inliner called on apply instruction in wrong function?"); assert(((CalleeFunction->getRepresentation() != SILFunctionTypeRepresentation::ObjCMethod && CalleeFunction->getRepresentation() != SILFunctionTypeRepresentation::CFunctionPointer) || IKind == InlineKind::PerformanceInline) && "Cannot inline Objective-C methods or C functions in mandatory " "inlining"); CalleeEntryBB = &*CalleeFunction->begin(); // Compute the SILLocation which should be used by all the inlined // instructions. if (IKind == InlineKind::PerformanceInline) { Loc = InlinedLocation::getInlinedLocation(AI.getLoc()); } else { assert(IKind == InlineKind::MandatoryInline && "Unknown InlineKind."); Loc = MandatoryInlinedLocation::getMandatoryInlinedLocation(AI.getLoc()); } auto AIScope = AI.getDebugScope(); // FIXME: Turn this into an assertion instead. if (!AIScope) AIScope = AI.getFunction()->getDebugScope(); if (IKind == InlineKind::MandatoryInline) { // Mandatory inlining: every instruction inherits scope/location // from the call site. CallSiteScope = AIScope; } else { // Performance inlining. Construct a proper inline scope pointing // back to the call site. CallSiteScope = new (F.getModule()) SILDebugScope(AI.getLoc(), nullptr, AIScope, AIScope->InlinedCallSite); } assert(CallSiteScope && "call site has no scope"); assert(CallSiteScope->getParentFunction() == &F); // Increment the ref count for the inlined function, so it doesn't // get deleted before we can emit abstract debug info for it. CalleeFunction->setInlined(); // If the caller's BB is not the last BB in the calling function, then keep // track of the next BB so we always insert new BBs before it; otherwise, // we just leave the new BBs at the end as they are by default. auto IBI = std::next(SILFunction::iterator(AI.getParent())); InsertBeforeBB = IBI != F.end() ? &*IBI : nullptr; BBMap.clear(); // Do not allow the entry block to be cloned again SILBasicBlock::iterator InsertPoint = SILBasicBlock::iterator(AI.getInstruction()); BBMap.insert(std::make_pair(CalleeEntryBB, AI.getParent())); getBuilder().setInsertionPoint(InsertPoint); // Clear argument map and map ApplyInst arguments to the arguments of the // callee's entry block. ValueMap.clear(); assert(CalleeFunction->getArguments().size() == Args.size() && "Unexpected number of callee arguments."); auto calleeConv = CalleeFunction->getConventions(); for (unsigned argIdx = 0, endIdx = Args.size(); argIdx < endIdx; ++argIdx) { SILValue callArg = Args[argIdx]; // Insert begin/end borrow for guaranteed arguments. if (argIdx >= calleeConv.getSILArgIndexOfFirstParam() && calleeConv.getParamInfoForSILArg(argIdx).isGuaranteed()) { callArg = borrowFunctionArgument(callArg, AI); } auto *calleeArg = CalleeFunction->getArgument(argIdx); ValueMap.insert(std::make_pair(calleeArg, callArg)); } // Recursively visit callee's BB in depth-first preorder, starting with the // entry block, cloning all instructions other than terminators. visitSILBasicBlock(CalleeEntryBB); // If we're inlining into a normal apply and the callee's entry // block ends in a return, then we can avoid a split. if (auto nonTryAI = dyn_cast<ApplyInst>(AI)) { if (auto *RI = dyn_cast<ReturnInst>(CalleeEntryBB->getTerminator())) { // Replace all uses of the apply instruction with the operands of the // return instruction, appropriately mapped. nonTryAI->replaceAllUsesWith(remapValue(RI->getOperand())); return; } } // If we're inlining into a try_apply, we already have a return-to BB. SILBasicBlock *ReturnToBB; if (auto tryAI = dyn_cast<TryApplyInst>(AI)) { ReturnToBB = tryAI->getNormalBB(); // Otherwise, split the caller's basic block to create a return-to BB. } else { SILBasicBlock *CallerBB = AI.getParent(); // Split the BB and do NOT create a branch between the old and new // BBs; we will create the appropriate terminator manually later. ReturnToBB = CallerBB->split(InsertPoint); // Place the return-to BB after all the other mapped BBs. if (InsertBeforeBB) F.getBlocks().splice(SILFunction::iterator(InsertBeforeBB), F.getBlocks(), SILFunction::iterator(ReturnToBB)); else F.getBlocks().splice(F.getBlocks().end(), F.getBlocks(), SILFunction::iterator(ReturnToBB)); // Create an argument on the return-to BB representing the returned value. auto apply = cast<ApplyInst>(AI.getInstruction()); auto *RetArg = ReturnToBB->createPHIArgument(apply->getType(), ValueOwnershipKind::Owned); // Replace all uses of the ApplyInst with the new argument. apply->replaceAllUsesWith(RetArg); } // Now iterate over the callee BBs and fix up the terminators. for (auto BI = BBMap.begin(), BE = BBMap.end(); BI != BE; ++BI) { getBuilder().setInsertionPoint(BI->second); // Modify return terminators to branch to the return-to BB, rather than // trying to clone the ReturnInst. if (auto *RI = dyn_cast<ReturnInst>(BI->first->getTerminator())) { auto thrownValue = remapValue(RI->getOperand()); getBuilder().createBranch(Loc.getValue(), ReturnToBB, thrownValue); continue; } // Modify throw terminators to branch to the error-return BB, rather than // trying to clone the ThrowInst. if (auto *TI = dyn_cast<ThrowInst>(BI->first->getTerminator())) { if (auto *A = dyn_cast<ApplyInst>(AI)) { (void)A; assert(A->isNonThrowing() && "apply of a function with error result must be non-throwing"); getBuilder().createUnreachable(Loc.getValue()); continue; } auto tryAI = cast<TryApplyInst>(AI); auto returnedValue = remapValue(TI->getOperand()); getBuilder().createBranch(Loc.getValue(), tryAI->getErrorBB(), returnedValue); continue; } // Otherwise use normal visitor, which clones the existing instruction // but remaps basic blocks and values. visit(BI->first->getTerminator()); } }
/// Emit a store of a native error to the foreign-error slot. static void emitStoreToForeignErrorSlot(SILGenFunction &SGF, SILLocation loc, SILValue foreignErrorSlot, const BridgedErrorSource &errorSrc) { ASTContext &ctx = SGF.getASTContext(); // The foreign error slot has type SomePointer<SomeError?>, // or possibly an optional thereof. // If the pointer itself is optional, we need to branch based on // whether it's really there. if (SILType errorPtrObjectTy = foreignErrorSlot->getType().getOptionalObjectType()) { SILBasicBlock *contBB = SGF.createBasicBlock(); SILBasicBlock *noSlotBB = SGF.createBasicBlock(); SILBasicBlock *hasSlotBB = SGF.createBasicBlock(); SGF.B.createSwitchEnum(loc, foreignErrorSlot, nullptr, { { ctx.getOptionalSomeDecl(), hasSlotBB }, { ctx.getOptionalNoneDecl(), noSlotBB } }); // If we have the slot, emit a store to it. SGF.B.emitBlock(hasSlotBB); SILValue slot = hasSlotBB->createPHIArgument(errorPtrObjectTy, ValueOwnershipKind::Owned); emitStoreToForeignErrorSlot(SGF, loc, slot, errorSrc); SGF.B.createBranch(loc, contBB); // Otherwise, just release the error. SGF.B.emitBlock(noSlotBB); errorSrc.emitRelease(SGF, loc); SGF.B.createBranch(loc, contBB); // Continue. SGF.B.emitBlock(contBB); return; } // Okay, break down the components of SomePointer<SomeError?>. // TODO: this should really be an unlowered AST type? auto bridgedErrorPtrType = foreignErrorSlot->getType().getASTType(); PointerTypeKind ptrKind; CanType bridgedErrorProto = CanType(bridgedErrorPtrType->getAnyPointerElementType(ptrKind)); FullExpr scope(SGF.Cleanups, CleanupLocation::get(loc)); FormalEvaluationScope writebacks(SGF); // Convert the error to a bridged form. SILValue bridgedError = errorSrc.emitBridged(SGF, loc, bridgedErrorProto); // Store to the "pointee" property. // If we can't find it, diagnose and then just don't store anything. VarDecl *pointeeProperty = ctx.getPointerPointeePropertyDecl(ptrKind); if (!pointeeProperty) { SGF.SGM.diagnose(loc, diag::could_not_find_pointer_pointee_property, bridgedErrorPtrType); return; } // Otherwise, do a normal assignment. LValue lvalue = SGF.emitPropertyLValue(loc, ManagedValue::forUnmanaged(foreignErrorSlot), bridgedErrorPtrType, pointeeProperty, LValueOptions(), AccessKind::Write, AccessSemantics::Ordinary); RValue rvalue(SGF, loc, bridgedErrorProto, SGF.emitManagedRValueWithCleanup(bridgedError)); SGF.emitAssignToLValue(loc, std::move(rvalue), std::move(lvalue)); }