#include "llvm/Transforms/IPO/Internalize.h"
#include "llvm/Transforms/IPO.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/Pass.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Transforms/Utils/GlobalStatus.h"
#include <fstream>
#include <set>
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);
ExternalNames.insert(APIList.begin(), APIList.end());
}
bool operator()(const GlobalValue &GV) {
return ExternalNames.count(GV.getName());
}
private:
StringSet<> ExternalNames;
void LoadFile(StringRef Filename) {
std::ifstream In(Filename.data());
if (!In.good()) {
errs() << "WARNING: Internalize couldn't load file '" << Filename
<< "'! Continuing as if it's empty.\n";
return; }
while (In) {
std::string Symbol;
In >> Symbol;
if (!Symbol.empty())
ExternalNames.insert(Symbol);
}
}
};
class InternalizePass : public ModulePass {
std::function<bool(const GlobalValue &)> MustPreserveGV;
public:
static char ID;
InternalizePass() : ModulePass(ID), MustPreserveGV(PreserveAPIList()) {}
InternalizePass(std::function<bool(const GlobalValue &)> MustPreserveGV)
: ModulePass(ID), MustPreserveGV(std::move(MustPreserveGV)) {
initializeInternalizePassPass(*PassRegistry::getPassRegistry());
}
bool runOnModule(Module &M) override {
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>();
}
};
class Internalizer {
const std::function<bool(const GlobalValue &)> &MustPreserveGV;
StringSet<> AlwaysPreserved;
bool ShouldPreserveGV(const GlobalValue &GV) {
if (GV.isDeclaration())
return true;
if (GV.hasAvailableExternallyLinkage())
return true;
if (GV.hasDLLExportStorageClass())
return true;
if (GV.hasLocalLinkage())
return false;
if (AlwaysPreserved.count(GV.getName()))
return true;
return MustPreserveGV(GV);
}
bool maybeInternalize(GlobalValue &GV,
const std::set<const Comdat *> &ExternalComdats);
void checkComdatVisibility(GlobalValue &GV,
std::set<const Comdat *> &ExternalComdats);
public:
Internalizer(const std::function<bool(const GlobalValue &)> &MustPreserveGV)
: MustPreserveGV(MustPreserveGV) {}
bool internalizeModule(Module &TheModule, CallGraph *CG = nullptr);
};
bool Internalizer::maybeInternalize(
GlobalValue &GV, const std::set<const Comdat *> &ExternalComdats) {
if (Comdat *C = GV.getComdat()) {
if (ExternalComdats.count(C))
return false;
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;
}
void Internalizer::checkComdatVisibility(
GlobalValue &GV, std::set<const Comdat *> &ExternalComdats) {
Comdat *C = GV.getComdat();
if (!C)
return;
if (ShouldPreserveGV(GV))
ExternalComdats.insert(C);
}
bool Internalizer::internalizeModule(Module &M, CallGraph *CG) {
bool Changed = false;
CallGraphNode *ExternalNode = CG ? CG->getExternalCallingNode() : nullptr;
SmallPtrSet<GlobalValue *, 8> Used;
collectUsedGlobalVariables(M, Used, false);
std::set<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);
}
for (GlobalValue *V : Used) {
AlwaysPreserved.insert(V->getName());
}
for (Function &I : M) {
if (!maybeInternalize(I, ExternalComdats))
continue;
Changed = true;
if (ExternalNode)
ExternalNode->removeOneAbstractEdgeTo((*CG)[&I]);
++NumFunctions;
DEBUG(dbgs() << "Internalizing func " << I.getName() << "\n");
}
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");
AlwaysPreserved.insert("__stack_chk_guard");
for (auto &GV : M.globals()) {
if (!maybeInternalize(GV, ExternalComdats))
continue;
Changed = true;
++NumGlobals;
DEBUG(dbgs() << "Internalized gvar " << GV.getName() << "\n");
}
for (auto &GA : M.aliases()) {
if (!maybeInternalize(GA, ExternalComdats))
continue;
Changed = true;
++NumAliases;
DEBUG(dbgs() << "Internalized alias " << GA.getName() << "\n");
}
return Changed;
}
}
char InternalizePass::ID = 0;
INITIALIZE_PASS(InternalizePass, "internalize", "Internalize Global Symbols",
false, false)
bool llvm::internalizeModule(
Module &TheModule,
const std::function<bool(const GlobalValue &)> &MustPreserveGV,
CallGraph *CG) {
return Internalizer(MustPreserveGV).internalizeModule(TheModule, CG);
}
ModulePass *llvm::createInternalizePass() { return new InternalizePass(); }
ModulePass *llvm::createInternalizePass(
std::function<bool(const GlobalValue &)> MustPreserveGV) {
return new InternalizePass(std::move(MustPreserveGV));
}