// A utility function for cloning the apply instruction. static FullApplySite CloneApply(FullApplySite AI, SILBuilder &Builder) { // Clone the Apply. Builder.setCurrentDebugScope(AI.getDebugScope()); Builder.addOpenedArchetypeOperands(AI.getInstruction()); auto Args = AI.getArguments(); SmallVector<SILValue, 8> Ret(Args.size()); for (unsigned i = 0, e = Args.size(); i != e; ++i) Ret[i] = Args[i]; FullApplySite NAI; switch (AI.getInstruction()->getKind()) { case SILInstructionKind::ApplyInst: NAI = Builder.createApply(AI.getLoc(), AI.getCallee(), AI.getSubstitutions(), Ret, cast<ApplyInst>(AI)->isNonThrowing()); break; case SILInstructionKind::TryApplyInst: { auto *TryApplyI = cast<TryApplyInst>(AI.getInstruction()); NAI = Builder.createTryApply(AI.getLoc(), AI.getCallee(), AI.getSubstitutions(), Ret, TryApplyI->getNormalBB(), TryApplyI->getErrorBB()); } break; default: llvm_unreachable("Trying to clone an unsupported apply instruction"); } NAI.getInstruction(); return NAI; }
/// Process an apply instruction which uses a partial_apply /// as its callee. /// Returns true on success. bool PartialApplyCombiner::processSingleApply(FullApplySite AI) { Builder.setInsertionPoint(AI.getInstruction()); Builder.setCurrentDebugScope(AI.getDebugScope()); // Prepare the args. SmallVector<SILValue, 8> Args; // First the ApplyInst args. for (auto Op : AI.getArguments()) Args.push_back(Op); SILInstruction *InsertionPoint = &*Builder.getInsertionPoint(); // Next, the partial apply args. // Pre-process partial_apply arguments only once, lazily. if (isFirstTime) { isFirstTime = false; if (!allocateTemporaries()) return false; } // Now, copy over the partial apply args. for (auto Op : PAI->getArguments()) { auto Arg = Op; // If there is new temporary for this argument, use it instead. if (isa<AllocStackInst>(Arg)) { if (ArgToTmp.count(Arg)) { Op = ArgToTmp.lookup(Arg); } } Args.push_back(Op); } Builder.setInsertionPoint(InsertionPoint); Builder.setCurrentDebugScope(AI.getDebugScope()); // The thunk that implements the partial apply calls the closure function // that expects all arguments to be consumed by the function. However, the // captured arguments are not arguments of *this* apply, so they are not // pre-incremented. When we combine the partial_apply and this apply into // a new apply we need to retain all of the closure non-address type // arguments. auto ParamInfo = PAI->getSubstCalleeType()->getParameters(); auto PartialApplyArgs = PAI->getArguments(); // Set of arguments that need to be released after each invocation. SmallVector<SILValue, 8> ToBeReleasedArgs; for (unsigned i = 0, e = PartialApplyArgs.size(); i < e; ++i) { SILValue Arg = PartialApplyArgs[i]; if (!Arg->getType().isAddress()) { // Retain the argument as the callee may consume it. Builder.emitRetainValueOperation(PAI->getLoc(), Arg); // For non consumed parameters (e.g. guaranteed), we also need to // insert releases after each apply instruction that we create. if (!ParamInfo[ParamInfo.size() - PartialApplyArgs.size() + i]. isConsumed()) ToBeReleasedArgs.push_back(Arg); } } auto *F = FRI->getReferencedFunction(); SILType FnType = F->getLoweredType(); SILType ResultTy = F->getLoweredFunctionType()->getSILResult(); ArrayRef<Substitution> Subs = PAI->getSubstitutions(); if (!Subs.empty()) { FnType = FnType.substGenericArgs(PAI->getModule(), Subs); ResultTy = FnType.getAs<SILFunctionType>()->getSILResult(); } FullApplySite NAI; if (auto *TAI = dyn_cast<TryApplyInst>(AI)) NAI = Builder.createTryApply(AI.getLoc(), FRI, FnType, Subs, Args, TAI->getNormalBB(), TAI->getErrorBB()); else NAI = Builder.createApply(AI.getLoc(), FRI, FnType, ResultTy, Subs, Args, cast<ApplyInst>(AI)->isNonThrowing()); // We also need to release the partial_apply instruction itself because it // is consumed by the apply_instruction. if (auto *TAI = dyn_cast<TryApplyInst>(AI)) { Builder.setInsertionPoint(TAI->getNormalBB()->begin()); for (auto Arg : ToBeReleasedArgs) { Builder.emitReleaseValueOperation(PAI->getLoc(), Arg); } Builder.createStrongRelease(AI.getLoc(), PAI, Atomicity::Atomic); Builder.setInsertionPoint(TAI->getErrorBB()->begin()); // Release the non-consumed parameters. for (auto Arg : ToBeReleasedArgs) { Builder.emitReleaseValueOperation(PAI->getLoc(), Arg); } Builder.createStrongRelease(AI.getLoc(), PAI, Atomicity::Atomic); Builder.setInsertionPoint(AI.getInstruction()); } else { // Release the non-consumed parameters. for (auto Arg : ToBeReleasedArgs) { Builder.emitReleaseValueOperation(PAI->getLoc(), Arg); } Builder.createStrongRelease(AI.getLoc(), PAI, Atomicity::Atomic); } SilCombiner->replaceInstUsesWith(*AI.getInstruction(), NAI.getInstruction()); SilCombiner->eraseInstFromFunction(*AI.getInstruction()); return true; }
/// \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). bool SILInliner::inlineFunction(FullApplySite AI, ArrayRef<SILValue> Args) { SILFunction *CalleeFunction = &Original; this->CalleeFunction = CalleeFunction; // Do not attempt to inline an apply into its parent function. if (AI.getFunction() == CalleeFunction) return false; SILFunction &F = getBuilder().getFunction(); if (CalleeFunction->getName() == "_TTSg5Vs4Int8___TFVs12_ArrayBufferg9_isNativeSb" && F.getName() == "_TTSg5Vs4Int8___TFVs12_ArrayBufferg8endIndexSi") llvm::errs(); 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(), &F, AIScope); assert(CallSiteScope->getParentFunction() == &F); } assert(CallSiteScope && "call site has no scope"); // 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; // Clear argument map and map ApplyInst arguments to the arguments of the // callee's entry block. ValueMap.clear(); assert(CalleeEntryBB->bbarg_size() == Args.size() && "Unexpected number of arguments to entry block of function?"); auto BAI = CalleeEntryBB->bbarg_begin(); for (auto AI = Args.begin(), AE = Args.end(); AI != AE; ++AI, ++BAI) ValueMap.insert(std::make_pair(*BAI, *AI)); InstructionMap.clear(); 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); // 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 (ReturnInst *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 true; } } // 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->splitBasicBlock(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 *RetArg = new (F.getModule()) SILArgument(ReturnToBB, AI.getInstruction()->getType()); // Replace all uses of the ApplyInst with the new argument. AI.getInstruction()->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 (ReturnInst *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 (ThrowInst *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()); } return true; }