| //===--- 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(); |
| } |