Skip to content

Commit cea738b

Browse files
DaMatrixdianqk
andauthored
[SimplifyCFG] Replace unreachable switch lookup table holes with poison (#94990)
As discussed in #94468, this causes switch lookup table entries which are unreachable to be poison instead of filling them with a value from one of the reachable cases. --------- Co-authored-by: DianQK <dianqk@dianqk.net>
1 parent d6e4353 commit cea738b

File tree

3 files changed

+254
-13
lines changed

3 files changed

+254
-13
lines changed

llvm/lib/Transforms/Utils/SimplifyCFG.cpp

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6531,8 +6531,8 @@ SwitchLookupTable::SwitchLookupTable(
65316531
uint64_t Idx = (CaseVal->getValue() - Offset->getValue()).getLimitedValue();
65326532
TableContents[Idx] = CaseRes;
65336533

6534-
if (CaseRes != SingleValue)
6535-
SingleValue = nullptr;
6534+
if (SingleValue && !isa<PoisonValue>(CaseRes) && CaseRes != SingleValue)
6535+
SingleValue = isa<PoisonValue>(SingleValue) ? CaseRes : nullptr;
65366536
}
65376537

65386538
// Fill in any holes in the table with the default result.
@@ -6545,7 +6545,10 @@ SwitchLookupTable::SwitchLookupTable(
65456545
TableContents[I] = DefaultValue;
65466546
}
65476547

6548-
if (DefaultValue != SingleValue)
6548+
// If the default value is poison, all the holes are poison.
6549+
bool DefaultValueIsPoison = isa<PoisonValue>(DefaultValue);
6550+
6551+
if (DefaultValue != SingleValue && !DefaultValueIsPoison)
65496552
SingleValue = nullptr;
65506553
}
65516554

