SimplifyCFG: Refactor the switch-to-lookup table transformation by

breaking out the building of lookup tables into a separate class.

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@164682 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Hans Wennborg 2012-09-26 09:34:53 +00:00
parent e6232ed86d
commit db5dbf013c

View File

@ -3240,61 +3240,127 @@ static bool GetCaseResults(SwitchInst *SI,
return true; return true;
} }
/// BuildLookupTable - Build a lookup table with the contents of Results, using namespace {
/// DefaultResult to fill the holes in the table. If the table ends up /// SwitchLookupTable - This class represents a lookup table that can be used
/// containing the same result in each element, set *SingleResult to that value /// to replace a switch.
/// and return NULL. class SwitchLookupTable {
static GlobalVariable *BuildLookupTable(Module &M, public:
uint64_t TableSize, /// SwitchLookupTable - Create a lookup table to use as a switch replacement
ConstantInt *Offset, /// with the contents of Values, using DefaultValue to fill any holes in the
const SmallVector<std::pair<ConstantInt*, Constant*>, 4>& Results, /// table.
Constant *DefaultResult, SwitchLookupTable(Module &M,
Constant **SingleResult) { uint64_t TableSize,
assert(Results.size() && "Need values to build lookup table"); ConstantInt *Offset,
assert(TableSize >= Results.size() && "Table needs to hold all values"); const SmallVector<std::pair<ConstantInt*, Constant*>, 4>& Values,
Constant *DefaultValue);
/// BuildLookup - Build instructions with Builder to retrieve the value at
/// the position given by Index in the lookup table.
Value *BuildLookup(Value *Index, IRBuilder<> &Builder);
private:
// Depending on the contents of the table, it can be represented in
// different ways.
enum {
// For tables where each element contains the same value, we just have to
// store that single value and return it for each lookup.
SingleValueKind,
// The table is stored as an array of values. Values are retrieved by load
// instructions from the table.
ArrayKind
} Kind;
// For SingleValueKind, this is the single value.
Constant *SingleValue;
// For ArrayKind, this is the array.
GlobalVariable *Array;
};
}
SwitchLookupTable::SwitchLookupTable(Module &M,
uint64_t TableSize,
ConstantInt *Offset,
const SmallVector<std::pair<ConstantInt*, Constant*>, 4>& Values,
Constant *DefaultValue) {
assert(Values.size() && "Can't build lookup table without values.");
assert(TableSize >= Values.size() && "Can't fit values in table.");
// If all values in the table are equal, this is that value. // If all values in the table are equal, this is that value.
Constant *SameResult = Results.begin()->second; SingleValue = Values.begin()->second;
// Build up the table contents. // Build up the table contents.
std::vector<Constant*> TableContents(TableSize); SmallVector<Constant*, 64> TableContents(TableSize);
for (size_t I = 0, E = Results.size(); I != E; ++I) { for (size_t I = 0, E = Values.size(); I != E; ++I) {
ConstantInt *CaseVal = Results[I].first; ConstantInt *CaseVal = Values[I].first;
Constant *CaseRes = Results[I].second; Constant *CaseRes = Values[I].second;
assert(CaseRes->getType() == DefaultValue->getType());
uint64_t Idx = (CaseVal->getValue() - Offset->getValue()).getLimitedValue(); uint64_t Idx = (CaseVal->getValue() - Offset->getValue())
.getLimitedValue();
TableContents[Idx] = CaseRes; TableContents[Idx] = CaseRes;
if (CaseRes != SameResult) if (CaseRes != SingleValue)
SameResult = NULL; SingleValue = NULL;
} }
// Fill in any holes in the table with the default result. // Fill in any holes in the table with the default result.
if (Results.size() < TableSize) { if (Values.size() < TableSize) {
for (unsigned i = 0; i < TableSize; ++i) { for (uint64_t I = 0; I < TableSize; ++I) {
if (!TableContents[i]) if (!TableContents[I])
TableContents[i] = DefaultResult; TableContents[I] = DefaultValue;
} }
if (DefaultResult != SameResult) if (DefaultValue != SingleValue)
SameResult = NULL; SingleValue = NULL;
} }
// Same result was used in the entire table; just return that. // If each element in the table contains the same value, we only need to store
if (SameResult) { // that single value.
*SingleResult = SameResult; if (SingleValue) {
return NULL; Kind = SingleValueKind;
return;
} }
ArrayType *ArrayTy = ArrayType::get(DefaultResult->getType(), TableSize); // Store the table in an array.
ArrayType *ArrayTy = ArrayType::get(DefaultValue->getType(), TableSize);
Constant *Initializer = ConstantArray::get(ArrayTy, TableContents); Constant *Initializer = ConstantArray::get(ArrayTy, TableContents);
GlobalVariable *GV = new GlobalVariable(M, ArrayTy, /*constant=*/ true, Array = new GlobalVariable(M, ArrayTy, /*constant=*/ true,
GlobalVariable::PrivateLinkage, GlobalVariable::PrivateLinkage,
Initializer, Initializer,
"switch.table"); "switch.table");
GV->setUnnamedAddr(true); Array->setUnnamedAddr(true);
return GV; Kind = ArrayKind;
}
Value *SwitchLookupTable::BuildLookup(Value *Index, IRBuilder<> &Builder) {
switch (Kind) {
case SingleValueKind:
return SingleValue;
case ArrayKind: {
Value *GEPIndices[] = { Builder.getInt32(0), Index };
Value *GEP = Builder.CreateInBoundsGEP(Array, GEPIndices,
"switch.gep");
return Builder.CreateLoad(GEP, "switch.load");
}
}
llvm_unreachable("Unknown lookup table kind!");
}
/// ShouldBuildLookupTable - Determine whether a lookup table should be built
/// for this switch, based on the number of caes, size of the table and the
/// types of the results.
static bool ShouldBuildLookupTable(SwitchInst *SI,
uint64_t TableSize) {
// The table density should be at least 40%. This is the same criterion as for
// jump tables, see SelectionDAGBuilder::handleJTSwitchCase.
// FIXME: Find the best cut-off.
if (SI->getNumCases() * 10 >= TableSize * 4)
return true;
return false;
} }
/// SwitchToLookupTable - If the switch is only used to initialize one or more /// SwitchToLookupTable - If the switch is only used to initialize one or more
@ -3316,7 +3382,7 @@ static bool SwitchToLookupTable(SwitchInst *SI,
// GEP needs a runtime relocation in PIC code. We should just build one big // GEP needs a runtime relocation in PIC code. We should just build one big
// string and lookup indices into that. // string and lookup indices into that.
// Ignore the switch if the number of cases are too small. // Ignore the switch if the number of cases is too small.
// This is similar to the check when building jump tables in // This is similar to the check when building jump tables in
// SelectionDAGBuilder::handleJTSwitchCase. // SelectionDAGBuilder::handleJTSwitchCase.
// FIXME: Determine the best cut-off. // FIXME: Determine the best cut-off.
@ -3370,33 +3436,16 @@ static bool SwitchToLookupTable(SwitchInst *SI,
} }
APInt RangeSpread = MaxCaseVal->getValue() - MinCaseVal->getValue(); APInt RangeSpread = MaxCaseVal->getValue() - MinCaseVal->getValue();
// The table density should be at lest 40%. This is the same criterion as for // Be careful to avoid overflow when TableSize is used in
// jump tables, see SelectionDAGBuilder::handleJTSwitchCase. // ShouldBuildLookupTable.
// FIXME: Find the best cut-off.
// Be careful to avoid overlow in the density computation.
if (RangeSpread.zextOrSelf(64).ugt(UINT64_MAX / 4 - 1)) if (RangeSpread.zextOrSelf(64).ugt(UINT64_MAX / 4 - 1))
return false; return false;
uint64_t TableSize = RangeSpread.getLimitedValue() + 1; uint64_t TableSize = RangeSpread.getLimitedValue() + 1;
if (SI->getNumCases() * 10 < TableSize * 4) if (!ShouldBuildLookupTable(SI, TableSize))
return false; return false;
// Build the lookup tables.
SmallDenseMap<PHINode*, GlobalVariable*> LookupTables;
SmallDenseMap<PHINode*, Constant*> SingleResults;
Module &Mod = *CommonDest->getParent()->getParent();
for (SmallVector<PHINode*, 4>::iterator I = PHIs.begin(), E = PHIs.end();
I != E; ++I) {
PHINode *PHI = *I;
Constant *SingleResult = NULL;
LookupTables[PHI] = BuildLookupTable(Mod, TableSize, MinCaseVal,
ResultLists[PHI], DefaultResults[PHI],
&SingleResult);
SingleResults[PHI] = SingleResult;
}
// Create the BB that does the lookups. // Create the BB that does the lookups.
Module &Mod = *CommonDest->getParent()->getParent();
BasicBlock *LookupBB = BasicBlock::Create(Mod.getContext(), BasicBlock *LookupBB = BasicBlock::Create(Mod.getContext(),
"switch.lookup", "switch.lookup",
CommonDest->getParent(), CommonDest->getParent(),
@ -3414,19 +3463,13 @@ static bool SwitchToLookupTable(SwitchInst *SI,
// Populate the BB that does the lookups. // Populate the BB that does the lookups.
Builder.SetInsertPoint(LookupBB); Builder.SetInsertPoint(LookupBB);
bool ReturnedEarly = false; bool ReturnedEarly = false;
for (SmallVector<PHINode*, 4>::iterator I = PHIs.begin(), E = PHIs.end(); for (size_t I = 0, E = PHIs.size(); I != E; ++I) {
I != E; ++I) { PHINode *PHI = PHIs[I];
PHINode *PHI = *I;
// There was a single result for this phi; just use that.
if (Constant *SingleResult = SingleResults[PHI]) {
PHI->addIncoming(SingleResult, LookupBB);
continue;
}
Value *GEPIndices[] = { Builder.getInt32(0), TableIndex }; SwitchLookupTable Table(Mod, TableSize, MinCaseVal, ResultLists[PHI],
Value *GEP = Builder.CreateInBoundsGEP(LookupTables[PHI], GEPIndices, DefaultResults[PHI]);
"switch.gep");
Value *Result = Builder.CreateLoad(GEP, "switch.load"); Value *Result = Table.BuildLookup(TableIndex, Builder);
// If the result is used to return immediately from the function, we want to // If the result is used to return immediately from the function, we want to
// do that right here. // do that right here.