void SideEffectAnalysis::analyzeFunction(FunctionInfo *FInfo, FunctionOrder &BottomUpOrder, int RecursionDepth) { FInfo->NeedUpdateCallers = true; if (BottomUpOrder.prepareForVisiting(FInfo)) return; // Handle @effects attributes if (getDefinedEffects(FInfo->FE, FInfo->F)) { DEBUG(llvm::dbgs() << " -- has defined effects " << FInfo->F->getName() << '\n'); return; } if (!FInfo->F->isDefinition()) { // We can't assume anything about external functions. DEBUG(llvm::dbgs() << " -- is external " << FInfo->F->getName() << '\n'); FInfo->FE.setWorstEffects(); return; } DEBUG(llvm::dbgs() << " >> analyze " << FInfo->F->getName() << '\n'); // Check all instructions of the function for (auto &BB : *FInfo->F) { for (auto &I : BB) { analyzeInstruction(FInfo, &I, BottomUpOrder, RecursionDepth); } } DEBUG(llvm::dbgs() << " << finished " << FInfo->F->getName() << '\n'); }
void GenericFunctionEffectAnalysis<FunctionEffects>::analyzeCall( FunctionInfo *functionInfo, FullApplySite fullApply, FunctionOrder &bottomUpOrder, int recursionDepth) { FunctionEffects applyEffects; if (applyEffects.summarizeCall(fullApply)) { functionInfo->functionEffects.mergeFromApply(applyEffects, fullApply); return; } if (recursionDepth >= MaxRecursionDepth) { functionInfo->functionEffects.setWorstEffects(); return; } CalleeList callees = BCA->getCalleeList(fullApply); 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. fullApply.getOrigCalleeType()->isCalleeConsumed()) { functionInfo->functionEffects.setWorstEffects(); return; } // Derive the effects of the apply from the known callees. // Defer merging callee effects until the callee is scheduled for (SILFunction *callee : callees) { FunctionInfo *calleeInfo = getFunctionInfo(callee); calleeInfo->addCaller(functionInfo, fullApply); if (!calleeInfo->isVisited()) { // Recursively visit the called function. analyzeFunction(calleeInfo, bottomUpOrder, recursionDepth + 1); bottomUpOrder.tryToSchedule(calleeInfo); } } }
void GenericFunctionEffectAnalysis<FunctionEffects>::analyzeFunction( FunctionInfo *functionInfo, FunctionOrder &bottomUpOrder, int recursionDepth) { functionInfo->needUpdateCallers = true; if (bottomUpOrder.prepareForVisiting(functionInfo)) return; auto *F = functionInfo->F; if (functionInfo->functionEffects.summarizeFunction(F)) return; DEBUG(llvm::dbgs() << " >> analyze " << F->getName() << '\n'); // Check all instructions of the function for (auto &BB : *F) { for (auto &I : BB) { if (auto fullApply = FullApplySite::isa(&I)) analyzeCall(functionInfo, fullApply, bottomUpOrder, recursionDepth); else functionInfo->functionEffects.analyzeInstruction(&I); } } DEBUG(llvm::dbgs() << " << finished " << F->getName() << '\n'); }
void AccessSummaryAnalysis::processCall(FunctionInfo *callerInfo, unsigned callerArgumentIndex, SILFunction *callee, unsigned argumentIndex, FunctionOrder &order) { // Record the flow of an argument from the caller to the callee so that // the interprocedural analysis can iterate to a fixpoint. FunctionInfo *calleeInfo = getFunctionInfo(callee); ArgumentFlow flow = {callerArgumentIndex, argumentIndex, calleeInfo}; callerInfo->recordFlow(flow); if (!calleeInfo->isVisited()) { processFunction(calleeInfo, order); order.tryToSchedule(calleeInfo); } propagateFromCalleeToCaller(callerInfo, flow); }
void AccessSummaryAnalysis::processFunction(FunctionInfo *info, FunctionOrder &order) { // Does the summary need to be recomputed? if (order.prepareForVisiting(info)) return; // Compute function summary on a per-argument basis. unsigned index = 0; for (SILArgument *arg : info->getFunction()->getArguments()) { FunctionSummary &functionSummary = info->getSummary(); ArgumentSummary &argSummary = functionSummary.getAccessForArgument(index); index++; auto *functionArg = cast<SILFunctionArgument>(arg); // Only summarize @inout_aliasable arguments. SILArgumentConvention convention = functionArg->getArgumentConvention().Value; if (convention != SILArgumentConvention::Indirect_InoutAliasable) continue; processArgument(info, functionArg, argSummary, order); } }
void SideEffectAnalysis::analyzeInstruction(FunctionInfo *FInfo, SILInstruction *I, FunctionOrder &BottomUpOrder, int RecursionDepth) { if (FullApplySite FAS = FullApplySite::isa(I)) { // Is this a call to a semantics function? ArraySemanticsCall ASC(I); if (ASC && ASC.hasSelf()) { FunctionEffects ApplyEffects(FAS.getNumArguments()); if (getSemanticEffects(ApplyEffects, ASC)) { FInfo->FE.mergeFromApply(ApplyEffects, FAS); return; } } if (SILFunction *SingleCallee = FAS.getCalleeFunction()) { // Does the function have any @effects? if (getDefinedEffects(FInfo->FE, SingleCallee)) return; } if (RecursionDepth < MaxRecursionDepth) { CalleeList 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()) { // Derive the effects of the apply from the known callees. for (SILFunction *Callee : Callees) { FunctionInfo *CalleeInfo = getFunctionInfo(Callee); CalleeInfo->addCaller(FInfo, FAS); if (!CalleeInfo->isVisited()) { // Recursively visit the called function. analyzeFunction(CalleeInfo, BottomUpOrder, RecursionDepth + 1); BottomUpOrder.tryToSchedule(CalleeInfo); } } return; } } // Be conservative for everything else. FInfo->FE.setWorstEffects(); return; } // Handle some kind of instructions specially. switch (I->getKind()) { case ValueKind::FixLifetimeInst: // A fix_lifetime instruction acts like a read on the operand. Retains can move after it // but the last release can't move before it. FInfo->FE.getEffectsOn(I->getOperand(0))->Reads = true; return; case ValueKind::AllocStackInst: case ValueKind::DeallocStackInst: return; case ValueKind::StrongRetainInst: case ValueKind::StrongRetainUnownedInst: case ValueKind::RetainValueInst: case ValueKind::UnownedRetainInst: FInfo->FE.getEffectsOn(I->getOperand(0))->Retains = true; return; case ValueKind::StrongReleaseInst: case ValueKind::ReleaseValueInst: case ValueKind::UnownedReleaseInst: FInfo->FE.getEffectsOn(I->getOperand(0))->Releases = true; // TODO: Check the call graph to be less conservative about what // destructors might be called. FInfo->FE.setWorstEffects(); return; case ValueKind::LoadInst: FInfo->FE.getEffectsOn(cast<LoadInst>(I)->getOperand())->Reads = true; return; case ValueKind::StoreInst: FInfo->FE.getEffectsOn(cast<StoreInst>(I)->getDest())->Writes = true; return; case ValueKind::CondFailInst: FInfo->FE.Traps = true; return; case ValueKind::PartialApplyInst: FInfo->FE.AllocsObjects = true; return; case ValueKind::BuiltinInst: { auto &BI = cast<BuiltinInst>(I)->getBuiltinInfo(); switch (BI.ID) { case BuiltinValueKind::IsUnique: // TODO: derive this information in a more general way, e.g. add it // to Builtins.def FInfo->FE.ReadsRC = true; break; default: break; } // Detailed memory effects of builtins are handled below by checking the // memory behavior of the instruction. break; } default: break; } if (isa<AllocationInst>(I)) { // Excluding AllocStackInst (which is handled above). FInfo->FE.AllocsObjects = true; } // Check the general memory behavior for instructions we didn't handle above. switch (I->getMemoryBehavior()) { case MemoryBehavior::None: break; case MemoryBehavior::MayRead: FInfo->FE.GlobalEffects.Reads = true; break; case MemoryBehavior::MayWrite: FInfo->FE.GlobalEffects.Writes = true; break; case MemoryBehavior::MayReadWrite: FInfo->FE.GlobalEffects.Reads = true; FInfo->FE.GlobalEffects.Writes = true; break; case MemoryBehavior::MayHaveSideEffects: FInfo->FE.setWorstEffects(); break; } if (I->mayTrap()) FInfo->FE.Traps = true; }