@@ -6569,6 +6572,16 @@ SwitchLookupTable::SwitchLookupTable(
65696572
// Check if there is the same distance between two consecutive values.
65706573
for (uint64_t I = 0; I < TableSize; ++I) {
65716574
ConstantInt *ConstVal = dyn_cast<ConstantInt>(TableContents[I]);
6575+
6576+
if (!ConstVal && isa<PoisonValue>(TableContents[I])) {
6577+
// This is an poison, so it's (probably) a lookup table hole.
6578+
// To prevent any regressions from before we switched to using poison as
6579+
// the default value, holes will fall back to using the first value.
6580+
// This can be removed once we add proper handling for poisons in lookup
6581+
// tables.
6582+
ConstVal = dyn_cast<ConstantInt>(Values[0].second);
6583+
}
6584+
65726585
if (!ConstVal) {
65736586
// This is an undef. We could deal with it, but undefs in lookup tables
65746587
// are very seldom. It's probably not worth the additional complexity.
@@ -7003,16 +7016,16 @@ static bool switchToLookupTable(SwitchInst *SI, IRBuilder<> &Builder,
70037016

70047017
// If the table has holes but the default destination doesn't produce any
70057018
// constant results, the lookup table entries corresponding to the holes will
7006-
// contain undefined values.
7007-
bool AllHolesAreUndefined = TableHasHoles && !HasDefaultResults;
7019+
// contain poison.
7020+
bool AllHolesArePoison = TableHasHoles && !HasDefaultResults;
70087021

70097022
// If the default destination doesn't produce a constant result but is still
70107023
// reachable, and the lookup table has holes, we need to use a mask to
70117024
// determine if the current index should load from the lookup table or jump
70127025
// to the default case.
70137026
// The mask is unnecessary if the table has holes but the default destination
70147027
// is unreachable, as in that case the holes must also be unreachable.
7015-
bool NeedMask = AllHolesAreUndefined && DefaultIsReachable;
7028+
bool NeedMask = AllHolesArePoison && DefaultIsReachable;
70167029
if (NeedMask) {
70177030
// As an extra penalty for the validity test we require more cases.
70187031
if (SI->getNumCases() < 4) // FIXME: Find best threshold value (benchmark).
@@ -7157,9 +7170,11 @@ static bool switchToLookupTable(SwitchInst *SI, IRBuilder<> &Builder,
71577170
for (PHINode *PHI : PHIs) {
71587171
const ResultListTy &ResultList = ResultLists[PHI];
71597172

7173+
Type *ResultType = ResultList.begin()->second->getType();
7174+
71607175
// Use any value to fill the lookup table holes.
71617176
Constant *DV =
7162-
AllHolesAreUndefined ? ResultLists[PHI][0].second : DefaultResults[PHI];
7177+
AllHolesArePoison ? PoisonValue::get(ResultType) : DefaultResults[PHI];
71637178
StringRef FuncName = Fn->getName();
71647179
SwitchLookupTable Table(Mod, TableSize, TableIndexOffset, ResultList, DV,
71657180
DL, FuncName);

llvm/test/Transforms/SimplifyCFG/X86/switch_to_lookup_table.ll

Lines changed: 229 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,14 @@ target triple = "x86_64-unknown-linux-gnu"
3434
; CHECK: @switch.table.unreachable_case = private unnamed_addr constant [9 x i32] [i32 0, i32 0, i32 0, i32 2, i32 -1, i32 1, i32 1, i32 1, i32 1], align 4
3535
; CHECK: @switch.table.unreachable_default = private unnamed_addr constant [4 x i32] [i32 42, i32 52, i32 1, i32 2], align 4
3636
; CHECK: @switch.table.nodefaultnoholes = private unnamed_addr constant [4 x i32] [i32 55, i32 123, i32 0, i32 -1], align 4
37-
; CHECK: @switch.table.nodefaultwithholes = private unnamed_addr constant [6 x i32] [i32 55, i32 123, i32 0, i32 -1, i32 55, i32 -1], align 4
37+
; CHECK: @switch.table.nodefaultwithholes = private unnamed_addr constant [6 x i32] [i32 55, i32 123, i32 0, i32 -1, i32 poison, i32 -1], align 4
3838
; CHECK: @switch.table.threecases = private unnamed_addr constant [3 x i32] [i32 10, i32 7, i32 5], align 4
39-
; CHECK: @switch.table.covered_switch_with_bit_tests = private unnamed_addr constant [8 x i32] [i32 2, i32 2, i32 2, i32 2, i32 2, i32 2, i32 1, i32 1], align 4
39+
; CHECK: @switch.table.covered_switch_with_bit_tests = private unnamed_addr constant [8 x i32] [i32 2, i32 2, i32 poison, i32 poison, i32 poison, i32 poison, i32 1, i32 1], align 4
4040
; CHECK: @switch.table.signed_overflow1 = private unnamed_addr constant [4 x i32] [i32 3333, i32 4444, i32 1111, i32 2222], align 4
41-
; CHECK: @switch.table.signed_overflow2 = private unnamed_addr constant [4 x i32] [i32 3333, i32 4444, i32 2222, i32 2222], align 4
41+
; CHECK: @switch.table.signed_overflow2 = private unnamed_addr constant [4 x i32] [i32 3333, i32 4444, i32 poison, i32 2222], align 4
42+
; CHECK: @switch.table.constant_hole_unreachable_default_firstundef = private unnamed_addr constant [5 x i32] [i32 undef, i32 poison, i32 1, i32 1, i32 1], align 4
43+
; CHECK: @switch.table.constant_hole_unreachable_default_lastundef = private unnamed_addr constant [5 x i32] [i32 1, i32 poison, i32 1, i32 1, i32 undef], align 4
44+
; CHECK: @switch.table.linearmap_hole_unreachable_default = private unnamed_addr constant [5 x i32] [i32 1, i32 poison, i32 5, i32 7, i32 9], align 4
4245
;.
4346
define i32 @f(i32 %c) {
4447
; CHECK-LABEL: @f(
@@ -2184,3 +2187,226 @@ return: ; preds = %sw.default, %entry,
21842187
%retval.0 = phi { i8, i8 } [ undef, %entry ], [ undef, %entry ], [ undef, %entry ], [ %1, %sw.default ]
21852188
ret { i8, i8 } %retval.0
21862189
}
2190+
2191+
; The switch has a hole which falls through to an unreachable default case, but it can still be optimized into a constant load because
2192+
; the poison value used for the hole is ignored.
2193+
define i32 @constant_hole_unreachable_default(i32 %x) {
2194+
; CHECK-LABEL: @constant_hole_unreachable_default(
2195+
; CHECK-NEXT: entry:
2196+
; CHECK-NEXT: ret i32 1
2197+
;
2198+
entry:
2199+
switch i32 %x, label %sw.default [
2200+
i32 0, label %bb0
2201+
i32 2, label %bb0
2202+
i32 3, label %bb0
2203+
i32 4, label %bb0
2204+
]
2205+
2206+
sw.default: unreachable
2207+
bb0: br label %return
2208+
2209+
return:
2210+
%res = phi i32 [ 1, %bb0 ]
2211+
ret i32 %res
2212+
}
2213+
2214+
; The switch has a hole which falls through to an unreachable default case and the first case explicitly returns undef, yet it cannot be optimized into a simple
2215+
; constant because we actually treat undef as a unique value rather than ignoring it.
2216+
define i32 @constant_hole_unreachable_default_firstundef(i32 %x) {
2217+
; CHECK-LABEL: @constant_hole_unreachable_default_firstundef(
2218+
; CHECK-NEXT: entry:
2219+
; CHECK-NEXT: [[SWITCH_GEP:%.*]] = getelementptr inbounds [5 x i32], ptr @switch.table.constant_hole_unreachable_default_firstundef, i32 0, i32 [[X:%.*]]
2220+
; CHECK-NEXT: [[SWITCH_LOAD:%.*]] = load i32, ptr [[SWITCH_GEP]], align 4
2221+
; CHECK-NEXT: ret i32 [[SWITCH_LOAD]]
2222+
;
2223+
entry:
2224+
switch i32 %x, label %sw.default [
2225+
i32 0, label %bb.undef
2226+
i32 2, label %bb0
2227+
i32 3, label %bb0
2228+
i32 4, label %bb0
2229+
]
2230+
2231+
sw.default: unreachable
2232+
bb.undef: br label %return
2233+
bb0: br label %return
2234+
2235+
return:
2236+
%res = phi i32 [ undef, %bb.undef ], [ 1, %bb0 ]
2237+
ret i32 %res
2238+
}
2239+
2240+
; The switch has a hole which falls through to an unreachable default case and the last case explicitly returns undef, yet it cannot be optimized into a simple
2241+
; constant because we actually treat undef as a unique value rather than ignoring it.
2242+
define i32 @constant_hole_unreachable_default_lastundef(i32 %x) {
2243+
; CHECK-LABEL: @constant_hole_unreachable_default_lastundef(
2244+
; CHECK-NEXT: entry:
2245+
; CHECK-NEXT: [[SWITCH_GEP:%.*]] = getelementptr inbounds [5 x i32], ptr @switch.table.constant_hole_unreachable_default_lastundef, i32 0, i32 [[X:%.*]]
2246+
; CHECK-NEXT: [[SWITCH_LOAD:%.*]] = load i32, ptr [[SWITCH_GEP]], align 4
2247+
; CHECK-NEXT: ret i32 [[SWITCH_LOAD]]
2248+
;
2249+
entry:
2250+
switch i32 %x, label %sw.default [
2251+
i32 0, label %bb0
2252+
i32 2, label %bb0
2253+
i32 3, label %bb0
2254+
i32 4, label %bb.undef
2255+
]
2256+
2257+
sw.default: unreachable
2258+
bb.undef: br label %return
2259+
bb0: br label %return
2260+
2261+
return:
2262+
%res = phi i32 [ undef, %bb.undef ], [ 1, %bb0 ]
2263+
ret i32 %res
2264+
}
2265+
2266+
; The switch has a hole which falls through to an unreachable default case and the first case explicitly returns poison, but it can still
2267+
; be optimized into a constant load because the poison values are ignored.
2268+
define i32 @constant_hole_unreachable_default_firstpoison(i32 %x) {
2269+
; CHECK-LABEL: @constant_hole_unreachable_default_firstpoison(
2270+
; CHECK-NEXT: entry:
2271+
; CHECK-NEXT: ret i32 1
2272+
;
2273+
entry:
2274+
switch i32 %x, label %sw.default [
2275+
i32 0, label %bb.poison
2276+
i32 2, label %bb0
2277+
i32 3, label %bb0
2278+
i32 4, label %bb0
2279+
]
2280+
2281+
sw.default: unreachable
2282+
bb.poison: br label %return
2283+
bb0: br label %return
2284+
2285+
return:
2286+
%res = phi i32 [ poison, %bb.poison ], [ 1, %bb0 ]
2287+
ret i32 %res
2288+
}
2289+
2290+
; The switch has a hole which falls through to an unreachable default case and the first case explicitly returns poison, but it can still
2291+
; be optimized into a constant load because the poison values are ignored.
2292+
define i32 @constant_hole_unreachable_default_lastpoison(i32 %x) {
2293+
; CHECK-LABEL: @constant_hole_unreachable_default_lastpoison(
2294+
; CHECK-NEXT: entry:
2295+
; CHECK-NEXT: ret i32 1
2296+
;
2297+
entry:
2298+
switch i32 %x, label %sw.default [
2299+
i32 0, label %bb0
2300+
i32 2, label %bb0
2301+
i32 3, label %bb0
2302+
i32 4, label %bb.poison
2303+
]
2304+
2305+
sw.default: unreachable
2306+
bb.poison: br label %return
2307+
bb0: br label %return
2308+
2309+
return:
2310+
%res = phi i32 [ poison, %bb.poison ], [ 1, %bb0 ]
2311+
ret i32 %res
2312+
}
2313+
2314+
define i32 @constant_hole_unreachable_default_undef_poison(i32 %x) {
2315+
; CHECK-LABEL: @constant_hole_unreachable_default_undef_poison(
2316+
; CHECK-NEXT: entry:
2317+
; CHECK-NEXT: ret i32 undef
2318+
;
2319+
entry:
2320+
switch i32 %x, label %sw.default [
2321+
i32 0, label %bb.undef
2322+
i32 2, label %bb.poison
2323+
i32 3, label %bb.poison
2324+
i32 4, label %bb.poison
2325+
]
2326+
2327+
sw.default: unreachable
2328+
bb.undef: br label %return
2329+
bb.poison: br label %return
2330+
2331+
return:
2332+
%res = phi i32 [ undef, %bb.undef ], [ poison, %bb.poison ]
2333+
ret i32 %res
2334+
}
2335+
2336+
define i32 @constant_hole_unreachable_default_poison_undef(i32 %x) {
2337+
; CHECK-LABEL: @constant_hole_unreachable_default_poison_undef(
2338+
; CHECK-NEXT: entry:
2339+
; CHECK-NEXT: ret i32 undef
2340+
;
2341+
entry:
2342+
switch i32 %x, label %sw.default [
2343+
i32 0, label %bb.poison
2344+
i32 2, label %bb.poison
2345+
i32 3, label %bb.poison
2346+
i32 4, label %bb.undef
2347+
]
2348+
2349+
sw.default: unreachable
2350+
bb.undef: br label %return
2351+
bb.poison: br label %return
2352+
2353+
return:
2354+
%res = phi i32 [ undef, %bb.undef ], [ poison, %bb.poison ]
2355+
ret i32 %res
2356+
}
2357+
2358+
; The switch has a hole which falls through to an unreachable default case, which prevents it from being optimized into a linear mapping 2*x+1.
2359+
; TODO: We should add support for this, at least in certain cases.
2360+
define i32 @linearmap_hole_unreachable_default(i32 %x) {
2361+
; CHECK-LABEL: @linearmap_hole_unreachable_default(
2362+
; CHECK-NEXT: entry:
2363+
; CHECK-NEXT: [[SWITCH_GEP:%.*]] = getelementptr inbounds [5 x i32], ptr @switch.table.linearmap_hole_unreachable_default, i32 0, i32 [[X:%.*]]
2364+
; CHECK-NEXT: [[SWITCH_LOAD:%.*]] = load i32, ptr [[SWITCH_GEP]], align 4
2365+
; CHECK-NEXT: ret i32 [[SWITCH_LOAD]]
2366+
;
2367+
entry:
2368+
switch i32 %x, label %sw.default [
2369+
i32 0, label %bb0
2370+
i32 2, label %bb2
2371+
i32 3, label %bb3
2372+
i32 4, label %bb4
2373+
]
2374+
2375+
sw.default: unreachable
2376+
bb0: br label %return
2377+
bb2: br label %return
2378+
bb3: br label %return
2379+
bb4: br label %return
2380+
2381+
return:
2382+
%res = phi i32 [ 1, %bb0 ], [ 5, %bb2 ], [ 7, %bb3 ], [ 9, %bb4 ]
2383+
ret i32 %res
2384+
}
2385+
2386+
; The switch has a hole which falls through to an unreachable default case, but it can still be optimized into a bitmask extraction because
2387+
; the poison value used for the hole is simply replaced with zero.
2388+
define i1 @bitset_hole_unreachable_default(i32 %x) {
2389+
; CHECK-LABEL: @bitset_hole_unreachable_default(
2390+
; CHECK-NEXT: entry:
2391+
; CHECK-NEXT: [[SWITCH_CAST:%.*]] = trunc i32 [[X:%.*]] to i5
2392+
; CHECK-NEXT: [[SWITCH_SHIFTAMT:%.*]] = mul nuw nsw i5 [[SWITCH_CAST]], 1
2393+
; CHECK-NEXT: [[SWITCH_DOWNSHIFT:%.*]] = lshr i5 8, [[SWITCH_SHIFTAMT]]
2394+
; CHECK-NEXT: [[SWITCH_MASKED:%.*]] = trunc i5 [[SWITCH_DOWNSHIFT]] to i1
2395+
; CHECK-NEXT: ret i1 [[SWITCH_MASKED]]
2396+
;
2397+
entry:
2398+
switch i32 %x, label %sw.default [
2399+
i32 0, label %bb0
2400+
i32 2, label %bb0
2401+
i32 3, label %bb1
2402+
i32 4, label %bb0
2403+
]
2404+
2405+
sw.default: unreachable
2406+
bb0: br label %return
2407+
bb1: br label %return
2408+
2409+
return:
2410+
%res = phi i1 [ 0, %bb0 ], [ 1, %bb1 ]
2411+
ret i1 %res
2412+
}

llvm/test/Transforms/SimplifyCFG/X86/switch_to_lookup_table_big.ll

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,11 @@ target triple = "i386-pc-linux-gnu"
77
;.
88
; CHECK: @switch.table.reachable_default_dense_0to31 = private unnamed_addr constant [32 x i32] [i32 0, i32 7, i32 6, i32 5, i32 4, i32 3, i32 2, i32 1, i32 0, i32 7, i32 6, i32 5, i32 4, i32 3, i32 2, i32 1, i32 0, i32 7, i32 6, i32 5, i32 4, i32 3, i32 2, i32 1, i32 0, i32 7, i32 6, i32 5, i32 4, i32 3, i32 2, i32 1], align 4
99
; CHECK: @switch.table.unreachable_default_dense_0to31 = private unnamed_addr constant [32 x i32] [i32 0, i32 7, i32 6, i32 5, i32 4, i32 3, i32 2, i32 1, i32 0, i32 7, i32 6, i32 5, i32 4, i32 3, i32 2, i32 1, i32 0, i32 7, i32 6, i32 5, i32 4, i32 3, i32 2, i32 1, i32 0, i32 7, i32 6, i32 5, i32 4, i32 3, i32 2, i32 1], align 4
10-
; CHECK: @switch.table.reachable_default_holes_0to31 = private unnamed_addr constant [32 x i32] [i32 0, i32 7, i32 6, i32 0, i32 4, i32 3, i32 2, i32 1, i32 0, i32 7, i32 6, i32 5, i32 4, i32 0, i32 2, i32 1, i32 0, i32 7, i32 0, i32 5, i32 4, i32 3, i32 2, i32 0, i32 0, i32 7, i32 6, i32 5, i32 0, i32 3, i32 2, i32 1], align 4
11-
; CHECK: @switch.table.unreachable_default_holes_0to31 = private unnamed_addr constant [32 x i32] [i32 0, i32 7, i32 6, i32 0, i32 4, i32 3, i32 2, i32 1, i32 0, i32 7, i32 6, i32 5, i32 4, i32 0, i32 2, i32 1, i32 0, i32 7, i32 0, i32 5, i32 4, i32 3, i32 2, i32 0, i32 0, i32 7, i32 6, i32 5, i32 0, i32 3, i32 2, i32 1], align 4
10+
; CHECK: @switch.table.reachable_default_holes_0to31 = private unnamed_addr constant [32 x i32] [i32 0, i32 7, i32 6, i32 poison, i32 4, i32 3, i32 2, i32 1, i32 poison, i32 7, i32 6, i32 5, i32 4, i32 poison, i32 2, i32 1, i32 0, i32 7, i32 poison, i32 5, i32 4, i32 3, i32 2, i32 poison, i32 0, i32 7, i32 6, i32 5, i32 poison, i32 3, i32 2, i32 1], align 4
11+
; CHECK: @switch.table.unreachable_default_holes_0to31 = private unnamed_addr constant [32 x i32] [i32 0, i32 7, i32 6, i32 poison, i32 4, i32 3, i32 2, i32 1, i32 poison, i32 7, i32 6, i32 5, i32 4, i32 poison, i32 2, i32 1, i32 0, i32 7, i32 poison, i32 5, i32 4, i32 3, i32 2, i32 poison, i32 0, i32 7, i32 6, i32 5, i32 poison, i32 3, i32 2, i32 1], align 4
1212
; CHECK: @switch.table.reachable_default_dense_0to32 = private unnamed_addr constant [33 x i32] [i32 0, i32 7, i32 6, i32 5, i32 4, i32 3, i32 2, i32 1, i32 0, i32 7, i32 6, i32 5, i32 4, i32 3, i32 2, i32 1, i32 0, i32 7, i32 6, i32 5, i32 4, i32 3, i32 2, i32 1, i32 0, i32 7, i32 6, i32 5, i32 4, i32 3, i32 2, i32 1, i32 0], align 4
1313
; CHECK: @switch.table.unreachable_default_dense_0to32 = private unnamed_addr constant [33 x i32] [i32 0, i32 7, i32 6, i32 5, i32 4, i32 3, i32 2, i32 1, i32 0, i32 7, i32 6, i32 5, i32 4, i32 3, i32 2, i32 1, i32 0, i32 7, i32 6, i32 5, i32 4, i32 3, i32 2, i32 1, i32 0, i32 7, i32 6, i32 5, i32 4, i32 3, i32 2, i32 1, i32 0], align 4
14-
; CHECK: @switch.table.unreachable_default_holes_0to32 = private unnamed_addr constant [33 x i32] [i32 0, i32 7, i32 6, i32 0, i32 4, i32 3, i32 2, i32 1, i32 0, i32 7, i32 6, i32 5, i32 4, i32 0, i32 2, i32 1, i32 0, i32 7, i32 0, i32 5, i32 4, i32 3, i32 2, i32 0, i32 0, i32 7, i32 6, i32 5, i32 0, i32 3, i32 2, i32 1, i32 0], align 4
14+
; CHECK: @switch.table.unreachable_default_holes_0to32 = private unnamed_addr constant [33 x i32] [i32 0, i32 7, i32 6, i32 poison, i32 4, i32 3, i32 2, i32 1, i32 poison, i32 7, i32 6, i32 5, i32 4, i32 poison, i32 2, i32 1, i32 0, i32 7, i32 poison, i32 5, i32 4, i32 3, i32 2, i32 poison, i32 0, i32 7, i32 6, i32 5, i32 poison, i32 3, i32 2, i32 1, i32 0], align 4
1515
;.
1616
define i32 @reachable_default_dense_0to31(i32 %x, i32 %y) {
1717
; CHECK-LABEL: @reachable_default_dense_0to31(

0 commit comments

Comments
 (0)