Skip to content

Commit b818da2

Browse files
committed
[SimplifyCFG] Enable switch to lookup table for more types.
This transform has been restricted to legal types since https://reviews.llvm.org/rG65df808f6254617b9eee931d00e95d900610b660 in 2012. This is particularly restrictive on RISCV64 which only has i64 as a legal integer type. i32 is a very common type in code generated from C, but we won't form a lookup table with it. This also effects other common types like i8/i16 types on ARM, AArch64, RISCV, etc. This patch proposes to allow power of 2 types larger than 8 bit, if they will fit in the largest legal integer type in DataLayout. These types are common in C code so generally well handled in the backends. We could probably do this for other types like i24 and rely on alignment and padding to allow the backend to use a single wider load. This isn't my main concern right now and it will need more tests. We could also allow larger types up to some limit and let the backend split into multiple loads, but we need to define that limit. It's also not my main concern right now. Reviewed By: lebedev.ri Differential Revision: https://reviews.llvm.org/D107233
1 parent b9139ac commit b818da2

File tree

4 files changed

+117
-176
lines changed

4 files changed

+117
-176
lines changed

llvm/lib/Transforms/Utils/SimplifyCFG.cpp

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5604,8 +5604,32 @@ bool SwitchLookupTable::WouldFitInRegister(const DataLayout &DL,
56045604
return DL.fitsInLegalInteger(TableSize * IT->getBitWidth());
56055605
}
56065606

5607+
static bool isTypeLegalForLookupTable(Type *Ty, const TargetTransformInfo &TTI,
5608+
const DataLayout &DL) {
5609+
// Allow any legal type.
5610+
if (TTI.isTypeLegal(Ty))
5611+
return true;
5612+
5613+
auto *IT = dyn_cast<IntegerType>(Ty);
5614+
if (!IT)
5615+
return false;
5616+
5617+
// Also allow power of 2 integer types that have at least 8 bits and fit in
5618+
// a register. These types are common in frontend languages and targets
5619+
// usually support loads of these types.
5620+
// TODO: We could relax this to any integer that fits in a register and rely
5621+
// on ABI alignment and padding in the table to allow the load to be widened.
5622+
// Or we could widen the constants and truncate the load.
5623+
unsigned BitWidth = IT->getBitWidth();
5624+
return BitWidth >= 8 && isPowerOf2_32(BitWidth) &&
5625+
DL.fitsInLegalInteger(IT->getBitWidth());
5626+
}
5627+
56075628
/// Determine whether a lookup table should be built for this switch, based on
56085629
/// the number of cases, size of the table, and the types of the results.
5630+
// TODO: We could support larger than legal types by limiting based on the
5631+
// number of loads required and/or table size. If the constants are small we
5632+
// could use smaller table entries and extend after the load.
56095633
static bool
56105634
ShouldBuildLookupTable(SwitchInst *SI, uint64_t TableSize,
56115635
const TargetTransformInfo &TTI, const DataLayout &DL,
@@ -5619,7 +5643,7 @@ ShouldBuildLookupTable(SwitchInst *SI, uint64_t TableSize,
56195643
Type *Ty = I.second;
56205644

56215645
// Saturate this flag to true.
5622-
HasIllegalType = HasIllegalType || !TTI.isTypeLegal(Ty);
5646+
HasIllegalType = HasIllegalType || !isTypeLegalForLookupTable(Ty, TTI, DL);
56235647

56245648
// Saturate this flag to false.
56255649
AllTablesFitInRegister =

llvm/test/Transforms/SimplifyCFG/RISCV/switch_to_lookup_table-rv32.ll

Lines changed: 23 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,12 @@ target triple = "riscv32-unknown-elf"
77
; The table for @f
88
; CHECK: @switch.table.f = private unnamed_addr constant [7 x i32] [i32 55, i32 123, i32 0, i32 -1, i32 27, i32 62, i32 1], align 4
99

10+
; The char table for char
11+
; CHECK: @switch.table.char = private unnamed_addr constant [9 x i8] c"7{\00\FF\1B>\01!T", align 1
12+
13+
; The float table for @h
14+
; CHECK: @switch.table.h = private unnamed_addr constant [4 x float] [float 0x40091EB860000000, float 0x3FF3BE76C0000000, float 0x4012449BA0000000, float 0x4001AE1480000000], align 4
15+
1016
; The table for @foostring
1117
; CHECK: @switch.table.foostring = private unnamed_addr constant [4 x i8*] [i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str, i64 0, i64 0), i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str1, i64 0, i64 0), i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str2, i64 0, i64 0), i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str3, i64 0, i64 0)], align 4
1218

