#include "llvm/Transforms/IPO/ConstantMerge.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/Statistic.h"
#include "llvm/IR/Constants.h"
#include "llvm/IR/DataLayout.h"
#include "llvm/IR/DerivedTypes.h"
#include "llvm/IR/GlobalValue.h"
#include "llvm/IR/GlobalVariable.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/Module.h"
#include "llvm/InitializePasses.h"
#include "llvm/Pass.h"
#include "llvm/Support/Casting.h"
#include "llvm/Transforms/IPO.h"
#include <algorithm>
#include <cassert>
#include <utility>
using namespace llvm;
#define DEBUG_TYPE "constmerge"
STATISTIC(NumIdenticalMerged, "Number of identical global constants merged");
static void FindUsedValues(GlobalVariable *LLVMUsed,
SmallPtrSetImpl<const GlobalValue*> &UsedValues) {
if (!LLVMUsed) return;
ConstantArray *Inits = cast<ConstantArray>(LLVMUsed->getInitializer());
for (unsigned i = 0, e = Inits->getNumOperands(); i != e; ++i) {
Value *Operand = Inits->getOperand(i)->stripPointerCasts();
GlobalValue *GV = cast<GlobalValue>(Operand);
UsedValues.insert(GV);
}
}
static bool IsBetterCanonical(const GlobalVariable &A,
const GlobalVariable &B) {
if (!A.hasLocalLinkage() && B.hasLocalLinkage())
return true;
if (A.hasLocalLinkage() && !B.hasLocalLinkage())
return false;
return A.hasGlobalUnnamedAddr();
}
static bool hasMetadataOtherThanDebugLoc(const GlobalVariable *GV) {
SmallVector<std::pair<unsigned, MDNode *>, 4> MDs;
GV->getAllMetadata(MDs);
for (const auto &V : MDs)
if (V.first != LLVMContext::MD_dbg)
return true;
return false;
}
static void copyDebugLocMetadata(const GlobalVariable *From,
GlobalVariable *To) {
SmallVector<DIGlobalVariableExpression *, 1> MDs;
From->getDebugInfo(MDs);
for (auto MD : MDs)
To->addDebugInfo(MD);
}
static Align getAlign(GlobalVariable *GV) {
return GV->getAlign().value_or(
GV->getParent()->getDataLayout().getPreferredAlign(GV));
}
static bool
isUnmergeableGlobal(GlobalVariable *GV,
const SmallPtrSetImpl<const GlobalValue *> &UsedGlobals) {
return !GV->isConstant() || !GV->hasDefinitiveInitializer() ||
GV->getType()->getAddressSpace() != 0 || GV->hasSection() ||
GV->isThreadLocal() ||
UsedGlobals.count(GV);
}
enum class CanMerge { No, Yes };
static CanMerge makeMergeable(GlobalVariable *Old, GlobalVariable *New) {
if (!Old->hasGlobalUnnamedAddr() && !New->hasGlobalUnnamedAddr())
return CanMerge::No;
if (hasMetadataOtherThanDebugLoc(Old))
return CanMerge::No;
assert(!hasMetadataOtherThanDebugLoc(New));
if (!Old->hasGlobalUnnamedAddr())
New->setUnnamedAddr(GlobalValue::UnnamedAddr::None);
return CanMerge::Yes;
}
static void replace(Module &M, GlobalVariable *Old, GlobalVariable *New) {
Constant *NewConstant = New;
LLVM_DEBUG(dbgs() << "Replacing global: @" << Old->getName() << " -> @"
<< New->getName() << "\n");
if (Old->getAlign() || New->getAlign())
New->setAlignment(std::max(getAlign(Old), getAlign(New)));
copyDebugLocMetadata(Old, New);
Old->replaceAllUsesWith(NewConstant);
assert(Old->hasLocalLinkage() &&
"Refusing to delete an externally visible global variable.");
Old->eraseFromParent();
}
static bool mergeConstants(Module &M) {
SmallPtrSet<const GlobalValue*, 8> UsedGlobals;
FindUsedValues(M.getGlobalVariable("llvm.used"), UsedGlobals);
FindUsedValues(M.getGlobalVariable("llvm.compiler.used"), UsedGlobals);
DenseMap<Constant *, GlobalVariable *> CMap;
SmallVector<std::pair<GlobalVariable *, GlobalVariable *>, 32>
SameContentReplacements;
size_t ChangesMade = 0;
size_t OldChangesMade = 0;
while (true) {
for (GlobalVariable &GV : llvm::make_early_inc_range(M.globals())) {
GV.removeDeadConstantUsers();
if (GV.use_empty() && GV.hasLocalLinkage()) {
GV.eraseFromParent();
++ChangesMade;
continue;
}
if (isUnmergeableGlobal(&GV, UsedGlobals))
continue;
if (GV.isWeakForLinker())
continue;
if (hasMetadataOtherThanDebugLoc(&GV))
continue;
Constant *Init = GV.getInitializer();
GlobalVariable *&Slot = CMap[Init];
bool FirstConstantFound = !Slot;
if (FirstConstantFound || IsBetterCanonical(GV, *Slot)) {
Slot = &GV;
LLVM_DEBUG(dbgs() << "Cmap[" << *Init << "] = " << GV.getName()
<< (FirstConstantFound ? "\n" : " (updated)\n"));
}
}
for (GlobalVariable &GV : llvm::make_early_inc_range(M.globals())) {
if (isUnmergeableGlobal(&GV, UsedGlobals))
continue;
if (!GV.hasLocalLinkage())
continue;
Constant *Init = GV.getInitializer();
auto Found = CMap.find(Init);
if (Found == CMap.end())
continue;
GlobalVariable *Slot = Found->second;
if (Slot == &GV)
continue;
if (makeMergeable(&GV, Slot) == CanMerge::No)
continue;
LLVM_DEBUG(dbgs() << "Will replace: @" << GV.getName() << " -> @"
<< Slot->getName() << "\n");
SameContentReplacements.push_back(std::make_pair(&GV, Slot));
}
for (unsigned i = 0, e = SameContentReplacements.size(); i != e; ++i) {
GlobalVariable *Old = SameContentReplacements[i].first;
GlobalVariable *New = SameContentReplacements[i].second;
replace(M, Old, New);
++ChangesMade;
++NumIdenticalMerged;
}
if (ChangesMade == OldChangesMade)
break;
OldChangesMade = ChangesMade;
SameContentReplacements.clear();
CMap.clear();
}
return ChangesMade;
}
PreservedAnalyses ConstantMergePass::run(Module &M, ModuleAnalysisManager &) {
if (!mergeConstants(M))
return PreservedAnalyses::all();
return PreservedAnalyses::none();
}
namespace {
struct ConstantMergeLegacyPass : public ModulePass {
static char ID;
ConstantMergeLegacyPass() : ModulePass(ID) {
initializeConstantMergeLegacyPassPass(*PassRegistry::getPassRegistry());
}
bool runOnModule(Module &M) override {
if (skipModule(M))
return false;
return mergeConstants(M);
}
};
}
char ConstantMergeLegacyPass::ID = 0;
INITIALIZE_PASS(ConstantMergeLegacyPass, "constmerge",
"Merge Duplicate Global Constants", false, false)
ModulePass *llvm::createConstantMergePass() {
return new ConstantMergeLegacyPass();
}