mirror of
https://github.com/c64scene-ar/llvm-6502.git
synced 2025-01-14 16:33:28 +00:00
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:
parent
e6232ed86d
commit
db5dbf013c
@ -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.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user