@@ -58,37 +64,15 @@ return:
5864
define i8 @char(i32 %c) {
5965
; CHECK-LABEL: @char(
6066
; CHECK-NEXT: entry:
61-
; CHECK-NEXT: switch i32 [[C:%.*]], label [[SW_DEFAULT:%.*]] [
62-
; CHECK-NEXT: i32 42, label [[RETURN:%.*]]
63-
; CHECK-NEXT: i32 43, label [[SW_BB1:%.*]]
64-
; CHECK-NEXT: i32 44, label [[SW_BB2:%.*]]
65-
; CHECK-NEXT: i32 45, label [[SW_BB3:%.*]]
66-
; CHECK-NEXT: i32 46, label [[SW_BB4:%.*]]
67-
; CHECK-NEXT: i32 47, label [[SW_BB5:%.*]]
68-
; CHECK-NEXT: i32 48, label [[SW_BB6:%.*]]
69-
; CHECK-NEXT: i32 49, label [[SW_BB7:%.*]]
70-
; CHECK-NEXT: i32 50, label [[SW_BB8:%.*]]
71-
; CHECK-NEXT: ]
72-
; CHECK: sw.bb1:
73-
; CHECK-NEXT: br label [[RETURN]]
74-
; CHECK: sw.bb2:
75-
; CHECK-NEXT: br label [[RETURN]]
76-
; CHECK: sw.bb3:
77-
; CHECK-NEXT: br label [[RETURN]]
78-
; CHECK: sw.bb4:
79-
; CHECK-NEXT: br label [[RETURN]]
80-
; CHECK: sw.bb5:
81-
; CHECK-NEXT: br label [[RETURN]]
82-
; CHECK: sw.bb6:
83-
; CHECK-NEXT: br label [[RETURN]]
84-
; CHECK: sw.bb7:
85-
; CHECK-NEXT: br label [[RETURN]]
86-
; CHECK: sw.bb8:
87-
; CHECK-NEXT: br label [[RETURN]]
88-
; CHECK: sw.default:
67+
; CHECK-NEXT: [[SWITCH_TABLEIDX:%.*]] = sub i32 [[C:%.*]], 42
68+
; CHECK-NEXT: [[TMP0:%.*]] = icmp ult i32 [[SWITCH_TABLEIDX]], 9
69+
; CHECK-NEXT: br i1 [[TMP0]], label [[SWITCH_LOOKUP:%.*]], label [[RETURN:%.*]]
70+
; CHECK: switch.lookup:
71+
; CHECK-NEXT: [[SWITCH_GEP:%.*]] = getelementptr inbounds [9 x i8], [9 x i8]* @switch.table.char, i32 0, i32 [[SWITCH_TABLEIDX]]
72+
; CHECK-NEXT: [[SWITCH_LOAD:%.*]] = load i8, i8* [[SWITCH_GEP]], align 1
8973
; CHECK-NEXT: br label [[RETURN]]
9074
; CHECK: return:
91-
; CHECK-NEXT: [[RETVAL_0:%.*]] = phi i8 [ 15, [[SW_DEFAULT]] ], [ 84, [[SW_BB8]] ], [ 33, [[SW_BB7]] ], [ 1, [[SW_BB6]] ], [ 62, [[SW_BB5]] ], [ 27, [[SW_BB4]] ], [ -1, [[SW_BB3]] ], [ 0, [[SW_BB2]] ], [ 123, [[SW_BB1]] ], [ 55, [[ENTRY:%.*]] ]
75+
; CHECK-NEXT: [[RETVAL_0:%.*]] = phi i8 [ [[SWITCH_LOAD]], [[SWITCH_LOOKUP]] ], [ 15, [[ENTRY:%.*]] ]
9276
; CHECK-NEXT: ret i8 [[RETVAL_0]]
9377
;
9478
entry:
@@ -125,23 +109,18 @@ declare void @dummy(i8 signext, float)
125109
define void @h(i32 %x) {
126110
; CHECK-LABEL: @h(
127111
; CHECK-NEXT: entry:
128-
; CHECK-NEXT: switch i32 [[X:%.*]], label [[SW_DEFAULT:%.*]] [
129-
; CHECK-NEXT: i32 0, label [[SW_EPILOG:%.*]]
130-
; CHECK-NEXT: i32 1, label [[SW_BB1:%.*]]
131-
; CHECK-NEXT: i32 2, label [[SW_BB2:%.*]]
132-
; CHECK-NEXT: i32 3, label [[SW_BB3:%.*]]
133-
; CHECK-NEXT: ]
134-
; CHECK: sw.bb1:
135-
; CHECK-NEXT: br label [[SW_EPILOG]]
136-
; CHECK: sw.bb2:
137-
; CHECK-NEXT: br label [[SW_EPILOG]]
138-
; CHECK: sw.bb3:
139-
; CHECK-NEXT: br label [[SW_EPILOG]]
140-
; CHECK: sw.default:
112+
; CHECK-NEXT: [[TMP0:%.*]] = icmp ult i32 [[X:%.*]], 4
113+
; CHECK-NEXT: br i1 [[TMP0]], label [[SWITCH_LOOKUP:%.*]], label [[SW_EPILOG:%.*]]
114+
; CHECK: switch.lookup:
115+
; CHECK-NEXT: [[SWITCH_SHIFTAMT:%.*]] = mul i32 [[X]], 8
116+
; CHECK-NEXT: [[SWITCH_DOWNSHIFT:%.*]] = lshr i32 89655594, [[SWITCH_SHIFTAMT]]
117+
; CHECK-NEXT: [[SWITCH_MASKED:%.*]] = trunc i32 [[SWITCH_DOWNSHIFT]] to i8
118+
; CHECK-NEXT: [[SWITCH_GEP:%.*]] = getelementptr inbounds [4 x float], [4 x float]* @switch.table.h, i32 0, i32 [[X]]
119+
; CHECK-NEXT: [[SWITCH_LOAD:%.*]] = load float, float* [[SWITCH_GEP]], align 4
141120
; CHECK-NEXT: br label [[SW_EPILOG]]
142121
; CHECK: sw.epilog:
143-
; CHECK-NEXT: [[A_0:%.*]] = phi i8 [ 7, [[SW_DEFAULT]] ], [ 5, [[SW_BB3]] ], [ 88, [[SW_BB2]] ], [ 9, [[SW_BB1]] ], [ 42, [[ENTRY:%.*]] ]
144-
; CHECK-NEXT: [[B_0:%.*]] = phi float [ 0x4023FAE140000000, [[SW_DEFAULT]] ], [ 0x4001AE1480000000, [[SW_BB3]] ], [ 0x4012449BA0000000, [[SW_BB2]] ], [ 0x3FF3BE76C0000000, [[SW_BB1]] ], [ 0x40091EB860000000, [[ENTRY]] ]
122+
; CHECK-NEXT: [[A_0:%.*]] = phi i8 [ [[SWITCH_MASKED]], [[SWITCH_LOOKUP]] ], [ 7, [[ENTRY:%.*]] ]
123+
; CHECK-NEXT: [[B_0:%.*]] = phi float [ [[SWITCH_LOAD]], [[SWITCH_LOOKUP]] ], [ 0x4023FAE140000000, [[ENTRY]] ]
145124
; CHECK-NEXT: call void @dummy(i8 signext [[A_0]], float [[B_0]])
146125
; CHECK-NEXT: ret void
147126
;

llvm/test/Transforms/SimplifyCFG/RISCV/switch_to_lookup_table-rv64.ll

Lines changed: 33 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,15 @@
44
target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n64-S128"
55
target triple = "riscv64-unknown-elf"
66

7+
; The table for @f
8+
; CHECK: @switch.table.f = private unnamed_addr constant [7 x i32] [i32 55, i32 123, i32 0, i32 -1, i32 27, i32 62, i32 1], align 4
9+
10+
; The char table for char
11+
; CHECK: @switch.table.char = private unnamed_addr constant [9 x i8] c"7{\00\FF\1B>\01!T", align 1
12+
13+
; The float table for @h
14+
; CHECK: @switch.table.h = private unnamed_addr constant [4 x float] [float 0x40091EB860000000, float 0x3FF3BE76C0000000, float 0x4012449BA0000000, float 0x4001AE1480000000], align 4
15+
716
; The table for @foostring
817
; CHECK: @switch.table.foostring = private unnamed_addr constant [4 x i8*] [i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str, i64 0, i64 0), i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str1, i64 0, i64 0), i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str2, i64 0, i64 0), i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str3, i64 0, i64 0)], align 8
918

