Skip to content

Commit c19f504

Browse files
author
ematejska
authored
Merge pull request #4153 from swiftix/swift-3.0-branch
[sil-combine] Fix a bug in the cond_br (select_enum) -> switch_enum peephole
2 parents 1534409 + 4b48718 commit c19f504

File tree

2 files changed

+187
-15
lines changed

2 files changed

+187
-15
lines changed

lib/SILOptimizer/SILCombiner/SILCombinerMiscVisitors.cpp

Lines changed: 60 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -997,35 +997,80 @@ SILInstruction *SILCombiner::visitCondBranchInst(CondBranchInst *CBI) {
997997
if (!CBI->getFalseBB()->getSinglePredecessor())
998998
return nullptr;
999999

1000-
SILBasicBlock *Default = nullptr;
1001-
1000+
SILBasicBlock *DefaultBB = nullptr;
10021001
match_integer<0> Zero;
10031002

10041003
if (SEI->hasDefault()) {
1004+
// Default result should be an integer constant.
1005+
if (!isa<IntegerLiteralInst>(SEI->getDefaultResult()))
1006+
return nullptr;
10051007
bool isFalse = match(SEI->getDefaultResult(), Zero);
1006-
Default = isFalse ? CBI->getFalseBB() : Default = CBI->getTrueBB();
1008+
// Pick the default BB.
1009+
DefaultBB = isFalse ? CBI->getFalseBB() : CBI->getTrueBB();
1010+
}
1011+
1012+
if (!DefaultBB) {
1013+
// Find the targets for the majority of cases and pick it
1014+
// as a default BB.
1015+
unsigned TrueBBCases = 0;
1016+
unsigned FalseBBCases = 0;
1017+
for (int i = 0, e = SEI->getNumCases(); i < e; ++i) {
1018+
auto Pair = SEI->getCase(i);
1019+
if (isa<IntegerLiteralInst>(Pair.second)) {
1020+
bool isFalse = match(Pair.second, Zero);
1021+
if (!isFalse) {
1022+
TrueBBCases++;
1023+
} else {
1024+
FalseBBCases++;
1025+
}
1026+
continue;
1027+
}
1028+
return nullptr;
1029+
}
1030+
1031+
if (FalseBBCases > TrueBBCases)
1032+
DefaultBB = CBI->getFalseBB();
1033+
else
1034+
DefaultBB = CBI->getTrueBB();
10071035
}
10081036

1009-
// We can now convert cond_br(select_enum) into switch_enum
1037+
assert(DefaultBB && "Default should be defined at this point");
1038+
1039+
unsigned NumTrueBBCases = 0;
1040+
unsigned NumFalseBBCases = 0;
1041+
1042+
if (DefaultBB == CBI->getFalseBB())
1043+
NumFalseBBCases++;
1044+
else
1045+
NumTrueBBCases++;
1046+
1047+
// We can now convert cond_br(select_enum) into switch_enum.
10101048
SmallVector<std::pair<EnumElementDecl *, SILBasicBlock *>, 8> Cases;
10111049
for (int i = 0, e = SEI->getNumCases(); i < e; ++i) {
10121050
auto Pair = SEI->getCase(i);
1013-
if (isa<IntegerLiteralInst>(Pair.second)) {
1014-
bool isFalse = match(Pair.second, Zero);
1015-
if (!isFalse && Default != CBI->getTrueBB()) {
1016-
Cases.push_back(std::make_pair(Pair.first, CBI->getTrueBB()));
1017-
}
1018-
if (isFalse && Default != CBI->getFalseBB()) {
1019-
Cases.push_back(std::make_pair(Pair.first, CBI->getFalseBB()));
1020-
}
1021-
continue;
1051+
1052+
// Bail if one of the results is not an integer constant.
1053+
if (!isa<IntegerLiteralInst>(Pair.second))
1054+
return nullptr;
1055+
1056+
// Add a switch case.
1057+
bool isFalse = match(Pair.second, Zero);
1058+
if (!isFalse && DefaultBB != CBI->getTrueBB()) {
1059+
Cases.push_back(std::make_pair(Pair.first, CBI->getTrueBB()));
1060+
NumTrueBBCases++;
1061+
}
1062+
if (isFalse && DefaultBB != CBI->getFalseBB()) {
1063+
Cases.push_back(std::make_pair(Pair.first, CBI->getFalseBB()));
1064+
NumFalseBBCases++;
10221065
}
1066+
}
10231067

1068+
// Bail if a switch_enum would introduce a critical edge.
1069+
if (NumTrueBBCases > 1 || NumFalseBBCases > 1)
10241070
return nullptr;
1025-
}
10261071

10271072
return Builder.createSwitchEnum(SEI->getLoc(), SEI->getEnumOperand(),
1028-
Default, Cases);
1073+
DefaultBB, Cases);
10291074
}
10301075

