Skip to content

Commit 95344f6

Browse files
author
Joe Shajrawi
committed
[LICM/Exclusivity] Hoist (some) conflicting begin_accesses out of loops
Consider the attached test cases: We have a begin_access [dynamic] to a global inside of a loop There’s a nested conflict on said access due to an apply() instruction between the begin and end accesses. LICM is currently very conservative: If there are any function calls inside of the loop that conflict with begin and end access, we do not hoist out of the loop. However, if all conflicting applies are “sandwiched” between the begin and end access. So there’s no reason we can’t hoist out of the loop. See radar rdar://problem/43660965 - this improves some internal benchmarks by over 3X
1 parent d1340c8 commit 95344f6

File tree

2 files changed

+94
-3
lines changed

2 files changed

+94
-3
lines changed

lib/SILOptimizer/LoopTransforms/LICM.cpp

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -537,7 +537,8 @@ static bool handledEndAccesses(BeginAccessInst *BI, SILLoop *Loop) {
537537
static bool analyzeBeginAccess(BeginAccessInst *BI,
538538
SmallVector<BeginAccessInst *, 8> &BeginAccesses,
539539
SmallVector<FullApplySite, 8> &fullApplies,
540-
AccessedStorageAnalysis *ASA) {
540+
AccessedStorageAnalysis *ASA,
541+
DominanceInfo *DT) {
541542
if (BI->getEnforcement() != SILAccessEnforcement::Dynamic) {
542543
return false;
543544
}
@@ -564,8 +565,18 @@ static bool analyzeBeginAccess(BeginAccessInst *BI,
564565
FunctionAccessedStorage callSiteAccesses;
565566
ASA->getCallSiteEffects(callSiteAccesses, fullApply);
566567
SILAccessKind accessKind = BI->getAccessKind();
567-
if (callSiteAccesses.mayConflictWith(accessKind, storage))
568+
if (!callSiteAccesses.mayConflictWith(accessKind, storage))
569+
continue;
570+
// Check if we can ignore this conflict:
571+
// If the apply is “sandwiched” between the begin and end access,
572+
// there’s no reason we can’t hoist out of the loop.
573+
auto *applyInstr = fullApply.getInstruction();
574+
if (!DT->dominates(BI, applyInstr))
568575
return false;
576+
for (auto *EI : BI->getEndAccesses()) {
577+
if (!DT->dominates(applyInstr, EI))
578+
return false;
579+
}
569580
}
570581

571582
return true;
@@ -687,7 +698,7 @@ void LoopTreeOptimization::analyzeCurrentLoop(
687698
LLVM_DEBUG(llvm::dbgs() << "Some end accesses can't be handled\n");
688699
continue;
689700
}
690-
if (analyzeBeginAccess(BI, BeginAccesses, fullApplies, ASA)) {
701+
if (analyzeBeginAccess(BI, BeginAccesses, fullApplies, ASA, DomTree)) {
691702
SpecialHoist.insert(BI);
692703
}
693704
}
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
// RUN: %target-sil-opt -assume-parsing-unqualified-ownership-sil -enforce-exclusivity=checked -enable-sil-verify-all %s -licm | %FileCheck %s
2+
3+
sil_stage canonical
4+
5+
6+
import Builtin
7+
import Swift
8+
9+
struct X {
10+
@sil_stored var i: Int64 { get set }
11+
init(i: Int64)
12+
init()
13+
}
14+
15+
var globalX: X
16+
17+
sil_global hidden @globalX : $X
18+
19+
20+
sil hidden_external [global_init] @globalAddressor : $@convention(thin) () -> Builtin.RawPointer
21+
22+
// public func hoist_access_with_conflict() {
23+
// Tests Hoisting of begin/end access when there's a "sandwiched" unidentified access
24+
//
25+
// CHECK-LABEL: sil @hoist_access_with_conflict : $@convention(thin) () -> () {
26+
// CHECK: [[GLOBAL:%.*]] = global_addr @globalX : $*X
27+
// CHECK: [[BEGIN:%.*]] = begin_access [read] [dynamic] %0 : $*X
28+
// CHECK-NEXT: br bb1
29+
// CHECK: apply
30+
// CHECK: load
31+
// CHECK: cond_br
32+
// CHECK: bb2
33+
// CHECK: end_access [[BEGIN]]
34+
// CHECK-LABEL: } // end sil function 'hoist_access_with_conflict'
35+
sil @hoist_access_with_conflict : $@convention(thin) () -> () {
36+
bb0:
37+
%0 = global_addr @globalX: $*X
38+
%u0 = function_ref @globalAddressor : $@convention(thin) () -> Builtin.RawPointer
39+
br bb1
40+
41+
bb1:
42+
%u3 = begin_access [read] [dynamic] %0 : $*X
43+
%u1 = apply %u0() : $@convention(thin) () -> Builtin.RawPointer
44+
%u4 = load %u3 : $*X
45+
end_access %u3 : $*X
46+
cond_br undef, bb1, bb2
47+
48+
bb2:
49+
%10 = tuple ()
50+
return %10 : $()
51+
}
52+
53+
// public func dont_hoist_access_with_conflict() {
54+
// Tests *not* hoisting begin/end access when there's an unidentified access not protected by them
55+
//
56+
// CHECK-LABEL: sil @dont_hoist_access_with_conflict : $@convention(thin) () -> () {
57+
// CHECK: [[GLOBAL:%.*]] = global_addr @globalX : $*X
58+
// CHECK: br bb1
59+
// CHECK: apply
60+
// CHECK-NEXT: [[BEGIN:%.*]] = begin_access [read] [dynamic] %0 : $*X
61+
// CHECK-NEXT: load
62+
// CHECK-NEXT: end_access [[BEGIN]]
63+
// CHECK-LABEL: } // end sil function 'dont_hoist_access_with_conflict'
64+
sil @dont_hoist_access_with_conflict : $@convention(thin) () -> () {
65+
bb0:
66+
%0 = global_addr @globalX: $*X
67+
%u0 = function_ref @globalAddressor : $@convention(thin) () -> Builtin.RawPointer
68+
br bb1
69+
70+
bb1:
71+
%u1 = apply %u0() : $@convention(thin) () -> Builtin.RawPointer
72+
%u3 = begin_access [read] [dynamic] %0 : $*X
73+
%u4 = load %u3 : $*X
74+
end_access %u3 : $*X
75+
cond_br undef, bb1, bb2
76+
77+
bb2:
78+
%10 = tuple ()
79+
return %10 : $()
80+
}

0 commit comments

Comments
 (0)