blob: 413bc8c5b5dbbaea2228a2317a5a31e664bc5261 [file] [log] [blame]
//===--- GlobalOpt.cpp - Optimize global initializers ---------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 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 "globalopt"
#include "swift/Basic/Demangle.h"
#include "swift/SIL/CFG.h"
#include "swift/SIL/DebugUtils.h"
#include "swift/SIL/SILInstruction.h"
#include "swift/SILOptimizer/Analysis/ColdBlockInfo.h"
#include "swift/SILOptimizer/Analysis/DominanceAnalysis.h"
#include "swift/SILOptimizer/PassManager/Passes.h"
#include "swift/SILOptimizer/PassManager/Transforms.h"
#include "swift/SILOptimizer/Utils/Local.h"
#include "llvm/ADT/MapVector.h"
#include "llvm/ADT/SCCIterator.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Debug.h"
#include "swift/AST/Mangle.h"
#include "swift/AST/ASTMangler.h"
using namespace swift;
namespace {
/// Optimize the placement of global initializers.
///
/// TODO:
///
/// - Analyze the module to move initializers to the module's public
/// entry points.
///
/// - Convert trivial initializers to static initialization. This requires
/// serializing globals.
///
/// - For global "lets", generate addressors that return by value. If we also
/// converted to a static initializer, then remove the load from the addressor.
///
/// - When the addressor is local to the module, be sure it is inlined to allow
/// constant propagation in case of statically initialized "lets".
class SILGlobalOpt {
SILModule *Module;
DominanceAnalysis* DA;
bool HasChanged = false;
// Map each global initializer to a list of call sites.
typedef SmallVector<ApplyInst *, 4> GlobalInitCalls;
typedef SmallVector<LoadInst *, 4> GlobalLoads;
llvm::MapVector<SILFunction*, GlobalInitCalls> GlobalInitCallMap;
// The following mappings are used if this is a compilation
// in scripting mode and global variables are accessed without
// addressors.
// Map each global let variable to a set of loads from it.
llvm::MapVector<SILGlobalVariable*, GlobalLoads> GlobalLoadMap;
// Map each global let variable to the store instruction which initializes it.
llvm::MapVector<SILGlobalVariable*, StoreInst *> GlobalVarStore;
// Variables in this set should not be processed by this pass
// anymore.
llvm::SmallPtrSet<SILGlobalVariable*, 16> GlobalVarSkipProcessing;
// Mark any block that this pass has determined to be inside a loop.
llvm::DenseSet<SILBasicBlock*> LoopBlocks;
// Mark any functions for which loops have been analyzed.
llvm::DenseSet<SILFunction*> LoopCheckedFunctions;
// Keep track of cold blocks.
ColdBlockInfo ColdBlocks;
// Whether we see a "once" call to callees that we currently don't handle.
bool UnhandledOnceCallee = false;
// Record number of times a globalinit_func is called by "once".
llvm::DenseMap<SILFunction*, unsigned> InitializerCount;
public:
SILGlobalOpt(SILModule *M, DominanceAnalysis *DA): Module(M), DA(DA),
ColdBlocks(DA) {}
bool run();
protected:
void collectGlobalInitCall(ApplyInst *AI);
void collectGlobalLoad(LoadInst *SI, SILGlobalVariable *SILG);
void collectGlobalStore(StoreInst *SI, SILGlobalVariable *SILG);
void collectGlobalAccess(GlobalAddrInst *GAI);
SILGlobalVariable *getVariableOfGlobalInit(SILFunction *AddrF);
bool isInLoop(SILBasicBlock *CurBB);
void placeInitializers(SILFunction *InitF, ArrayRef<ApplyInst*> Calls);
// Update UnhandledOnceCallee and InitializerCount by going through all "once"
// calls.
void collectOnceCall(BuiltinInst *AI);
// Set the static initializer and remove "once" from addressor if a global can
// be statically initialized.
void optimizeInitializer(SILFunction *AddrF, GlobalInitCalls &Calls);
void optimizeGlobalAccess(SILGlobalVariable *SILG, StoreInst *SI);
// Replace loads from a global variable by the known value.
void replaceLoadsByKnownValue(BuiltinInst *CallToOnce,
SILFunction *AddrF,
SILFunction *InitF,
SILGlobalVariable *SILG,
GlobalInitCalls &Calls);
};
/// Helper class to copy only a set of SIL instructions providing in the
/// constructor.
class InstructionsCloner : public SILClonerWithScopes<InstructionsCloner> {
friend class SILVisitor<InstructionsCloner>;
friend class SILCloner<InstructionsCloner>;
ArrayRef<SILInstruction *> Insns;
protected:
SILBasicBlock *FromBB, *DestBB;
public:
// A map of old to new available values.
SmallVector<std::pair<ValueBase *, SILValue>, 16> AvailVals;
InstructionsCloner(SILFunction &F,
ArrayRef<SILInstruction *> Insns,
SILBasicBlock *Dest = nullptr)
: SILClonerWithScopes(F), Insns(Insns), FromBB(nullptr), DestBB(Dest) {}
void process(SILInstruction *I) { visit(I); }
SILBasicBlock *remapBasicBlock(SILBasicBlock *BB) { return BB; }
SILValue remapValue(SILValue Value) {
return SILCloner<InstructionsCloner>::remapValue(Value);
}
void postProcess(SILInstruction *Orig, SILInstruction *Cloned) {
DestBB->push_back(Cloned);
SILClonerWithScopes<InstructionsCloner>::postProcess(Orig, Cloned);
AvailVals.push_back(std::make_pair(Orig, Cloned));
}
// Clone all instructions from Insns into DestBB
void clone() {
for (auto I : Insns)
process(I);
}
};
} // end anonymous namespace
/// If this is a call to a global initializer, map it.
void SILGlobalOpt::collectGlobalInitCall(ApplyInst *AI) {
SILFunction *F = AI->getReferencedFunction();
if (!F || !F->isGlobalInit())
return;
GlobalInitCallMap[F].push_back(AI);
}
/// If this is a read from a global let variable, map it.
void SILGlobalOpt::collectGlobalLoad(LoadInst *LI, SILGlobalVariable *SILG) {
assert(SILG);
//assert(SILG->isLet());
// This is read from a let variable.
// Figure out if the value of this variable is statically known.
GlobalLoadMap[SILG].push_back(LI);
}
/// Remove an unused global token used by once calls.
static void removeToken(SILValue Op) {
if (auto *ATPI = dyn_cast<AddressToPointerInst>(Op)) {
Op = ATPI->getOperand();
if (ATPI->use_empty())
ATPI->eraseFromParent();
}
if (GlobalAddrInst *GAI = dyn_cast<GlobalAddrInst>(Op)) {
auto *Global = GAI->getReferencedGlobal();
// If "global_addr token" is used more than one time, bail.
if (!(GAI->use_empty() || GAI->hasOneUse()))
return;
// If it is not a *_token global variable, bail.
if (!Global || Global->getName().find("_token") == StringRef::npos)
return;
GAI->getModule().eraseGlobalVariable(Global);
GAI->replaceAllUsesWithUndef();
GAI->eraseFromParent();
}
}
static std::string mangleGetter(VarDecl *varDecl) {
Mangle::Mangler getterMangler;
getterMangler.append("_T");
getterMangler.mangleGlobalGetterEntity(varDecl);
std::string Old = getterMangler.finalize();
NewMangling::ASTMangler NewMangler;
std::string New = NewMangler.mangleGlobalGetterEntity(varDecl);
return NewMangling::selectMangling(Old, New);
}
/// Generate getter from the initialization code whose
/// result is stored by a given store instruction.
static SILFunction *genGetterFromInit(StoreInst *Store,
SILGlobalVariable *SILG) {
auto *varDecl = SILG->getDecl();
auto getterName = mangleGetter(varDecl);
// Check if a getter was generated already.
if (auto *F = Store->getModule().lookUpFunction(getterName))
return F;
// Find the code that performs the initialization first.
// Recursively walk the SIL value being assigned to the SILG.
auto V = Store->getSrc();
SmallVector<SILInstruction *, 8> ReverseInsns;
SmallVector<SILInstruction *, 8> Insns;
ReverseInsns.push_back(Store);
ReverseInsns.push_back(dyn_cast<SILInstruction>(Store->getDest()));
if (!analyzeStaticInitializer(V, ReverseInsns))
return nullptr;
// Produce a correct order of instructions.
while (!ReverseInsns.empty()) {
Insns.push_back(ReverseInsns.pop_back_val());
}
// Generate a getter from the global init function without side-effects.
auto refType = varDecl->getInterfaceType()->getCanonicalType();
// Function takes no arguments and returns refType
SILResultInfo Results[] = { SILResultInfo(refType, ResultConvention::Owned) };
SILFunctionType::ExtInfo EInfo;
EInfo = EInfo.withRepresentation(SILFunctionType::Representation::Thin);
auto LoweredType = SILFunctionType::get(nullptr, EInfo,
ParameterConvention::Direct_Owned, { }, Results, None,
Store->getModule().getASTContext());
auto *GetterF = Store->getModule().getOrCreateFunction(
Store->getLoc(),
getterName, SILLinkage::Private, LoweredType,
IsBare_t::IsBare, IsTransparent_t::IsNotTransparent,
IsFragile_t::IsFragile);
GetterF->setDebugScope(Store->getFunction()->getDebugScope());
if (Store->getFunction()->hasUnqualifiedOwnership())
GetterF->setUnqualifiedOwnership();
auto *EntryBB = GetterF->createBasicBlock();
// Copy instructions into GetterF
InstructionsCloner Cloner(*GetterF, Insns, EntryBB);
Cloner.clone();
GetterF->setInlined();
// Find the store instruction and turn it into return.
// Remove the alloc_global instruction.
auto BB = EntryBB;
SILValue Val;
for (auto II = BB->begin(), E = BB->end(); II != E;) {
auto &I = *II++;
if (isa<AllocGlobalInst>(&I)) {
I.eraseFromParent();
continue;
}
if (StoreInst *SI = dyn_cast<StoreInst>(&I)) {
Val = SI->getSrc();
SILBuilderWithScope B(SI);
B.createReturn(SI->getLoc(), Val);
eraseUsesOfInstruction(SI);
recursivelyDeleteTriviallyDeadInstructions(SI, true);
return GetterF;
}
}
Store->getModule().getFunctionList().addNodeToList(GetterF);
return GetterF;
}
/// If this is a read from a global let variable, map it.
void SILGlobalOpt::collectGlobalStore(StoreInst *SI, SILGlobalVariable *SILG) {
if (GlobalVarStore.count(SILG)) {
// There is more then one assignment to a given global variable.
// Therefore we don't know its value.
GlobalVarSkipProcessing.insert(SILG);
}
// Figure out if the value of this variable is statically known.
GlobalVarStore[SILG] = SI;
}
/// Return the callee of a once call.
static SILFunction *getCalleeOfOnceCall(BuiltinInst *BI) {
assert(BI->getNumOperands() == 2 && "once call should have 2 operands.");
auto Callee = BI->getOperand(1);
assert(cast<SILFunctionType>(Callee->getType().getSwiftRValueType())
->getRepresentation() == SILFunctionTypeRepresentation::Thin &&
"Expected thin function representation!");
if (auto *FR = dyn_cast<FunctionRefInst>(Callee))
return FR->getReferencedFunction();
return nullptr;
}
/// Update UnhandledOnceCallee and InitializerCount by going through all "once"
/// calls.
void SILGlobalOpt::collectOnceCall(BuiltinInst *BI) {
if (UnhandledOnceCallee)
return;
const BuiltinInfo &Builtin = Module->getBuiltinInfo(BI->getName());
if (Builtin.ID != BuiltinValueKind::Once)
return;
SILFunction *Callee = getCalleeOfOnceCall(BI);
if (!Callee) {
DEBUG(llvm::dbgs() << "GlobalOpt: unhandled once callee\n");
UnhandledOnceCallee = true;
return;
}
if (!Callee->getName().startswith("globalinit_"))
return;
// We currently disable optimizing the initializer if a globalinit_func
// is called by "once" from multiple locations.
if (!BI->getFunction()->isGlobalInit())
// If a globalinit_func is called by "once" from a function that is not
// an addressor, we set count to 2 to disable optimizing the initializer.
InitializerCount[Callee] = 2;
else
InitializerCount[Callee]++;
}
/// return true if this block is inside a loop.
bool SILGlobalOpt::isInLoop(SILBasicBlock *CurBB) {
SILFunction *F = CurBB->getParent();
// Catch the common case in which we've already hoisted the initializer.
if (CurBB == &F->front())
return false;
if (LoopCheckedFunctions.insert(F).second) {
for (auto I = scc_begin(F); !I.isAtEnd(); ++I) {
if (I.hasLoop())
for (SILBasicBlock *BB : *I)
LoopBlocks.insert(BB);
}
}
return LoopBlocks.count(CurBB);
}
/// Returns true if the block \p BB is terminated with a cond_br based on an
/// availability check.
static bool isAvailabilityCheck(SILBasicBlock *BB) {
CondBranchInst *CBR = dyn_cast<CondBranchInst>(BB->getTerminator());
if (!CBR)
return false;
ApplyInst *AI = dyn_cast<ApplyInst>(CBR->getCondition());
if (!AI)
return false;
SILFunction *F = AI->getReferencedFunction();
if (!F || !F->hasSemanticsAttrs())
return false;
return F->hasSemanticsAttrThatStartsWith("availability");
}
/// Returns true if there are any availability checks along the dominator tree
/// from \p From to \p To.
static bool isAvailabilityCheckOnDomPath(SILBasicBlock *From, SILBasicBlock *To,
DominanceInfo *DT) {
if (From == To)
return false;
auto *Node = DT->getNode(To)->getIDom();
for (;;) {
SILBasicBlock *BB = Node->getBlock();
if (isAvailabilityCheck(BB))
return true;
if (BB == From)
return false;
Node = Node->getIDom();
assert(Node && "Should have hit To-block");
}
}
/// Optimize placement of initializer calls given a list of calls to the
/// same initializer. All original initialization points must be dominated by
/// the final initialization calls.
///
/// The current heuristic hoists all initialization points within a function to
/// a single dominating call in the outer loop preheader.
void SILGlobalOpt::placeInitializers(SILFunction *InitF,
ArrayRef<ApplyInst*> Calls) {
DEBUG(llvm::dbgs() << "GlobalOpt: calls to "
<< Demangle::demangleSymbolAsString(InitF->getName())
<< " : " << Calls.size() << "\n");
// Map each initializer-containing function to its final initializer call.
llvm::DenseMap<SILFunction*, ApplyInst*> ParentFuncs;
for (auto *AI : Calls) {
assert(AI->getNumArguments() == 0 && "ill-formed global init call");
assert(cast<FunctionRefInst>(AI->getCallee())->getReferencedFunction()
== InitF && "wrong init call");
SILFunction *ParentF = AI->getFunction();
DominanceInfo *DT = DA->get(ParentF);
auto PFI = ParentFuncs.find(ParentF);
ApplyInst *HoistAI = nullptr;
if (PFI != ParentFuncs.end()) {
// Found a replacement for this init call.
// Ensure the replacement dominates the original call site.
ApplyInst *CommonAI = PFI->second;
assert(cast<FunctionRefInst>(CommonAI->getCallee())
->getReferencedFunction() == InitF &&
"ill-formed global init call");
SILBasicBlock *DomBB =
DT->findNearestCommonDominator(AI->getParent(), CommonAI->getParent());
// We must not move initializers around availability-checks.
if (!isAvailabilityCheckOnDomPath(DomBB, CommonAI->getParent(), DT)) {
if (DomBB != CommonAI->getParent()) {
CommonAI->moveBefore(&*DomBB->begin());
placeFuncRef(CommonAI, DT);
// Try to hoist the existing AI again if we move it to another block,
// e.g. from a loop exit into the loop.
HoistAI = CommonAI;
}
AI->replaceAllUsesWith(CommonAI);
AI->eraseFromParent();
HasChanged = true;
}
} else {
ParentFuncs[ParentF] = AI;
// It's the first time we found a call to InitF in this function, so we
// try to hoist it out of any loop.
HoistAI = AI;
}
if (HoistAI) {
// Move this call to the outermost loop preheader.
SILBasicBlock *BB = HoistAI->getParent();
typedef llvm::DomTreeNodeBase<SILBasicBlock> DomTreeNode;
DomTreeNode *Node = DT->getNode(BB);
while (Node) {
SILBasicBlock *DomParentBB = Node->getBlock();
if (isAvailabilityCheck(DomParentBB)) {
DEBUG(llvm::dbgs() << " don't hoist above availability check at bb"
<< DomParentBB->getDebugID() << "\n");
break;
}
BB = DomParentBB;
if (!isInLoop(BB))
break;
Node = Node->getIDom();
}
if (BB == HoistAI->getParent()) {
// BB is either unreachable or not in a loop.
DEBUG(llvm::dbgs() << " skipping (not in a loop): " << *HoistAI
<< " in " << HoistAI->getFunction()->getName() << "\n");
}
else {
DEBUG(llvm::dbgs() << " hoisting: " << *HoistAI
<< " in " << HoistAI->getFunction()->getName() << "\n");
HoistAI->moveBefore(&*BB->begin());
placeFuncRef(HoistAI, DT);
HasChanged = true;
}
}
}
}
/// Create a getter function from the initializer function.
static SILFunction *genGetterFromInit(SILFunction *InitF, VarDecl *varDecl) {
// Generate a getter from the global init function without side-effects.
auto getterName = mangleGetter(varDecl);
// Check if a getter was generated already.
if (auto *F = InitF->getModule().lookUpFunction(getterName))
return F;
auto refType = varDecl->getInterfaceType()->getCanonicalType();
// Function takes no arguments and returns refType
SILResultInfo Results[] = { SILResultInfo(refType, ResultConvention::Owned) };
SILFunctionType::ExtInfo EInfo;
EInfo = EInfo.withRepresentation(SILFunctionType::Representation::Thin);
auto LoweredType = SILFunctionType::get(nullptr, EInfo,
ParameterConvention::Direct_Owned, { }, Results, None,
InitF->getASTContext());
auto *GetterF = InitF->getModule().getOrCreateFunction(
InitF->getLocation(),
getterName, SILLinkage::Private, LoweredType,
IsBare_t::IsBare, IsTransparent_t::IsNotTransparent,
IsFragile_t::IsFragile);
if (InitF->hasUnqualifiedOwnership())
GetterF->setUnqualifiedOwnership();
auto *EntryBB = GetterF->createBasicBlock();
// Copy InitF into GetterF
BasicBlockCloner Cloner(&*InitF->begin(), EntryBB, /*WithinFunction=*/false);
Cloner.clone();
GetterF->setInlined();
// Find the store instruction
auto BB = EntryBB;
SILValue Val;
SILInstruction *Store;
for (auto II = BB->begin(), E = BB->end(); II != E;) {
auto &I = *II++;
if (isa<AllocGlobalInst>(&I)) {
I.eraseFromParent();
continue;
}
if (StoreInst *SI = dyn_cast<StoreInst>(&I)) {
Val = SI->getSrc();
Store = SI;
continue;
}
if (ReturnInst *RI = dyn_cast<ReturnInst>(&I)) {
SILBuilderWithScope B(RI);
B.createReturn(RI->getLoc(), Val);
eraseUsesOfInstruction(RI);
recursivelyDeleteTriviallyDeadInstructions(RI, true);
recursivelyDeleteTriviallyDeadInstructions(Store, true);
return GetterF;
}
}
InitF->getModule().getFunctionList().addNodeToList(GetterF);
return GetterF;
}
/// Find the globalinit_func by analyzing the body of the addressor.
static SILFunction *findInitializer(SILModule *Module, SILFunction *AddrF,
BuiltinInst *&CallToOnce) {
// We only handle a single SILBasicBlock for now.
if (AddrF->size() != 1)
return nullptr;
CallToOnce = nullptr;
SILBasicBlock *BB = &AddrF->front();
for (auto &I : *BB) {
// Find the builtin "once" call.
if (BuiltinInst *BI = dyn_cast<BuiltinInst>(&I)) {
const BuiltinInfo &Builtin = Module->getBuiltinInfo(BI->getName());
if (Builtin.ID != BuiltinValueKind::Once)
continue;
// Bail if we have multiple "once" calls in the addressor.
if (CallToOnce)
return nullptr;
CallToOnce = BI;
}
}
if (!CallToOnce)
return nullptr;
return getCalleeOfOnceCall(CallToOnce);
}
/// Checks if a given global variable is assigned only once.
static bool isAssignedOnlyOnceInInitializer(SILGlobalVariable *SILG) {
if (SILG->isLet())
return true;
// TODO: If we can prove that a given global variable
// is assigned only once, during initialization, then
// we can treat it as if it is a let.
// If this global is internal or private, it should be
return false;
}
/// Replace load sequence which may contain
/// a chain of struct_element_addr followed by a load.
/// The sequence is traversed starting from the load
/// instruction.
static SILInstruction *convertLoadSequence(SILInstruction *I,
SILInstruction *Value,
SILBuilder &B) {
if (isa<GlobalAddrInst>(I))
return Value;
if (auto *LI = dyn_cast<LoadInst>(I)) {
Value =
convertLoadSequence(cast<SILInstruction>(LI->getOperand()), Value, B);
LI->replaceAllUsesWith(Value);
return Value;
}
// It is a series of struct_element_addr followed by load.
if (auto *SEAI = dyn_cast<StructElementAddrInst>(I)) {
Value =
convertLoadSequence(cast<SILInstruction>(SEAI->getOperand()), Value, B);
auto *SEI = B.createStructExtract(SEAI->getLoc(), Value, SEAI->getField());
return SEI;
}
if (auto *TEAI = dyn_cast<TupleElementAddrInst>(I)) {
Value =
convertLoadSequence(cast<SILInstruction>(TEAI->getOperand()), Value, B);
auto *TEI = B.createTupleExtract(TEAI->getLoc(), Value, TEAI->getFieldNo());
return TEI;
}
llvm_unreachable("Unknown instruction sequence for reading from a global");
return nullptr;
}
/// Replace loads from a global variable by the known value.
void SILGlobalOpt::
replaceLoadsByKnownValue(BuiltinInst *CallToOnce, SILFunction *AddrF,
SILFunction *InitF, SILGlobalVariable *SILG,
GlobalInitCalls &Calls) {
assert(isAssignedOnlyOnceInInitializer(SILG) &&
"The value of the initializer should be known at compile-time");
assert(SILG->getDecl() &&
"Decl corresponding to the global variable should be known");
removeToken(CallToOnce->getOperand(0));
eraseUsesOfInstruction(CallToOnce);
recursivelyDeleteTriviallyDeadInstructions(CallToOnce, true);
// Make this addressor transparent.
AddrF->setTransparent(IsTransparent_t::IsTransparent);
for (int i = 0, e = Calls.size(); i < e; ++i) {
auto *Call = Calls[i];
SILBuilderWithScope B(Call);
SmallVector<SILValue, 1> Args;
auto *NewAI = B.createApply(Call->getLoc(), Call->getCallee(), Args, false);
Call->replaceAllUsesWith(NewAI);
eraseUsesOfInstruction(Call);
recursivelyDeleteTriviallyDeadInstructions(Call, true);
Calls[i] = NewAI;
}
// Generate a getter from InitF which returns the value of the global.
auto *GetterF = genGetterFromInit(InitF, SILG->getDecl());
// Replace all calls of an addressor by calls of a getter .
for (int i = 0, e = Calls.size(); i < e; ++i) {
auto *Call = Calls[i];
// Now find all uses of Call. They all should be loads, so that
// we can replace it.
bool isValid = true;
for (auto Use : Call->getUses()) {
if (!isa<PointerToAddressInst>(Use->getUser())) {
isValid = false;
break;
}
}
if (!isValid)
continue;
SILBuilderWithScope B(Call);
SmallVector<SILValue, 1> Args;
auto *GetterRef = B.createFunctionRef(Call->getLoc(), GetterF);
auto *NewAI = B.createApply(Call->getLoc(), GetterRef, Args, false);
for (auto Use : Call->getUses()) {
auto *PTAI = dyn_cast<PointerToAddressInst>(Use->getUser());
assert(PTAI && "All uses should be pointer_to_address");
for (auto PTAIUse : PTAI->getUses()) {
replaceLoadSequence(PTAIUse->getUser(), NewAI, B);
}
}
eraseUsesOfInstruction(Call);
recursivelyDeleteTriviallyDeadInstructions(Call, true);
}
Calls.clear();
SILG->setInitializer(InitF);
}
/// We analyze the body of globalinit_func to see if it can be statically
/// initialized. If yes, we set the initial value of the SILGlobalVariable and
/// remove the "once" call to globalinit_func from the addressor.
void SILGlobalOpt::optimizeInitializer(SILFunction *AddrF,
GlobalInitCalls &Calls) {
if (UnhandledOnceCallee)
return;
// Find the initializer and the SILGlobalVariable.
BuiltinInst *CallToOnce;
// If the addressor contains a single "once" call, it calls globalinit_func,
// and the globalinit_func is called by "once" from a single location,
// continue; otherwise bail.
auto *InitF = findInitializer(Module, AddrF, CallToOnce);
if (!InitF || !InitF->getName().startswith("globalinit_") ||
InitializerCount[InitF] > 1)
return;
// If the globalinit_func is trivial, continue; otherwise bail.
auto *SILG = SILGlobalVariable::getVariableOfStaticInitializer(InitF);
if (!SILG || !SILG->isDefinition())
return;
DEBUG(llvm::dbgs() << "GlobalOpt: use static initializer for " <<
SILG->getName() << '\n');
// Remove "once" call from the addressor.
if (!isAssignedOnlyOnceInInitializer(SILG) || !SILG->getDecl()) {
removeToken(CallToOnce->getOperand(0));
CallToOnce->eraseFromParent();
SILG->setInitializer(InitF);
HasChanged = true;
return;
}
replaceLoadsByKnownValue(CallToOnce, AddrF, InitF, SILG, Calls);
HasChanged = true;
}
SILGlobalVariable *SILGlobalOpt::getVariableOfGlobalInit(SILFunction *AddrF) {
if (AddrF->isGlobalInit()) {
// If the addressor contains a single "once" call, it calls globalinit_func,
// and the globalinit_func is called by "once" from a single location,
// continue; otherwise bail.
BuiltinInst *CallToOnce;
auto *InitF = findInitializer(Module, AddrF, CallToOnce);
if (!InitF || !InitF->getName().startswith("globalinit_")
|| InitializerCount[InitF] > 1)
return nullptr;
// If the globalinit_func is trivial, continue; otherwise bail.
auto *SILG = SILGlobalVariable::getVariableOfStaticInitializer(InitF);
if (!SILG || !SILG->isDefinition())
return nullptr;
return SILG;
}
return nullptr;
}
static bool canBeChangedExternally(SILGlobalVariable *SILG) {
// Don't assume anything about globals which are imported from other modules.
if (isAvailableExternally(SILG->getLinkage()))
return true;
// Use access specifiers from the declarations,
// if possible.
if (auto *Decl = SILG->getDecl()) {
switch (Decl->getEffectiveAccess()) {
case Accessibility::Private:
case Accessibility::FilePrivate:
return false;
case Accessibility::Internal:
return !SILG->getModule().isWholeModule();
case Accessibility::Public:
case Accessibility::Open:
return true;
}
}
if (SILG->getLinkage() == SILLinkage::Private)
return false;
if (SILG->getLinkage() == SILLinkage::Hidden
&& SILG->getModule().isWholeModule()) {
return false;
}
return true;
}
/// Check if instruction I is a load from instruction V or
/// or a struct_element_addr from instruction V.
/// returns instruction I if this condition holds, or nullptr otherwise.
static LoadInst *getValidLoad(SILInstruction *I, SILInstruction *V) {
if (auto *LI = dyn_cast<LoadInst>(I)) {
if (LI->getOperand() == V)
return LI;
}
if (auto *SEAI = dyn_cast<StructElementAddrInst>(I)) {
if (SEAI->getOperand() == V && SEAI->hasOneUse())
return getValidLoad(SEAI->use_begin()->getUser(), SEAI);
}
if (auto *TEAI = dyn_cast<TupleElementAddrInst>(I)) {
if (TEAI->getOperand() == V && TEAI->hasOneUse())
return getValidLoad(TEAI->use_begin()->getUser(), TEAI);
}
return nullptr;
}
/// If this is a read from a global let variable, map it.
void SILGlobalOpt::collectGlobalAccess(GlobalAddrInst *GAI) {
auto *SILG = GAI->getReferencedGlobal();
if (!SILG)
return;
if (!SILG->isLet()) {
// We cannot determine the value for global variables which could be
// changed externally at run-time.
if (canBeChangedExternally(SILG))
return;
}
if (GlobalVarSkipProcessing.count(SILG))
return;
if (!isSimpleType(SILG->getLoweredType(), *Module)) {
GlobalVarSkipProcessing.insert(SILG);
return;
}
// Ignore any accesses inside addressors for SILG
auto *F = GAI->getFunction();
auto GlobalVar = getVariableOfGlobalInit(F);
if (GlobalVar == SILG)
return;
if (!SILG->getDecl())
return;
SILValue V = GAI;
for (auto Use : getNonDebugUses(V)) {
if (auto *SI = dyn_cast<StoreInst>(Use->getUser())) {
if (SI->getDest() == GAI)
collectGlobalStore(SI, SILG);
continue;
}
if (auto *Load = getValidLoad(Use->getUser(), GAI)) {
collectGlobalLoad(Load, SILG);
continue;
}
// This global is not initialized by a simple
// constant value at this moment.
GlobalVarSkipProcessing.insert(SILG);
break;
}
}
/// Optimize access to the global variable, which is known
/// to have a constant value. Replace all loads from the
/// global address by invocations of a getter that returns
/// the value of this variable.
void SILGlobalOpt::optimizeGlobalAccess(SILGlobalVariable *SILG,
StoreInst *SI) {
DEBUG(llvm::dbgs() << "GlobalOpt: use static initializer for " <<
SILG->getName() << '\n');
if (GlobalVarSkipProcessing.count(SILG))
return;
if (//!isAssignedOnlyOnceInInitializer(SILG) ||
!SILG->getDecl()) {
return;
}
if (!GlobalLoadMap.count(SILG))
return;
// Generate a getter only if there are any loads from this variable.
SILFunction *GetterF = genGetterFromInit(SI, SILG);
if (!GetterF)
return;
// Iterate over all loads and replace them by values.
// TODO: In principle, we could invoke the getter only once
// inside each function that loads from the global. This
// invocation should happen at the common dominator of all
// loads inside this function.
for (auto *Load: GlobalLoadMap[SILG]) {
SILBuilderWithScope B(Load);
auto *GetterRef = B.createFunctionRef(Load->getLoc(), GetterF);
auto *Value = B.createApply(Load->getLoc(), GetterRef, {}, false);
convertLoadSequence(Load, Value, B);
HasChanged = true;
}
}
bool SILGlobalOpt::run() {
for (auto &F : *Module) {
// Don't optimize functions that are marked with the opt.never attribute.
if (!F.shouldOptimize())
continue;
// Cache cold blocks per function.
ColdBlockInfo ColdBlocks(DA);
for (auto &BB : F) {
bool IsCold = ColdBlocks.isCold(&BB);
for (auto &I : BB)
if (BuiltinInst *BI = dyn_cast<BuiltinInst>(&I)) {
collectOnceCall(BI);
} else if (ApplyInst *AI = dyn_cast<ApplyInst>(&I)) {
if (!IsCold)
collectGlobalInitCall(AI);
} else if (GlobalAddrInst *GAI = dyn_cast<GlobalAddrInst>(&I)) {
collectGlobalAccess(GAI);
}
}
}
for (auto &InitCalls : GlobalInitCallMap) {
// Optimize the addressors if possible.
optimizeInitializer(InitCalls.first, InitCalls.second);
placeInitializers(InitCalls.first, InitCalls.second);
}
for (auto &Init : GlobalVarStore) {
// Optimize the access to globals if possible.
optimizeGlobalAccess(Init.first, Init.second);
}
return HasChanged;
}
namespace {
class SILGlobalOptPass : public SILModuleTransform
{
void run() override {
DominanceAnalysis *DA = PM->getAnalysis<DominanceAnalysis>();
if (SILGlobalOpt(getModule(), DA).run()) {
invalidateAnalysis(SILAnalysis::InvalidationKind::FunctionBody);
}
}
StringRef getName() override { return "SIL Global Optimization"; }
};
} // end anonymous namespace
SILTransform *swift::createGlobalOpt() {
return new SILGlobalOptPass();
}