Skip to content

Commit 821bd3a

Browse files
committed
ARCCodeMotion: fix two problems in release hoisting:
1) PostOrderAnalysis is not invalidated after splitting critical edges. This let the data flow solver omit new inserted blocks. 2) Handle infinite loops in the CFG correctly. So that we don’t insert random release instructions into such CFG pathes. https://bugs.swift.org/browse/SR-5187 rdar://problem/32713742
1 parent 3671a2f commit 821bd3a

File tree

2 files changed

+126
-7
lines changed

2 files changed

+126
-7
lines changed

lib/SILOptimizer/Transforms/ARCCodeMotion.cpp

Lines changed: 38 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -618,13 +618,16 @@ class ReleaseBlockState : public BlockState {
618618
}
619619

620620
/// constructor.
621-
ReleaseBlockState(bool IsExit, unsigned size, bool MultiIteration) {
621+
///
622+
/// If \p InitOptimistic is true, the block in-bits are initialized to 1
623+
/// which enables optimistic data flow evaluation.
624+
ReleaseBlockState(bool InitOptimistic, unsigned size) {
622625
// backward data flow.
623626
// Initialize to true if we are running optimistic data flow, i.e.
624627
// MultiIteration is true.
625-
BBSetIn.resize(size, MultiIteration);
628+
BBSetIn.resize(size, InitOptimistic);
626629
BBSetOut.resize(size, false);
627-
BBMaxSet.resize(size, !IsExit && MultiIteration);
630+
BBMaxSet.resize(size, InitOptimistic);
628631

629632
// Genset and Killset are initially empty.
630633
BBGenSet.resize(size, false);
@@ -735,6 +738,17 @@ bool ReleaseCodeMotionContext::requireIteration() {
735738
}
736739

737740
void ReleaseCodeMotionContext::initializeCodeMotionDataFlow() {
741+
// All blocks which are initialized with 1-bits. These are all blocks which
742+
// eventually reach the function exit (return, throw), excluding the
743+
// function exit blocks themselves.
744+
// Optimistic initialization enables moving releases across loops. On the
745+
// other hand, blocks, which never reach the function exit, e.g. infinite
746+
// loop blocks, must be excluded. Otherwise we would end up inserting
747+
// completely unrelated release instructions in such blocks.
748+
llvm::SmallPtrSet<SILBasicBlock *, 32> BlocksInitOptimistically;
749+
750+
llvm::SmallVector<SILBasicBlock *, 32> Worklist;
751+
738752
// Find all the RC roots in the function.
739753
for (auto &BB : *F) {
740754
for (auto &II : BB) {
@@ -750,13 +764,25 @@ void ReleaseCodeMotionContext::initializeCodeMotionDataFlow() {
750764
RCRootIndex[Root] = RCRootVault.size();
751765
RCRootVault.insert(Root);
752766
}
767+
if (MultiIteration && BB.getTerminator()->isFunctionExiting())
768+
Worklist.push_back(&BB);
769+
}
770+
771+
// Find all blocks from which there is a path to the function exit.
772+
// Note: the Worklist is empty if we are not in MultiIteration mode.
773+
while (!Worklist.empty()) {
774+
SILBasicBlock *BB = Worklist.pop_back_val();
775+
for (SILBasicBlock *Pred : BB->getPredecessorBlocks()) {
776+
if (BlocksInitOptimistically.insert(Pred).second)
777+
Worklist.push_back(Pred);
778+
}
753779
}
754780

755781
// Initialize all the data flow bit vector for all basic blocks.
756782
for (auto &BB : *F) {
757783
BlockStates[&BB] = new (BPA.Allocate())
758-
ReleaseBlockState(BB.getTerminator()->isFunctionExiting(),
759-
RCRootVault.size(), MultiIteration);
784+
ReleaseBlockState(BlocksInitOptimistically.count(&BB) != 0,
785+
RCRootVault.size());
760786
}
761787
}
762788

@@ -1038,17 +1064,22 @@ class ARCCodeMotion : public SILFunctionTransform {
10381064
return;
10391065

10401066
DEBUG(llvm::dbgs() << "*** ARCCM on function: " << F->getName() << " ***\n");
1067+
1068+
PostOrderAnalysis *POA = PM->getAnalysis<PostOrderAnalysis>();
1069+
10411070
// Split all critical edges.
10421071
//
10431072
// TODO: maybe we can do this lazily or maybe we should disallow SIL passes
10441073
// to create critical edges.
10451074
bool EdgeChanged = splitAllCriticalEdges(*F, false, nullptr, nullptr);
1075+
if (EdgeChanged)
1076+
POA->invalidateFunction(F);
10461077

1047-
llvm::SpecificBumpPtrAllocator<BlockState> BPA;
1048-
auto *PO = PM->getAnalysis<PostOrderAnalysis>()->get(F);
1078+
auto *PO = POA->get(F);
10491079
auto *AA = PM->getAnalysis<AliasAnalysis>();
10501080
auto *RCFI = PM->getAnalysis<RCIdentityAnalysis>()->get(F);
10511081

1082+
llvm::SpecificBumpPtrAllocator<BlockState> BPA;
10521083
bool InstChanged = false;
10531084
if (Kind == Release) {
10541085
// TODO: we should consider Throw block as well, or better we should

test/SILOptimizer/retain_release_code_motion.sil

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -443,3 +443,91 @@ bb3:
443443
%5 = tuple()
444444
return %5 : $()
445445
}
446+
447+
// CHECK-LABEL: sil @move_retain_over_loop
448+
// CHECK: bb0({{.*}}):
449+
// CHECK-NEXT: br bb1
450+
// CHECK: bb1:
451+
// CHECK-NEXT: cond_br
452+
// CHECK: bb2:
453+
// CHECK: strong_retain
454+
// CHECK: apply
455+
// CHECK: strong_release
456+
// CHECK: return
457+
sil @move_retain_over_loop : $@convention(thin) (Builtin.NativeObject) -> () {
458+
bb0(%0 : $Builtin.NativeObject):
459+
strong_retain %0 : $Builtin.NativeObject
460+
br bb1
461+
462+
bb1:
463+
cond_br undef, bb1, bb2
464+
465+
bb2:
466+
%2 = function_ref @blocker : $@convention(thin) () -> ()
467+
apply %2() : $@convention(thin) () -> ()
468+
strong_release %0 : $Builtin.NativeObject
469+
%1 = tuple()
470+
return %1 : $()
471+
}
472+
473+
// CHECK-LABEL: sil @move_release_over_loop
474+
// CHECK: bb0{{.*}}:
475+
// CHECK: strong_retain
476+
// CHECK: apply
477+
// CHECK: strong_release
478+
// CHECK: br bb1
479+
// CHECK: bb1:
480+
// CHECK-NEXT: cond_br
481+
// CHECK: bb2:
482+
// CHECK-NEXT: br bb1
483+
// CHECK: bb3:
484+
// CHECK-NEXT: tuple
485+
// CHECK-NEXT: return
486+
sil @move_release_over_loop : $@convention(thin) (Builtin.NativeObject) -> () {
487+
bb0(%0 : $Builtin.NativeObject):
488+
strong_retain %0 : $Builtin.NativeObject
489+
%2 = function_ref @blocker : $@convention(thin) () -> ()
490+
apply %2() : $@convention(thin) () -> ()
491+
br bb1
492+
493+
bb1:
494+
cond_br undef, bb1, bb2
495+
496+
bb2:
497+
strong_release %0 : $Builtin.NativeObject
498+
%1 = tuple()
499+
return %1 : $()
500+
}
501+
502+
// CHECK-LABEL: sil @handle_infinite_loop
503+
// CHECK: bb0{{.*}}:
504+
// CHECK-NEXT: cond_br
505+
// CHECK: bb1:
506+
// CHECK-NOT: {{(retain|release)}}
507+
// CHECK: apply
508+
// CHECK-NEXT: br bb2
509+
// CHECK: bb2:
510+
// CHECK-NEXT: br bb2
511+
// CHECK: bb3:
512+
// CHECK: strong_retain
513+
// CHECK: strong_release
514+
// CHECK: return
515+
sil @handle_infinite_loop : $@convention(thin) (@inout Builtin.NativeObject) -> () {
516+
bb0(%a : $*Builtin.NativeObject):
517+
cond_br undef, bb1, bb3
518+
519+
bb1:
520+
%2 = function_ref @blocker : $@convention(thin) () -> ()
521+
apply %2() : $@convention(thin) () -> ()
522+
br bb2
523+
524+
bb2:
525+
br bb2
526+
527+
bb3:
528+
%0 = load %a : $*Builtin.NativeObject
529+
strong_retain %0 : $Builtin.NativeObject
530+
strong_release %0 : $Builtin.NativeObject
531+
%1 = tuple()
532+
return %1 : $()
533+
}

0 commit comments

Comments
 (0)