mirror of
https://github.com/c64scene-ar/llvm-6502.git
synced 2024-12-15 04:30:12 +00:00
Optimize switch lookup tables with linear mapping.
This is a simple optimization for switch table lookup: It computes the output value directly with an (optional) mul and add if there is a linear mapping between index and output. Example: int f1(int x) { switch (x) { case 0: return 10; case 1: return 11; case 2: return 12; case 3: return 13; } return 0; } generates: define i32 @f1(i32 %x) #0 { entry: %0 = icmp ult i32 %x, 4 br i1 %0, label %switch.lookup, label %return switch.lookup: %switch.offset = add i32 %x, 10 ret i32 %switch.offset return: ret i32 0 } git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@222121 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
parent
17e95ead36
commit
72a1394991
@ -70,6 +70,7 @@ static cl::opt<bool> HoistCondStores(
|
|||||||
cl::desc("Hoist conditional stores if an unconditional store precedes"));
|
cl::desc("Hoist conditional stores if an unconditional store precedes"));
|
||||||
|
|
||||||
STATISTIC(NumBitMaps, "Number of switch instructions turned into bitmaps");
|
STATISTIC(NumBitMaps, "Number of switch instructions turned into bitmaps");
|
||||||
|
STATISTIC(NumLinearMaps, "Number of switch instructions turned into linear mapping");
|
||||||
STATISTIC(NumLookupTables, "Number of switch instructions turned into lookup tables");
|
STATISTIC(NumLookupTables, "Number of switch instructions turned into lookup tables");
|
||||||
STATISTIC(NumLookupTablesHoles, "Number of switch instructions turned into lookup tables (holes checked)");
|
STATISTIC(NumLookupTablesHoles, "Number of switch instructions turned into lookup tables (holes checked)");
|
||||||
STATISTIC(NumSinkCommons, "Number of common instructions sunk down to the end block");
|
STATISTIC(NumSinkCommons, "Number of common instructions sunk down to the end block");
|
||||||
@ -3656,6 +3657,11 @@ namespace {
|
|||||||
// store that single value and return it for each lookup.
|
// store that single value and return it for each lookup.
|
||||||
SingleValueKind,
|
SingleValueKind,
|
||||||
|
|
||||||
|
// For tables where there is a linear relationship between table index
|
||||||
|
// and values. We calculate the result with a simple multiplication
|
||||||
|
// and addition instead of a table lookup.
|
||||||
|
LinearMapKind,
|
||||||
|
|
||||||
// For small tables with integer elements, we can pack them into a bitmap
|
// For small tables with integer elements, we can pack them into a bitmap
|
||||||
// that fits into a target-legal register. Values are retrieved by
|
// that fits into a target-legal register. Values are retrieved by
|
||||||
// shift and mask operations.
|
// shift and mask operations.
|
||||||
@ -3673,6 +3679,10 @@ namespace {
|
|||||||
ConstantInt *BitMap;
|
ConstantInt *BitMap;
|
||||||
IntegerType *BitMapElementTy;
|
IntegerType *BitMapElementTy;
|
||||||
|
|
||||||
|
// For LinearMapKind, these are the constants used to derive the value.
|
||||||
|
ConstantInt *LinearOffset;
|
||||||
|
ConstantInt *LinearMultiplier;
|
||||||
|
|
||||||
// For ArrayKind, this is the array.
|
// For ArrayKind, this is the array.
|
||||||
GlobalVariable *Array;
|
GlobalVariable *Array;
|
||||||
};
|
};
|
||||||
@ -3685,7 +3695,7 @@ SwitchLookupTable::SwitchLookupTable(Module &M,
|
|||||||
Constant *DefaultValue,
|
Constant *DefaultValue,
|
||||||
const DataLayout *DL)
|
const DataLayout *DL)
|
||||||
: SingleValue(nullptr), BitMap(nullptr), BitMapElementTy(nullptr),
|
: SingleValue(nullptr), BitMap(nullptr), BitMapElementTy(nullptr),
|
||||||
Array(nullptr) {
|
LinearOffset(nullptr), LinearMultiplier(nullptr), Array(nullptr) {
|
||||||
assert(Values.size() && "Can't build lookup table without values!");
|
assert(Values.size() && "Can't build lookup table without values!");
|
||||||
assert(TableSize >= Values.size() && "Can't fit values in table!");
|
assert(TableSize >= Values.size() && "Can't fit values in table!");
|
||||||
|
|
||||||
@ -3730,6 +3740,43 @@ SwitchLookupTable::SwitchLookupTable(Module &M,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if we can derive the value with a linear transformation from the
|
||||||
|
// table index.
|
||||||
|
if (isa<IntegerType>(ValueType)) {
|
||||||
|
bool LinearMappingPossible = true;
|
||||||
|
APInt PrevVal;
|
||||||
|
APInt DistToPrev;
|
||||||
|
assert(TableSize >= 2 && "Should be a SingleValue table.");
|
||||||
|
// Check if there is the same distance between two consecutive values.
|
||||||
|
for (uint64_t I = 0; I < TableSize; ++I) {
|
||||||
|
ConstantInt *ConstVal = dyn_cast<ConstantInt>(TableContents[I]);
|
||||||
|
if (!ConstVal) {
|
||||||
|
// This is an undef. We could deal with it, but undefs in lookup tables
|
||||||
|
// are very seldom. It's probably not worth the additional complexity.
|
||||||
|
LinearMappingPossible = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
APInt Val = ConstVal->getValue();
|
||||||
|
if (I != 0) {
|
||||||
|
APInt Dist = Val - PrevVal;
|
||||||
|
if (I == 1) {
|
||||||
|
DistToPrev = Dist;
|
||||||
|
} else if (Dist != DistToPrev) {
|
||||||
|
LinearMappingPossible = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
PrevVal = Val;
|
||||||
|
}
|
||||||
|
if (LinearMappingPossible) {
|
||||||
|
LinearOffset = cast<ConstantInt>(TableContents[0]);
|
||||||
|
LinearMultiplier = ConstantInt::get(M.getContext(), DistToPrev);
|
||||||
|
Kind = LinearMapKind;
|
||||||
|
++NumLinearMaps;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// If the type is integer and the table fits in a register, build a bitmap.
|
// If the type is integer and the table fits in a register, build a bitmap.
|
||||||
if (WouldFitInRegister(DL, TableSize, ValueType)) {
|
if (WouldFitInRegister(DL, TableSize, ValueType)) {
|
||||||
IntegerType *IT = cast<IntegerType>(ValueType);
|
IntegerType *IT = cast<IntegerType>(ValueType);
|
||||||
@ -3765,6 +3812,16 @@ Value *SwitchLookupTable::BuildLookup(Value *Index, IRBuilder<> &Builder) {
|
|||||||
switch (Kind) {
|
switch (Kind) {
|
||||||
case SingleValueKind:
|
case SingleValueKind:
|
||||||
return SingleValue;
|
return SingleValue;
|
||||||
|
case LinearMapKind: {
|
||||||
|
// Derive the result value from the input value.
|
||||||
|
Value *Result = Builder.CreateIntCast(Index, LinearMultiplier->getType(),
|
||||||
|
false, "switch.idx.cast");
|
||||||
|
if (!LinearMultiplier->isOne())
|
||||||
|
Result = Builder.CreateMul(Result, LinearMultiplier, "switch.idx.mult");
|
||||||
|
if (!LinearOffset->isZero())
|
||||||
|
Result = Builder.CreateAdd(Result, LinearOffset, "switch.offset");
|
||||||
|
return Result;
|
||||||
|
}
|
||||||
case BitMapKind: {
|
case BitMapKind: {
|
||||||
// Type of the bitmap (e.g. i59).
|
// Type of the bitmap (e.g. i59).
|
||||||
IntegerType *MapTy = BitMap->getType();
|
IntegerType *MapTy = BitMap->getType();
|
||||||
|
@ -895,7 +895,7 @@ sw.bb1: br label %return
|
|||||||
sw.bb2: br label %return
|
sw.bb2: br label %return
|
||||||
sw.default: br label %return
|
sw.default: br label %return
|
||||||
return:
|
return:
|
||||||
%x = phi i32 [ 3, %sw.default ], [ 5, %sw.bb2 ], [ 7, %sw.bb1 ], [ 9, %entry ]
|
%x = phi i32 [ 3, %sw.default ], [ 5, %sw.bb2 ], [ 7, %sw.bb1 ], [ 10, %entry ]
|
||||||
ret i32 %x
|
ret i32 %x
|
||||||
; CHECK-LABEL: @threecases(
|
; CHECK-LABEL: @threecases(
|
||||||
; CHECK-NOT: switch i32
|
; CHECK-NOT: switch i32
|
||||||
@ -977,3 +977,104 @@ return:
|
|||||||
; CHECK: switch i32
|
; CHECK: switch i32
|
||||||
; CHECK-NOT: @switch.table
|
; CHECK-NOT: @switch.table
|
||||||
}
|
}
|
||||||
|
|
||||||
|
; We can use linear mapping.
|
||||||
|
define i8 @linearmap1(i32 %c) {
|
||||||
|
entry:
|
||||||
|
switch i32 %c, label %sw.default [
|
||||||
|
i32 10, label %return
|
||||||
|
i32 11, label %sw.bb1
|
||||||
|
i32 12, label %sw.bb2
|
||||||
|
i32 13, label %sw.bb3
|
||||||
|
]
|
||||||
|
sw.bb1: br label %return
|
||||||
|
sw.bb2: br label %return
|
||||||
|
sw.bb3: br label %return
|
||||||
|
sw.default: br label %return
|
||||||
|
return:
|
||||||
|
%x = phi i8 [ 3, %sw.default ], [ 3, %sw.bb3 ], [ 8, %sw.bb2 ], [ 13, %sw.bb1 ], [ 18, %entry ]
|
||||||
|
ret i8 %x
|
||||||
|
; CHECK-LABEL: @linearmap1(
|
||||||
|
; CHECK: entry:
|
||||||
|
; CHECK-NEXT: %switch.tableidx = sub i32 %c, 10
|
||||||
|
; CHECK: switch.lookup:
|
||||||
|
; CHECK-NEXT: %switch.idx.cast = trunc i32 %switch.tableidx to i8
|
||||||
|
; CHECK-NEXT: %switch.idx.mult = mul i8 %switch.idx.cast, -5
|
||||||
|
; CHECK-NEXT: %switch.offset = add i8 %switch.idx.mult, 18
|
||||||
|
; CHECK-NEXT: ret i8 %switch.offset
|
||||||
|
}
|
||||||
|
|
||||||
|
; Linear mapping in a different configuration.
|
||||||
|
define i32 @linearmap2(i8 %c) {
|
||||||
|
entry:
|
||||||
|
switch i8 %c, label %sw.default [
|
||||||
|
i8 -10, label %return
|
||||||
|
i8 -11, label %sw.bb1
|
||||||
|
i8 -12, label %sw.bb2
|
||||||
|
i8 -13, label %sw.bb3
|
||||||
|
]
|
||||||
|
sw.bb1: br label %return
|
||||||
|
sw.bb2: br label %return
|
||||||
|
sw.bb3: br label %return
|
||||||
|
sw.default: br label %return
|
||||||
|
return:
|
||||||
|
%x = phi i32 [ 3, %sw.default ], [ 18, %sw.bb3 ], [ 19, %sw.bb2 ], [ 20, %sw.bb1 ], [ 21, %entry ]
|
||||||
|
ret i32 %x
|
||||||
|
; CHECK-LABEL: @linearmap2(
|
||||||
|
; CHECK: entry:
|
||||||
|
; CHECK-NEXT: %switch.tableidx = sub i8 %c, -13
|
||||||
|
; CHECK: switch.lookup:
|
||||||
|
; CHECK-NEXT: %switch.idx.cast = zext i8 %switch.tableidx to i32
|
||||||
|
; CHECK-NEXT: %switch.offset = add i32 %switch.idx.cast, 18
|
||||||
|
; CHECK-NEXT: ret i32 %switch.offset
|
||||||
|
}
|
||||||
|
|
||||||
|
; Linear mapping with overflows.
|
||||||
|
define i8 @linearmap3(i32 %c) {
|
||||||
|
entry:
|
||||||
|
switch i32 %c, label %sw.default [
|
||||||
|
i32 10, label %return
|
||||||
|
i32 11, label %sw.bb1
|
||||||
|
i32 12, label %sw.bb2
|
||||||
|
i32 13, label %sw.bb3
|
||||||
|
]
|
||||||
|
sw.bb1: br label %return
|
||||||
|
sw.bb2: br label %return
|
||||||
|
sw.bb3: br label %return
|
||||||
|
sw.default: br label %return
|
||||||
|
return:
|
||||||
|
%x = phi i8 [ 3, %sw.default ], [ 44, %sw.bb3 ], [ -56, %sw.bb2 ], [ 100, %sw.bb1 ], [ 0, %entry ]
|
||||||
|
ret i8 %x
|
||||||
|
; CHECK-LABEL: @linearmap3(
|
||||||
|
; CHECK: entry:
|
||||||
|
; CHECK-NEXT: %switch.tableidx = sub i32 %c, 10
|
||||||
|
; CHECK: switch.lookup:
|
||||||
|
; CHECK-NEXT: %switch.idx.cast = trunc i32 %switch.tableidx to i8
|
||||||
|
; CHECK-NEXT: %switch.idx.mult = mul i8 %switch.idx.cast, 100
|
||||||
|
; CHECK-NEXT: ret i8 %switch.idx.mult
|
||||||
|
}
|
||||||
|
|
||||||
|
; Linear mapping with with multiplier 1 and offset 0.
|
||||||
|
define i8 @linearmap4(i32 %c) {
|
||||||
|
entry:
|
||||||
|
switch i32 %c, label %sw.default [
|
||||||
|
i32 -2, label %return
|
||||||
|
i32 -1, label %sw.bb1
|
||||||
|
i32 0, label %sw.bb2
|
||||||
|
i32 1, label %sw.bb3
|
||||||
|
]
|
||||||
|
sw.bb1: br label %return
|
||||||
|
sw.bb2: br label %return
|
||||||
|
sw.bb3: br label %return
|
||||||
|
sw.default: br label %return
|
||||||
|
return:
|
||||||
|
%x = phi i8 [ 3, %sw.default ], [ 3, %sw.bb3 ], [ 2, %sw.bb2 ], [ 1, %sw.bb1 ], [ 0, %entry ]
|
||||||
|
ret i8 %x
|
||||||
|
; CHECK-LABEL: @linearmap4(
|
||||||
|
; CHECK: entry:
|
||||||
|
; CHECK-NEXT: %switch.tableidx = sub i32 %c, -2
|
||||||
|
; CHECK: switch.lookup:
|
||||||
|
; CHECK-NEXT: %switch.idx.cast = trunc i32 %switch.tableidx to i8
|
||||||
|
; CHECK-NEXT: ret i8 %switch.idx.cast
|
||||||
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user