#define DEBUG_TYPE "constmerge"
#include "llvm/Transforms/IPO.h"
#include "llvm/Constants.h"
#include "llvm/DerivedTypes.h"
#include "llvm/Module.h"
#include "llvm/Pass.h"
#include "llvm/Target/TargetData.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/PointerIntPair.h"
#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/ADT/Statistic.h"
using namespace llvm;
STATISTIC(NumMerged, "Number of global constants merged");
namespace {
struct ConstantMerge : public ModulePass {
static char ID; ConstantMerge() : ModulePass(ID) {
initializeConstantMergePass(*PassRegistry::getPassRegistry());
}
bool runOnModule(Module &M);
bool hasKnownAlignment(GlobalVariable *GV) const;
unsigned getAlignment(GlobalVariable *GV) const;
const TargetData *TD;
};
}
char ConstantMerge::ID = 0;
INITIALIZE_PASS(ConstantMerge, "constmerge",
"Merge Duplicate Global Constants", false, false)
ModulePass *llvm::createConstantMergePass() { return new ConstantMerge(); }
static void FindUsedValues(GlobalVariable *LLVMUsed,
SmallPtrSet<const GlobalValue*, 8> &UsedValues) {
if (LLVMUsed == 0) return;
ConstantArray *Inits = dyn_cast<ConstantArray>(LLVMUsed->getInitializer());
if (Inits == 0) return;
for (unsigned i = 0, e = Inits->getNumOperands(); i != e; ++i)
if (GlobalValue *GV =
dyn_cast<GlobalValue>(Inits->getOperand(i)->stripPointerCasts()))
UsedValues.insert(GV);
}
static bool IsBetterCannonical(const GlobalVariable &A,
const GlobalVariable &B) {
if (!A.hasLocalLinkage() && B.hasLocalLinkage())
return true;
if (A.hasLocalLinkage() && !B.hasLocalLinkage())
return false;
return A.hasUnnamedAddr();
}
bool ConstantMerge::hasKnownAlignment(GlobalVariable *GV) const {
return TD || GV->getAlignment() != 0;
}
unsigned ConstantMerge::getAlignment(GlobalVariable *GV) const {
if (TD)
return TD->getPreferredAlignment(GV);
return GV->getAlignment();
}
bool ConstantMerge::runOnModule(Module &M) {
TD = getAnalysisIfAvailable<TargetData>();
SmallPtrSet<const GlobalValue*, 8> UsedGlobals;
FindUsedValues(M.getGlobalVariable("llvm.used"), UsedGlobals);
FindUsedValues(M.getGlobalVariable("llvm.compiler.used"), UsedGlobals);
DenseMap<PointerIntPair<Constant*, 1, bool>, GlobalVariable*> CMap;
SmallVector<std::pair<GlobalVariable*, GlobalVariable*>, 32> Replacements;
bool MadeChange = false;
while (1) {
for (Module::global_iterator GVI = M.global_begin(), E = M.global_end();
GVI != E; ) {
GlobalVariable *GV = GVI++;
GV->removeDeadConstantUsers();
if (GV->use_empty() && GV->hasLocalLinkage()) {
GV->eraseFromParent();
continue;
}
if (!GV->isConstant() || !GV->hasDefinitiveInitializer() ||
GV->getType()->getAddressSpace() != 0 || GV->hasSection() ||
UsedGlobals.count(GV))
continue;
if (GV->isWeakForLinker())
continue;
Constant *Init = GV->getInitializer();
PointerIntPair<Constant*, 1, bool> Pair(Init, hasKnownAlignment(GV));
GlobalVariable *&Slot = CMap[Pair];
if (Slot == 0 || IsBetterCannonical(*GV, *Slot))
Slot = GV;
}
for (Module::global_iterator GVI = M.global_begin(), E = M.global_end();
GVI != E; ) {
GlobalVariable *GV = GVI++;
if (!GV->isConstant() || !GV->hasDefinitiveInitializer() ||
GV->getType()->getAddressSpace() != 0 || GV->hasSection() ||
UsedGlobals.count(GV))
continue;
if (!GV->hasLocalLinkage())
continue;
Constant *Init = GV->getInitializer();
PointerIntPair<Constant*, 1, bool> Pair(Init, hasKnownAlignment(GV));
GlobalVariable *Slot = CMap[Pair];
if (!Slot || Slot == GV)
continue;
if (!Slot->hasUnnamedAddr() && !GV->hasUnnamedAddr())
continue;
if (!GV->hasUnnamedAddr())
Slot->setUnnamedAddr(false);
Replacements.push_back(std::make_pair(GV, Slot));
}
if (Replacements.empty())
return MadeChange;
CMap.clear();
for (unsigned i = 0, e = Replacements.size(); i != e; ++i) {
if (Replacements[i].first->getAlignment() ||
Replacements[i].second->getAlignment()) {
Replacements[i].second->setAlignment(std::max(
Replacements[i].first->getAlignment(),
Replacements[i].second->getAlignment()));
}
Replacements[i].first->replaceAllUsesWith(Replacements[i].second);
assert(Replacements[i].first->hasLocalLinkage() &&
"Refusing to delete an externally visible global variable.");
Replacements[i].first->eraseFromParent();
}
NumMerged += Replacements.size();
Replacements.clear();
}
}