10311076
return nullptr;

test/SILOptimizer/sil_combine_enums.sil

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,13 @@ class SomeClass {
99
func hash() -> Int
1010
}
1111

12+
enum Numerals {
13+
case One
14+
case Two
15+
case Three
16+
case Four
17+
}
18+
1219
sil @external_func: $@convention(thin) () -> ()
1320

1421
//CHECK-LABEL: eliminate_sw_enum_addr
@@ -177,6 +184,68 @@ bb2:
177184
unreachable
178185
}
179186

187+
// Check that cond_br(select_enum) is converted into switch_enum.
188+
// CHECK-LABEL: sil @convert_select_enum_cond_br_to_switch_enum2
189+
// CHECK: bb0
190+
// CHECK-NOT: select_enum
191+
// CHECK-NOT: return
192+
// CHECK: switch_enum %0 : $Numerals, case #Numerals.Two!enumelt: bb3, default bb2
193+
// CHECK: return
194+
// CHECK: }
195+
sil @convert_select_enum_cond_br_to_switch_enum2 : $@convention(thin) (Numerals) -> Builtin.Int64 {
196+
bb0(%0 : $Numerals):
197+
%2 = integer_literal $Builtin.Int1, 0
198+
%3 = integer_literal $Builtin.Int1, -1
199+
// All cases but one are the same. So, they can be made a default for the switch_enum.
200+
%4 = select_enum %0 : $Numerals, case #Numerals.One!enumelt: %3, case #Numerals.Two!enumelt: %2, case #Numerals.Three!enumelt: %3, case #Numerals.Four!enumelt: %3 : $Builtin.Int1
201+
202+
cond_br %4, bb2, bb3
203+
204+
bb1:
205+
%7 = integer_literal $Builtin.Int64, 10
206+
return %7 : $Builtin.Int64
207+
208+
bb2:
209+
%10 = function_ref @external_func: $@convention(thin) () -> ()
210+
apply %10(): $@convention(thin) () -> ()
211+
br bb1
212+
213+
bb3:
214+
br bb1
215+
}
216+
217+
// Check that cond_br(select_enum) is converted into switch_enum.
218+
// This test checks that select_enum instructions with default cases are handled correctly.
219+
// CHECK-LABEL: sil @convert_select_enum_cond_br_to_switch_enum3
220+
// CHECK: bb0
221+
// CHECK-NOT: select_enum
222+
// CHECK-NOT: return
223+
// CHECK: switch_enum %0 : $Numerals, case #Numerals.Two!enumelt: bb3, default bb2
224+
// CHECK: return
225+
// CHECK: }
226+
sil @convert_select_enum_cond_br_to_switch_enum3 : $@convention(thin) (Numerals) -> Builtin.Int64 {
227+
bb0(%0 : $Numerals):
228+
%2 = integer_literal $Builtin.Int1, 0
229+
%3 = integer_literal $Builtin.Int1, -1
230+
// There is only one case, whose result is different from default and other cases
231+
// Thus all other cases can be folded into a default cases of a switch_enum.
232+
%4 = select_enum %0 : $Numerals, case #Numerals.One!enumelt: %3, case #Numerals.Two!enumelt: %2, case #Numerals.Three!enumelt: %3, default %3 : $Builtin.Int1
233+
234+
cond_br %4, bb2, bb3
235+
236+
bb1:
237+
%7 = integer_literal $Builtin.Int64, 10
238+
return %7 : $Builtin.Int64
239+
240+
bb2:
241+
%10 = function_ref @external_func: $@convention(thin) () -> ()
242+
apply %10(): $@convention(thin) () -> ()
243+
br bb1
244+
245+
bb3:
246+
br bb1
247+
}
248+
180249

