//
//===----------------------------------------------------------------------===//
//
-// This file implements the ARM64PromoteConstant pass which promotes constant
-// to global variables when this is likely to be more efficient.
-// Currently only types related to constant vector (i.e., constant vector, array
-// of constant vectors, constant structure with a constant vector field, etc.)
-// are promoted to global variables.
-// Indeed, constant vector are likely to be lowered in target constant pool
-// during instruction selection.
-// Therefore, the access will remain the same (memory load), but the structures
-// types are not split into different constant pool accesses for each field.
-// The bonus side effect is that created globals may be merged by the global
-// merge pass.
+// This file implements the ARM64PromoteConstant pass which promotes constants
+// to global variables when this is likely to be more efficient. Currently only
+// types related to constant vector (i.e., constant vector, array of constant
+// vectors, constant structure with a constant vector field, etc.) are promoted
+// to global variables. Constant vectors are likely to be lowered in target
+// constant pool during instruction selection already; therefore, the access
+// will remain the same (memory load), but the structure types are not split
+// into different constant pool accesses for each field. A bonus side effect is
+// that created globals may be merged by the global merge pass.
//
// FIXME: This pass may be useful for other targets too.
//===----------------------------------------------------------------------===//
-#define DEBUG_TYPE "arm64-promote-const"
#include "ARM64.h"
#include "llvm/ADT/Statistic.h"
#include "llvm/ADT/DenseMap.h"
using namespace llvm;
+#define DEBUG_TYPE "arm64-promote-const"
+
// Stress testing mode - disable heuristics.
static cl::opt<bool> Stress("arm64-stress-promote-const", cl::Hidden,
cl::desc("Promote all vector constants"));
/// return ret;
/// }
///
-/// The constants in that example are folded into the uses. Thus, 4 different
+/// The constants in this example are folded into the uses. Thus, 4 different
/// constants are created.
+///
/// As their type is vector the cheapest way to create them is to load them
/// for the memory.
-/// Therefore the final assembly final has 4 different load.
-/// With this pass enabled, only one load is issued for the constants.
+///
+/// Therefore the final assembly final has 4 different loads. With this pass
+/// enabled, only one load is issued for the constants.
class ARM64PromoteConstant : public ModulePass {
public:
static char ID;
ARM64PromoteConstant() : ModulePass(ID) {}
- virtual const char *getPassName() const { return "ARM64 Promote Constant"; }
+ const char *getPassName() const override { return "ARM64 Promote Constant"; }
/// Iterate over the functions and promote the interesting constants into
/// global variables with module scope.
- bool runOnModule(Module &M) {
+ bool runOnModule(Module &M) override {
DEBUG(dbgs() << getPassName() << '\n');
bool Changed = false;
for (auto &MF : M) {
bool runOnFunction(Function &F);
// This transformation requires dominator info
- virtual void getAnalysisUsage(AnalysisUsage &AU) const {
+ void getAnalysisUsage(AnalysisUsage &AU) const override {
AU.setPreservesCFG();
AU.addRequired<DominatorTreeWrapperPass>();
AU.addPreserved<DominatorTreeWrapperPass>();
}
- /// Type to store a list of User
+ /// Type to store a list of User.
typedef SmallVector<Value::user_iterator, 4> Users;
/// Map an insertion point to all the uses it dominates.
typedef DenseMap<Instruction *, Users> InsertionPoints;
/// Map a function to the required insertion point of load for a
- /// global variable
+ /// global variable.
typedef DenseMap<Function *, InsertionPoints> InsertionPointsPerFunc;
/// Find the closest point that dominates the given Use.
Value::user_iterator &UseIt,
InsertionPoints::iterator &IPI,
InsertionPoints &InsertPts) {
- // Record the dominated use
+ // Record the dominated use.
IPI->second.push_back(UseIt);
// Transfer the dominated uses of IPI to NewPt
// Inserting into the DenseMap may invalidate existing iterator.
// Keep a copy of the key to find the iterator to erase.
Instruction *OldInstr = IPI->first;
InsertPts.insert(InsertionPoints::value_type(NewPt, IPI->second));
- // Erase IPI
+ // Erase IPI.
IPI = InsertPts.find(OldInstr);
InsertPts.erase(IPI);
}
if (isa<const ShuffleVectorInst>(Instr) && OpIdx == 2)
return false;
- // extractvalue instruction expects a const idx
+ // extractvalue instruction expects a const idx.
if (isa<const ExtractValueInst>(Instr) && OpIdx > 0)
return false;
- // extractvalue instruction expects a const idx
+ // extractvalue instruction expects a const idx.
if (isa<const InsertValueInst>(Instr) && OpIdx > 1)
return false;
if (isa<const AllocaInst>(Instr) && OpIdx > 0)
return false;
- // Alignment argument must be constant
+ // Alignment argument must be constant.
if (isa<const LoadInst>(Instr) && OpIdx > 0)
return false;
- // Alignment argument must be constant
+ // Alignment argument must be constant.
if (isa<const StoreInst>(Instr) && OpIdx > 1)
return false;
- // Index must be constant
+ // Index must be constant.
if (isa<const GetElementPtrInst>(Instr) && OpIdx > 0)
return false;
if (isa<const LandingPadInst>(Instr))
return false;
- // switch instruction expects constants to compare to
+ // Switch instruction expects constants to compare to.
if (isa<const SwitchInst>(Instr))
return false;
- // Expected address must be a constant
+ // Expected address must be a constant.
if (isa<const IndirectBrInst>(Instr))
return false;
- // Do not mess with intrinsic
+ // Do not mess with intrinsics.
if (isa<const IntrinsicInst>(Instr))
return false;
- // Do not mess with inline asm
+ // Do not mess with inline asm.
const CallInst *CI = dyn_cast<const CallInst>(Instr);
if (CI && isa<const InlineAsm>(CI->getCalledValue()))
return false;
Instruction *
ARM64PromoteConstant::findInsertionPoint(Value::user_iterator &Use) {
// If this user is a phi, the insertion point is in the related
- // incoming basic block
+ // incoming basic block.
PHINode *PhiInst = dyn_cast<PHINode>(*Use);
Instruction *InsertionPoint;
if (PhiInst)
DominatorTree &DT = getAnalysis<DominatorTreeWrapperPass>(
*NewPt->getParent()->getParent()).getDomTree();
- // Traverse all the existing insertion point and check if one is dominating
- // NewPt
- for (InsertionPoints::iterator IPI = InsertPts.begin(),
- EndIPI = InsertPts.end();
- IPI != EndIPI; ++IPI) {
- if (NewPt == IPI->first || DT.dominates(IPI->first, NewPt) ||
- // When IPI->first is a terminator instruction, DT may think that
+ // Traverse all the existing insertion points and check if one is dominating
+ // NewPt. If it is, remember that.
+ for (auto &IPI : InsertPts) {
+ if (NewPt == IPI.first || DT.dominates(IPI.first, NewPt) ||
+ // When IPI.first is a terminator instruction, DT may think that
// the result is defined on the edge.
// Here we are testing the insertion point, not the definition.
- (IPI->first->getParent() != NewPt->getParent() &&
- DT.dominates(IPI->first->getParent(), NewPt->getParent()))) {
- // No need to insert this point
- // Record the dominated use
+ (IPI.first->getParent() != NewPt->getParent() &&
+ DT.dominates(IPI.first->getParent(), NewPt->getParent()))) {
+ // No need to insert this point. Just record the dominated use.
DEBUG(dbgs() << "Insertion point dominated by:\n");
- DEBUG(IPI->first->print(dbgs()));
+ DEBUG(IPI.first->print(dbgs()));
DEBUG(dbgs() << '\n');
- IPI->second.push_back(UseIt);
+ IPI.second.push_back(UseIt);
return true;
}
}
// Traverse all the existing insertion point and check if one is dominated by
// NewPt and thus useless or can be combined with NewPt into a common
- // dominator
+ // dominator.
for (InsertionPoints::iterator IPI = InsertPts.begin(),
EndIPI = InsertPts.end();
IPI != EndIPI; ++IPI) {
// Look for a common dominator
BasicBlock *CommonDominator = DT.findNearestCommonDominator(NewBB, CurBB);
- // If none exists, we cannot merge these two points
+ // If none exists, we cannot merge these two points.
if (!CommonDominator)
continue;
if (CommonDominator != NewBB) {
- // By construction, the CommonDominator cannot be CurBB
+ // By construction, the CommonDominator cannot be CurBB.
assert(CommonDominator != CurBB &&
"Instruction has not been rejected during isDominated check!");
// Take the last instruction of the CommonDominator as insertion point
NewPt = CommonDominator->getTerminator();
}
// else, CommonDominator is the block of NewBB, hence NewBB is the last
- // possible insertion point in that block
+ // possible insertion point in that block.
DEBUG(dbgs() << "Merge insertion point with:\n");
DEBUG(IPI->first->print(dbgs()));
DEBUG(dbgs() << '\n');
for (Value::user_iterator UseIt = Val->user_begin(),
EndUseIt = Val->user_end();
UseIt != EndUseIt; ++UseIt) {
- // If the user is not an Instruction, we cannot modify it
+ // If the user is not an Instruction, we cannot modify it.
if (!isa<Instruction>(*UseIt))
continue;
- // Filter out uses that should not be converted
+ // Filter out uses that should not be converted.
if (!shouldConvertUse(Val, cast<Instruction>(*UseIt), UseIt.getOperandNo()))
continue;
bool
ARM64PromoteConstant::insertDefinitions(Constant *Cst,
InsertionPointsPerFunc &InsPtsPerFunc) {
- // We will create one global variable per Module
+ // We will create one global variable per Module.
DenseMap<Module *, GlobalVariable *> ModuleToMergedGV;
bool HasChanged = false;
- // Traverse all insertion points in all the function
+ // Traverse all insertion points in all the function.
for (InsertionPointsPerFunc::iterator FctToInstPtsIt = InsPtsPerFunc.begin(),
EndIt = InsPtsPerFunc.end();
FctToInstPtsIt != EndIt; ++FctToInstPtsIt) {
InsertionPoints &InsertPts = FctToInstPtsIt->second;
-// Do more check for debug purposes
+// Do more checking for debug purposes.
#ifndef NDEBUG
DominatorTree &DT = getAnalysis<DominatorTreeWrapperPass>(
*FctToInstPtsIt->first).getDomTree();
ModuleToMergedGV.find(M);
if (MapIt == ModuleToMergedGV.end()) {
PromotedGV = new GlobalVariable(
- *M, Cst->getType(), true, GlobalValue::InternalLinkage, 0,
- "_PromotedConst", 0, GlobalVariable::NotThreadLocal);
+ *M, Cst->getType(), true, GlobalValue::InternalLinkage, nullptr,
+ "_PromotedConst", nullptr, GlobalVariable::NotThreadLocal);
PromotedGV->setInitializer(Cst);
ModuleToMergedGV[M] = PromotedGV;
DEBUG(dbgs() << "Global replacement: ");
for (InsertionPoints::iterator IPI = InsertPts.begin(),
EndIPI = InsertPts.end();
IPI != EndIPI; ++IPI) {
- // Create the load of the global variable
+ // Create the load of the global variable.
IRBuilder<> Builder(IPI->first->getParent(), IPI->first);
LoadInst *LoadedCst = Builder.CreateLoad(PromotedGV);
DEBUG(dbgs() << "**********\n");
DEBUG(LoadedCst->print(dbgs()));
DEBUG(dbgs() << '\n');
- // Update the dominated uses
+ // Update the dominated uses.
Users &DominatedUsers = IPI->second;
- for (Users::iterator UseIt = DominatedUsers.begin(),
- EndIt = DominatedUsers.end();
- UseIt != EndIt; ++UseIt) {
+ for (Value::user_iterator Use : DominatedUsers) {
#ifndef NDEBUG
- assert((DT.dominates(LoadedCst, cast<Instruction>(**UseIt)) ||
- (isa<PHINode>(**UseIt) &&
- DT.dominates(LoadedCst, findInsertionPoint(*UseIt)))) &&
+ assert((DT.dominates(LoadedCst, cast<Instruction>(*Use)) ||
+ (isa<PHINode>(*Use) &&
+ DT.dominates(LoadedCst, findInsertionPoint(Use)))) &&
"Inserted definition does not dominate all its uses!");
#endif
- DEBUG(dbgs() << "Use to update " << UseIt->getOperandNo() << ":");
- DEBUG((*UseIt)->print(dbgs()));
+ DEBUG(dbgs() << "Use to update " << Use.getOperandNo() << ":");
+ DEBUG(Use->print(dbgs()));
DEBUG(dbgs() << '\n');
- (*UseIt)->setOperand(UseIt->getOperandNo(), LoadedCst);
+ Use->setOperand(Use.getOperandNo(), LoadedCst);
++NumPromotedUses;
}
}
}
bool ARM64PromoteConstant::runOnFunction(Function &F) {
- // Look for instructions using constant vector
- // Promote that constant to a global variable.
- // Create as few load of this variable as possible and update the uses
- // accordingly
+ // Look for instructions using constant vector. Promote that constant to a
+ // global variable. Create as few loads of this variable as possible and
+ // update the uses accordingly.
bool LocalChange = false;
SmallSet<Constant *, 8> AlreadyChecked;
for (auto &MBB : F) {
for (auto &MI : MBB) {
- // Traverse the operand, looking for constant vectors
- // Replace them by a load of a global variable of type constant vector
+ // Traverse the operand, looking for constant vectors. Replace them by a
+ // load of a global variable of constant vector type.
for (unsigned OpIdx = 0, EndOpIdx = MI.getNumOperands();
OpIdx != EndOpIdx; ++OpIdx) {
Constant *Cst = dyn_cast<Constant>(MI.getOperand(OpIdx));
- // There is no point is promoting global value, they are already global.
- // Do not promote constant expression, as they may require some code
- // expansion.
+ // There is no point in promoting global values as they are already
+ // global. Do not promote constant expressions either, as they may
+ // require some code expansion.
if (Cst && !isa<GlobalValue>(Cst) && !isa<ConstantExpr>(Cst) &&
AlreadyChecked.insert(Cst))
LocalChange |= promoteConstant(Cst);