Skip to content

Commit 107d8ff

Browse files
Merge pull request swiftlang#63276 from nate-chandler/rdar104635319
[CanonicalOSSALifetime] Respect access scope on flag.
2 parents 547d85e + 4f845cc commit 107d8ff

File tree

8 files changed

+75
-22
lines changed

8 files changed

+75
-22
lines changed

include/swift/SILOptimizer/Utils/CanonicalizeOSSALifetime.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,11 @@ class CanonicalizeOSSALifetime final {
222222
/// lifetime boundary in case we need to emit diagnostics.
223223
std::function<void(Operand *)> moveOnlyFinalConsumingUse;
224224

225+
// If present, will be used to ensure that the lifetime is not shortened to
226+
// end inside an access scope which it previously enclosed. (Note that ending
227+
// before such an access scope is fine regardless.)
228+
//
229+
// For details, see extendLivenessThroughOverlappingAccess.
225230
NonLocalAccessBlockAnalysis *accessBlockAnalysis;
226231
// Lazily initialize accessBlocks only when
227232
// extendLivenessThroughOverlappingAccess is invoked.

lib/SILOptimizer/Mandatory/MoveOnlyAddressChecker.cpp

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -859,7 +859,6 @@ struct MoveOnlyChecker {
859859
ConsumeInfo consumes;
860860

861861
MoveOnlyChecker(SILFunction *fn, DeadEndBlocks *deBlocks,
862-
NonLocalAccessBlockAnalysis *accessBlockAnalysis,
863862
DominanceInfo *domTree)
864863
: fn(fn), deleter(), canonicalizer(), diagnosticEmitter() {
865864
deleter.setCallbacks(std::move(
@@ -868,7 +867,7 @@ struct MoveOnlyChecker {
868867
moveIntroducersToProcess.remove(mvi);
869868
instToDelete->eraseFromParent();
870869
})));
871-
canonicalizer.init(fn, accessBlockAnalysis, domTree, deleter);
870+
canonicalizer.init(fn, domTree, deleter);
872871
diagnosticEmitter.init(fn, &canonicalizer);
873872
}
874873

@@ -1966,13 +1965,11 @@ class MoveOnlyCheckerPass : public SILFunctionTransform {
19661965
"Should only run on Raw SIL");
19671966
LLVM_DEBUG(llvm::dbgs() << "===> MoveOnly Addr Checker. Visiting: "
19681967
<< fn->getName() << '\n');
1969-
auto *accessBlockAnalysis = getAnalysis<NonLocalAccessBlockAnalysis>();
19701968
auto *dominanceAnalysis = getAnalysis<DominanceAnalysis>();
19711969
DominanceInfo *domTree = dominanceAnalysis->get(fn);
19721970
auto *deAnalysis = getAnalysis<DeadEndBlocksAnalysis>()->get(fn);
19731971

1974-
if (MoveOnlyChecker(getFunction(), deAnalysis, accessBlockAnalysis, domTree)
1975-
.checkFunction()) {
1972+
if (MoveOnlyChecker(getFunction(), deAnalysis, domTree).checkFunction()) {
19761973
invalidateAnalysis(SILAnalysis::InvalidationKind::Instructions);
19771974
}
19781975
}

lib/SILOptimizer/Mandatory/MoveOnlyObjectChecker.cpp

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -314,8 +314,7 @@ struct MoveOnlyChecker {
314314

315315
MoveOnlyChecker(SILFunction *fn, DeadEndBlocks *deBlocks) : fn(fn) {}
316316

317-
void check(NonLocalAccessBlockAnalysis *accessBlockAnalysis,
318-
DominanceInfo *domTree);
317+
void check(DominanceInfo *domTree);
319318

320319
/// After we have emitted a diagnostic, we need to clean up the instruction
321320
/// stream by converting /all/ copies of move only typed things to use
@@ -336,8 +335,7 @@ struct MoveOnlyChecker {
336335
// MARK: Main Routine
337336
//===----------------------------------------------------------------------===//
338337

339-
void MoveOnlyChecker::check(NonLocalAccessBlockAnalysis *accessBlockAnalysis,
340-
DominanceInfo *domTree) {
338+
void MoveOnlyChecker::check(DominanceInfo *domTree) {
341339
auto callbacks =
342340
InstModCallbacks().onDelete([&](SILInstruction *instToDelete) {
343341
if (auto *mvi = dyn_cast<MarkMustCheckInst>(instToDelete))
@@ -346,7 +344,7 @@ void MoveOnlyChecker::check(NonLocalAccessBlockAnalysis *accessBlockAnalysis,
346344
});
347345
InstructionDeleter deleter(std::move(callbacks));
348346
OSSACanonicalizer canonicalizer;
349-
canonicalizer.init(fn, accessBlockAnalysis, domTree, deleter);
347+
canonicalizer.init(fn, domTree, deleter);
350348
DiagnosticEmitter diagnosticEmitter;
351349
diagnosticEmitter.init(fn, &canonicalizer);
352350

@@ -508,13 +506,12 @@ class MoveOnlyCheckerPass : public SILFunctionTransform {
508506
LLVM_DEBUG(llvm::dbgs() << "===> MoveOnly Object Checker. Visiting: "
509507
<< fn->getName() << '\n');
510508

511-
auto *accessBlockAnalysis = getAnalysis<NonLocalAccessBlockAnalysis>();
512509
auto *dominanceAnalysis = getAnalysis<DominanceAnalysis>();
513510
DominanceInfo *domTree = dominanceAnalysis->get(fn);
514511
auto *deAnalysis = getAnalysis<DeadEndBlocksAnalysis>()->get(fn);
515512

516513
MoveOnlyChecker checker(getFunction(), deAnalysis);
517-
checker.check(accessBlockAnalysis, domTree);
514+
checker.check(domTree);
518515
if (checker.changed) {
519516
invalidateAnalysis(SILAnalysis::InvalidationKind::Instructions);
520517
}

lib/SILOptimizer/Mandatory/MoveOnlyObjectChecker.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -53,17 +53,17 @@ struct OSSACanonicalizer {
5353

5454
OSSACanonicalizer() {}
5555

56-
void init(SILFunction *fn, NonLocalAccessBlockAnalysis *accessBlockAnalysis,
57-
DominanceInfo *domTree, InstructionDeleter &deleter) {
56+
void init(SILFunction *fn, DominanceInfo *domTree,
57+
InstructionDeleter &deleter) {
5858
auto foundConsumingUseNeedingCopy = std::function<void(Operand *)>(
5959
[&](Operand *use) { consumingUsesNeedingCopy.push_back(use); });
6060
auto foundConsumingUseNotNeedingCopy = std::function<void(Operand *)>(
6161
[&](Operand *use) { finalConsumingUses.push_back(use); });
6262

6363
canonicalizer.emplace(
6464
false /*pruneDebugMode*/, !fn->shouldOptimize() /*maximizeLifetime*/,
65-
accessBlockAnalysis, domTree, deleter, foundConsumingUseNeedingCopy,
66-
foundConsumingUseNotNeedingCopy);
65+
nullptr /*accessBlockAnalysis*/, domTree, deleter,
66+
foundConsumingUseNeedingCopy, foundConsumingUseNotNeedingCopy);
6767
}
6868

6969
void clear() {

lib/SILOptimizer/UtilityPasses/UnitTestRunner.cpp

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -373,6 +373,9 @@ struct MultiDefLivenessTest : UnitTest {
373373
// Arguments:
374374
// - bool: pruneDebug
375375
// - bool: maximizeLifetimes
376+
// - bool: "respectAccessScopes", whether to contract lifetimes to end within
377+
// access scopes which they previously enclosed but can't be hoisted
378+
// before
376379
// - SILValue: value to canonicalize
377380
// Dumps:
378381
// - function after value canonicalization
@@ -384,9 +387,11 @@ struct CanonicalizeOSSALifetimeTest : UnitTest {
384387
DominanceInfo *domTree = dominanceAnalysis->get(getFunction());
385388
auto pruneDebug = arguments.takeBool();
386389
auto maximizeLifetimes = arguments.takeBool();
390+
auto respectAccessScopes = arguments.takeBool();
387391
InstructionDeleter deleter;
388-
CanonicalizeOSSALifetime canonicalizer(pruneDebug, maximizeLifetimes, accessBlockAnalysis,
389-
domTree, deleter);
392+
CanonicalizeOSSALifetime canonicalizer(
393+
pruneDebug, maximizeLifetimes,
394+
respectAccessScopes ? accessBlockAnalysis : nullptr, domTree, deleter);
390395
auto value = arguments.takeValue();
391396
canonicalizer.canonicalizeValueLifetime(value);
392397
getFunction()->dump();

lib/SILOptimizer/Utils/CanonicalizeOSSALifetime.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1028,7 +1028,9 @@ bool CanonicalizeOSSALifetime::canonicalizeValueLifetime(SILValue def) {
10281028
clearLiveness();
10291029
return false;
10301030
}
1031-
extendLivenessThroughOverlappingAccess();
1031+
if (accessBlockAnalysis) {
1032+
extendLivenessThroughOverlappingAccess();
1033+
}
10321034
// Step 2: compute original boundary
10331035
PrunedLivenessBoundary originalBoundary;
10341036
findOriginalBoundary(originalBoundary);
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
// RUN: %target-sil-opt -unit-test-runner %s -o /dev/null 2>&1 | %FileCheck %s
2+
3+
class C {}
4+
5+
// When access scopes are respected, the lifetime which previously extended
6+
// beyond the access scope still extends beyond it.
7+
// CHECK-LABEL: begin running test 1 of 2 on retract_value_lifetime_into_access_scope_when_access_scopes_not_respected: canonicalize-ossa-lifetime with: true, false, true, @trace
8+
// CHECK-LABEL: sil [ossa] @retract_value_lifetime_into_access_scope_when_access_scopes_not_respected {{.*}} {
9+
// CHECK: {{bb[0-9]+}}([[ADDR:%[^,]+]] :
10+
// CHECK: [[INSTANCE:%[^,]+]] = apply
11+
// CHECK: [[COPY:%[^,]+]] = copy_value [[INSTANCE]]
12+
// CHECK: [[ACCESS:%[^,]+]] = begin_access [modify] [static] [[ADDR]]
13+
// CHECK: store [[COPY]] to [init] [[ACCESS]]
14+
// CHECK: end_access [[ACCESS]]
15+
// CHECK: destroy_value [[INSTANCE]]
16+
// CHECK-LABEL: } // end sil function 'retract_value_lifetime_into_access_scope_when_access_scopes_not_respected'
17+
// CHECK-LABEL: end running test 1 of 2 on retract_value_lifetime_into_access_scope_when_access_scopes_not_respected: canonicalize-ossa-lifetime with: true, false, true, @trace
18+
19+
// When access scopes are not respected, the lifetime which previously extended
20+
// beyond the access scope is retracted into the scope.
21+
// CHECK-LABEL: begin running test 2 of 2 on retract_value_lifetime_into_access_scope_when_access_scopes_not_respected: canonicalize-ossa-lifetime with: true, false, false, @trace
22+
// CHECK-LABEL: sil [ossa] @retract_value_lifetime_into_access_scope_when_access_scopes_not_respected {{.*}} {
23+
// CHECK: {{bb[0-9]+}}([[ADDR:%[^,]+]] :
24+
// CHECK: [[INSTANCE:%[^,]+]] = apply
25+
// CHECK: [[ACCESS:%[^,]+]] = begin_access [modify] [static] [[ADDR]]
26+
// CHECK: store [[INSTANCE]] to [init] [[ACCESS]]
27+
// CHECK: end_access [[ACCESS]]
28+
// CHECK-LABEL: } // end sil function 'retract_value_lifetime_into_access_scope_when_access_scopes_not_respected'
29+
// CHECK-LABEL: end running test 2 of 2 on retract_value_lifetime_into_access_scope_when_access_scopes_not_respected: canonicalize-ossa-lifetime with: true, false, false, @trace
30+
sil [ossa] @retract_value_lifetime_into_access_scope_when_access_scopes_not_respected : $@convention(thin) () -> @out C {
31+
bb0(%addr : $*C):
32+
%instance = apply undef() : $@convention(thin) () -> @owned C
33+
debug_value [trace] %instance : $C
34+
// respect access scopes
35+
// VVVV
36+
test_specification "canonicalize-ossa-lifetime true false true @trace"
37+
test_specification "canonicalize-ossa-lifetime true false false @trace"
38+
// ^^^^^
39+
// respect access scopes
40+
%copy = copy_value %instance : $C
41+
%access = begin_access [modify] [static] %addr : $*C
42+
store %copy to [init] %access : $*C
43+
end_access %access : $*C
44+
destroy_value %instance : $C
45+
%retval = tuple ()
46+
return %retval : $()
47+
}

test/SILOptimizer/unit_test.sil

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -156,11 +156,11 @@ sil [ossa] @getC : $@convention(thin) () -> @owned C
156156
sil [ossa] @borrowC : $@convention(thin) (@guaranteed C) -> ()
157157
sil [ossa] @takeC : $@convention(thin) (@owned C) -> ()
158158

159-
// CHECK-LABEL: begin running test 1 of 1 on fn: canonicalize-ossa-lifetime with: true, true, @trace
160-
// CHECK-LABEL: end running test 1 of 1 on fn: canonicalize-ossa-lifetime with: true, true, @trace
159+
// CHECK-LABEL: begin running test 1 of 1 on fn: canonicalize-ossa-lifetime with: true, true, true, @trace
160+
// CHECK-LABEL: end running test 1 of 1 on fn: canonicalize-ossa-lifetime with: true, true, true, @trace
161161
sil [ossa] @fn : $@convention(thin) () -> () {
162162
entry:
163-
test_specification "canonicalize-ossa-lifetime true true @trace"
163+
test_specification "canonicalize-ossa-lifetime true true true @trace"
164164
%getC = function_ref @getC : $@convention(thin) () -> @owned C
165165
%c = apply %getC() : $@convention(thin) () -> @owned C
166166
debug_value [trace] %c : $C

0 commit comments

Comments
 (0)