181250
// Check that cond_br(select_enum) is not converted into switch_enum as it would create a critical edge, which
182251
// is not originating from cond_br/br. And this is forbidden in a canonical SIL form.
@@ -206,6 +275,64 @@ bb2:
206275
br bb1
207276
}
208277

278+
// Check that cond_br(select_enum) is not converted into switch_enum as it would create a critical edge, which
279+
// is not originating from cond_br/br. And this is forbidden in a canonical SIL form.
280+
//
281+
// CHECK-LABEL: sil @dont_convert_select_enum_cond_br_to_switch_enum2
282+
// CHECK: select_enum
283+
// CHECK-NOT: switch_enum
284+
// CHECK: return
285+
sil @dont_convert_select_enum_cond_br_to_switch_enum2 : $@convention(thin) (Numerals) -> Builtin.Int64 {
286+
bb0(%0 : $Numerals):
287+
%2 = integer_literal $Builtin.Int1, 0
288+
%3 = integer_literal $Builtin.Int1, -1
289+
// There are two cases for each possible outcome.
290+
// This means that we would always get a critical edge, if we convert it into a switch_enum.
291+
%4 = select_enum %0 : $Numerals, case #Numerals.One!enumelt: %3, case #Numerals.Two!enumelt: %2, case #Numerals.Three!enumelt: %3, case #Numerals.Four!enumelt: %2 : $Builtin.Int1
292+
293+
cond_br %4, bb2, bb3
294+
295+
bb1:
296+
%7 = integer_literal $Builtin.Int64, 10
297+
return %7 : $Builtin.Int64
298+
299+
bb2:
300+
%10 = function_ref @external_func: $@convention(thin) () -> ()
301+
apply %10(): $@convention(thin) () -> ()
302+
br bb1
303+
304+
bb3:
305+
br bb1
306+
}
307+
308+
// Check that cond_br(select_enum) is not converted into switch_enum,
309+
// because the result of the default case is not an integer literal.
310+
// CHECK-LABEL: sil @dont_convert_select_enum_cond_br_to_switch_enum3
311+
// CHECK: select_enum
312+
// CHECK-NOT: switch_enum
313+
// CHECK: return
314+
sil @dont_convert_select_enum_cond_br_to_switch_enum3 : $@convention(thin) (Numerals, Builtin.Int1) -> Builtin.Int64 {
315+
bb0(%0 : $Numerals, %1 : $Builtin.Int1):
316+
%2 = integer_literal $Builtin.Int1, 0
317+
%3 = integer_literal $Builtin.Int1, -1
318+
// All cases but one are the same. So, they can be made a default for the switch_enum.
319+
%4 = select_enum %0 : $Numerals, case #Numerals.One!enumelt: %3, case #Numerals.Two!enumelt: %2, case #Numerals.Three!enumelt: %3, default %1 : $Builtin.Int1
320+
321+
cond_br %4, bb2, bb3
322+
323+
bb1:
324+
%7 = integer_literal $Builtin.Int64, 10
325+
return %7 : $Builtin.Int64
326+
327+
bb2:
328+
%10 = function_ref @external_func: $@convention(thin) () -> ()
329+
apply %10(): $@convention(thin) () -> ()
330+
br bb1
331+
332+
bb3:
333+
br bb1
334+
}
335+
209336
public class C {}
210337
public struct S {}
211338
public struct T {

0 commit comments

Comments
 (0)