Skip to content

Commit 8c6e704

Browse files
authored
Merge pull request #33431 from atrick/fix-licm-condfail
Fix LICM to avoid hoisting never-executed traps
2 parents 75ea5a5 + 4ca3c23 commit 8c6e704

File tree

3 files changed

+92
-3
lines changed

3 files changed

+92
-3
lines changed

include/swift/SIL/LoopInfo.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,16 @@ class SILLoop : public llvm::LoopBase<SILBasicBlock, SILLoop> {
5050
/// this loop by unrolling or versioning.
5151
bool canDuplicate(SILInstruction *Inst) const;
5252

53+
void getExitingAndLatchBlocks(
54+
SmallVectorImpl<SILBasicBlock *> &ExitingAndLatchBlocks) const {
55+
this->getExitingBlocks(ExitingAndLatchBlocks);
56+
SILBasicBlock *header = getHeader();
57+
for (auto *predBB : header->getPredecessorBlocks()) {
58+
if (contains(predBB) && !this->isLoopExiting(predBB))
59+
ExitingAndLatchBlocks.push_back(predBB);
60+
}
61+
}
62+
5363
private:
5464
friend class llvm::LoopInfoBase<SILBasicBlock, SILLoop>;
5565

lib/SILOptimizer/LoopTransforms/LICM.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -214,16 +214,16 @@ static void getDominatingBlocks(SmallVectorImpl<SILBasicBlock *> &domBlocks,
214214
SILLoop *Loop, DominanceInfo *DT) {
215215
auto HeaderBB = Loop->getHeader();
216216
auto DTRoot = DT->getNode(HeaderBB);
217-
SmallVector<SILBasicBlock *, 8> ExitingBBs;
218-
Loop->getExitingBlocks(ExitingBBs);
217+
SmallVector<SILBasicBlock *, 8> ExitingAndLatchBBs;
218+
Loop->getExitingAndLatchBlocks(ExitingAndLatchBBs);
219219
for (llvm::df_iterator<DominanceInfoNode *> It = llvm::df_begin(DTRoot),
220220
E = llvm::df_end(DTRoot);
221221
It != E;) {
222222
auto *CurBB = It->getBlock();
223223

224224
// Don't decent into control-dependent code. Only traverse into basic blocks
225225
// that dominate all exits.
226-
if (!std::all_of(ExitingBBs.begin(), ExitingBBs.end(),
226+
if (!std::all_of(ExitingAndLatchBBs.begin(), ExitingAndLatchBBs.end(),
227227
[=](SILBasicBlock *ExitBB) {
228228
return DT->dominates(CurBB, ExitBB);
229229
})) {

test/SILOptimizer/licm.sil

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -854,3 +854,82 @@ bb9(%39 : $Builtin.Int64): // Preds: bb7
854854
dealloc_stack %3 : $*Index // id: %41
855855
return %40 : $Int64 // id: %42
856856
}
857+
858+
// testConditionalTrapInInfiniteSyncLoop and
859+
// testConditionalTrapDominatingSyncLoopExit
860+
//
861+
// It's legal for the optimizer to consider code after the loop as
862+
// always reachable, but when a loop has no exits, or when the loops
863+
// exits are dominated by a conditional statement we should not
864+
// consider conditional statements within the loop as dominating all
865+
// possible execution paths through the loop. At least not when there
866+
// is at least one path through the loop that contains a
867+
// "synchronization point", such as a function that may contain a
868+
// memory barrier, perform I/O, or exit the program.
869+
sil @mayExit : $@convention(thin) () -> ()
870+
871+
// CHECK-LABEL: sil @testConditionalTrapInInfiniteSyncLoop : $@convention(thin) (Builtin.Int1, Builtin.Int1) -> () {
872+
// CHECK: bb0
873+
// CHECK-NOT: cond_fail
874+
// CHECK: br bb1
875+
// CHECK: bb1:
876+
// CHECK: cond_br %0, bb2, bb3
877+
// CHECK: bb2:
878+
// CHECK: cond_fail %1 : $Builtin.Int1, "arithmetic overflow"
879+
// CHECK-LABEL: } // end sil function 'testConditionalTrapInInfiniteSyncLoop'
880+
sil @testConditionalTrapInInfiniteSyncLoop : $@convention(thin) (Builtin.Int1, Builtin.Int1) -> () {
881+
bb0(%0 : $Builtin.Int1, %1 : $Builtin.Int1):
882+
br bb1
883+
884+
bb1: // loop head
885+
cond_br %0, bb2, bb3
886+
887+
bb2: // maybe never executed
888+
cond_fail %1 : $Builtin.Int1, "arithmetic overflow"
889+
br bb4
890+
891+
bb3:
892+
// synchronization point: has "real" side-effects that we can't
893+
// reorder with traps
894+
%f = function_ref @mayExit : $@convention(thin) () -> ()
895+
apply %f() : $@convention(thin) () -> ()
896+
br bb4
897+
898+
bb4: // latch
899+
br bb1
900+
}
901+
902+
// CHECK-LABEL: sil @testConditionalTrapDominatingSyncLoopExit : $@convention(thin) (Builtin.Int1, Builtin.Int1, Builtin.Int1) -> () {
903+
// CHECK: bb0
904+
// CHECK-NOT: cond_fail
905+
// CHECK: br bb1
906+
// CHECK: bb1:
907+
// CHECK: cond_br %0, bb2, bb4
908+
// CHECK: bb2:
909+
// CHECK: cond_fail %1 : $Builtin.Int1, "arithmetic overflow"
910+
// CHECK-LABEL: } // end sil function 'testConditionalTrapDominatingSyncLoopExit'
911+
sil @testConditionalTrapDominatingSyncLoopExit : $@convention(thin) (Builtin.Int1, Builtin.Int1, Builtin.Int1) -> () {
912+
bb0(%0 : $Builtin.Int1, %1 : $Builtin.Int1, %2 : $Builtin.Int1):
913+
br bb1
914+
915+
bb1: // loop head
916+
cond_br %0, bb2, bb4
917+
918+
bb2: // maybe never executed
919+
cond_fail %1 : $Builtin.Int1, "arithmetic overflow"
920+
cond_br %2, bb3, bb5
921+
922+
bb3: // tail
923+
br bb1
924+
925+
bb4:
926+
// synchronization point: has "real" side-effects that we can't
927+
// reorder with traps
928+
%f = function_ref @mayExit : $@convention(thin) () -> ()
929+
apply %f() : $@convention(thin) () -> ()
930+
br bb1
931+
932+
bb5:
933+
%99 = tuple ()
934+
return %99 : $()
935+
}

0 commit comments

Comments
 (0)