Skip to content

Commit ac2ef83

Browse files
committed
[sil-combine] Enable cond_br canonicalizations in ossa.
The key thing is that all of these while they do modify the branches of the CFG do not invalidate block level CFG analyses like dominance and dead end blocks.
1 parent 6c960af commit ac2ef83

File tree

6 files changed

+133
-112
lines changed

6 files changed

+133
-112
lines changed

include/swift/SIL/SILType.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -429,6 +429,13 @@ class SILType {
429429
SILType getEnumElementType(EnumElementDecl *elt, SILModule &M,
430430
TypeExpansionContext context) const;
431431

432+
/// Given that this is an enum type, return the lowered type of the
433+
/// data for the given element. Applies substitutions as necessary.
434+
/// The result will have the same value category as the base type.
435+
///
436+
/// NOTE: Takes the type expansion context from \p fn.
437+
SILType getEnumElementType(EnumElementDecl *elt, SILFunction *fn) const;
438+
432439
/// Given that this is an enum type, return true if this type is effectively
433440
/// exhausted.
434441
bool isEffectivelyExhaustiveEnumType(SILFunction *f);

lib/SIL/IR/SILType.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,12 @@ SILType SILType::getEnumElementType(EnumElementDecl *elt, SILModule &M,
195195
return getEnumElementType(elt, M.Types, context);
196196
}
197197

198+
SILType SILType::getEnumElementType(EnumElementDecl *elt,
199+
SILFunction *fn) const {
200+
return getEnumElementType(elt, fn->getModule(),
201+
fn->getTypeExpansionContext());
202+
}
203+
198204
bool SILType::isLoadableOrOpaque(const SILFunction &F) const {
199205
SILModule &M = F.getModule();
200206
return isLoadable(F) || !SILModuleConventions(M).useLoweredAddresses();

lib/SILOptimizer/SILCombiner/SILCombiner.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#ifndef SWIFT_SILOPTIMIZER_PASSMANAGER_SILCOMBINER_H
2222
#define SWIFT_SILOPTIMIZER_PASSMANAGER_SILCOMBINER_H
2323

24+
#include "swift/Basic/Defer.h"
2425
#include "swift/SIL/BasicBlockUtils.h"
2526
#include "swift/SIL/SILBuilder.h"
2627
#include "swift/SIL/SILInstruction.h"
@@ -437,6 +438,17 @@ class SILCombiner :
437438
bool hasOwnership() const {
438439
return Builder.hasOwnership();
439440
}
441+
442+
/// Gets access to the joint post dominance computer and clears it after \p
443+
/// callback.
444+
template <typename ResultTy>
445+
ResultTy withJointPostDomComputer(
446+
function_ref<ResultTy(JointPostDominanceSetComputer &)> callback) {
447+
// Make sure we clear the joint post dom computer after callback.
448+
SWIFT_DEFER { jPostDomComputer.clear(); };
449+
// Then return callback passing in the computer.
450+
return callback(jPostDomComputer);
451+
}
440452
};
441453

442454
} // end namespace swift

lib/SILOptimizer/SILCombiner/SILCombinerMiscVisitors.cpp

Lines changed: 55 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1753,8 +1753,9 @@ SILInstruction *SILCombiner::visitStrongReleaseInst(StrongReleaseInst *SRI) {
17531753
}
17541754

