| //===-- Internalize.cpp - Mark functions internal -------------------------===// | |
| // | |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | |
| // See https://llvm.org/LICENSE.txt for license information. | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |
| // | |
| //===----------------------------------------------------------------------===// | |
| // | |
| // This pass loops over all of the functions and variables in the input module. | |
| // If the function or variable does not need to be preserved according to the | |
| // client supplied callback, it is marked as internal. | |
| // | |
| // This transformation would not be legal in a regular compilation, but it gets | |
| // extra information from the linker about what is safe. | |
| // | |
| // For example: Internalizing a function with external linkage. Only if we are | |
| // told it is only used from within this module, it is safe to do it. | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #include "llvm/Transforms/IPO/Internalize.h" | |
| #include "llvm/ADT/SmallPtrSet.h" | |
| #include "llvm/ADT/Statistic.h" | |
| #include "llvm/ADT/StringSet.h" | |
| #include "llvm/Analysis/CallGraph.h" | |
| #include "llvm/IR/Module.h" | |
| #include "llvm/InitializePasses.h" | |
| #include "llvm/Pass.h" | |
| #include "llvm/Support/CommandLine.h" | |
| #include "llvm/Support/Debug.h" | |
| #include "llvm/Support/LineIterator.h" | |
| #include "llvm/Support/MemoryBuffer.h" | |
| #include "llvm/Support/raw_ostream.h" | |
| #include "llvm/Transforms/IPO.h" | |
| #include "llvm/Transforms/Utils/GlobalStatus.h" | |
| using namespace llvm; | |
| #define DEBUG_TYPE "internalize" | |
| STATISTIC(NumAliases, "Number of aliases internalized"); | |
| STATISTIC(NumFunctions, "Number of functions internalized"); | |
| STATISTIC(NumGlobals, "Number of global vars internalized"); | |
| // APIFile - A file which contains a list of symbols that should not be marked | |
| // external. | |
| static cl::opt<std::string> | |
| APIFile("internalize-public-api-file", cl::value_desc("filename"), | |
| cl::desc("A file containing list of symbol names to preserve")); | |
| // APIList - A list of symbols that should not be marked internal. | |
| static cl::list<std::string> | |
| APIList("internalize-public-api-list", cl::value_desc("list"), | |
| cl::desc("A list of symbol names to preserve"), cl::CommaSeparated); | |
| namespace { | |
| // Helper to load an API list to preserve from file and expose it as a functor | |
| // for internalization. | |
| class PreserveAPIList { | |
| public: | |
| PreserveAPIList() { | |
| if (!APIFile.empty()) | |
| LoadFile(APIFile); | |
| ExternalNames.insert(APIList.begin(), APIList.end()); | |
| } | |
| bool operator()(const GlobalValue &GV) { | |
| return ExternalNames.count(GV.getName()); | |
| } | |
| private: | |
| // Contains the set of symbols loaded from file | |
| StringSet<> ExternalNames; | |
| void LoadFile(StringRef Filename) { | |
| // Load the APIFile... | |
| ErrorOr<std::unique_ptr<MemoryBuffer>> Buf = | |
| MemoryBuffer::getFile(Filename); | |
| if (!Buf) { | |
| errs() << "WARNING: Internalize couldn't load file '" << Filename | |
| << "'! Continuing as if it's empty.\n"; | |
| return; // Just continue as if the file were empty | |
| } | |
| for (line_iterator I(*Buf->get(), true), E; I != E; ++I) | |
| ExternalNames.insert(*I); | |
| } | |
| }; | |
| } // end anonymous namespace | |
| bool InternalizePass::shouldPreserveGV(const GlobalValue &GV) { | |
| // Function must be defined here | |
| if (GV.isDeclaration()) | |
| return true; | |
| // Available externally is really just a "declaration with a body". | |
| if (GV.hasAvailableExternallyLinkage()) | |
| return true; | |
| // Assume that dllexported symbols are referenced elsewhere | |
| if (GV.hasDLLExportStorageClass()) | |
| return true; | |
| // Already local, has nothing to do. | |
| if (GV.hasLocalLinkage()) | |
| return false; | |
| // Check some special cases | |
| if (AlwaysPreserved.count(GV.getName())) | |
| return true; | |
| return MustPreserveGV(GV); | |
| } | |
| bool InternalizePass::maybeInternalize( | |
| GlobalValue &GV, const DenseSet<const Comdat *> &ExternalComdats) { | |
| if (Comdat *C = GV.getComdat()) { | |
| if (ExternalComdats.count(C)) | |
| return false; | |
| // If a comdat is not externally visible we can drop it. | |
| if (auto GO = dyn_cast<GlobalObject>(&GV)) | |
| GO->setComdat(nullptr); | |
| if (GV.hasLocalLinkage()) | |
| return false; | |
| } else { | |
| if (GV.hasLocalLinkage()) | |
| return false; | |
| if (shouldPreserveGV(GV)) | |
| return false; | |
| } | |
| GV.setVisibility(GlobalValue::DefaultVisibility); | |
| GV.setLinkage(GlobalValue::InternalLinkage); | |
| return true; | |
| } | |
| // If GV is part of a comdat and is externally visible, keep track of its | |
| // comdat so that we don't internalize any of its members. | |
| void InternalizePass::checkComdatVisibility( | |
| GlobalValue &GV, DenseSet<const Comdat *> &ExternalComdats) { | |
| Comdat *C = GV.getComdat(); | |
| if (!C) | |
| return; | |
| if (shouldPreserveGV(GV)) | |
| ExternalComdats.insert(C); | |
| } | |
| bool InternalizePass::internalizeModule(Module &M, CallGraph *CG) { | |
| bool Changed = false; | |
| CallGraphNode *ExternalNode = CG ? CG->getExternalCallingNode() : nullptr; | |
| SmallPtrSet<GlobalValue *, 8> Used; | |
| collectUsedGlobalVariables(M, Used, false); | |
| // Collect comdat visiblity information for the module. | |
| DenseSet<const Comdat *> ExternalComdats; | |
| if (!M.getComdatSymbolTable().empty()) { | |
| for (Function &F : M) | |
| checkComdatVisibility(F, ExternalComdats); | |
| for (GlobalVariable &GV : M.globals()) | |
| checkComdatVisibility(GV, ExternalComdats); | |
| for (GlobalAlias &GA : M.aliases()) | |
| checkComdatVisibility(GA, ExternalComdats); | |
| } | |
| // We must assume that globals in llvm.used have a reference that not even | |
| // the linker can see, so we don't internalize them. | |
| // For llvm.compiler.used the situation is a bit fuzzy. The assembler and | |
| // linker can drop those symbols. If this pass is running as part of LTO, | |
| // one might think that it could just drop llvm.compiler.used. The problem | |
| // is that even in LTO llvm doesn't see every reference. For example, | |
| // we don't see references from function local inline assembly. To be | |
| // conservative, we internalize symbols in llvm.compiler.used, but we | |
| // keep llvm.compiler.used so that the symbol is not deleted by llvm. | |
| for (GlobalValue *V : Used) { | |
| AlwaysPreserved.insert(V->getName()); | |
| } | |
| // Mark all functions not in the api as internal. | |
| for (Function &I : M) { | |
| if (!maybeInternalize(I, ExternalComdats)) | |
| continue; | |
| Changed = true; | |
| if (ExternalNode) | |
| // Remove a callgraph edge from the external node to this function. | |
| ExternalNode->removeOneAbstractEdgeTo((*CG)[&I]); | |
| ++NumFunctions; | |
| LLVM_DEBUG(dbgs() << "Internalizing func " << I.getName() << "\n"); | |
| } | |
| // Never internalize the llvm.used symbol. It is used to implement | |
| // attribute((used)). | |
| // FIXME: Shouldn't this just filter on llvm.metadata section?? | |
| AlwaysPreserved.insert("llvm.used"); | |
| AlwaysPreserved.insert("llvm.compiler.used"); | |
| // Never internalize anchors used by the machine module info, else the info | |
| // won't find them. (see MachineModuleInfo.) | |
| AlwaysPreserved.insert("llvm.global_ctors"); | |
| AlwaysPreserved.insert("llvm.global_dtors"); | |
| AlwaysPreserved.insert("llvm.global.annotations"); | |
| // Never internalize symbols code-gen inserts. | |
| // FIXME: We should probably add this (and the __stack_chk_guard) via some | |
| // type of call-back in CodeGen. | |
| AlwaysPreserved.insert("__stack_chk_fail"); | |
| AlwaysPreserved.insert("__stack_chk_guard"); | |
| // Mark all global variables with initializers that are not in the api as | |
| // internal as well. | |
| for (auto &GV : M.globals()) { | |
| if (!maybeInternalize(GV, ExternalComdats)) | |
| continue; | |
| Changed = true; | |
| ++NumGlobals; | |
| LLVM_DEBUG(dbgs() << "Internalized gvar " << GV.getName() << "\n"); | |
| } | |
| // Mark all aliases that are not in the api as internal as well. | |
| for (auto &GA : M.aliases()) { | |
| if (!maybeInternalize(GA, ExternalComdats)) | |
| continue; | |
| Changed = true; | |
| ++NumAliases; | |
| LLVM_DEBUG(dbgs() << "Internalized alias " << GA.getName() << "\n"); | |
| } | |
| return Changed; | |
| } | |
| InternalizePass::InternalizePass() : MustPreserveGV(PreserveAPIList()) {} | |
| PreservedAnalyses InternalizePass::run(Module &M, ModuleAnalysisManager &AM) { | |
| if (!internalizeModule(M, AM.getCachedResult<CallGraphAnalysis>(M))) | |
| return PreservedAnalyses::all(); | |
| PreservedAnalyses PA; | |
| PA.preserve<CallGraphAnalysis>(); | |
| return PA; | |
| } | |
| namespace { | |
| class InternalizeLegacyPass : public ModulePass { | |
| // Client supplied callback to control wheter a symbol must be preserved. | |
| std::function<bool(const GlobalValue &)> MustPreserveGV; | |
| public: | |
| static char ID; // Pass identification, replacement for typeid | |
| InternalizeLegacyPass() : ModulePass(ID), MustPreserveGV(PreserveAPIList()) {} | |
| InternalizeLegacyPass(std::function<bool(const GlobalValue &)> MustPreserveGV) | |
| : ModulePass(ID), MustPreserveGV(std::move(MustPreserveGV)) { | |
| initializeInternalizeLegacyPassPass(*PassRegistry::getPassRegistry()); | |
| } | |
| bool runOnModule(Module &M) override { | |
| if (skipModule(M)) | |
| return false; | |
| CallGraphWrapperPass *CGPass = | |
| getAnalysisIfAvailable<CallGraphWrapperPass>(); | |
| CallGraph *CG = CGPass ? &CGPass->getCallGraph() : nullptr; | |
| return internalizeModule(M, MustPreserveGV, CG); | |
| } | |
| void getAnalysisUsage(AnalysisUsage &AU) const override { | |
| AU.setPreservesCFG(); | |
| AU.addPreserved<CallGraphWrapperPass>(); | |
| } | |
| }; | |
| } | |
| char InternalizeLegacyPass::ID = 0; | |
| INITIALIZE_PASS(InternalizeLegacyPass, "internalize", | |
| "Internalize Global Symbols", false, false) | |
| ModulePass *llvm::createInternalizePass() { | |
| return new InternalizeLegacyPass(); | |
| } | |
| ModulePass *llvm::createInternalizePass( | |
| std::function<bool(const GlobalValue &)> MustPreserveGV) { | |
| return new InternalizeLegacyPass(std::move(MustPreserveGV)); | |
| } |