mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
593 lines
20 KiB
C++
593 lines
20 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/SIL/SILArgument.h"
|
|
#include "swift/SILOptimizer/Analysis/AccessedStorageAnalysis.h"
|
|
#include "swift/SILOptimizer/Analysis/BasicCalleeAnalysis.h"
|
|
#include "swift/SILOptimizer/Analysis/FunctionOrder.h"
|
|
#include "swift/SILOptimizer/PassManager/PassManager.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();
|
|
LLVM_DEBUG(llvm::dbgs() << "invalidate all\n");
|
|
}
|
|
|
|
template <typename FunctionEffects>
|
|
void GenericFunctionEffectAnalysis<FunctionEffects>::invalidate(
|
|
SILFunction *F, InvalidationKind K) {
|
|
if (FunctionInfo *FInfo = functionInfoMap.lookup(F)) {
|
|
LLVM_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;
|
|
|
|
LLVM_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);
|
|
}
|
|
}
|
|
LLVM_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();
|
|
|
|
LLVM_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 {
|
|
LLVM_DEBUG(llvm::dbgs() << "new iteration\n");
|
|
needAnotherIteration = false;
|
|
|
|
for (FunctionInfo *functionInfo : bottomUpOrder) {
|
|
if (!functionInfo->needUpdateCallers)
|
|
continue;
|
|
|
|
LLVM_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;
|
|
|
|
LLVM_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>;
|
|
template class swift::GenericFunctionEffectAnalysis<FunctionAccessedStorage>;
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// 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(SEMANTICS_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->isDynamicallyReplaceable()) {
|
|
LLVM_DEBUG(llvm::dbgs()
|
|
<< " -- is dynamically_replaceable " << F->getName() << '\n');
|
|
setWorstEffects();
|
|
return true;
|
|
}
|
|
|
|
if (!F->empty())
|
|
ParamEffects.resize(F->getArguments().size());
|
|
|
|
// Handle @_effects attributes
|
|
if (setDefinedEffects(F)) {
|
|
LLVM_DEBUG(llvm::dbgs() << " -- has defined effects " << F->getName()
|
|
<< '\n');
|
|
return true;
|
|
}
|
|
|
|
if (!F->isDefinition()) {
|
|
// We can't assume anything about external functions.
|
|
LLVM_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;
|
|
#define ALWAYS_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \
|
|
case SILInstructionKind::Name##RetainInst: \
|
|
case SILInstructionKind::StrongRetain##Name##Inst: \
|
|
case SILInstructionKind::Copy##Name##ValueInst:
|
|
#include "swift/AST/ReferenceStorage.def"
|
|
case SILInstructionKind::StrongRetainInst:
|
|
case SILInstructionKind::RetainValueInst:
|
|
getEffectsOn(I->getOperand(0))->Retains = true;
|
|
return;
|
|
#define ALWAYS_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \
|
|
case SILInstructionKind::Name##ReleaseInst:
|
|
#include "swift/AST/ReferenceStorage.def"
|
|
case SILInstructionKind::StrongReleaseInst:
|
|
case SILInstructionKind::ReleaseValueInst:
|
|
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();
|
|
}
|