17551755
SILInstruction *SILCombiner::visitCondBranchInst(CondBranchInst *CBI) {
1756-
if (CBI->getFunction()->hasOwnership())
1757-
return nullptr;
1756+
// NOTE: All of the following optimizations do invalidates branches by
1757+
// replacing the branches, but do not modify the underlying CFG properties
1758+
// such as dominance and reachability.
17581759

17591760
// cond_br(xor(x, 1)), t_label, f_label -> cond_br x, f_label, t_label
17601761
// cond_br(x == 0), t_label, f_label -> cond_br x, f_label, t_label
@@ -1880,8 +1881,58 @@ SILInstruction *SILCombiner::visitCondBranchInst(CondBranchInst *CBI) {
18801881
if (NumTrueBBCases > 1 || NumFalseBBCases > 1)
18811882
return nullptr;
18821883

1883-
return Builder.createSwitchEnum(SEI->getLoc(), SEI->getEnumOperand(),
1884-
DefaultBB, Cases);
1884+
if (!hasOwnership()) {
1885+
return Builder.createSwitchEnum(SEI->getLoc(), SEI->getEnumOperand(),
1886+
DefaultBB, Cases);
1887+
}
1888+
1889+
// If we do have ownership, we need to do significantly more
1890+
// work. Specifically:
1891+
//
1892+
// 1. Our select_enum may not be right next to our cond_br, so we need to
1893+
// lifetime extend our enum parameter to our switch_enum.
1894+
//
1895+
// 2. A switch_enum needs to propagate its operands into destination block
1896+
// arguments. We need to create those.
1897+
//
1898+
// 3. In each destination block, we need to create an argument and end the
1899+
// lifetime of that argument.
1900+
auto enumOperandType = SEI->getEnumOperand()->getType();
1901+
if (DefaultBB) {
1902+
auto *defaultArg =
1903+
DefaultBB->createPhiArgument(enumOperandType, OwnershipKind::Owned);
1904+
SILBuilderWithScope innerBuilder(defaultArg->getNextInstruction(),
1905+
Builder);
1906+
auto loc = RegularLocation::getAutoGeneratedLocation();
1907+
innerBuilder.emitDestroyValueOperation(loc, defaultArg);
1908+
}
1909+
1910+
for (auto pair : Cases) {
1911+
// We only need to create the phi argument if our case doesn't have an
1912+
// associated value.
1913+
auto *enumEltDecl = pair.first;
1914+
if (!enumEltDecl->hasAssociatedValues())
1915+
continue;
1916+
1917+
auto *block = pair.second;
1918+
1919+
auto enumEltType =
1920+
enumOperandType.getEnumElementType(enumEltDecl, block->getParent());
1921+
auto *arg = block->createPhiArgument(enumEltType, OwnershipKind::Owned);
1922+
SILBuilderWithScope innerBuilder(arg->getNextInstruction(), Builder);
1923+
auto loc = RegularLocation::getAutoGeneratedLocation();
1924+
innerBuilder.emitDestroyValueOperation(loc, arg);
1925+
}
1926+
1927+
SILValue selectEnumOperand = SEI->getEnumOperand();
1928+
auto switchEnumOperand = withJointPostDomComputer<SILValue>([&](auto &j) {
1929+
if (selectEnumOperand.getOwnershipKind() == OwnershipKind::None)
1930+
return selectEnumOperand;
1931+
return makeCopiedValueAvailable(selectEnumOperand,
1932+
Builder.getInsertionBB(), &j);
1933+
});
1934+
return Builder.createSwitchEnum(SEI->getLoc(), switchEnumOperand, DefaultBB,
1935+
Cases);
18851936
}
18861937

18871938
return nullptr;

test/SILOptimizer/sil_combine_enums_ossa.sil

Lines changed: 34 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,10 @@ bb2: // Preds: bb0
5656

5757
// CHECK-LABEL: sil [ossa] @eliminate_select_enum_addr :
5858
// CHECK-NOT: select_enum_addr
59-
// CHECK: [[BORROWED_VAL:%.*]] = load_borrow
60-
// CHECK: select_enum [[BORROWED_VAL]]
59+
// CHECK-NOT: select_enum
60+
// CHECK: switch_enum
61+
// CHECK-NOT: select_enum_addr
62+
// CHECK-NOT: select_enum
6163
// CHECK: } // end sil function 'eliminate_select_enum_addr'
6264
sil [ossa] @eliminate_select_enum_addr : $@convention(thin) () -> Int {
6365
bb0:
@@ -278,11 +280,11 @@ bb2:
278280

279281
// Check the cond_br(select_enum) -> switch_enum conversion.
280282
//
281-
// CHECK-LABEL: sil [ossa] @convert_select_enum_cond_br_to_switch_enum
282-
// XHECK-NOT: select_enum
283-
// XHECK: switch_enum
284-
// XHECK: return
285-
283+
// CHECK-LABEL: sil [ossa] @convert_select_enum_cond_br_to_switch_enum :
284+
// CHECK-NOT: select_enum
285+
// CHECK: switch_enum
286+
// CHECK: return
287+
// CHECK: } // end sil function 'convert_select_enum_cond_br_to_switch_enum'
286288
sil [ossa] @convert_select_enum_cond_br_to_switch_enum : $@convention(thin) (@owned Optional<SomeClass>) -> Int {
287289
bb0(%0 : @owned $Optional<SomeClass>):
288290
%1 = integer_literal $Builtin.Int1, 0
@@ -304,13 +306,13 @@ bb2:
304306
}
305307

306308
// Check that cond_br(select_enum) is converted into switch_enum.
307-
// CHECK-LABEL: sil [ossa] @convert_select_enum_cond_br_to_switch_enum2
308-
// XHECK: bb0
309-
// XHECK-NOT: select_enum
310-
// XHECK-NOT: return
311-
// XHECK: switch_enum %0 : $Numerals, case #Numerals.Two!enumelt: bb3, default bb2
312-
// XHECK: return
313-
// XHECK: }
309+
// CHECK-LABEL: sil [ossa] @convert_select_enum_cond_br_to_switch_enum2 :
310+
// CHECK: bb0
311+
// CHECK-NOT: select_enum
312+
// CHECK-NOT: return
313+
// CHECK: switch_enum %0 : $Numerals, case #Numerals.Two!enumelt: bb3, default bb2
314+
// CHECK: return
315+
// CHECK: } // end sil function 'convert_select_enum_cond_br_to_switch_enum2'
314316
sil [ossa] @convert_select_enum_cond_br_to_switch_enum2 : $@convention(thin) (Numerals) -> Builtin.Int64 {
315317
bb0(%0 : $Numerals):
316318
%2 = integer_literal $Builtin.Int1, 0
@@ -336,12 +338,12 @@ bb3:
336338
// Check that cond_br(select_enum) is converted into switch_enum.
337339
// This test checks that select_enum instructions with default cases are handled correctly.
338340
// CHECK-LABEL: sil [ossa] @convert_select_enum_cond_br_to_switch_enum3
339-
// XHECK: bb0
340-
// XHECK-NOT: select_enum
341-
// XHECK-NOT: return
342-
// XHECK: switch_enum %0 : $Numerals, case #Numerals.Two!enumelt: bb3, default bb2
343-
// XHECK: return
344-
// XHECK: }
341+
// CHECK: bb0
342+
// CHECK-NOT: select_enum
343+
// CHECK-NOT: return
344+
// CHECK: switch_enum %0 : $Numerals, case #Numerals.Two!enumelt: bb3, default bb2
345+
// CHECK: return
346+
// CHECK: } // end sil function 'convert_select_enum_cond_br_to_switch_enum3'
345347
sil [ossa] @convert_select_enum_cond_br_to_switch_enum3 : $@convention(thin) (Numerals) -> Builtin.Int64 {
346348
bb0(%0 : $Numerals):
347349
%2 = integer_literal $Builtin.Int1, 0
@@ -365,74 +367,12 @@ bb3:
365367
br bb1
366368
}
367369

368-
369-
// Check that cond_br(select_enum) is not converted into switch_enum as it would create a critical edge, which
370-
// is not originating from cond_br/br. And this is forbidden in a canonical SIL form.
371-
//
372-
// CHECK-LABEL: sil [ossa] @dont_convert_select_enum_cond_br_to_switch_enum
373-
// XHECK: select_enum
374-
// XHECK-NOT: switch_enum
375-
// XHECK: return
376-
sil [ossa] @dont_convert_select_enum_cond_br_to_switch_enum : $@convention(thin) (@owned Optional<SomeClass>) -> Int {
377-
bb0(%0 : @owned $Optional<SomeClass>):
378-
%2 = integer_literal $Builtin.Int1, 0
379-
%3 = integer_literal $Builtin.Int1, -1
380-
%4 = select_enum %0 : $Optional<SomeClass>, case #Optional.none!enumelt: %3, case #Optional.some!enumelt: %2 : $Builtin.Int1
381-
cond_br %4, bb2, bb1a
382-
383-
bb1a:
384-
br bb1
385-
386-
bb1:
387-
%5 = unchecked_enum_data %0 : $Optional<SomeClass>, #Optional.some!enumelt
388-
%6 = class_method %5 : $SomeClass, #SomeClass.hash : (SomeClass) -> () -> Int, $@convention(method) (@guaranteed SomeClass) -> Int
389-
%7 = apply %6(%5) : $@convention(method) (@guaranteed SomeClass) -> Int
390-
fix_lifetime %5 : $SomeClass
391-
destroy_value %5 : $SomeClass
392-
return %7 : $Int
393-
394-
bb2:
395-
%10 = function_ref @external_func: $@convention(thin) () -> ()
396-
apply %10(): $@convention(thin) () -> ()
397-
br bb1
398-
}
399-
400-
// Check that cond_br(select_enum) is not converted into switch_enum as it would create a critical edge, which
401-
// is not originating from cond_br/br. And this is forbidden in a canonical SIL form.
402-
//
403-
// CHECK-LABEL: sil [ossa] @dont_convert_select_enum_cond_br_to_switch_enum2
404-
// XHECK: select_enum
405-
// XHECK-NOT: switch_enum
406-
// XHECK: return
407-
sil [ossa] @dont_convert_select_enum_cond_br_to_switch_enum2 : $@convention(thin) (Numerals) -> Builtin.Int64 {
408-
bb0(%0 : $Numerals):
409-
%2 = integer_literal $Builtin.Int1, 0
410-
%3 = integer_literal $Builtin.Int1, -1
411-
// There are two cases for each possible outcome.
412-
// This means that we would always get a critical edge, if we convert it into a switch_enum.
413-
%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
414-
415-
cond_br %4, bb2, bb3
416-
417-
bb1:
418-
%7 = integer_literal $Builtin.Int64, 10
419-
return %7 : $Builtin.Int64
420-
421-
bb2:
422-
%10 = function_ref @external_func: $@convention(thin) () -> ()
423-
apply %10(): $@convention(thin) () -> ()
424-
br bb1
425-
426-
bb3:
427-
br bb1
428-
}
429-
430370
// Check that cond_br(select_enum) is not converted into switch_enum,
431371
// because the result of the default case is not an integer literal.
432-
// CHECK-LABEL: sil [ossa] @dont_convert_select_enum_cond_br_to_switch_enum3
433-
// XHECK: select_enum
434-
// XHECK-NOT: switch_enum
435-
// XHECK: return
372+
// CHECK-LABEL: sil [ossa] @dont_convert_select_enum_cond_br_to_switch_enum3 :
373+
// CHECK: select_enum
374+
// CHECK-NOT: switch_enum
375+
// CHECK: } // end sil function 'dont_convert_select_enum_cond_br_to_switch_enum3'
436376
sil [ossa] @dont_convert_select_enum_cond_br_to_switch_enum3 : $@convention(thin) (Numerals, Builtin.Int1) -> Builtin.Int64 {
437377
bb0(%0 : $Numerals, %1 : $Builtin.Int1):
438378
%2 = integer_literal $Builtin.Int1, 0
@@ -478,33 +418,33 @@ public enum Y {
478418
// the types involved, and we'll generate a trap at IRGen-time if the
479419
// bitcasted types are not the same size.
480420

481-
// CHECK-LABEL: sil [ossa] @keep_unchecked_enum_data
482-
// TODO: Fix
421+
// CHECK-LABEL: sil [ossa] @keep_unchecked_enum_data :
422+
// CHECK: bb0
423+
// CHECK: [[CAST:%.*]] = unchecked_bitwise_cast %0 : $X to $Y
424+
// CHECK: bb1
425+
// CHECK: bb2
426+
// CHECK: bb3
427+
// CHECK: return
428+
// CHECK: } // end sil function 'keep_unchecked_enum_data'
483429
sil [ossa] @keep_unchecked_enum_data : $@convention(thin) (@owned X, @owned T) -> @owned T {
484-
// XHECK: bb0
485430
bb0(%0 : @owned $X, %1 : @owned $T):
486-
// XHECK: [[CAST:%.*]] = unchecked_bitwise_cast %0 : $X to $Y
487431
%4 = unchecked_bitwise_cast %0 : $X to $Y
488432
%5 = copy_value %4 : $Y
489433
switch_enum %5 : $Y, case #Y.none!enumelt: bb1, case #Y.some!enumelt: bb2
490434

491-
// XHECK: bb1
492435
bb1:
493436
(%7, %8) = destructure_struct %1 : $T
494437
br bb3(%7 : $C)
495438

496-
// XHECK: bb2
497439
bb2(%10 : @owned $T):
498440
(%12a, %12b) = destructure_struct %10 : $T
499441
destroy_value %1 : $T
500442
br bb3(%12a : $C)
501443

502-
// XHECK: bb3
503444
bb3(%14 : @owned $C):
504445
%15 = struct $S ()
505446
%16 = struct $T (%14 : $C, %15 : $S)
506447
destroy_value %0 : $X
507-
// XHECK: return
508448
return %16 : $T
509449
}
510450

test/SILOptimizer/sil_combine_ossa.sil

Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1214,9 +1214,10 @@ bb0:
12141214
return %12 : $(Builtin.Int1, Builtin.Int32, Builtin.Int64)
12151215
}
12161216

1217-
// CHECK-LABEL: @_TF6expect3fooFSiSi
1218-
// XHECK: bb0
1219-
// XHECK-NEXT: cond_br %0, bb2, bb1
1217+
// CHECK-LABEL: @_TF6expect3fooFSiSi :
1218+
// CHECK: bb0
1219+
// CHECK-NEXT: cond_br %0, bb2, bb1
1220+
// CHECK: } // end sil function '_TF6expect3fooFSiSi'
12201221
sil [ossa] @_TF6expect3fooFSiSi : $@convention(thin) (Builtin.Int1) -> Int32 {
12211222
bb0(%0 : $Builtin.Int1):
12221223
%5 = integer_literal $Builtin.Int1, -1
@@ -3929,8 +3930,8 @@ bb0(%0 : $Builtin.Int1):
39293930

39303931
// cond_br(xor(x, 1)), t_label, f_label -> cond_br x, f_label, t_label
39313932
// CHECK-LABEL: sil [ossa] @negate_branch_condition_xor1 :
3932-
// XHECK: bb0
3933-
// XHECK-NEXT: cond_br %0, bb2, bb1
3933+
// CHECK: bb0
3934+
// CHECK-NEXT: cond_br %0, bb2, bb1
39343935
// CHECK: } // end sil function 'negate_branch_condition_xor1'
39353936
sil [ossa] @negate_branch_condition_xor1 : $@convention(thin) (Builtin.Int1) -> Int32 {
39363937
bb0(%0 : $Builtin.Int1):
@@ -3953,9 +3954,10 @@ bb3(%16 : $Int32): // Preds: bb1 bb2
39533954
}
39543955

39553956
// cond_br(xor(1, x)), t_label, f_label -> cond_br x, f_label, t_label
3956-
// CHECK-LABEL: sil [ossa] @negate_branch_condition_xor2
3957-
// XHECK: bb0
3958-
// XHECK-NEXT: cond_br %0, bb2, bb1
3957+
// CHECK-LABEL: sil [ossa] @negate_branch_condition_xor2 :
3958+
// CHECK: bb0
3959+
// CHECK-NEXT: cond_br %0, bb2, bb1
3960+
// CHECK: } // end sil function 'negate_branch_condition_xor2'
39593961
sil [ossa] @negate_branch_condition_xor2 : $@convention(thin) (Builtin.Int1) -> Int32 {
39603962
bb0(%0 : $Builtin.Int1):
39613963
%5 = integer_literal $Builtin.Int1, -1
@@ -3977,9 +3979,11 @@ bb3(%16 : $Int32): // Preds: bb1 bb2
39773979
}
39783980

39793981
// cond_br(x == 0), t_label, f_label -> cond_br x, f_label, t_label
3980-
// CHECK-LABEL: sil [ossa] @negate_branch_condition_eq_false
3981-
// XHECK: bb0
3982-
// XHECK-NEXT: cond_br %0, bb2, bb1
3982+
//
3983+
// CHECK-LABEL: sil [ossa] @negate_branch_condition_eq_false :
3984+
// CHECK: bb0
3985+
// CHECK-NEXT: cond_br %0, bb2, bb1
3986+
// CHECK: } // end sil function 'negate_branch_condition_eq_false'
39833987
sil [ossa] @negate_branch_condition_eq_false : $@convention(thin) (Builtin.Int1) -> Int32 {
39843988
bb0(%0 : $Builtin.Int1):
39853989
%5 = integer_literal $Builtin.Int1, 0
@@ -4001,9 +4005,10 @@ bb3(%16 : $Int32): // Preds: bb1 bb2
40014005
}
40024006

40034007
// cond_br(x != -1), t_label, f_label -> cond_br x, f_label, t_label
4004-
// CHECK-LABEL: sil [ossa] @negate_branch_condition_ne_true
4005-
// XHECK: bb0
4006-
// XHECK-NEXT: cond_br %0, bb2, bb1
4008+
// CHECK-LABEL: sil [ossa] @negate_branch_condition_ne_true :
4009+
// CHECK: bb0
4010+
// CHECK-NEXT: cond_br %0, bb2, bb1
4011+
// CHECK: } // end sil function 'negate_branch_condition_ne_true'
40074012
sil [ossa] @negate_branch_condition_ne_true : $@convention(thin) (Builtin.Int1) -> Int32 {
40084013
bb0(%0 : $Builtin.Int1):
40094014
%5 = integer_literal $Builtin.Int1, -1

0 commit comments

Comments
 (0)