Skip to content

Commit 602e2e7

Browse files
committed
[IRGen] Use condbrs for big int switches.
Previously, switches over extremely large integers (e.g. i832) were emitted when emitting switches with many spare bits. Such switches result in unfortunate downstream codegen. Here, for large enums (currently, more than two words) the preexisting EnumPayload::emitCompare function is used to compare each of the enum cases in turn with the payload's value. The result is a series of cond_br where the conditional is made by anding together word-size chunks of the payload value with word-size chunks of each enum case's tag, subject to masking. rdar://83158525
1 parent 2d370fb commit 602e2e7

File tree

3 files changed

+142
-0
lines changed

3 files changed

+142
-0
lines changed

lib/IRGen/EnumPayload.cpp

+18
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,24 @@ void EnumPayload::emitSwitch(IRGenFunction &IGF,
285285
return;
286286
}
287287

288+
if (mask.getBitWidth() > IGF.IGM.getPointerSize().getValueInBits() * 2) {
289+
for (int index = 0, size = cases.size(); index < size; ++index) {
290+
auto &c = cases[index];
291+
auto *cmp = emitCompare(IGF, mask, c.first);
292+
llvm::BasicBlock *elseBlock;
293+
if (index < size - 1) {
294+
elseBlock = IGF.createBasicBlock("");
295+
} else {
296+
elseBlock = dflt.getPointer();
297+
}
298+
IGF.Builder.CreateCondBr(cmp, c.second, elseBlock);
299+
if (index < size - 1) {
300+
IGF.Builder.emitBlock(elseBlock);
301+
}
302+
}
303+
return;
304+
}
305+
288306
// Otherwise emit a switch statement.
289307
auto &C = IGF.IGM.getLLVMContext();
290308
unsigned numBits = mask.countPopulation();

test/IRGen/enum_large.swift

+61
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
// RUN: %swift-target-frontend -emit-irgen %s | %FileCheck %s
2+
3+
// REQUIRES: PTRSIZE=64
4+
5+
public struct SpareBits {
6+
var o: UInt64 = 0
7+
var x: UInt8 = 0
8+
var y: UInt64 = 0
9+
var x_2: UInt8 = 0
10+
var y_2: UInt64 = 0
11+
var x_3: UInt8 = 0
12+
var y_3: UInt64 = 0
13+
var x_4: UInt8 = 0
14+
var y_4: UInt64 = 0
15+
var x_5: UInt8 = 0
16+
var y_5: UInt64 = 0
17+
var x_6: UInt8 = 0
18+
var y_6: UInt64 = 0
19+
}
20+
21+
public class MyClass {}
22+
23+
public enum Multipayload {
24+
case a
25+
case b(MyClass)
26+
case c(SpareBits)
27+
case e
28+
case f
29+
case g
30+
}
31+
32+
@inline(never)
33+
func dump<T>(_ t : T) {}
34+
func dumpInt(_ i: Int) {}
35+
36+
// CHECK-LABEL: define{{.*}} @"$s10enum_large6testIt1eyAA12MultipayloadO_tF"{{.*}}{
37+
// CHECK: switch i8 {{%[^,]+}}, label {{%[^,]+}} [
38+
// CHECK: i8 0, label {{%[^,]+}}
39+
// CHECK: i8 1, label {{%[^,]+}}
40+
// CHECK: i8 2, label %[[REGISTER_41:[^,]+]]
41+
// CHECK: [[REGISTER_41]]:
42+
// CHECK: br i1 {{%[^,]+}}, label {{%[^,]+}}, label %[[REGISTER_67:[^,]+]]
43+
// CHECK: [[REGISTER_67]]:
44+
// CHECK: br i1 {{%[^,]+}}, label {{%[^,]+}}, label %[[REGISTER_93:[^,]+]]
45+
// CHECK: [[REGISTER_93]]:
46+
// CHECK: br i1 {{%[^,]+}}, label {{%[^,]+}}, label %[[REGISTER_119:[^,]+]]
47+
// CHECK: [[REGISTER_119]]:
48+
// CHECK: br i1 {{%[^,]+}}, label %[[REGISTER_149:[^,]+]], label {{%[^,]+}}
49+
// CHECK: [[REGISTER_149]]
50+
// CHECK: call swiftcc void @"$s10enum_large7dumpIntyySiF"(i64 8675309)
51+
public func testIt(e : Multipayload) {
52+
switch e {
53+
case .a, .e, .f, .g:
54+
dumpInt(8675309)
55+
case .b(let c):
56+
dump(c)
57+
case .c(let s):
58+
dump(s)
59+
}
60+
}
61+
+63
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
// RUN: %target-run-simple-swift | %FileCheck %s
2+
// REQUIRES: executable_test
3+
4+
public struct SpareBits {
5+
var o: UInt64 = 0
6+
var x: UInt8 = 0
7+
var y: UInt64 = 0
8+
var x_2: UInt8 = 0
9+
var y_2: UInt64 = 0
10+
var x_3: UInt8 = 0
11+
var y_3: UInt64 = 0
12+
var x_4: UInt8 = 0
13+
var y_4: UInt64 = 0
14+
var x_5: UInt8 = 0
15+
var y_5: UInt64 = 0
16+
var x_6: UInt8 = 0
17+
var y_6: UInt64 = 0
18+
}
19+
20+
public class MyClass {}
21+
22+
public enum Multipayload {
23+
case a
24+
case b(MyClass)
25+
case c(SpareBits)
26+
case e
27+
case f
28+
case g
29+
}
30+
31+
public func testIt(_ e : Multipayload) {
32+
switch e {
33+
case .a:
34+
print(".a (no payload)")
35+
case .e:
36+
print(".e (no payload)")
37+
case .f:
38+
print(".f (no payload)")
39+
case .g:
40+
print(".g (no payload)")
41+
case .b(let s):
42+
print(".b(\(s))")
43+
case .c(let x):
44+
print(".c(\(x))")
45+
}
46+
}
47+
48+
func doit() {
49+
testIt(Multipayload.a)
50+
testIt(Multipayload.e)
51+
testIt(Multipayload.f)
52+
testIt(Multipayload.g)
53+
testIt(Multipayload.b(MyClass()))
54+
testIt(Multipayload.c(SpareBits()))
55+
}
56+
doit()
57+
58+
// CHECK: .a (no payload)
59+
// CHECK: .e (no payload)
60+
// CHECK: .f (no payload)
61+
// CHECK: .g (no payload)
62+
// CHECK: .b(main.MyClass)
63+
// CHECK: .c(SpareBits(o: 0, x: 0, y: 0, x_2: 0, y_2: 0, x_3: 0, y_3: 0, x_4: 0, y_4: 0, x_5: 0, y_5: 0, x_6: 0, y_6: 0))

0 commit comments

Comments
 (0)