#include "llvm/Transforms/IPO/Internalize.h"
#include "llvm/ADT/Statistic.h"
#include "llvm/ADT/StringSet.h"
#include "llvm/ADT/Triple.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/GlobPattern.h"
#include "llvm/Support/LineIterator.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Transforms/IPO.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");
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"));
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 {
class PreserveAPIList {
public:
PreserveAPIList() {
if (!APIFile.empty())
LoadFile(APIFile);
for (StringRef Pattern : APIList)
addGlob(Pattern);
}
bool operator()(const GlobalValue &GV) {
return llvm::any_of(
ExternalNames, [&](GlobPattern &GP) { return GP.match(GV.getName()); });
}
private:
SmallVector<GlobPattern> ExternalNames;
void addGlob(StringRef Pattern) {
auto GlobOrErr = GlobPattern::create(Pattern);
if (!GlobOrErr) {
errs() << "WARNING: when loading pattern: '"
<< toString(GlobOrErr.takeError()) << "' ignoring";
return;
}
ExternalNames.emplace_back(std::move(*GlobOrErr));
}
void LoadFile(StringRef Filename) {
ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrErr =
MemoryBuffer::getFile(Filename);
if (!BufOrErr) {
errs() << "WARNING: Internalize couldn't load file '" << Filename
<< "'! Continuing as if it's empty.\n";
return; }
Buf = std::move(*BufOrErr);
for (line_iterator I(*Buf, true), E; I != E; ++I)
addGlob(*I);
}
std::shared_ptr<MemoryBuffer> Buf;
};
}
bool InternalizePass::shouldPreserveGV(const GlobalValue &GV) {
if (GV.isDeclaration())
return true;
if (GV.hasAvailableExternallyLinkage())
return true;
if (GV.hasDLLExportStorageClass())
return true;
if (const auto *G = dyn_cast<GlobalVariable>(&GV))
if (G->isExternallyInitialized())
return true;
if (GV.hasLocalLinkage())
return false;
if (AlwaysPreserved.count(GV.getName()))
return true;
return MustPreserveGV(GV);
}
bool InternalizePass::maybeInternalize(
GlobalValue &GV, DenseMap<const Comdat *, ComdatInfo> &ComdatMap) {
SmallString<0> ComdatName;
if (Comdat *C = GV.getComdat()) {
if (ComdatMap.lookup(C).External)
return false;
if (auto *GO = dyn_cast<GlobalObject>(&GV)) {
ComdatInfo &Info = ComdatMap.find(C)->second;
if (Info.Size == 1)
GO->setComdat(nullptr);
else if (!IsWasm)
C->setSelectionKind(Comdat::NoDeduplicate);
}
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;
}
void InternalizePass::checkComdat(
GlobalValue &GV, DenseMap<const Comdat *, ComdatInfo> &ComdatMap) {
Comdat *C = GV.getComdat();
if (!C)
return;
ComdatInfo &Info = ComdatMap.try_emplace(C).first->second;
++Info.Size;
if (shouldPreserveGV(GV))
Info.External = true;
}
bool InternalizePass::internalizeModule(Module &M, CallGraph *CG) {
bool Changed = false;
CallGraphNode *ExternalNode = CG ? CG->getExternalCallingNode() : nullptr;
SmallVector<GlobalValue *, 4> Used;
collectUsedGlobalVariables(M, Used, false);
DenseMap<const Comdat *, ComdatInfo> ComdatMap;
if (!M.getComdatSymbolTable().empty()) {
for (Function &F : M)
checkComdat(F, ComdatMap);
for (GlobalVariable &GV : M.globals())
checkComdat(GV, ComdatMap);
for (GlobalAlias &GA : M.aliases())
checkComdat(GA, ComdatMap);
}
for (GlobalValue *V : Used) {
AlwaysPreserved.insert(V->getName());
}
AlwaysPreserved.insert("llvm.used");
AlwaysPreserved.insert("llvm.compiler.used");
AlwaysPreserved.insert("llvm.global_ctors");
AlwaysPreserved.insert("llvm.global_dtors");
AlwaysPreserved.insert("llvm.global.annotations");
AlwaysPreserved.insert("__stack_chk_fail");
if (Triple(M.getTargetTriple()).isOSAIX())
AlwaysPreserved.insert("__ssp_canary_word");
else
AlwaysPreserved.insert("__stack_chk_guard");
IsWasm = Triple(M.getTargetTriple()).isOSBinFormatWasm();
for (Function &I : M) {
if (!maybeInternalize(I, ComdatMap))
continue;
Changed = true;
if (ExternalNode)
ExternalNode->removeOneAbstractEdgeTo((*CG)[&I]);
++NumFunctions;
LLVM_DEBUG(dbgs() << "Internalizing func " << I.getName() << "\n");
}
for (auto &GV : M.globals()) {
if (!maybeInternalize(GV, ComdatMap))
continue;
Changed = true;
++NumGlobals;
LLVM_DEBUG(dbgs() << "Internalized gvar " << GV.getName() << "\n");
}
for (auto &GA : M.aliases()) {
if (!maybeInternalize(GA, ComdatMap))
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 {
std::function<bool(const GlobalValue &)> MustPreserveGV;
public:
static char ID;
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));
}