mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
Make this a generic analysis so that it can be used to analyze any kind of function effect. FunctionSideEffect becomes a trivial specialization of the analysis. The immediate need for this is to introduce an new AccessedStorageAnalysis, although I foresee it as a generally very useful utility. This way, new kinds of function effects can be computed without adding any complexity or compile time to FunctionSideEffects. We have the flexibility of computing different kinds of function effects at different points in the pipeline. In the case of AccessedStorageAnalysis, it will compute both FunctionSideEffects and FunctionAccessedStorage in the same pass by implementing a simple wrapper on top of FunctionEffects. This cleanup reflects my feeling that nested classes make the code extremely unreadable unless they are very small and either private or only used directly via its parent class. It's easier to see how these classes compose with a flat type system. In addition to enabling new kinds of function effects analyses, I think this makes the implementation of side effect analysis easier to understand by separating concerns.
577 lines
19 KiB
C++
577 lines
19 KiB
C++
//===--- SideEffectAnalysis.cpp - SIL Side Effect Analysis ----------------===//
|
|
//
|
|
// This source file is part of the Swift.org open source project
|
|
//
|
|
// Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors
|
|
// Licensed under Apache License v2.0 with Runtime Library Exception
|
|
//
|
|
// See https://swift.org/LICENSE.txt for license information
|
|
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#define DEBUG_TYPE "sil-sea"
|
|
#include "swift/SILOptimizer/Analysis/SideEffectAnalysis.h"
|
|
#include "swift/SILOptimizer/Analysis/BasicCalleeAnalysis.h"
|
|
#include "swift/SILOptimizer/Analysis/FunctionOrder.h"
|
|
#include "swift/SILOptimizer/PassManager/PassManager.h"
|
|
#include "swift/SIL/SILArgument.h"
|
|
|
|
using namespace swift;
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// GenericFunctionEffectAnalysis
|
|
// -----------------------------------------------------------------------------
|
|
|
|
template <typename FunctionEffects>
|
|
void GenericFunctionEffectAnalysis<FunctionEffects>::initialize(
|
|
SILPassManager *PM) {
|
|
BCA = PM->getAnalysis<BasicCalleeAnalysis>();
|
|
}
|
|
|
|
template <typename FunctionEffects>
|
|
void GenericFunctionEffectAnalysis<FunctionEffects>::invalidate() {
|
|
functionInfoMap.clear();
|
|
allocator.DestroyAll();
|
|
DEBUG(llvm::dbgs() << "invalidate all\n");
|
|
}
|
|
|
|
template <typename FunctionEffects>
|
|
void GenericFunctionEffectAnalysis<FunctionEffects>::invalidate(
|
|
SILFunction *F, InvalidationKind K) {
|
|
if (FunctionInfo *FInfo = functionInfoMap.lookup(F)) {
|
|
DEBUG(llvm::dbgs() << " invalidate " << FInfo->F->getName() << '\n');
|
|
invalidateIncludingAllCallers(FInfo);
|
|
}
|
|
}
|
|
|
|
template <typename FunctionEffects>
|
|
void GenericFunctionEffectAnalysis<FunctionEffects>::getCalleeEffects(
|
|
FunctionEffects &calleeEffects, FullApplySite fullApply) {
|
|
if (calleeEffects.summarizeCall(fullApply))
|
|
return;
|
|
|
|
auto 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()) {
|
|
calleeEffects.setWorstEffects();
|
|
return;
|
|
}
|
|
|
|
// We can see all the callees, so merge the effects from all of them.
|
|
for (auto *callee : callees)
|
|
calleeEffects.mergeFrom(getEffects(callee));
|
|
}
|
|
|
|
template <typename FunctionEffects>
|
|
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');
|
|
}
|
|
|
|
template <typename FunctionEffects>
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
|
|
template <typename FunctionEffects>
|
|
void GenericFunctionEffectAnalysis<FunctionEffects>::recompute(
|
|
FunctionInfo *initialInfo) {
|
|
allocNewUpdateID();
|
|
|
|
DEBUG(llvm::dbgs() << "recompute function-effect analysis with UpdateID "
|
|
<< getCurrentUpdateID() << '\n');
|
|
|
|
// Collect and analyze all functions to recompute, starting at initialInfo.
|
|
FunctionOrder bottomUpOrder(getCurrentUpdateID());
|
|
analyzeFunction(initialInfo, bottomUpOrder, 0);
|
|
|
|
// Build the bottom-up order.
|
|
bottomUpOrder.tryToSchedule(initialInfo);
|
|
bottomUpOrder.finishScheduling();
|
|
|
|
// Second step: propagate the side-effect information up the call-graph until
|
|
// it stabilizes.
|
|
bool needAnotherIteration;
|
|
do {
|
|
DEBUG(llvm::dbgs() << "new iteration\n");
|
|
needAnotherIteration = false;
|
|
|
|
for (FunctionInfo *functionInfo : bottomUpOrder) {
|
|
if (!functionInfo->needUpdateCallers)
|
|
continue;
|
|
|
|
DEBUG(llvm::dbgs() << " update callers of " << functionInfo->F->getName()
|
|
<< '\n');
|
|
functionInfo->needUpdateCallers = false;
|
|
|
|
// Propagate the function effects to all callers.
|
|
for (const auto &E : functionInfo->getCallers()) {
|
|
assert(E.isValid());
|
|
|
|
// Only include callers which we are actually recomputing.
|
|
if (!bottomUpOrder.wasRecomputedWithCurrentUpdateID(E.Caller))
|
|
continue;
|
|
|
|
DEBUG(llvm::dbgs() << " merge into caller " << E.Caller->F->getName()
|
|
<< '\n');
|
|
|
|
if (E.Caller->functionEffects.mergeFromApply(
|
|
functionInfo->functionEffects, FullApplySite(E.FAS))) {
|
|
E.Caller->needUpdateCallers = true;
|
|
if (!E.Caller->isScheduledAfter(functionInfo)) {
|
|
// This happens if we have a cycle in the call-graph.
|
|
needAnotherIteration = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} while (needAnotherIteration);
|
|
}
|
|
|
|
// Instantiate template members.
|
|
template class swift::GenericFunctionEffectAnalysis<FunctionSideEffects>;
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// FunctionSideEffects
|
|
// -----------------------------------------------------------------------------
|
|
|
|
using MemoryBehavior = SILInstruction::MemoryBehavior;
|
|
|
|
MemoryBehavior
|
|
FunctionSideEffects::getMemBehavior(RetainObserveKind ScanKind) const {
|
|
|
|
bool Observe = (ScanKind == RetainObserveKind::ObserveRetains);
|
|
if ((Observe && mayAllocObjects()) || mayReadRC())
|
|
return MemoryBehavior::MayHaveSideEffects;
|
|
|
|
// Start with the global effects.
|
|
auto Behavior = GlobalEffects.getMemBehavior(ScanKind);
|
|
|
|
// Add effects from the parameters.
|
|
for (auto &ParamEffect : ParamEffects) {
|
|
MemoryBehavior ArgBehavior = ParamEffect.getMemBehavior(ScanKind);
|
|
|
|
Behavior = combineMemoryBehavior(Behavior, ArgBehavior);
|
|
|
|
// Stop the scan if we've reached the highest level of side effect.
|
|
if (Behavior == MemoryBehavior::MayHaveSideEffects)
|
|
break;
|
|
}
|
|
return Behavior;
|
|
}
|
|
|
|
bool FunctionSideEffects::mergeFrom(const FunctionSideEffects &RHS) {
|
|
bool Changed = mergeFlags(RHS);
|
|
Changed |= GlobalEffects.mergeFrom(RHS.GlobalEffects);
|
|
Changed |= LocalEffects.mergeFrom(RHS.LocalEffects);
|
|
// In case of an external function, the RHS may have 0 arguments.
|
|
unsigned NumArgs = RHS.ParamEffects.size();
|
|
for (unsigned Idx = 0; Idx < NumArgs; Idx++) {
|
|
// In case of a partial_apply, the RHS (= callee) may have more arguments
|
|
// than the apply instruction.
|
|
if (Idx < ParamEffects.size()) {
|
|
Changed |= ParamEffects[Idx].mergeFrom(RHS.ParamEffects[Idx]);
|
|
} else {
|
|
Changed |= GlobalEffects.mergeFrom(RHS.ParamEffects[Idx]);
|
|
}
|
|
}
|
|
return Changed;
|
|
}
|
|
|
|
bool FunctionSideEffects::mergeFromApply(
|
|
const FunctionSideEffects &ApplyEffects, FullApplySite FAS) {
|
|
bool Changed = mergeFlags(ApplyEffects);
|
|
Changed |= GlobalEffects.mergeFrom(ApplyEffects.GlobalEffects);
|
|
unsigned numCallerArgs = FAS.getNumArguments();
|
|
unsigned numCalleeArgs = ApplyEffects.ParamEffects.size();
|
|
assert(numCalleeArgs >= numCallerArgs);
|
|
for (unsigned Idx = 0; Idx < numCalleeArgs; Idx++) {
|
|
// Map the callee argument effects to parameters of this function.
|
|
// If there are more callee parameters than arguments it means that the
|
|
// callee is the result of a partial_apply.
|
|
FunctionSideEffectFlags *E = (Idx < numCallerArgs
|
|
? getEffectsOn(FAS.getArgument(Idx))
|
|
: &GlobalEffects);
|
|
Changed |= E->mergeFrom(ApplyEffects.ParamEffects[Idx]);
|
|
}
|
|
return Changed;
|
|
}
|
|
|
|
void FunctionSideEffects::dump() const { llvm::errs() << *this << '\n'; }
|
|
|
|
static SILValue skipAddrProjections(SILValue V) {
|
|
for (;;) {
|
|
switch (V->getKind()) {
|
|
case ValueKind::IndexAddrInst:
|
|
case ValueKind::IndexRawPointerInst:
|
|
case ValueKind::StructElementAddrInst:
|
|
case ValueKind::TupleElementAddrInst:
|
|
case ValueKind::RefElementAddrInst:
|
|
case ValueKind::RefTailAddrInst:
|
|
case ValueKind::ProjectBoxInst:
|
|
case ValueKind::UncheckedTakeEnumDataAddrInst:
|
|
case ValueKind::PointerToAddressInst:
|
|
V = cast<SingleValueInstruction>(V)->getOperand(0);
|
|
break;
|
|
default:
|
|
return V;
|
|
}
|
|
}
|
|
llvm_unreachable("there is no escape from an infinite loop");
|
|
}
|
|
|
|
static SILValue skipValueProjections(SILValue V) {
|
|
for (;;) {
|
|
switch (V->getKind()) {
|
|
case ValueKind::StructExtractInst:
|
|
case ValueKind::TupleExtractInst:
|
|
case ValueKind::UncheckedEnumDataInst:
|
|
case ValueKind::UncheckedTrivialBitCastInst:
|
|
case ValueKind::UncheckedRefCastInst:
|
|
V = cast<SingleValueInstruction>(V)->getOperand(0);
|
|
break;
|
|
default:
|
|
return V;
|
|
}
|
|
}
|
|
llvm_unreachable("there is no escape from an infinite loop");
|
|
}
|
|
|
|
FunctionSideEffectFlags *FunctionSideEffects::getEffectsOn(SILValue Addr) {
|
|
SILValue BaseAddr = skipValueProjections(skipAddrProjections(Addr));
|
|
switch (BaseAddr->getKind()) {
|
|
case swift::ValueKind::SILFunctionArgument: {
|
|
// Can we associate the address to a function parameter?
|
|
auto *Arg = cast<SILFunctionArgument>(BaseAddr);
|
|
return &ParamEffects[Arg->getIndex()];
|
|
break;
|
|
}
|
|
case ValueKind::AllocStackInst:
|
|
case ValueKind::AllocRefInst:
|
|
case ValueKind::AllocRefDynamicInst:
|
|
case ValueKind::AllocBoxInst:
|
|
// Effects on locally allocated storage.
|
|
return &LocalEffects;
|
|
default:
|
|
break;
|
|
}
|
|
// Everything else.
|
|
return &GlobalEffects;
|
|
}
|
|
|
|
// Return true if the given function has defined effects that were successfully
|
|
// recorded in this FunctionSideEffects object.
|
|
bool FunctionSideEffects::setDefinedEffects(SILFunction *F) {
|
|
if (F->hasSemanticsAttr("arc.programtermination_point")) {
|
|
Traps = true;
|
|
return true;
|
|
}
|
|
switch (F->getEffectsKind()) {
|
|
case EffectsKind::ReleaseNone:
|
|
GlobalEffects.Reads = true;
|
|
GlobalEffects.Writes = true;
|
|
GlobalEffects.Releases = false;
|
|
return true;
|
|
case EffectsKind::ReadNone:
|
|
return true;
|
|
case EffectsKind::ReadOnly:
|
|
// @effects(readonly) is worthless if we have owned parameters, because
|
|
// the release inside the callee may call a deinit, which itself can do
|
|
// anything.
|
|
if (!F->hasOwnedParameters()) {
|
|
GlobalEffects.Reads = true;
|
|
return true;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
// Return true if this function's effects have been fully summarized in this
|
|
// FunctionSideEffects object without visiting its body.
|
|
bool FunctionSideEffects::summarizeFunction(SILFunction *F) {
|
|
assert(ParamEffects.empty() && "Expect uninitialized effects.");
|
|
if (!F->empty())
|
|
ParamEffects.resize(F->getArguments().size());
|
|
|
|
// Handle @effects attributes
|
|
if (setDefinedEffects(F)) {
|
|
DEBUG(llvm::dbgs() << " -- has defined effects " << F->getName() << '\n');
|
|
return true;
|
|
}
|
|
|
|
if (!F->isDefinition()) {
|
|
// We can't assume anything about external functions.
|
|
DEBUG(llvm::dbgs() << " -- is external " << F->getName() << '\n');
|
|
setWorstEffects();
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// Return true if the side effects of this semantic call are fully known without
|
|
// visiting the callee and have been recorded in this FunctionSideEffects
|
|
// object.
|
|
bool FunctionSideEffects::setSemanticEffects(ArraySemanticsCall ASC) {
|
|
assert(ASC.hasSelf());
|
|
auto &SelfEffects = ParamEffects[ParamEffects.size() - 1];
|
|
|
|
// Currently we only handle array semantics.
|
|
// TODO: also handle other semantic functions.
|
|
|
|
switch (ASC.getKind()) {
|
|
case ArrayCallKind::kGetCount:
|
|
case ArrayCallKind::kGetCapacity:
|
|
if (!ASC.mayHaveBridgedObjectElementType()) {
|
|
SelfEffects.Reads = true;
|
|
SelfEffects.Releases |= !ASC.hasGuaranteedSelf();
|
|
return true;
|
|
}
|
|
return false;
|
|
|
|
case ArrayCallKind::kCheckSubscript:
|
|
case ArrayCallKind::kCheckIndex:
|
|
if (!ASC.mayHaveBridgedObjectElementType()) {
|
|
SelfEffects.Reads = true;
|
|
SelfEffects.Releases |= !ASC.hasGuaranteedSelf();
|
|
Traps = true;
|
|
return true;
|
|
}
|
|
return false;
|
|
|
|
case ArrayCallKind::kGetElement:
|
|
if (!ASC.mayHaveBridgedObjectElementType()) {
|
|
SelfEffects.Reads = true;
|
|
SelfEffects.Releases |= !ASC.hasGuaranteedSelf();
|
|
for (auto i : range(((ApplyInst *)ASC)
|
|
->getOrigCalleeConv()
|
|
.getNumIndirectSILResults())) {
|
|
assert(!ASC.hasGetElementDirectResult());
|
|
ParamEffects[i].Writes = true;
|
|
}
|
|
return true;
|
|
}
|
|
return false;
|
|
|
|
case ArrayCallKind::kArrayPropsIsNativeTypeChecked:
|
|
SelfEffects.Releases |= !ASC.hasGuaranteedSelf();
|
|
// The isNative checks evaluate to a constant (no read!) if the array
|
|
// cannot be bridged.
|
|
if (ASC.mayHaveBridgedObjectElementType())
|
|
SelfEffects.Reads = true;
|
|
return true;
|
|
|
|
case ArrayCallKind::kGetElementAddress:
|
|
SelfEffects.Reads = true;
|
|
SelfEffects.Releases |= !ASC.hasGuaranteedSelf();
|
|
return true;
|
|
|
|
case ArrayCallKind::kMakeMutable:
|
|
if (!ASC.mayHaveBridgedObjectElementType()) {
|
|
SelfEffects.Writes = true;
|
|
GlobalEffects.Releases = true;
|
|
AllocsObjects = true;
|
|
ReadsRC = true;
|
|
return true;
|
|
}
|
|
return false;
|
|
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Summarize the callee side effects of a call instruction using this
|
|
// FunctionSideEffects object without analyzing the callee function bodies or
|
|
// scheduling the callees for bottom-up propagation.
|
|
//
|
|
// Return true if this call-site's effects are summarized without visiting the
|
|
// callee.
|
|
bool FunctionSideEffects::summarizeCall(FullApplySite fullApply) {
|
|
assert(ParamEffects.empty() && "Expect uninitialized effects.");
|
|
ParamEffects.resize(fullApply.getNumArguments());
|
|
|
|
// Is this a call to a semantics function?
|
|
if (auto apply = dyn_cast<ApplyInst>(fullApply.getInstruction())) {
|
|
ArraySemanticsCall ASC(apply);
|
|
if (ASC && ASC.hasSelf()) {
|
|
if (setSemanticEffects(ASC))
|
|
return true;
|
|
}
|
|
}
|
|
|
|
if (SILFunction *SingleCallee = fullApply.getReferencedFunction()) {
|
|
// Does the function have any @effects?
|
|
if (setDefinedEffects(SingleCallee))
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void FunctionSideEffects::analyzeInstruction(SILInstruction *I) {
|
|
// Handle some kind of instructions specially.
|
|
switch (I->getKind()) {
|
|
case SILInstructionKind::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.
|
|
getEffectsOn(I->getOperand(0))->Reads = true;
|
|
return;
|
|
case SILInstructionKind::AllocStackInst:
|
|
case SILInstructionKind::DeallocStackInst:
|
|
return;
|
|
case SILInstructionKind::StrongRetainInst:
|
|
case SILInstructionKind::StrongRetainUnownedInst:
|
|
case SILInstructionKind::RetainValueInst:
|
|
case SILInstructionKind::UnownedRetainInst:
|
|
getEffectsOn(I->getOperand(0))->Retains = true;
|
|
return;
|
|
case SILInstructionKind::StrongReleaseInst:
|
|
case SILInstructionKind::ReleaseValueInst:
|
|
case SILInstructionKind::UnownedReleaseInst:
|
|
getEffectsOn(I->getOperand(0))->Releases = true;
|
|
return;
|
|
case SILInstructionKind::UnconditionalCheckedCastInst:
|
|
getEffectsOn(cast<UnconditionalCheckedCastInst>(I)->getOperand())->Reads =
|
|
true;
|
|
Traps = true;
|
|
return;
|
|
case SILInstructionKind::LoadInst:
|
|
getEffectsOn(cast<LoadInst>(I)->getOperand())->Reads = true;
|
|
return;
|
|
case SILInstructionKind::StoreInst:
|
|
getEffectsOn(cast<StoreInst>(I)->getDest())->Writes = true;
|
|
return;
|
|
case SILInstructionKind::CondFailInst:
|
|
Traps = true;
|
|
return;
|
|
case SILInstructionKind::PartialApplyInst: {
|
|
AllocsObjects = true;
|
|
auto *PAI = cast<PartialApplyInst>(I);
|
|
auto Args = PAI->getArguments();
|
|
auto Params = PAI->getSubstCalleeType()->getParameters();
|
|
Params = Params.slice(Params.size() - Args.size(), Args.size());
|
|
for (unsigned Idx : indices(Args)) {
|
|
if (isIndirectFormalParameter(Params[Idx].getConvention()))
|
|
getEffectsOn(Args[Idx])->Reads = true;
|
|
}
|
|
return;
|
|
}
|
|
case SILInstructionKind::BuiltinInst: {
|
|
auto *BInst = cast<BuiltinInst>(I);
|
|
auto &BI = BInst->getBuiltinInfo();
|
|
switch (BI.ID) {
|
|
case BuiltinValueKind::IsUnique:
|
|
// TODO: derive this information in a more general way, e.g. add it
|
|
// to Builtins.def
|
|
ReadsRC = true;
|
|
break;
|
|
case BuiltinValueKind::CondUnreachable:
|
|
Traps = true;
|
|
return;
|
|
default:
|
|
break;
|
|
}
|
|
const IntrinsicInfo &IInfo = BInst->getIntrinsicInfo();
|
|
if (IInfo.ID == llvm::Intrinsic::trap) {
|
|
Traps = true;
|
|
return;
|
|
}
|
|
// 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).
|
|
AllocsObjects = true;
|
|
}
|
|
|
|
// Check the general memory behavior for instructions we didn't handle above.
|
|
switch (I->getMemoryBehavior()) {
|
|
case MemoryBehavior::None:
|
|
break;
|
|
case MemoryBehavior::MayRead:
|
|
GlobalEffects.Reads = true;
|
|
break;
|
|
case MemoryBehavior::MayWrite:
|
|
GlobalEffects.Writes = true;
|
|
break;
|
|
case MemoryBehavior::MayReadWrite:
|
|
GlobalEffects.Reads = true;
|
|
GlobalEffects.Writes = true;
|
|
break;
|
|
case MemoryBehavior::MayHaveSideEffects:
|
|
setWorstEffects();
|
|
break;
|
|
}
|
|
if (I->mayTrap())
|
|
Traps = true;
|
|
}
|
|
|
|
SILAnalysis *swift::createSideEffectAnalysis(SILModule *M) {
|
|
return new SideEffectAnalysis();
|
|
}
|