@@ -15,31 +24,15 @@ target triple = "riscv64-unknown-elf"
1524
define i32 @f(i32 %c) {
1625
; CHECK-LABEL: @f(
1726
; CHECK-NEXT: entry:
18-
; CHECK-NEXT: switch i32 [[C:%.*]], label [[SW_DEFAULT:%.*]] [
19-
; CHECK-NEXT: i32 42, label [[RETURN:%.*]]
20-
; CHECK-NEXT: i32 43, label [[SW_BB1:%.*]]
21-
; CHECK-NEXT: i32 44, label [[SW_BB2:%.*]]
22-
; CHECK-NEXT: i32 45, label [[SW_BB3:%.*]]
23-
; CHECK-NEXT: i32 46, label [[SW_BB4:%.*]]
24-
; CHECK-NEXT: i32 47, label [[SW_BB5:%.*]]
25-
; CHECK-NEXT: i32 48, label [[SW_BB6:%.*]]
26-
; CHECK-NEXT: ]
27-
; CHECK: sw.bb1:
28-
; CHECK-NEXT: br label [[RETURN]]
29-
; CHECK: sw.bb2:
30-
; CHECK-NEXT: br label [[RETURN]]
31-
; CHECK: sw.bb3:
32-
; CHECK-NEXT: br label [[RETURN]]
33-
; CHECK: sw.bb4:
34-
; CHECK-NEXT: br label [[RETURN]]
35-
; CHECK: sw.bb5:
36-
; CHECK-NEXT: br label [[RETURN]]
37-
; CHECK: sw.bb6:
38-
; CHECK-NEXT: br label [[RETURN]]
39-
; CHECK: sw.default:
27+
; CHECK-NEXT: [[SWITCH_TABLEIDX:%.*]] = sub i32 [[C:%.*]], 42
28+
; CHECK-NEXT: [[TMP0:%.*]] = icmp ult i32 [[SWITCH_TABLEIDX]], 7
29+
; CHECK-NEXT: br i1 [[TMP0]], label [[SWITCH_LOOKUP:%.*]], label [[RETURN:%.*]]
30+
; CHECK: switch.lookup:
31+
; CHECK-NEXT: [[SWITCH_GEP:%.*]] = getelementptr inbounds [7 x i32], [7 x i32]* @switch.table.f, i32 0, i32 [[SWITCH_TABLEIDX]]
32+
; CHECK-NEXT: [[SWITCH_LOAD:%.*]] = load i32, i32* [[SWITCH_GEP]], align 4
4033
; CHECK-NEXT: br label [[RETURN]]
4134
; CHECK: return:
42-
; CHECK-NEXT: [[RETVAL_0:%.*]] = phi i32 [ 15, [[SW_DEFAULT]] ], [ 1, [[SW_BB6]] ], [ 62, [[SW_BB5]] ], [ 27, [[SW_BB4]] ], [ -1, [[SW_BB3]] ], [ 0, [[SW_BB2]] ], [ 123, [[SW_BB1]] ], [ 55, [[ENTRY:%.*]] ]
35+
; CHECK-NEXT: [[RETVAL_0:%.*]] = phi i32 [ [[SWITCH_LOAD]], [[SWITCH_LOOKUP]] ], [ 15, [[ENTRY:%.*]] ]
4336
; CHECK-NEXT: ret i32 [[RETVAL_0]]
4437
;
4538
entry:
@@ -71,37 +64,15 @@ return:
7164
define i8 @char(i32 %c) {
7265
; CHECK-LABEL: @char(
7366
; CHECK-NEXT: entry:
74-
; CHECK-NEXT: switch i32 [[C:%.*]], label [[SW_DEFAULT:%.*]] [
75-
; CHECK-NEXT: i32 42, label [[RETURN:%.*]]
76-
; CHECK-NEXT: i32 43, label [[SW_BB1:%.*]]
77-
; CHECK-NEXT: i32 44, label [[SW_BB2:%.*]]
78-
; CHECK-NEXT: i32 45, label [[SW_BB3:%.*]]
79-
; CHECK-NEXT: i32 46, label [[SW_BB4:%.*]]
80-
; CHECK-NEXT: i32 47, label [[SW_BB5:%.*]]
81-
; CHECK-NEXT: i32 48, label [[SW_BB6:%.*]]
82-
; CHECK-NEXT: i32 49, label [[SW_BB7:%.*]]
83-
; CHECK-NEXT: i32 50, label [[SW_BB8:%.*]]
84-
; CHECK-NEXT: ]
85-
; CHECK: sw.bb1:
86-
; CHECK-NEXT: br label [[RETURN]]
87-
; CHECK: sw.bb2:
88-
; CHECK-NEXT: br label [[RETURN]]
89-
; CHECK: sw.bb3:
90-
; CHECK-NEXT: br label [[RETURN]]
91-
; CHECK: sw.bb4:
92-
; CHECK-NEXT: br label [[RETURN]]
93-
; CHECK: sw.bb5:
94-
; CHECK-NEXT: br label [[RETURN]]
95-
; CHECK: sw.bb6:
96-
; CHECK-NEXT: br label [[RETURN]]
97-
; CHECK: sw.bb7:
98-
; CHECK-NEXT: br label [[RETURN]]
99-
; CHECK: sw.bb8:
100-
; CHECK-NEXT: br label [[RETURN]]
101-
; CHECK: sw.default:
67+
; CHECK-NEXT: [[SWITCH_TABLEIDX:%.*]] = sub i32 [[C:%.*]], 42
68+
; CHECK-NEXT: [[TMP0:%.*]] = icmp ult i32 [[SWITCH_TABLEIDX]], 9
69+
; CHECK-NEXT: br i1 [[TMP0]], label [[SWITCH_LOOKUP:%.*]], label [[RETURN:%.*]]
70+
; CHECK: switch.lookup:
71+
; CHECK-NEXT: [[SWITCH_GEP:%.*]] = getelementptr inbounds [9 x i8], [9 x i8]* @switch.table.char, i32 0, i32 [[SWITCH_TABLEIDX]]
72+
; CHECK-NEXT: [[SWITCH_LOAD:%.*]] = load i8, i8* [[SWITCH_GEP]], align 1
10273
; CHECK-NEXT: br label [[RETURN]]
10374
; CHECK: return:
104-
; CHECK-NEXT: [[RETVAL_0:%.*]] = phi i8 [ 15, [[SW_DEFAULT]] ], [ 84, [[SW_BB8]] ], [ 33, [[SW_BB7]] ], [ 1, [[SW_BB6]] ], [ 62, [[SW_BB5]] ], [ 27, [[SW_BB4]] ], [ -1, [[SW_BB3]] ], [ 0, [[SW_BB2]] ], [ 123, [[SW_BB1]] ], [ 55, [[ENTRY:%.*]] ]
75+
; CHECK-NEXT: [[RETVAL_0:%.*]] = phi i8 [ [[SWITCH_LOAD]], [[SWITCH_LOOKUP]] ], [ 15, [[ENTRY:%.*]] ]
10576
; CHECK-NEXT: ret i8 [[RETVAL_0]]
10677
;
10778
entry:
@@ -138,23 +109,18 @@ declare void @dummy(i8 signext, float)
138109
define void @h(i32 %x) {
139110
; CHECK-LABEL: @h(
140111
; CHECK-NEXT: entry:
141-
; CHECK-NEXT: switch i32 [[X:%.*]], label [[SW_DEFAULT:%.*]] [
142-
; CHECK-NEXT: i32 0, label [[SW_EPILOG:%.*]]
143-
; CHECK-NEXT: i32 1, label [[SW_BB1:%.*]]
144-
; CHECK-NEXT: i32 2, label [[SW_BB2:%.*]]
145-
; CHECK-NEXT: i32 3, label [[SW_BB3:%.*]]
146-
; CHECK-NEXT: ]
147-
; CHECK: sw.bb1:
148-
; CHECK-NEXT: br label [[SW_EPILOG]]
149-
; CHECK: sw.bb2:
150-
; CHECK-NEXT: br label [[SW_EPILOG]]
151-
; CHECK: sw.bb3:
152-
; CHECK-NEXT: br label [[SW_EPILOG]]
153-
; CHECK: sw.default:
112+
; CHECK-NEXT: [[TMP0:%.*]] = icmp ult i32 [[X:%.*]], 4
113+
; CHECK-NEXT: br i1 [[TMP0]], label [[SWITCH_LOOKUP:%.*]], label [[SW_EPILOG:%.*]]
114+
; CHECK: switch.lookup:
115+
; CHECK-NEXT: [[SWITCH_SHIFTAMT:%.*]] = mul i32 [[X]], 8
116+
; CHECK-NEXT: [[SWITCH_DOWNSHIFT:%.*]] = lshr i32 89655594, [[SWITCH_SHIFTAMT]]
117+
; CHECK-NEXT: [[SWITCH_MASKED:%.*]] = trunc i32 [[SWITCH_DOWNSHIFT]] to i8
118+
; CHECK-NEXT: [[SWITCH_GEP:%.*]] = getelementptr inbounds [4 x float], [4 x float]* @switch.table.h, i32 0, i32 [[X]]
119+
; CHECK-NEXT: [[SWITCH_LOAD:%.*]] = load float, float* [[SWITCH_GEP]], align 4
154120
; CHECK-NEXT: br label [[SW_EPILOG]]
155121
; CHECK: sw.epilog:
156-
; CHECK-NEXT: [[A_0:%.*]] = phi i8 [ 7, [[SW_DEFAULT]] ], [ 5, [[SW_BB3]] ], [ 88, [[SW_BB2]] ], [ 9, [[SW_BB1]] ], [ 42, [[ENTRY:%.*]] ]
157-
; CHECK-NEXT: [[B_0:%.*]] = phi float [ 0x4023FAE140000000, [[SW_DEFAULT]] ], [ 0x4001AE1480000000, [[SW_BB3]] ], [ 0x4012449BA0000000, [[SW_BB2]] ], [ 0x3FF3BE76C0000000, [[SW_BB1]] ], [ 0x40091EB860000000, [[ENTRY]] ]
122+
; CHECK-NEXT: [[A_0:%.*]] = phi i8 [ [[SWITCH_MASKED]], [[SWITCH_LOOKUP]] ], [ 7, [[ENTRY:%.*]] ]
123+
; CHECK-NEXT: [[B_0:%.*]] = phi float [ [[SWITCH_LOAD]], [[SWITCH_LOOKUP]] ], [ 0x4023FAE140000000, [[ENTRY]] ]
158124
; CHECK-NEXT: call void @dummy(i8 signext [[A_0]], float [[B_0]])
159125
; CHECK-NEXT: ret void
160126
;

0 commit comments

Comments
 (0)