#include "llvm/IntrinsicInst.h"
#include "llvm/Module.h"
#include "llvm/Pass.h"
-#include "llvm/Support/Debug.h"
+#include "llvm/Analysis/ConstantFolding.h"
#include "llvm/Target/TargetData.h"
-#include "llvm/Transforms/Utils/Local.h"
+#include "llvm/Support/Compiler.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/Statistic.h"
#include "llvm/ADT/StringExtras.h"
-#include <set>
#include <algorithm>
+#include <set>
using namespace llvm;
+STATISTIC(NumMarked , "Number of globals marked constant");
+STATISTIC(NumSRA , "Number of aggregate globals broken into scalars");
+STATISTIC(NumHeapSRA , "Number of heap objects SRA'd");
+STATISTIC(NumSubstitute,"Number of globals with initializers stored into them");
+STATISTIC(NumDeleted , "Number of globals deleted");
+STATISTIC(NumFnDeleted , "Number of functions deleted");
+STATISTIC(NumGlobUses , "Number of global uses devirtualized");
+STATISTIC(NumLocalized , "Number of globals localized");
+STATISTIC(NumShrunkToBool , "Number of global vars shrunk to booleans");
+STATISTIC(NumFastCallFns , "Number of functions converted to fastcc");
+STATISTIC(NumCtorsEvaluated, "Number of static ctors evaluated");
+
namespace {
- Statistic<> NumMarked ("globalopt", "Number of globals marked constant");
- Statistic<> NumSRA ("globalopt", "Number of aggregate globals broken "
- "into scalars");
- Statistic<> NumSubstitute("globalopt",
- "Number of globals with initializers stored into them");
- Statistic<> NumDeleted ("globalopt", "Number of globals deleted");
- Statistic<> NumFnDeleted("globalopt", "Number of functions deleted");
- Statistic<> NumGlobUses ("globalopt", "Number of global uses devirtualized");
- Statistic<> NumLocalized("globalopt", "Number of globals localized");
- Statistic<> NumShrunkToBool("globalopt",
- "Number of global vars shrunk to booleans");
- Statistic<> NumFastCallFns("globalopt",
- "Number of functions converted to fastcc");
- Statistic<> NumCtorsEvaluated("globalopt","Number of static ctors evaluated");
-
- struct GlobalOpt : public ModulePass {
+ struct VISIBILITY_HIDDEN GlobalOpt : public ModulePass {
virtual void getAnalysisUsage(AnalysisUsage &AU) const {
AU.addRequired<TargetData>();
}
+ static char ID; // Pass identification, replacement for typeid
+ GlobalOpt() : ModulePass((intptr_t)&ID) {}
bool runOnModule(Module &M);
bool OptimizeFunctions(Module &M);
bool OptimizeGlobalVars(Module &M);
bool OptimizeGlobalCtorsList(GlobalVariable *&GCL);
- bool ProcessInternalGlobal(GlobalVariable *GV, Module::global_iterator &GVI);
+ bool ProcessInternalGlobal(GlobalVariable *GV,Module::global_iterator &GVI);
};
- RegisterOpt<GlobalOpt> X("globalopt", "Global Variable Optimizer");
+ char GlobalOpt::ID = 0;
+ RegisterPass<GlobalOpt> X("globalopt", "Global Variable Optimizer");
}
ModulePass *llvm::createGlobalOptimizerPass() { return new GlobalOpt(); }
/// GlobalStatus - As we analyze each global, keep track of some information
/// about it. If we find out that the address of the global is taken, none of
/// this info will be accurate.
-struct GlobalStatus {
+struct VISIBILITY_HIDDEN GlobalStatus {
/// isLoaded - True if the global is ever loaded. If the global isn't ever
/// loaded it can be deleted.
bool isLoaded;
/// ever stored to this global, keep track of what value it is.
Value *StoredOnceValue;
- // AccessingFunction/HasMultipleAccessingFunctions - These start out
- // null/false. When the first accessing function is noticed, it is recorded.
- // When a second different accessing function is noticed,
- // HasMultipleAccessingFunctions is set to true.
+ /// AccessingFunction/HasMultipleAccessingFunctions - These start out
+ /// null/false. When the first accessing function is noticed, it is recorded.
+ /// When a second different accessing function is noticed,
+ /// HasMultipleAccessingFunctions is set to true.
Function *AccessingFunction;
bool HasMultipleAccessingFunctions;
- // HasNonInstructionUser - Set to true if this global has a user that is not
- // an instruction (e.g. a constant expr or GV initializer).
+ /// HasNonInstructionUser - Set to true if this global has a user that is not
+ /// an instruction (e.g. a constant expr or GV initializer).
bool HasNonInstructionUser;
+ /// HasPHIUser - Set to true if this global has a user that is a PHI node.
+ bool HasPHIUser;
+
/// isNotSuitableForSRA - Keep track of whether any SRA preventing users of
/// the global exist. Such users include GEP instruction with variable
/// indexes, and non-gep/load/store users like constant expr casts.
GlobalStatus() : isLoaded(false), StoredType(NotStored), StoredOnceValue(0),
AccessingFunction(0), HasMultipleAccessingFunctions(false),
- HasNonInstructionUser(false), isNotSuitableForSRA(false) {}
+ HasNonInstructionUser(false), HasPHIUser(false),
+ isNotSuitableForSRA(false) {}
};
if (PHIUsers.insert(PN).second) // Not already visited.
if (AnalyzeGlobal(I, GS, PHIUsers)) return true;
GS.isNotSuitableForSRA = true;
- } else if (isa<SetCondInst>(I)) {
+ GS.HasPHIUser = true;
+ } else if (isa<CmpInst>(I)) {
GS.isNotSuitableForSRA = true;
} else if (isa<MemCpyInst>(I) || isa<MemMoveInst>(I)) {
if (I->getOperand(1) == V)
static Constant *getAggregateConstantElement(Constant *Agg, Constant *Idx) {
ConstantInt *CI = dyn_cast<ConstantInt>(Idx);
if (!CI) return 0;
- unsigned IdxV = (unsigned)CI->getRawValue();
+ unsigned IdxV = CI->getZExtValue();
if (ConstantStruct *CS = dyn_cast<ConstantStruct>(Agg)) {
if (IdxV < CS->getNumOperands()) return CS->getOperand(IdxV);
} else if (ConstantArray *CA = dyn_cast<ConstantArray>(Agg)) {
if (IdxV < CA->getNumOperands()) return CA->getOperand(IdxV);
- } else if (ConstantPacked *CP = dyn_cast<ConstantPacked>(Agg)) {
+ } else if (ConstantVector *CP = dyn_cast<ConstantVector>(Agg)) {
if (IdxV < CP->getNumOperands()) return CP->getOperand(IdxV);
} else if (isa<ConstantAggregateZero>(Agg)) {
if (const StructType *STy = dyn_cast<StructType>(Agg->getType())) {
if (Init)
SubInit = ConstantFoldLoadThroughGEPConstantExpr(Init, CE);
Changed |= CleanupConstantGlobalUsers(CE, SubInit);
- } else if (CE->getOpcode() == Instruction::Cast &&
+ } else if (CE->getOpcode() == Instruction::BitCast &&
isa<PointerType>(CE->getType())) {
// Pointer cast, delete any stores and memsets to the global.
Changed |= CleanupConstantGlobalUsers(CE, 0);
Constant *SubInit = 0;
ConstantExpr *CE =
dyn_cast_or_null<ConstantExpr>(ConstantFoldInstruction(GEP));
- if (CE && CE->getOpcode() == Instruction::GetElementPtr)
+ if (Init && CE && CE->getOpcode() == Instruction::GetElementPtr)
SubInit = ConstantFoldLoadThroughGEPConstantExpr(Init, CE);
Changed |= CleanupConstantGlobalUsers(GEP, SubInit);
NewGlobals.reserve(STy->getNumElements());
for (unsigned i = 0, e = STy->getNumElements(); i != e; ++i) {
Constant *In = getAggregateConstantElement(Init,
- ConstantUInt::get(Type::UIntTy, i));
+ ConstantInt::get(Type::Int32Ty, i));
assert(In && "Couldn't get element of initializer?");
GlobalVariable *NGV = new GlobalVariable(STy->getElementType(i), false,
GlobalVariable::InternalLinkage,
- In, GV->getName()+"."+utostr(i));
+ In, GV->getName()+"."+utostr(i),
+ (Module *)NULL,
+ GV->isThreadLocal());
Globals.insert(GV, NGV);
NewGlobals.push_back(NGV);
}
unsigned NumElements = 0;
if (const ArrayType *ATy = dyn_cast<ArrayType>(STy))
NumElements = ATy->getNumElements();
- else if (const PackedType *PTy = dyn_cast<PackedType>(STy))
+ else if (const VectorType *PTy = dyn_cast<VectorType>(STy))
NumElements = PTy->getNumElements();
else
assert(0 && "Unknown aggregate sequential type!");
NewGlobals.reserve(NumElements);
for (unsigned i = 0, e = NumElements; i != e; ++i) {
Constant *In = getAggregateConstantElement(Init,
- ConstantUInt::get(Type::UIntTy, i));
+ ConstantInt::get(Type::Int32Ty, i));
assert(In && "Couldn't get element of initializer?");
GlobalVariable *NGV = new GlobalVariable(STy->getElementType(), false,
GlobalVariable::InternalLinkage,
- In, GV->getName()+"."+utostr(i));
+ In, GV->getName()+"."+utostr(i),
+ (Module *)NULL,
+ GV->isThreadLocal());
Globals.insert(GV, NGV);
NewGlobals.push_back(NGV);
}
if (NewGlobals.empty())
return 0;
- DEBUG(std::cerr << "PERFORMING GLOBAL SRA ON: " << *GV);
+ DOUT << "PERFORMING GLOBAL SRA ON: " << *GV;
- Constant *NullInt = Constant::getNullValue(Type::IntTy);
+ Constant *NullInt = Constant::getNullValue(Type::Int32Ty);
// Loop over all of the uses of the global, replacing the constantexpr geps,
// with smaller constantexpr geps or direct references.
// Ignore the 1th operand, which has to be zero or else the program is quite
// broken (undefined). Get the 2nd operand, which is the structure or array
// index.
- unsigned Val =
- (unsigned)cast<ConstantInt>(GEP->getOperand(2))->getRawValue();
+ unsigned Val = cast<ConstantInt>(GEP->getOperand(2))->getZExtValue();
if (Val >= NewGlobals.size()) Val = 0; // Out of bound array access.
Value *NewPtr = NewGlobals[Val];
// Form a shorter GEP if needed.
if (GEP->getNumOperands() > 3)
if (ConstantExpr *CE = dyn_cast<ConstantExpr>(GEP)) {
- std::vector<Constant*> Idxs;
+ SmallVector<Constant*, 8> Idxs;
Idxs.push_back(NullInt);
for (unsigned i = 3, e = CE->getNumOperands(); i != e; ++i)
Idxs.push_back(CE->getOperand(i));
- NewPtr = ConstantExpr::getGetElementPtr(cast<Constant>(NewPtr), Idxs);
+ NewPtr = ConstantExpr::getGetElementPtr(cast<Constant>(NewPtr),
+ &Idxs[0], Idxs.size());
} else {
GetElementPtrInst *GEPI = cast<GetElementPtrInst>(GEP);
- std::vector<Value*> Idxs;
+ SmallVector<Value*, 8> Idxs;
Idxs.push_back(NullInt);
for (unsigned i = 3, e = GEPI->getNumOperands(); i != e; ++i)
Idxs.push_back(GEPI->getOperand(i));
- NewPtr = new GetElementPtrInst(NewPtr, Idxs,
+ NewPtr = new GetElementPtrInst(NewPtr, &Idxs[0], Idxs.size(),
GEPI->getName()+"."+utostr(Val), GEPI);
}
GEP->replaceAllUsesWith(NewPtr);
// Will trap.
} else if (StoreInst *SI = dyn_cast<StoreInst>(*UI)) {
if (SI->getOperand(0) == V) {
- //std::cerr << "NONTRAPPING USE: " << **UI;
+ //cerr << "NONTRAPPING USE: " << **UI;
return false; // Storing the value.
}
} else if (CallInst *CI = dyn_cast<CallInst>(*UI)) {
if (CI->getOperand(0) != V) {
- //std::cerr << "NONTRAPPING USE: " << **UI;
+ //cerr << "NONTRAPPING USE: " << **UI;
return false; // Not calling the ptr
}
} else if (InvokeInst *II = dyn_cast<InvokeInst>(*UI)) {
if (II->getOperand(0) != V) {
- //std::cerr << "NONTRAPPING USE: " << **UI;
+ //cerr << "NONTRAPPING USE: " << **UI;
return false; // Not calling the ptr
}
} else if (CastInst *CI = dyn_cast<CastInst>(*UI)) {
if (!AllUsesOfValueWillTrapIfNull(CI)) return false;
} else if (GetElementPtrInst *GEPI = dyn_cast<GetElementPtrInst>(*UI)) {
if (!AllUsesOfValueWillTrapIfNull(GEPI)) return false;
- } else if (isa<SetCondInst>(*UI) &&
+ } else if (isa<ICmpInst>(*UI) &&
isa<ConstantPointerNull>(UI->getOperand(1))) {
// Ignore setcc X, null
} else {
- //std::cerr << "NONTRAPPING USE: " << **UI;
+ //cerr << "NONTRAPPING USE: " << **UI;
return false;
}
return true;
// Ignore stores to the global.
} else {
// We don't know or understand this user, bail out.
- //std::cerr << "UNKNOWN USER OF GLOBAL!: " << **UI;
+ //cerr << "UNKNOWN USER OF GLOBAL!: " << **UI;
return false;
}
}
} else if (CastInst *CI = dyn_cast<CastInst>(I)) {
Changed |= OptimizeAwayTrappingUsesOfValue(CI,
- ConstantExpr::getCast(NewV, CI->getType()));
+ ConstantExpr::getCast(CI->getOpcode(),
+ NewV, CI->getType()));
if (CI->use_empty()) {
Changed = true;
CI->eraseFromParent();
}
} else if (GetElementPtrInst *GEPI = dyn_cast<GetElementPtrInst>(I)) {
// Should handle GEP here.
- std::vector<Constant*> Indices;
- Indices.reserve(GEPI->getNumOperands()-1);
+ SmallVector<Constant*, 8> Idxs;
+ Idxs.reserve(GEPI->getNumOperands()-1);
for (unsigned i = 1, e = GEPI->getNumOperands(); i != e; ++i)
if (Constant *C = dyn_cast<Constant>(GEPI->getOperand(i)))
- Indices.push_back(C);
+ Idxs.push_back(C);
else
break;
- if (Indices.size() == GEPI->getNumOperands()-1)
+ if (Idxs.size() == GEPI->getNumOperands()-1)
Changed |= OptimizeAwayTrappingUsesOfValue(GEPI,
- ConstantExpr::getGetElementPtr(NewV, Indices));
+ ConstantExpr::getGetElementPtr(NewV, &Idxs[0],
+ Idxs.size()));
if (GEPI->use_empty()) {
Changed = true;
GEPI->eraseFromParent();
Loads.push_back(LI);
Changed |= OptimizeAwayTrappingUsesOfValue(LI, LV);
} else {
- assert(isa<StoreInst>(*GUI) && "Only expect load and stores!");
+ // If we get here we could have stores, selects, or phi nodes whose values
+ // are loaded.
+ assert((isa<StoreInst>(*GUI) || isa<PHINode>(*GUI) ||
+ isa<SelectInst>(*GUI)) &&
+ "Only expect load and stores!");
}
if (Changed) {
- DEBUG(std::cerr << "OPTIMIZED LOADS FROM STORED ONCE POINTER: " << *GV);
+ DOUT << "OPTIMIZED LOADS FROM STORED ONCE POINTER: " << *GV;
++NumGlobUses;
}
// If we nuked all of the loads, then none of the stores are needed either,
// nor is the global.
if (AllLoadsGone) {
- DEBUG(std::cerr << " *** GLOBAL NOW DEAD!\n");
+ DOUT << " *** GLOBAL NOW DEAD!\n";
CleanupConstantGlobalUsers(GV, 0);
if (GV->use_empty()) {
GV->eraseFromParent();
/// variable, and transforms the program as if it always contained the result of
/// the specified malloc. Because it is always the result of the specified
/// malloc, there is no reason to actually DO the malloc. Instead, turn the
-/// malloc into a global, and any laods of GV as uses of the new global.
+/// malloc into a global, and any loads of GV as uses of the new global.
static GlobalVariable *OptimizeGlobalAddressOfMalloc(GlobalVariable *GV,
MallocInst *MI) {
- DEBUG(std::cerr << "PROMOTING MALLOC GLOBAL: " << *GV << " MALLOC = " <<*MI);
+ DOUT << "PROMOTING MALLOC GLOBAL: " << *GV << " MALLOC = " << *MI;
ConstantInt *NElements = cast<ConstantInt>(MI->getArraySize());
- if (NElements->getRawValue() != 1) {
+ if (NElements->getZExtValue() != 1) {
// If we have an array allocation, transform it to a single element
// allocation to make the code below simpler.
Type *NewTy = ArrayType::get(MI->getAllocatedType(),
- (unsigned)NElements->getRawValue());
+ NElements->getZExtValue());
MallocInst *NewMI =
- new MallocInst(NewTy, Constant::getNullValue(Type::UIntTy),
- MI->getName(), MI);
- std::vector<Value*> Indices;
- Indices.push_back(Constant::getNullValue(Type::IntTy));
- Indices.push_back(Indices[0]);
- Value *NewGEP = new GetElementPtrInst(NewMI, Indices,
+ new MallocInst(NewTy, Constant::getNullValue(Type::Int32Ty),
+ MI->getAlignment(), MI->getName(), MI);
+ Value* Indices[2];
+ Indices[0] = Indices[1] = Constant::getNullValue(Type::Int32Ty);
+ Value *NewGEP = new GetElementPtrInst(NewMI, Indices, 2,
NewMI->getName()+".el0", MI);
MI->replaceAllUsesWith(NewGEP);
MI->eraseFromParent();
Constant *Init = UndefValue::get(MI->getAllocatedType());
GlobalVariable *NewGV = new GlobalVariable(MI->getAllocatedType(), false,
GlobalValue::InternalLinkage, Init,
- GV->getName()+".body");
+ GV->getName()+".body",
+ (Module *)NULL,
+ GV->isThreadLocal());
GV->getParent()->getGlobalList().insert(GV, NewGV);
// Anything that used the malloc now uses the global directly.
Constant *RepValue = NewGV;
if (NewGV->getType() != GV->getType()->getElementType())
- RepValue = ConstantExpr::getCast(RepValue, GV->getType()->getElementType());
+ RepValue = ConstantExpr::getBitCast(RepValue,
+ GV->getType()->getElementType());
// If there is a comparison against null, we will insert a global bool to
// keep track of whether the global was initialized yet or not.
GlobalVariable *InitBool =
- new GlobalVariable(Type::BoolTy, false, GlobalValue::InternalLinkage,
- ConstantBool::False, GV->getName()+".init");
+ new GlobalVariable(Type::Int1Ty, false, GlobalValue::InternalLinkage,
+ ConstantInt::getFalse(), GV->getName()+".init",
+ (Module *)NULL, GV->isThreadLocal());
bool InitBoolUsed = false;
// Loop over all uses of GV, processing them in turn.
if (LoadInst *LI = dyn_cast<LoadInst>(GV->use_back())) {
while (!LI->use_empty()) {
Use &LoadUse = LI->use_begin().getUse();
- if (!isa<SetCondInst>(LoadUse.getUser()))
+ if (!isa<ICmpInst>(LoadUse.getUser()))
LoadUse = RepValue;
else {
- // Replace the setcc X, 0 with a use of the bool value.
- SetCondInst *SCI = cast<SetCondInst>(LoadUse.getUser());
- Value *LV = new LoadInst(InitBool, InitBool->getName()+".val", SCI);
+ ICmpInst *CI = cast<ICmpInst>(LoadUse.getUser());
+ // Replace the cmp X, 0 with a use of the bool value.
+ Value *LV = new LoadInst(InitBool, InitBool->getName()+".val", CI);
InitBoolUsed = true;
- switch (SCI->getOpcode()) {
- default: assert(0 && "Unknown opcode!");
- case Instruction::SetLT:
- LV = ConstantBool::False; // X < null -> always false
+ switch (CI->getPredicate()) {
+ default: assert(0 && "Unknown ICmp Predicate!");
+ case ICmpInst::ICMP_ULT:
+ case ICmpInst::ICMP_SLT:
+ LV = ConstantInt::getFalse(); // X < null -> always false
break;
- case Instruction::SetEQ:
- case Instruction::SetLE:
- LV = BinaryOperator::createNot(LV, "notinit", SCI);
+ case ICmpInst::ICMP_ULE:
+ case ICmpInst::ICMP_SLE:
+ case ICmpInst::ICMP_EQ:
+ LV = BinaryOperator::createNot(LV, "notinit", CI);
break;
- case Instruction::SetNE:
- case Instruction::SetGE:
- case Instruction::SetGT:
+ case ICmpInst::ICMP_NE:
+ case ICmpInst::ICMP_UGE:
+ case ICmpInst::ICMP_SGE:
+ case ICmpInst::ICMP_UGT:
+ case ICmpInst::ICMP_SGT:
break; // no change.
}
- SCI->replaceAllUsesWith(LV);
- SCI->eraseFromParent();
+ CI->replaceAllUsesWith(LV);
+ CI->eraseFromParent();
}
}
LI->eraseFromParent();
} else {
StoreInst *SI = cast<StoreInst>(GV->use_back());
// The global is initialized when the store to it occurs.
- new StoreInst(ConstantBool::True, InitBool, SI);
+ new StoreInst(ConstantInt::getTrue(), InitBool, SI);
SI->eraseFromParent();
}
static bool ValueIsOnlyUsedLocallyOrStoredToOneGlobal(Instruction *V,
GlobalVariable *GV) {
for (Value::use_iterator UI = V->use_begin(), E = V->use_end(); UI != E;++UI)
- if (isa<LoadInst>(*UI) || isa<SetCondInst>(*UI)) {
+ if (isa<LoadInst>(*UI) || isa<CmpInst>(*UI)) {
// Fine, ignore.
} else if (StoreInst *SI = dyn_cast<StoreInst>(*UI)) {
if (SI->getOperand(0) == V && SI->getOperand(1) != GV)
return false;
}
return true;
+}
+
+/// ReplaceUsesOfMallocWithGlobal - The Alloc pointer is stored into GV
+/// somewhere. Transform all uses of the allocation into loads from the
+/// global and uses of the resultant pointer. Further, delete the store into
+/// GV. This assumes that these value pass the
+/// 'ValueIsOnlyUsedLocallyOrStoredToOneGlobal' predicate.
+static void ReplaceUsesOfMallocWithGlobal(Instruction *Alloc,
+ GlobalVariable *GV) {
+ while (!Alloc->use_empty()) {
+ Instruction *U = Alloc->use_back();
+ if (StoreInst *SI = dyn_cast<StoreInst>(U)) {
+ // If this is the store of the allocation into the global, remove it.
+ if (SI->getOperand(1) == GV) {
+ SI->eraseFromParent();
+ continue;
+ }
+ }
+
+ // Insert a load from the global, and use it instead of the malloc.
+ Value *NL = new LoadInst(GV, GV->getName()+".val", U);
+ U->replaceUsesOfWith(Alloc, NL);
+ }
+}
+/// GlobalLoadUsesSimpleEnoughForHeapSRA - If all users of values loaded from
+/// GV are simple enough to perform HeapSRA, return true.
+static bool GlobalLoadUsesSimpleEnoughForHeapSRA(GlobalVariable *GV) {
+ for (Value::use_iterator UI = GV->use_begin(), E = GV->use_end(); UI != E;
+ ++UI)
+ if (LoadInst *LI = dyn_cast<LoadInst>(*UI)) {
+ // We permit two users of the load: setcc comparing against the null
+ // pointer, and a getelementptr of a specific form.
+ for (Value::use_iterator UI = LI->use_begin(), E = LI->use_end(); UI != E;
+ ++UI) {
+ // Comparison against null is ok.
+ if (ICmpInst *ICI = dyn_cast<ICmpInst>(*UI)) {
+ if (!isa<ConstantPointerNull>(ICI->getOperand(1)))
+ return false;
+ continue;
+ }
+
+ // getelementptr is also ok, but only a simple form.
+ GetElementPtrInst *GEPI = dyn_cast<GetElementPtrInst>(*UI);
+ if (!GEPI) return false;
+
+ // Must index into the array and into the struct.
+ if (GEPI->getNumOperands() < 3)
+ return false;
+
+ // Otherwise the GEP is ok.
+ continue;
+ }
+ }
+ return true;
+}
+
+/// RewriteUsesOfLoadForHeapSRoA - We are performing Heap SRoA on a global. Ptr
+/// is a value loaded from the global. Eliminate all uses of Ptr, making them
+/// use FieldGlobals instead. All uses of loaded values satisfy
+/// GlobalLoadUsesSimpleEnoughForHeapSRA.
+static void RewriteUsesOfLoadForHeapSRoA(LoadInst *Ptr,
+ const std::vector<GlobalVariable*> &FieldGlobals) {
+ std::vector<Value *> InsertedLoadsForPtr;
+ //InsertedLoadsForPtr.resize(FieldGlobals.size());
+ while (!Ptr->use_empty()) {
+ Instruction *User = Ptr->use_back();
+
+ // If this is a comparison against null, handle it.
+ if (ICmpInst *SCI = dyn_cast<ICmpInst>(User)) {
+ assert(isa<ConstantPointerNull>(SCI->getOperand(1)));
+ // If we have a setcc of the loaded pointer, we can use a setcc of any
+ // field.
+ Value *NPtr;
+ if (InsertedLoadsForPtr.empty()) {
+ NPtr = new LoadInst(FieldGlobals[0], Ptr->getName()+".f0", Ptr);
+ InsertedLoadsForPtr.push_back(Ptr);
+ } else {
+ NPtr = InsertedLoadsForPtr.back();
+ }
+
+ Value *New = new ICmpInst(SCI->getPredicate(), NPtr,
+ Constant::getNullValue(NPtr->getType()),
+ SCI->getName(), SCI);
+ SCI->replaceAllUsesWith(New);
+ SCI->eraseFromParent();
+ continue;
+ }
+
+ // Otherwise, this should be: 'getelementptr Ptr, Idx, uint FieldNo ...'
+ GetElementPtrInst *GEPI = cast<GetElementPtrInst>(User);
+ assert(GEPI->getNumOperands() >= 3 && isa<ConstantInt>(GEPI->getOperand(2))
+ && "Unexpected GEPI!");
+
+ // Load the pointer for this field.
+ unsigned FieldNo = cast<ConstantInt>(GEPI->getOperand(2))->getZExtValue();
+ if (InsertedLoadsForPtr.size() <= FieldNo)
+ InsertedLoadsForPtr.resize(FieldNo+1);
+ if (InsertedLoadsForPtr[FieldNo] == 0)
+ InsertedLoadsForPtr[FieldNo] = new LoadInst(FieldGlobals[FieldNo],
+ Ptr->getName()+".f" +
+ utostr(FieldNo), Ptr);
+ Value *NewPtr = InsertedLoadsForPtr[FieldNo];
+
+ // Create the new GEP idx vector.
+ SmallVector<Value*, 8> GEPIdx;
+ GEPIdx.push_back(GEPI->getOperand(1));
+ GEPIdx.append(GEPI->op_begin()+3, GEPI->op_end());
+
+ Value *NGEPI = new GetElementPtrInst(NewPtr, &GEPIdx[0], GEPIdx.size(),
+ GEPI->getName(), GEPI);
+ GEPI->replaceAllUsesWith(NGEPI);
+ GEPI->eraseFromParent();
+ }
}
+/// PerformHeapAllocSRoA - MI is an allocation of an array of structures. Break
+/// it up into multiple allocations of arrays of the fields.
+static GlobalVariable *PerformHeapAllocSRoA(GlobalVariable *GV, MallocInst *MI){
+ DOUT << "SROA HEAP ALLOC: " << *GV << " MALLOC = " << *MI;
+ const StructType *STy = cast<StructType>(MI->getAllocatedType());
+
+ // There is guaranteed to be at least one use of the malloc (storing
+ // it into GV). If there are other uses, change them to be uses of
+ // the global to simplify later code. This also deletes the store
+ // into GV.
+ ReplaceUsesOfMallocWithGlobal(MI, GV);
+
+ // Okay, at this point, there are no users of the malloc. Insert N
+ // new mallocs at the same place as MI, and N globals.
+ std::vector<GlobalVariable*> FieldGlobals;
+ std::vector<MallocInst*> FieldMallocs;
+
+ for (unsigned FieldNo = 0, e = STy->getNumElements(); FieldNo != e;++FieldNo){
+ const Type *FieldTy = STy->getElementType(FieldNo);
+ const Type *PFieldTy = PointerType::get(FieldTy);
+
+ GlobalVariable *NGV =
+ new GlobalVariable(PFieldTy, false, GlobalValue::InternalLinkage,
+ Constant::getNullValue(PFieldTy),
+ GV->getName() + ".f" + utostr(FieldNo), GV,
+ GV->isThreadLocal());
+ FieldGlobals.push_back(NGV);
+
+ MallocInst *NMI = new MallocInst(FieldTy, MI->getArraySize(),
+ MI->getName() + ".f" + utostr(FieldNo),MI);
+ FieldMallocs.push_back(NMI);
+ new StoreInst(NMI, NGV, MI);
+ }
+
+ // The tricky aspect of this transformation is handling the case when malloc
+ // fails. In the original code, malloc failing would set the result pointer
+ // of malloc to null. In this case, some mallocs could succeed and others
+ // could fail. As such, we emit code that looks like this:
+ // F0 = malloc(field0)
+ // F1 = malloc(field1)
+ // F2 = malloc(field2)
+ // if (F0 == 0 || F1 == 0 || F2 == 0) {
+ // if (F0) { free(F0); F0 = 0; }
+ // if (F1) { free(F1); F1 = 0; }
+ // if (F2) { free(F2); F2 = 0; }
+ // }
+ Value *RunningOr = 0;
+ for (unsigned i = 0, e = FieldMallocs.size(); i != e; ++i) {
+ Value *Cond = new ICmpInst(ICmpInst::ICMP_EQ, FieldMallocs[i],
+ Constant::getNullValue(FieldMallocs[i]->getType()),
+ "isnull", MI);
+ if (!RunningOr)
+ RunningOr = Cond; // First seteq
+ else
+ RunningOr = BinaryOperator::createOr(RunningOr, Cond, "tmp", MI);
+ }
+
+ // Split the basic block at the old malloc.
+ BasicBlock *OrigBB = MI->getParent();
+ BasicBlock *ContBB = OrigBB->splitBasicBlock(MI, "malloc_cont");
+
+ // Create the block to check the first condition. Put all these blocks at the
+ // end of the function as they are unlikely to be executed.
+ BasicBlock *NullPtrBlock = new BasicBlock("malloc_ret_null",
+ OrigBB->getParent());
+
+ // Remove the uncond branch from OrigBB to ContBB, turning it into a cond
+ // branch on RunningOr.
+ OrigBB->getTerminator()->eraseFromParent();
+ new BranchInst(NullPtrBlock, ContBB, RunningOr, OrigBB);
+
+ // Within the NullPtrBlock, we need to emit a comparison and branch for each
+ // pointer, because some may be null while others are not.
+ for (unsigned i = 0, e = FieldGlobals.size(); i != e; ++i) {
+ Value *GVVal = new LoadInst(FieldGlobals[i], "tmp", NullPtrBlock);
+ Value *Cmp = new ICmpInst(ICmpInst::ICMP_NE, GVVal,
+ Constant::getNullValue(GVVal->getType()),
+ "tmp", NullPtrBlock);
+ BasicBlock *FreeBlock = new BasicBlock("free_it", OrigBB->getParent());
+ BasicBlock *NextBlock = new BasicBlock("next", OrigBB->getParent());
+ new BranchInst(FreeBlock, NextBlock, Cmp, NullPtrBlock);
+
+ // Fill in FreeBlock.
+ new FreeInst(GVVal, FreeBlock);
+ new StoreInst(Constant::getNullValue(GVVal->getType()), FieldGlobals[i],
+ FreeBlock);
+ new BranchInst(NextBlock, FreeBlock);
+
+ NullPtrBlock = NextBlock;
+ }
+
+ new BranchInst(ContBB, NullPtrBlock);
+
+
+ // MI is no longer needed, remove it.
+ MI->eraseFromParent();
+
+
+ // Okay, the malloc site is completely handled. All of the uses of GV are now
+ // loads, and all uses of those loads are simple. Rewrite them to use loads
+ // of the per-field globals instead.
+ while (!GV->use_empty()) {
+ if (LoadInst *LI = dyn_cast<LoadInst>(GV->use_back())) {
+ RewriteUsesOfLoadForHeapSRoA(LI, FieldGlobals);
+ LI->eraseFromParent();
+ } else {
+ // Must be a store of null.
+ StoreInst *SI = cast<StoreInst>(GV->use_back());
+ assert(isa<Constant>(SI->getOperand(0)) &&
+ cast<Constant>(SI->getOperand(0))->isNullValue() &&
+ "Unexpected heap-sra user!");
+
+ // Insert a store of null into each global.
+ for (unsigned i = 0, e = FieldGlobals.size(); i != e; ++i) {
+ Constant *Null =
+ Constant::getNullValue(FieldGlobals[i]->getType()->getElementType());
+ new StoreInst(Null, FieldGlobals[i], SI);
+ }
+ // Erase the original store.
+ SI->eraseFromParent();
+ }
+ }
+
+ // The old global is now dead, remove it.
+ GV->eraseFromParent();
+
+ ++NumHeapSRA;
+ return FieldGlobals[0];
+}
+
+
// OptimizeOnceStoredGlobal - Try to optimize globals based on the knowledge
// that only one value (besides its initializer) is ever stored to the global.
static bool OptimizeOnceStoredGlobal(GlobalVariable *GV, Value *StoredOnceVal,
- Module::global_iterator &GVI, TargetData &TD) {
+ Module::global_iterator &GVI,
+ TargetData &TD) {
if (CastInst *CI = dyn_cast<CastInst>(StoredOnceVal))
StoredOnceVal = CI->getOperand(0);
else if (GetElementPtrInst *GEPI =dyn_cast<GetElementPtrInst>(StoredOnceVal)){
GV->getInitializer()->isNullValue()) {
if (Constant *SOVC = dyn_cast<Constant>(StoredOnceVal)) {
if (GV->getInitializer()->getType() != SOVC->getType())
- SOVC = ConstantExpr::getCast(SOVC, GV->getInitializer()->getType());
+ SOVC = ConstantExpr::getBitCast(SOVC, GV->getInitializer()->getType());
// Optimize away any trapping uses of the loaded value.
if (OptimizeAwayTrappingUsesOfLoads(GV, SOVC))
return true;
} else if (MallocInst *MI = dyn_cast<MallocInst>(StoredOnceVal)) {
+ // If this is a malloc of an abstract type, don't touch it.
+ if (!MI->getAllocatedType()->isSized())
+ return false;
+
+ // We can't optimize this global unless all uses of it are *known* to be
+ // of the malloc value, not of the null initializer value (consider a use
+ // that compares the global's value against zero to see if the malloc has
+ // been reached). To do this, we check to see if all uses of the global
+ // would trap if the global were null: this proves that they must all
+ // happen after the malloc.
+ if (!AllUsesOfLoadedValueWillTrapIfNull(GV))
+ return false;
+
+ // We can't optimize this if the malloc itself is used in a complex way,
+ // for example, being stored into multiple globals. This allows the
+ // malloc to be stored into the specified global, loaded setcc'd, and
+ // GEP'd. These are all things we could transform to using the global
+ // for.
+ if (!ValueIsOnlyUsedLocallyOrStoredToOneGlobal(MI, GV))
+ return false;
+
+
// If we have a global that is only initialized with a fixed size malloc,
- // and if all users of the malloc trap, and if the malloc'd address is not
- // put anywhere else, transform the program to use global memory instead
- // of malloc'd memory. This eliminates dynamic allocation (good) and
- // exposes the resultant global to further GlobalOpt (even better). Note
- // that we restrict this transformation to only working on small
- // allocations (2048 bytes currently), as we don't want to introduce a 16M
- // global or something.
- if (ConstantInt *NElements = dyn_cast<ConstantInt>(MI->getArraySize()))
- if (MI->getAllocatedType()->isSized() &&
- NElements->getRawValue()*
- TD.getTypeSize(MI->getAllocatedType()) < 2048 &&
- AllUsesOfLoadedValueWillTrapIfNull(GV) &&
- ValueIsOnlyUsedLocallyOrStoredToOneGlobal(MI, GV)) {
+ // transform the program to use global memory instead of malloc'd memory.
+ // This eliminates dynamic allocation, avoids an indirection accessing the
+ // data, and exposes the resultant global to further GlobalOpt.
+ if (ConstantInt *NElements = dyn_cast<ConstantInt>(MI->getArraySize())) {
+ // Restrict this transformation to only working on small allocations
+ // (2048 bytes currently), as we don't want to introduce a 16M global or
+ // something.
+ if (NElements->getZExtValue()*
+ TD.getTypeSize(MI->getAllocatedType()) < 2048) {
GVI = OptimizeGlobalAddressOfMalloc(GV, MI);
return true;
}
+ }
+
+ // If the allocation is an array of structures, consider transforming this
+ // into multiple malloc'd arrays, one for each field. This is basically
+ // SRoA for malloc'd memory.
+ if (const StructType *AllocTy =
+ dyn_cast<StructType>(MI->getAllocatedType())) {
+ // This the structure has an unreasonable number of fields, leave it
+ // alone.
+ if (AllocTy->getNumElements() <= 16 && AllocTy->getNumElements() > 0 &&
+ GlobalLoadUsesSimpleEnoughForHeapSRA(GV)) {
+ GVI = PerformHeapAllocSRoA(GV, MI);
+ return true;
+ }
+ }
}
}
/// values ever stored into GV are its initializer and OtherVal.
static void ShrinkGlobalToBoolean(GlobalVariable *GV, Constant *OtherVal) {
// Create the new global, initializing it to false.
- GlobalVariable *NewGV = new GlobalVariable(Type::BoolTy, false,
- GlobalValue::InternalLinkage, ConstantBool::False, GV->getName()+".b");
+ GlobalVariable *NewGV = new GlobalVariable(Type::Int1Ty, false,
+ GlobalValue::InternalLinkage, ConstantInt::getFalse(),
+ GV->getName()+".b",
+ (Module *)NULL,
+ GV->isThreadLocal());
GV->getParent()->getGlobalList().insert(GV, NewGV);
Constant *InitVal = GV->getInitializer();
- assert(InitVal->getType() != Type::BoolTy && "No reason to shrink to bool!");
+ assert(InitVal->getType() != Type::Int1Ty && "No reason to shrink to bool!");
// If initialized to zero and storing one into the global, we can use a cast
// instead of a select to synthesize the desired value.
bool IsOneZero = false;
if (ConstantInt *CI = dyn_cast<ConstantInt>(OtherVal))
- IsOneZero = InitVal->isNullValue() && CI->equalsInt(1);
+ IsOneZero = InitVal->isNullValue() && CI->isOne();
while (!GV->use_empty()) {
Instruction *UI = cast<Instruction>(GV->use_back());
// Only do this if we weren't storing a loaded value.
Value *StoreVal;
if (StoringOther || SI->getOperand(0) == InitVal)
- StoreVal = ConstantBool::get(StoringOther);
+ StoreVal = ConstantInt::get(Type::Int1Ty, StoringOther);
else {
// Otherwise, we are storing a previously loaded copy. To do this,
// change the copy from copying the original value to just copying the
} else if (!UI->use_empty()) {
// Change the load into a load of bool then a select.
LoadInst *LI = cast<LoadInst>(UI);
-
- std::string Name = LI->getName(); LI->setName("");
- LoadInst *NLI = new LoadInst(NewGV, Name+".b", LI);
+ LoadInst *NLI = new LoadInst(NewGV, LI->getName()+".b", LI);
Value *NSI;
if (IsOneZero)
- NSI = new CastInst(NLI, LI->getType(), Name, LI);
+ NSI = new ZExtInst(NLI, LI->getType(), "", LI);
else
- NSI = new SelectInst(NLI, OtherVal, InitVal, Name, LI);
+ NSI = new SelectInst(NLI, OtherVal, InitVal, "", LI);
+ NSI->takeName(LI);
LI->replaceAllUsesWith(NSI);
}
UI->eraseFromParent();
GV->removeDeadConstantUsers();
if (GV->use_empty()) {
- DEBUG(std::cerr << "GLOBAL DEAD: " << *GV);
+ DOUT << "GLOBAL DEAD: " << *GV;
GV->eraseFromParent();
++NumDeleted;
return true;
}
if (!AnalyzeGlobal(GV, GS, PHIUsers)) {
+#if 0
+ cerr << "Global: " << *GV;
+ cerr << " isLoaded = " << GS.isLoaded << "\n";
+ cerr << " StoredType = ";
+ switch (GS.StoredType) {
+ case GlobalStatus::NotStored: cerr << "NEVER STORED\n"; break;
+ case GlobalStatus::isInitializerStored: cerr << "INIT STORED\n"; break;
+ case GlobalStatus::isStoredOnce: cerr << "STORED ONCE\n"; break;
+ case GlobalStatus::isStored: cerr << "stored\n"; break;
+ }
+ if (GS.StoredType == GlobalStatus::isStoredOnce && GS.StoredOnceValue)
+ cerr << " StoredOnceValue = " << *GS.StoredOnceValue << "\n";
+ if (GS.AccessingFunction && !GS.HasMultipleAccessingFunctions)
+ cerr << " AccessingFunction = " << GS.AccessingFunction->getName()
+ << "\n";
+ cerr << " HasMultipleAccessingFunctions = "
+ << GS.HasMultipleAccessingFunctions << "\n";
+ cerr << " HasNonInstructionUser = " << GS.HasNonInstructionUser<<"\n";
+ cerr << " isNotSuitableForSRA = " << GS.isNotSuitableForSRA << "\n";
+ cerr << "\n";
+#endif
+
// If this is a first class global and has only one accessing function
// and this function is main (which we know is not recursive we can make
// this global a local variable) we replace the global with a local alloca
GV->getType()->getElementType()->isFirstClassType() &&
GS.AccessingFunction->getName() == "main" &&
GS.AccessingFunction->hasExternalLinkage()) {
- DEBUG(std::cerr << "LOCALIZING GLOBAL: " << *GV);
+ DOUT << "LOCALIZING GLOBAL: " << *GV;
Instruction* FirstI = GS.AccessingFunction->getEntryBlock().begin();
const Type* ElemTy = GV->getType()->getElementType();
+ // FIXME: Pass Global's alignment when globals have alignment
AllocaInst* Alloca = new AllocaInst(ElemTy, NULL, GV->getName(), FirstI);
if (!isa<UndefValue>(GV->getInitializer()))
new StoreInst(GV->getInitializer(), Alloca, FirstI);
++NumLocalized;
return true;
}
+
// If the global is never loaded (but may be stored to), it is dead.
// Delete it now.
if (!GS.isLoaded) {
- DEBUG(std::cerr << "GLOBAL NEVER LOADED: " << *GV);
+ DOUT << "GLOBAL NEVER LOADED: " << *GV;
// Delete any stores we can find to the global. We may not be able to
// make it completely dead though.
return Changed;
} else if (GS.StoredType <= GlobalStatus::isInitializerStored) {
- DEBUG(std::cerr << "MARKING CONSTANT: " << *GV);
+ DOUT << "MARKING CONSTANT: " << *GV;
GV->setConstant(true);
// Clean up any obviously simplifiable users now.
// If the global is dead now, just nuke it.
if (GV->use_empty()) {
- DEBUG(std::cerr << " *** Marking constant allowed us to simplify "
- "all users and delete global!\n");
+ DOUT << " *** Marking constant allowed us to simplify "
+ << "all users and delete global!\n";
GV->eraseFromParent();
++NumDeleted;
}
CleanupConstantGlobalUsers(GV, GV->getInitializer());
if (GV->use_empty()) {
- DEBUG(std::cerr << " *** Substituting initializer allowed us to "
- "simplify all users and delete global!\n");
+ DOUT << " *** Substituting initializer allowed us to "
+ << "simplify all users and delete global!\n";
GV->eraseFromParent();
++NumDeleted;
} else {
// Otherwise, if the global was not a boolean, we can shrink it to be a
// boolean.
if (Constant *SOVConstant = dyn_cast<Constant>(GS.StoredOnceValue))
- if (GV->getType()->getElementType() != Type::BoolTy &&
- !GV->getType()->getElementType()->isFloatingPoint()) {
- DEBUG(std::cerr << " *** SHRINKING TO BOOL: " << *GV);
+ if (GV->getType()->getElementType() != Type::Int1Ty &&
+ !GV->getType()->getElementType()->isFloatingPoint() &&
+ !isa<VectorType>(GV->getType()->getElementType()) &&
+ !GS.HasPHIUser && !GS.isNotSuitableForSRA) {
+ DOUT << " *** SHRINKING TO BOOL: " << *GV;
ShrinkGlobalToBoolean(GV, SOVConstant);
++NumShrunkToBool;
return true;
/// FindGlobalCtors - Find the llvm.globalctors list, verifying that all
/// initializers have an init priority of 65535.
GlobalVariable *GlobalOpt::FindGlobalCtors(Module &M) {
- for (Module::giterator I = M.global_begin(), E = M.global_end(); I != E; ++I)
+ for (Module::global_iterator I = M.global_begin(), E = M.global_end();
+ I != E; ++I)
if (I->getName() == "llvm.global_ctors") {
// Found it, verify it's an array of { int, void()* }.
const ArrayType *ATy =dyn_cast<ArrayType>(I->getType()->getElementType());
if (!ATy) return 0;
const StructType *STy = dyn_cast<StructType>(ATy->getElementType());
if (!STy || STy->getNumElements() != 2 ||
- STy->getElementType(0) != Type::IntTy) return 0;
+ STy->getElementType(0) != Type::Int32Ty) return 0;
const PointerType *PFTy = dyn_cast<PointerType>(STy->getElementType(1));
if (!PFTy) return 0;
const FunctionType *FTy = dyn_cast<FunctionType>(PFTy->getElementType());
// Init priority must be standard.
ConstantInt *CI = dyn_cast<ConstantInt>(CS->getOperand(0));
- if (!CI || CI->getRawValue() != 65535)
+ if (!CI || CI->getZExtValue() != 65535)
return 0;
} else {
return 0;
const std::vector<Function*> &Ctors) {
// If we made a change, reassemble the initializer list.
std::vector<Constant*> CSVals;
- CSVals.push_back(ConstantSInt::get(Type::IntTy, 65535));
+ CSVals.push_back(ConstantInt::get(Type::Int32Ty, 65535));
CSVals.push_back(0);
// Create the new init list.
std::vector<const Type*>(), false);
const PointerType *PFTy = PointerType::get(FTy);
CSVals[1] = Constant::getNullValue(PFTy);
- CSVals[0] = ConstantSInt::get(Type::IntTy, 2147483647);
+ CSVals[0] = ConstantInt::get(Type::Int32Ty, 2147483647);
}
CAList.push_back(ConstantStruct::get(CSVals));
}
// Create the new global and insert it next to the existing list.
GlobalVariable *NGV = new GlobalVariable(CA->getType(), GCL->isConstant(),
- GCL->getLinkage(), CA,
- GCL->getName());
- GCL->setName("");
+ GCL->getLinkage(), CA, "",
+ (Module *)NULL,
+ GCL->isThreadLocal());
GCL->getParent()->getGlobalList().insert(GCL, NGV);
+ NGV->takeName(GCL);
// Nuke the old list, replacing any uses with the new one.
if (!GCL->use_empty()) {
Constant *V = NGV;
if (V->getType() != GCL->getType())
- V = ConstantExpr::getCast(V, GCL->getType());
+ V = ConstantExpr::getBitCast(V, GCL->getType());
GCL->replaceAllUsesWith(V);
}
GCL->eraseFromParent();
/// we punt. We basically just support direct accesses to globals and GEP's of
/// globals. This should be kept up to date with CommitValueTo.
static bool isSimpleEnoughPointerToCommit(Constant *C) {
- if (GlobalVariable *GV = dyn_cast<GlobalVariable>(C))
- return !GV->isExternal(); // reject external globals.
+ if (GlobalVariable *GV = dyn_cast<GlobalVariable>(C)) {
+ if (!GV->hasExternalLinkage() && !GV->hasInternalLinkage())
+ return false; // do not allow weak/linkonce/dllimport/dllexport linkage.
+ return !GV->isDeclaration(); // reject external globals.
+ }
if (ConstantExpr *CE = dyn_cast<ConstantExpr>(C))
// Handle a constantexpr gep.
if (CE->getOpcode() == Instruction::GetElementPtr &&
isa<GlobalVariable>(CE->getOperand(0))) {
GlobalVariable *GV = cast<GlobalVariable>(CE->getOperand(0));
+ if (!GV->hasExternalLinkage() && !GV->hasInternalLinkage())
+ return false; // do not allow weak/linkonce/dllimport/dllexport linkage.
return GV->hasInitializer() &&
ConstantFoldLoadThroughGEPConstantExpr(GV->getInitializer(), CE);
}
}
// Replace the element that we are supposed to.
- ConstantUInt *CU = cast<ConstantUInt>(Addr->getOperand(OpNo));
- assert(CU->getValue() < STy->getNumElements() &&
- "Struct index out of range!");
- unsigned Idx = (unsigned)CU->getValue();
+ ConstantInt *CU = cast<ConstantInt>(Addr->getOperand(OpNo));
+ unsigned Idx = CU->getZExtValue();
+ assert(Idx < STy->getNumElements() && "Struct index out of range!");
Elts[Idx] = EvaluateStoreInto(Elts[Idx], Val, Addr, OpNo+1);
// Return the modified struct.
- return ConstantStruct::get(Elts);
+ return ConstantStruct::get(&Elts[0], Elts.size(), STy->isPacked());
} else {
ConstantInt *CI = cast<ConstantInt>(Addr->getOperand(OpNo));
const ArrayType *ATy = cast<ArrayType>(Init->getType());
" ConstantFoldLoadThroughGEPConstantExpr");
}
- assert((uint64_t)CI->getRawValue() < ATy->getNumElements());
- Elts[(uint64_t)CI->getRawValue()] =
- EvaluateStoreInto(Elts[(uint64_t)CI->getRawValue()], Val, Addr, OpNo+1);
+ assert(CI->getZExtValue() < ATy->getNumElements());
+ Elts[CI->getZExtValue()] =
+ EvaluateStoreInto(Elts[CI->getZExtValue()], Val, Addr, OpNo+1);
return ConstantArray::get(ATy, Elts);
}
}
/// EvaluateFunction - Evaluate a call to function F, returning true if
/// successful, false if we can't evaluate it. ActualArgs contains the formal
/// arguments for the function.
-static bool EvaluateFunction(Function *F,
+static bool EvaluateFunction(Function *F, Constant *&RetVal,
const std::vector<Constant*> &ActualArgs,
std::vector<Function*> &CallStack,
std::map<Constant*, Constant*> &MutatedMemory,
std::vector<GlobalVariable*> &AllocaTmps) {
+ // Check to see if this function is already executing (recursion). If so,
+ // bail out. TODO: we might want to accept limited recursion.
+ if (std::find(CallStack.begin(), CallStack.end(), F) != CallStack.end())
+ return false;
+
+ CallStack.push_back(F);
+
/// Values - As we compute SSA register values, we store their contents here.
std::map<Value*, Constant*> Values;
+
+ // Initialize arguments to the incoming values specified.
+ unsigned ArgNo = 0;
+ for (Function::arg_iterator AI = F->arg_begin(), E = F->arg_end(); AI != E;
+ ++AI, ++ArgNo)
+ Values[AI] = ActualArgs[ArgNo];
/// ExecutedBlocks - We only handle non-looping, non-recursive code. As such,
/// we can only evaluate any one basic block at most once. This set keeps
// CurInst - The current instruction we're evaluating.
BasicBlock::iterator CurInst = F->begin()->begin();
- ExecutedBlocks.insert(F->begin());
// This is the main evaluation loop.
while (1) {
Constant *InstResult = 0;
if (StoreInst *SI = dyn_cast<StoreInst>(CurInst)) {
- if (SI->isVolatile()) break; // no volatile accesses.
+ if (SI->isVolatile()) return false; // no volatile accesses.
Constant *Ptr = getVal(Values, SI->getOperand(1));
if (!isSimpleEnoughPointerToCommit(Ptr))
// If this is too complex for us to commit, reject it.
- break;
+ return false;
Constant *Val = getVal(Values, SI->getOperand(0));
MutatedMemory[Ptr] = Val;
} else if (BinaryOperator *BO = dyn_cast<BinaryOperator>(CurInst)) {
InstResult = ConstantExpr::get(BO->getOpcode(),
getVal(Values, BO->getOperand(0)),
getVal(Values, BO->getOperand(1)));
- } else if (ShiftInst *SI = dyn_cast<ShiftInst>(CurInst)) {
- InstResult = ConstantExpr::get(SI->getOpcode(),
- getVal(Values, SI->getOperand(0)),
- getVal(Values, SI->getOperand(1)));
+ } else if (CmpInst *CI = dyn_cast<CmpInst>(CurInst)) {
+ InstResult = ConstantExpr::getCompare(CI->getPredicate(),
+ getVal(Values, CI->getOperand(0)),
+ getVal(Values, CI->getOperand(1)));
} else if (CastInst *CI = dyn_cast<CastInst>(CurInst)) {
- InstResult = ConstantExpr::getCast(getVal(Values, CI->getOperand(0)),
+ InstResult = ConstantExpr::getCast(CI->getOpcode(),
+ getVal(Values, CI->getOperand(0)),
CI->getType());
} else if (SelectInst *SI = dyn_cast<SelectInst>(CurInst)) {
InstResult = ConstantExpr::getSelect(getVal(Values, SI->getOperand(0)),
getVal(Values, SI->getOperand(2)));
} else if (GetElementPtrInst *GEP = dyn_cast<GetElementPtrInst>(CurInst)) {
Constant *P = getVal(Values, GEP->getOperand(0));
- std::vector<Constant*> GEPOps;
+ SmallVector<Constant*, 8> GEPOps;
for (unsigned i = 1, e = GEP->getNumOperands(); i != e; ++i)
GEPOps.push_back(getVal(Values, GEP->getOperand(i)));
- InstResult = ConstantExpr::getGetElementPtr(P, GEPOps);
+ InstResult = ConstantExpr::getGetElementPtr(P, &GEPOps[0], GEPOps.size());
} else if (LoadInst *LI = dyn_cast<LoadInst>(CurInst)) {
- if (LI->isVolatile()) break; // no volatile accesses.
+ if (LI->isVolatile()) return false; // no volatile accesses.
InstResult = ComputeLoadResult(getVal(Values, LI->getOperand(0)),
MutatedMemory);
- if (InstResult == 0) break; // Could not evaluate load.
+ if (InstResult == 0) return false; // Could not evaluate load.
} else if (AllocaInst *AI = dyn_cast<AllocaInst>(CurInst)) {
- if (AI->isArrayAllocation()) break; // Cannot handle array allocs.
+ if (AI->isArrayAllocation()) return false; // Cannot handle array allocs.
const Type *Ty = AI->getType()->getElementType();
AllocaTmps.push_back(new GlobalVariable(Ty, false,
GlobalValue::InternalLinkage,
UndefValue::get(Ty),
AI->getName()));
- InstResult = AllocaTmps.back();
- } else if (TerminatorInst *TI = dyn_cast<TerminatorInst>(CurInst)) {
+ InstResult = AllocaTmps.back();
+ } else if (CallInst *CI = dyn_cast<CallInst>(CurInst)) {
+ // Cannot handle inline asm.
+ if (isa<InlineAsm>(CI->getOperand(0))) return false;
+
+ // Resolve function pointers.
+ Function *Callee = dyn_cast<Function>(getVal(Values, CI->getOperand(0)));
+ if (!Callee) return false; // Cannot resolve.
+
+ std::vector<Constant*> Formals;
+ for (unsigned i = 1, e = CI->getNumOperands(); i != e; ++i)
+ Formals.push_back(getVal(Values, CI->getOperand(i)));
+
+ if (Callee->isDeclaration()) {
+ // If this is a function we can constant fold, do it.
+ if (Constant *C = ConstantFoldCall(Callee, &Formals[0],
+ Formals.size())) {
+ InstResult = C;
+ } else {
+ return false;
+ }
+ } else {
+ if (Callee->getFunctionType()->isVarArg())
+ return false;
+
+ Constant *RetVal;
+
+ // Execute the call, if successful, use the return value.
+ if (!EvaluateFunction(Callee, RetVal, Formals, CallStack,
+ MutatedMemory, AllocaTmps))
+ return false;
+ InstResult = RetVal;
+ }
+ } else if (isa<TerminatorInst>(CurInst)) {
BasicBlock *NewBB = 0;
if (BranchInst *BI = dyn_cast<BranchInst>(CurInst)) {
if (BI->isUnconditional()) {
NewBB = BI->getSuccessor(0);
} else {
- ConstantBool *Cond =
- dyn_cast<ConstantBool>(getVal(Values, BI->getCondition()));
- if (!Cond) break; // Cannot determine.
- NewBB = BI->getSuccessor(!Cond->getValue());
+ ConstantInt *Cond =
+ dyn_cast<ConstantInt>(getVal(Values, BI->getCondition()));
+ if (!Cond) return false; // Cannot determine.
+
+ NewBB = BI->getSuccessor(!Cond->getZExtValue());
}
} else if (SwitchInst *SI = dyn_cast<SwitchInst>(CurInst)) {
ConstantInt *Val =
dyn_cast<ConstantInt>(getVal(Values, SI->getCondition()));
- if (!Val) break; // Cannot determine.
+ if (!Val) return false; // Cannot determine.
NewBB = SI->getSuccessor(SI->findCaseValue(Val));
} else if (ReturnInst *RI = dyn_cast<ReturnInst>(CurInst)) {
- assert(RI->getNumOperands() == 0);
+ if (RI->getNumOperands())
+ RetVal = getVal(Values, RI->getOperand(0));
+
+ CallStack.pop_back(); // return from fn.
return true; // We succeeded at evaluating this ctor!
} else {
- // unwind, unreachable.
- break; // Cannot handle this terminator.
+ // invoke, unwind, unreachable.
+ return false; // Cannot handle this terminator.
}
// Okay, we succeeded in evaluating this control flow. See if we have
- // executed the new block before. If so, we have a looping or recursive
- // function, which we cannot evaluate in reasonable time.
+ // executed the new block before. If so, we have a looping function,
+ // which we cannot evaluate in reasonable time.
if (!ExecutedBlocks.insert(NewBB).second)
- break; // Recursed/looped!
+ return false; // looped!
// Okay, we have never been in this block before. Check to see if there
// are any PHI nodes. If so, evaluate them with information about where
// Do NOT increment CurInst. We know that the terminator had no value.
continue;
} else {
- // TODO: use ConstantFoldCall for function calls.
-
// Did not know how to evaluate this!
- break;
+ return false;
}
if (!CurInst->use_empty())
// Advance program counter.
++CurInst;
}
-
- return false;
}
/// EvaluateStaticConstructor - Evaluate static constructors in the function, if
std::vector<Function*> CallStack;
// Call the function.
- bool EvalSuccess = EvaluateFunction(F, std::vector<Constant*>(), CallStack,
- MutatedMemory, AllocaTmps);
+ Constant *RetValDummy;
+ bool EvalSuccess = EvaluateFunction(F, RetValDummy, std::vector<Constant*>(),
+ CallStack, MutatedMemory, AllocaTmps);
if (EvalSuccess) {
// We succeeded at evaluation: commit the result.
- DEBUG(std::cerr << "FULLY EVALUATED GLOBAL CTOR FUNCTION '" <<
- F->getName() << "' to " << MutatedMemory.size() << " stores.\n");
+ DOUT << "FULLY EVALUATED GLOBAL CTOR FUNCTION '"
+ << F->getName() << "' to " << MutatedMemory.size()
+ << " stores.\n";
for (std::map<Constant*, Constant*>::iterator I = MutatedMemory.begin(),
E = MutatedMemory.end(); I != E; ++I)
CommitValueTo(I->second, I->first);
-
/// OptimizeGlobalCtorsList - Simplify and evaluation global ctors if possible.
/// Return true if anything changed.
bool GlobalOpt::OptimizeGlobalCtorsList(GlobalVariable *&GCL) {