/// 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->createArgument(exnResult.getSILType()); 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 &gen, SILLocation loc, ManagedValue errorSlot, bool suppressErrorCheck) { // If we're suppressing the check, just don't check. if (suppressErrorCheck) return; SILValue optionalError = gen.B.emitLoadValueOperation( loc, errorSlot.forward(gen), LoadOwnershipQualifier::Take); ASTContext &ctx = gen.getASTContext(); // Switch on the optional error. SILBasicBlock *errorBB = gen.createBasicBlock(FunctionSection::Postmatter); errorBB->createArgument(optionalError->getType().unwrapAnyOptionalType()); SILBasicBlock *contBB = gen.createBasicBlock(); gen.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. gen.emitForeignErrorBlock(loc, errorBB, None); // Return the result. gen.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->createArgument(argTy); } 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); }
/// Perform a foreign error check by testing whether the call result is nil. static ManagedValue emitResultIsNilErrorCheck(SILGenFunction &gen, SILLocation loc, ManagedValue origResult, ManagedValue errorSlot, bool suppressErrorCheck) { // Take local ownership of the optional result value. SILValue optionalResult = origResult.forward(gen); SILType resultObjectType = optionalResult->getType().getAnyOptionalObjectType(); ASTContext &ctx = gen.getASTContext(); // If we're suppressing the check, just do an unchecked take. if (suppressErrorCheck) { SILValue objectResult = gen.B.createUncheckedEnumData(loc, optionalResult, ctx.getOptionalSomeDecl()); return gen.emitManagedRValueWithCleanup(objectResult); } // Switch on the optional result. SILBasicBlock *errorBB = gen.createBasicBlock(FunctionSection::Postmatter); SILBasicBlock *contBB = gen.createBasicBlock(); gen.B.createSwitchEnum(loc, optionalResult, /*default*/ nullptr, { { ctx.getOptionalSomeDecl(), contBB }, { ctx.getOptionalNoneDecl(), errorBB } }); // Emit the error block. gen.emitForeignErrorBlock(loc, errorBB, errorSlot); // In the continuation block, take ownership of the now non-optional // result value. gen.B.emitBlock(contBB); SILValue objectResult = contBB->createArgument(resultObjectType); return gen.emitManagedRValueWithCleanup(objectResult); }
void FunctionSignatureTransform::createFunctionSignatureOptimizedFunction() { // Create the optimized function ! SILModule &M = F->getModule(); std::string Name = getUniqueName(createOptimizedSILFunctionName(), M); SILLinkage linkage = F->getLinkage(); if (isAvailableExternally(linkage)) linkage = SILLinkage::Shared; DEBUG(llvm::dbgs() << " -> create specialized function " << Name << "\n"); NewF = M.createFunction( linkage, Name, createOptimizedSILFunctionType(), nullptr, F->getLocation(), F->isBare(), F->isTransparent(), F->isFragile(), F->isThunk(), F->getClassVisibility(), F->getInlineStrategy(), F->getEffectsKind(), 0, F->getDebugScope(), F->getDeclContext()); if (F->hasUnqualifiedOwnership()) { NewF->setUnqualifiedOwnership(); } // Then we transfer the body of F to NewF. NewF->spliceBody(F); NewF->setDeclCtx(F->getDeclContext()); // 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->createArgument(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 = LoweredType.getFunctionInterfaceResultType(); 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->createArgument(ResultType, 0); SILBasicBlock *ErrorBlock = Thunk->createBasicBlock(); SILType Error = SILType::getPrimitiveObjectType(FunctionTy->getErrorResult().getType()); auto *ErrorArg = ErrorBlock->createArgument(Error, 0); Builder.createTryApply(Loc, FRI, LoweredType, ArrayRef<Substitution>(), ThunkArgs, NormalBlock, ErrorBlock); Builder.setInsertionPoint(ErrorBlock); Builder.createThrow(Loc, ErrorArg); Builder.setInsertionPoint(NormalBlock); } else { ReturnValue = Builder.createApply(Loc, FRI, LoweredType, ResultType, ArrayRef<Substitution>(), 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); }
/// \brief Populate the body of the cloned closure, modifying instructions as /// necessary. This is where we create the actual specialized BB Arguments. void ClosureSpecCloner::populateCloned() { SILFunction *Cloned = getCloned(); SILFunction *ClosureUser = CallSiteDesc.getApplyCallee(); // Create arguments for the entry block. SILBasicBlock *ClosureUserEntryBB = &*ClosureUser->begin(); SILBasicBlock *ClonedEntryBB = Cloned->createBasicBlock(); // Remove the closure argument. SILArgument *ClosureArg = nullptr; for (size_t i = 0, e = ClosureUserEntryBB->args_size(); i != e; ++i) { SILArgument *Arg = ClosureUserEntryBB->getArgument(i); if (i == CallSiteDesc.getClosureIndex()) { ClosureArg = Arg; continue; } // Otherwise, create a new argument which copies the original argument SILValue MappedValue = ClonedEntryBB->createArgument(Arg->getType(), Arg->getDecl()); ValueMap.insert(std::make_pair(Arg, MappedValue)); } // Next we need to add in any arguments that are not captured as arguments to // the cloned function. // // We do not insert the new mapped arguments into the value map since there by // definition is nothing in the partial apply user function that references // such arguments. After this pass is done the only thing that will reference // the arguments is the partial apply that we will create. SILFunction *ClosedOverFun = CallSiteDesc.getClosureCallee(); CanSILFunctionType ClosedOverFunTy = ClosedOverFun->getLoweredFunctionType(); unsigned NumTotalParams = ClosedOverFunTy->getParameters().size(); unsigned NumNotCaptured = NumTotalParams - CallSiteDesc.getNumArguments(); llvm::SmallVector<SILValue, 4> NewPAIArgs; for (auto &PInfo : ClosedOverFunTy->getParameters().slice(NumNotCaptured)) { SILValue MappedValue = ClonedEntryBB->createArgument(PInfo.getSILType()); NewPAIArgs.push_back(MappedValue); } SILBuilder &Builder = getBuilder(); Builder.setInsertionPoint(ClonedEntryBB); // Clone FRI and PAI, and replace usage of the removed closure argument // with result of cloned PAI. SILValue FnVal = Builder.createFunctionRef(CallSiteDesc.getLoc(), ClosedOverFun); auto *NewClosure = CallSiteDesc.createNewClosure(Builder, FnVal, NewPAIArgs); ValueMap.insert(std::make_pair(ClosureArg, SILValue(NewClosure))); BBMap.insert(std::make_pair(ClosureUserEntryBB, ClonedEntryBB)); // Recursively visit original BBs in depth-first preorder, starting with the // entry block, cloning all instructions other than terminators. visitSILBasicBlock(ClosureUserEntryBB); // Now iterate over the BBs and fix up the terminators. for (auto BI = BBMap.begin(), BE = BBMap.end(); BI != BE; ++BI) { Builder.setInsertionPoint(BI->second); visit(BI->first->getTerminator()); } // Then insert a release in all non failure exit BBs if our partial apply was // guaranteed. This is b/c it was passed at +0 originally and we need to // balance the initial increment of the newly created closure. if (CallSiteDesc.isClosureGuaranteed() && CallSiteDesc.closureHasRefSemanticContext()) { for (SILBasicBlock *BB : CallSiteDesc.getNonFailureExitBBs()) { SILBasicBlock *OpBB = BBMap[BB]; TermInst *TI = OpBB->getTerminator(); auto Loc = CleanupLocation::get(NewClosure->getLoc()); // If we have a return, we place the release right before it so we know // that it will be executed at the end of the epilogue. if (isa<ReturnInst>(TI)) { Builder.setInsertionPoint(TI); Builder.createReleaseValue(Loc, SILValue(NewClosure), Atomicity::Atomic); continue; } // We use casts where findAllNonFailureExitBBs should have made sure that // this is true. This will ensure that the code is updated when we hit the // cast failure in debug builds. auto *Unreachable = cast<UnreachableInst>(TI); auto PrevIter = std::prev(SILBasicBlock::iterator(Unreachable)); auto NoReturnApply = FullApplySite::isa(&*PrevIter); // We insert the release value right before the no return apply so that if // the partial apply is passed into the no-return function as an @owned // value, we will retain the partial apply before we release it and // potentially eliminate it. Builder.setInsertionPoint(NoReturnApply.getInstruction()); Builder.createReleaseValue(Loc, SILValue(NewClosure), Atomicity::Atomic); } } }
/// Emit a store of a native error to the foreign-error slot. static void emitStoreToForeignErrorSlot(SILGenFunction &gen, SILLocation loc, SILValue foreignErrorSlot, const BridgedErrorSource &errorSrc) { ASTContext &ctx = gen.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().getAnyOptionalObjectType()) { SILBasicBlock *contBB = gen.createBasicBlock(); SILBasicBlock *noSlotBB = gen.createBasicBlock(); SILBasicBlock *hasSlotBB = gen.createBasicBlock(); gen.B.createSwitchEnum(loc, foreignErrorSlot, nullptr, { { ctx.getOptionalSomeDecl(), hasSlotBB }, { ctx.getOptionalNoneDecl(), noSlotBB } }); // If we have the slot, emit a store to it. gen.B.emitBlock(hasSlotBB); SILValue slot = hasSlotBB->createArgument(errorPtrObjectTy); emitStoreToForeignErrorSlot(gen, loc, slot, errorSrc); gen.B.createBranch(loc, contBB); // Otherwise, just release the error. gen.B.emitBlock(noSlotBB); errorSrc.emitRelease(gen, loc); gen.B.createBranch(loc, contBB); // Continue. gen.B.emitBlock(contBB); return; } // Okay, break down the components of SomePointer<SomeError?>. // TODO: this should really be an unlowered AST type? CanType bridgedErrorPtrType = foreignErrorSlot->getType().getSwiftRValueType(); PointerTypeKind ptrKind; CanType bridgedErrorProto = CanType(bridgedErrorPtrType->getAnyPointerElementType(ptrKind)); FullExpr scope(gen.Cleanups, CleanupLocation::get(loc)); WritebackScope writebacks(gen); // Convert the error to a bridged form. SILValue bridgedError = errorSrc.emitBridged(gen, 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) { gen.SGM.diagnose(loc, diag::could_not_find_pointer_pointee_property, bridgedErrorPtrType); return; } // Otherwise, do a normal assignment. LValue lvalue = gen.emitPropertyLValue(loc, ManagedValue::forUnmanaged(foreignErrorSlot), bridgedErrorPtrType, pointeeProperty, AccessKind::Write, AccessSemantics::Ordinary); RValue rvalue(gen, loc, bridgedErrorProto, gen.emitManagedRValueWithCleanup(bridgedError)); gen.emitAssignToLValue(loc, std::move(rvalue), std::move(lvalue)); }
void GenericCloner::populateCloned() { SILFunction *Cloned = getCloned(); // Create arguments for the entry block. SILBasicBlock *OrigEntryBB = &*Original.begin(); SILBasicBlock *ClonedEntryBB = Cloned->createBasicBlock(); getBuilder().setInsertionPoint(ClonedEntryBB); llvm::SmallVector<AllocStackInst *, 8> AllocStacks; AllocStackInst *ReturnValueAddr = nullptr; // Create the entry basic block with the function arguments. auto I = OrigEntryBB->args_begin(), E = OrigEntryBB->args_end(); int ArgIdx = 0; while (I != E) { SILArgument *OrigArg = *I; RegularLocation Loc((Decl *)OrigArg->getDecl()); AllocStackInst *ASI = nullptr; SILType mappedType = remapType(OrigArg->getType()); if (ReInfo.isArgConverted(ArgIdx)) { // We need an alloc_stack as a replacement for the indirect parameter. assert(mappedType.isAddress()); mappedType = mappedType.getObjectType(); ASI = getBuilder().createAllocStack(Loc, mappedType); ValueMap[OrigArg] = ASI; AllocStacks.push_back(ASI); if (ReInfo.isResultIndex(ArgIdx)) { // This result is converted from indirect to direct. The return inst // needs to load the value from the alloc_stack. See below. assert(!ReturnValueAddr); ReturnValueAddr = ASI; } else { // Store the new direct parameter to the alloc_stack. auto *NewArg = ClonedEntryBB->createArgument(mappedType, OrigArg->getDecl()); getBuilder().createStore(Loc, NewArg, ASI, StoreOwnershipQualifier::Unqualified); // Try to create a new debug_value from an existing debug_value_addr. for (Operand *ArgUse : OrigArg->getUses()) { if (auto *DVAI = dyn_cast<DebugValueAddrInst>(ArgUse->getUser())) { getBuilder().createDebugValue(DVAI->getLoc(), NewArg, DVAI->getVarInfo()); break; } } } } else { auto *NewArg = ClonedEntryBB->createArgument(mappedType, OrigArg->getDecl()); ValueMap[OrigArg] = NewArg; } ++I; ++ArgIdx; } BBMap.insert(std::make_pair(OrigEntryBB, ClonedEntryBB)); // Recursively visit original BBs in depth-first preorder, starting with the // entry block, cloning all instructions other than terminators. visitSILBasicBlock(OrigEntryBB); // Now iterate over the BBs and fix up the terminators. for (auto BI = BBMap.begin(), BE = BBMap.end(); BI != BE; ++BI) { getBuilder().setInsertionPoint(BI->second); TermInst *OrigTermInst = BI->first->getTerminator(); if (auto *RI = dyn_cast<ReturnInst>(OrigTermInst)) { SILValue ReturnValue; if (ReturnValueAddr) { // The result is converted from indirect to direct. We have to load the // returned value from the alloc_stack. ReturnValue = getBuilder().createLoad(ReturnValueAddr->getLoc(), ReturnValueAddr, LoadOwnershipQualifier::Unqualified); } for (AllocStackInst *ASI : reverse(AllocStacks)) { getBuilder().createDeallocStack(ASI->getLoc(), ASI); } if (ReturnValue) { getBuilder().createReturn(RI->getLoc(), ReturnValue); continue; } } else if (isa<ThrowInst>(OrigTermInst)) { for (AllocStackInst *ASI : reverse(AllocStacks)) { getBuilder().createDeallocStack(ASI->getLoc(), ASI); } } visit(BI->first->getTerminator()); } }