void SideEffectAnalysis::getEffects(FunctionEffects &ApplyEffects, FullApplySite FAS) { assert(ApplyEffects.ParamEffects.size() == 0 && "Not using a new ApplyEffects?"); ApplyEffects.ParamEffects.resize(FAS.getNumArguments()); // Is this a call to a semantics function? ArraySemanticsCall ASC(FAS.getInstruction()); if (ASC && ASC.hasSelf()) { if (getSemanticEffects(ApplyEffects, ASC)) return; } if (SILFunction *SingleCallee = FAS.getCalleeFunction()) { // Does the function have any @effects? if (getDefinedEffects(ApplyEffects, SingleCallee)) return; } auto Callees = BCA->getCalleeList(FAS); if (!Callees.allCalleesVisible() || // @callee_owned function calls implicitly release the context, which // may call deinits of boxed values. // TODO: be less conservative about what destructors might be called. FAS.getOrigCalleeType()->isCalleeConsumed()) { ApplyEffects.setWorstEffects(); return; } // We can see all the callees. So we just merge the effects from all of // them. for (auto *Callee : Callees) { const FunctionEffects &CalleeFE = getEffects(Callee); ApplyEffects.mergeFrom(CalleeFE); } }
/// FIXME: Total hack to work around issues exposed while ripping call /// graph maintenance from the inliner. static void tryLinkCallee(FullApplySite Apply) { auto *F = Apply.getCalleeFunction(); if (!F || F->isDefinition()) return; auto &M = Apply.getFunction()->getModule(); M.linkFunction(F, SILModule::LinkingMode::LinkAll); }
/// Checks whether any of the arguments to the apply are closures and diagnoses /// if any of the @inout_aliasable captures passed to those closures have /// in-progress accesses that would conflict with any access the summary /// says the closure would perform. // /// TODO: We currently fail to statically diagnose non-escaping closures pased /// via @block_storage convention. To enforce this case, we should statically /// recognize when the apply takes a block argument that has been initialized to /// a non-escaping closure. static void checkForViolationsInNoEscapeClosures( const StorageMap &Accesses, FullApplySite FAS, AccessSummaryAnalysis *ASA, llvm::SmallVectorImpl<ConflictingAccess> &ConflictingAccesses) { SILFunction *Callee = FAS.getCalleeFunction(); if (Callee && !Callee->empty()) { // Check for violation with directly called closure checkForViolationWithCall(Accesses, Callee, 0, FAS.getArguments(), ASA, ConflictingAccesses); } // Check for violation with closures passed as arguments for (SILValue Argument : FAS.getArguments()) { auto *PAI = lookThroughForPartialApply(Argument); if (!PAI) continue; SILFunction *Closure = PAI->getCalleeFunction(); if (!Closure || Closure->empty()) continue; // Check the closure's captures, which are a suffix of the closure's // parameters. unsigned StartIndex = Closure->getArguments().size() - PAI->getNumCallArguments(); checkForViolationWithCall(Accesses, Closure, StartIndex, PAI->getArguments(), ASA, ConflictingAccesses); } }
void SideEffectAnalysis::getEffects(FunctionEffects &ApplyEffects, FullApplySite FAS) { assert(ApplyEffects.ParamEffects.size() == 0 && "Not using a new ApplyEffects?"); ApplyEffects.ParamEffects.resize(FAS.getNumArguments()); // Is this a call to a semantics function? ArraySemanticsCall ASC(FAS.getInstruction()); if (ASC && ASC.hasSelf()) { if (getSemanticEffects(ApplyEffects, ASC)) return; } if (SILFunction *SingleCallee = FAS.getCalleeFunction()) { // Does the function have any @effects? if (getDefinedEffects(ApplyEffects, SingleCallee)) return; } auto Callees = BCA->getCalleeList(FAS); if (!Callees.allCalleesVisible()) { ApplyEffects.setWorstEffects(); return; } // We can see all the callees. So we just merge the effects from all of // them. for (auto *Callee : Callees) { const FunctionEffects &CalleeFE = getEffects(Callee); ApplyEffects.mergeFrom(CalleeFE); } }
void AccessSummaryAnalysis::processFullApply(FunctionInfo *callerInfo, unsigned callerArgumentIndex, FullApplySite apply, Operand *argumentOperand, FunctionOrder &order) { unsigned operandNumber = argumentOperand->getOperandNumber(); assert(operandNumber > 0 && "Summarizing apply for non-argument?"); unsigned calleeArgumentIndex = operandNumber - 1; SILFunction *callee = apply.getCalleeFunction(); // We can't apply a summary for function whose body we can't see. // Since user-provided closures are always in the same module as their callee // This likely indicates a missing begin_access before an open-coded // call. if (!callee || callee->empty()) return; processCall(callerInfo, callerArgumentIndex, callee, calleeArgumentIndex, order); }
/// Return true if inlining this call site is profitable. bool SILPerformanceInliner::isProfitableToInline(FullApplySite AI, unsigned loopDepthOfAI, DominanceAnalysis *DA, SILLoopAnalysis *LA, ConstantTracker &callerTracker, unsigned &NumCallerBlocks) { SILFunction *Callee = AI.getCalleeFunction(); if (Callee->getInlineStrategy() == AlwaysInline) return true; ConstantTracker constTracker(Callee, &callerTracker, AI); DominanceInfo *DT = DA->get(Callee); SILLoopInfo *LI = LA->get(Callee); DominanceOrder domOrder(&Callee->front(), DT, Callee->size()); // Calculate the inlining cost of the callee. unsigned CalleeCost = 0; unsigned Benefit = InlineCostThreshold > 0 ? InlineCostThreshold : RemovedCallBenefit; Benefit += loopDepthOfAI * LoopBenefitFactor; int testThreshold = TestThreshold; while (SILBasicBlock *block = domOrder.getNext()) { constTracker.beginBlock(); unsigned loopDepth = LI->getLoopDepth(block); for (SILInstruction &I : *block) { constTracker.trackInst(&I); auto ICost = instructionInlineCost(I); if (testThreshold >= 0) { // We are in test-mode: use a simplified cost model. CalleeCost += testCost(&I); } else { // Use the regular cost model. CalleeCost += unsigned(ICost); } if (ApplyInst *AI = dyn_cast<ApplyInst>(&I)) { // Check if the callee is passed as an argument. If so, increase the // threshold, because inlining will (probably) eliminate the closure. SILInstruction *def = constTracker.getDefInCaller(AI->getCallee()); if (def && (isa<FunctionRefInst>(def) || isa<PartialApplyInst>(def))) { DEBUG(llvm::dbgs() << " Boost: apply const function at" << *AI); Benefit += ConstCalleeBenefit + loopDepth * LoopBenefitFactor; testThreshold *= 2; } } } // Don't count costs in blocks which are dead after inlining. SILBasicBlock *takenBlock = getTakenBlock(block->getTerminator(), constTracker); if (takenBlock) { Benefit += ConstTerminatorBenefit + TestOpt; DEBUG(llvm::dbgs() << " Take bb" << takenBlock->getDebugID() << " of" << *block->getTerminator()); domOrder.pushChildrenIf(block, [=] (SILBasicBlock *child) { return child->getSinglePredecessor() != block || child == takenBlock; }); } else { domOrder.pushChildren(block); } } unsigned Threshold = Benefit; // The default. if (testThreshold >= 0) { // We are in testing mode. Threshold = testThreshold; } else if (AI.getFunction()->isThunk()) { // Only inline trivial functions into thunks (which will not increase the // code size). Threshold = TrivialFunctionThreshold; } else { // The default case. // We reduce the benefit if the caller is too large. For this we use a // cubic function on the number of caller blocks. This starts to prevent // inlining at about 800 - 1000 caller blocks. unsigned blockMinus = (NumCallerBlocks * NumCallerBlocks) / BlockLimitDenominator * NumCallerBlocks / BlockLimitDenominator; if (Threshold > blockMinus + TrivialFunctionThreshold) Threshold -= blockMinus; else Threshold = TrivialFunctionThreshold; } if (CalleeCost > Threshold) { DEBUG(llvm::dbgs() << " NO: Function too big to inline, " "cost: " << CalleeCost << ", threshold: " << Threshold << "\n"); return false; } DEBUG(llvm::dbgs() << " YES: ready to inline, " "cost: " << CalleeCost << ", threshold: " << Threshold << "\n"); NumCallerBlocks += Callee->size(); return true; }