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 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; }