Skip to content

Commit 39acb9a

Browse files
Merge pull request #79521 from nate-chandler/rdar145154549
[OSSACanonicalizeOwned] Dead-end extend base lifetime.
2 parents a7f9340 + 8e7dae5 commit 39acb9a

File tree

4 files changed

+144
-7
lines changed

4 files changed

+144
-7
lines changed

include/swift/SILOptimizer/Utils/CanonicalizeOSSALifetime.h

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,8 @@ class CanonicalizeOSSALifetime final {
237237
/// copies.
238238
const MaximizeLifetime_t maximizeLifetime;
239239

240+
SILFunction *function;
241+
240242
// If present, will be used to ensure that the lifetime is not shortened to
241243
// end inside an access scope which it previously enclosed. (Note that ending
242244
// before such an access scope is fine regardless.)
@@ -354,7 +356,7 @@ class CanonicalizeOSSALifetime final {
354356
DeadEndBlocksAnalysis *deadEndBlocksAnalysis, DominanceInfo *domTree,
355357
BasicCalleeAnalysis *calleeAnalysis, InstructionDeleter &deleter)
356358
: pruneDebugMode(pruneDebugMode), maximizeLifetime(maximizeLifetime),
357-
accessBlockAnalysis(accessBlockAnalysis),
359+
function(function), accessBlockAnalysis(accessBlockAnalysis),
358360
deadEndBlocksAnalysis(deadEndBlocksAnalysis), domTree(domTree),
359361
calleeAnalysis(calleeAnalysis), deleter(deleter) {}
360362

@@ -368,9 +370,7 @@ class CanonicalizeOSSALifetime final {
368370
currentDef = def;
369371
currentLexicalLifetimeEnds = lexicalLifetimeEnds;
370372

371-
if (maximizeLifetime || respectsDeinitBarriers()) {
372-
liveness->initializeDiscoveredBlocks(&discoveredBlocks);
373-
}
373+
liveness->initializeDiscoveredBlocks(&discoveredBlocks);
374374
liveness->initializeDef(getCurrentDef());
375375
}
376376

@@ -499,6 +499,7 @@ class CanonicalizeOSSALifetime final {
499499
PrunedLivenessBoundary &boundary);
500500

501501
void extendLivenessToDeadEnds();
502+
void extendLexicalLivenessToDeadEnds();
502503
void extendLivenessToDeinitBarriers();
503504

504505
void extendUnconsumedLiveness(PrunedLivenessBoundary const &boundary);

lib/SILOptimizer/Utils/CanonicalizeOSSALifetime.cpp

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,13 @@ bool CanonicalizeOSSALifetime::computeCanonicalLiveness() {
239239
break;
240240
case OperandOwnership::InteriorPointer:
241241
case OperandOwnership::AnyInteriorPointer:
242+
if (liveness->checkAndUpdateInteriorPointer(use) !=
243+
AddressUseKind::NonEscaping) {
244+
LLVM_DEBUG(llvm::dbgs()
245+
<< " Inner address use is escaping! Giving up\n");
246+
return false;
247+
}
248+
break;
242249
case OperandOwnership::GuaranteedForwarding:
243250
case OperandOwnership::EndBorrow:
244251
// Guaranteed values are exposed by inner adjacent reborrows. If user is
@@ -274,6 +281,49 @@ bool CanonicalizeOSSALifetime::computeCanonicalLiveness() {
274281
return true;
275282
}
276283

284+
/// Extend liveness to the availability boundary of currentDef. Even if a copy
285+
/// is consumed on a path to the dead-end, if the def stays live through to the
286+
/// dead-end, its lifetime must not be shrunk back from it (eventually we'll
287+
/// support shrinking it back to deinit barriers).
288+
///
289+
/// Example:
290+
/// %def is lexical
291+
/// %copy = copy_value %def
292+
/// consume %copy
293+
/// apply %foo() // deinit barrier
294+
/// // Must extend lifetime of %def up to this point per language rules.
295+
/// unreachable
296+
void CanonicalizeOSSALifetime::extendLexicalLivenessToDeadEnds() {
297+
// TODO: OSSALifetimeCompletion: Once lifetimes are always complete, delete
298+
// this method.
299+
SmallVector<SILBasicBlock *, 32> directDiscoverdBlocks;
300+
SSAPrunedLiveness directLiveness(function, &directDiscoverdBlocks);
301+
directLiveness.initializeDef(getCurrentDef());
302+
directLiveness.computeSimple();
303+
OSSALifetimeCompletion::visitAvailabilityBoundary(
304+
getCurrentDef(), directLiveness, [&](auto *unreachable, auto end) {
305+
if (end == OSSALifetimeCompletion::LifetimeEnd::Boundary) {
306+
recordUnreachableLifetimeEnd(unreachable);
307+
}
308+
unreachable->visitPriorInstructions([&](auto *inst) {
309+
liveness->extendToNonUse(inst);
310+
return true;
311+
});
312+
});
313+
}
314+
315+
/// Extend liveness to the copy-extended availability boundary of currentDef.
316+
/// Prevents destroys from being inserted between borrows of (copies of) the
317+
/// def and dead-ends.
318+
///
319+
/// Example:
320+
/// %def need not be lexical
321+
/// %c = copy_value %def
322+
/// %sb = store_borrow %c to %addr
323+
/// // Must extend lifetime of %def up to this point. Otherwise, a
324+
/// // destroy_value could be inserted within a borrow scope or interior
325+
/// // pointer use.
326+
/// unreachable
277327
void CanonicalizeOSSALifetime::extendLivenessToDeadEnds() {
278328
// TODO: OSSALifetimeCompletion: Once lifetimes are always complete, delete
279329
// this method.
@@ -1357,7 +1407,10 @@ bool CanonicalizeOSSALifetime::computeLiveness() {
13571407
return false;
13581408
}
13591409
if (respectsDeinitBarriers()) {
1360-
extendLivenessToDeadEnds();
1410+
extendLexicalLivenessToDeadEnds();
1411+
}
1412+
extendLivenessToDeadEnds();
1413+
if (respectsDeinitBarriers()) {
13611414
extendLivenessToDeinitBarriers();
13621415
}
13631416
if (accessBlockAnalysis) {

test/SILOptimizer/assemblyvision_remark/chacha.swift

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,8 @@ func checkResult(_ plaintext: [UInt8]) {
2828

2929
@_semantics("optremark.sil-assembly-vision-remark-gen")
3030
public func run_ChaCha(_ N: Int) {
31-
let key = Array(repeating: UInt8(1), count: 32) // expected-remark {{release of type '}}
32-
let nonce = Array(repeating: UInt8(2), count: 12) // expected-remark {{release of type '}}
31+
let key = Array(repeating: UInt8(1), count: 32) // expected-note {{of 'key}}
32+
let nonce = Array(repeating: UInt8(2), count: 12) // expected-note {{of 'nonce}}
3333

3434
var checkedtext = Array(repeating: UInt8(0), count: 1024) // expected-note {{of 'checkedtext}}
3535
ChaCha20.encrypt(bytes: &checkedtext, key: key, nonce: nonce)
@@ -44,3 +44,5 @@ public func run_ChaCha(_ N: Int) {
4444
}
4545
} // expected-remark {{release of type '}}
4646
// expected-remark @-1 {{release of type '}}
47+
// expected-remark @-2 {{release of type '}}
48+
// expected-remark @-3 {{release of type '}}

test/SILOptimizer/canonicalize_ossa_lifetime_unit.sil

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,27 @@
11
// RUN: %target-sil-opt -sil-print-types -test-runner %s -o /dev/null 2>&1 | %FileCheck %s
22

3+
import Builtin
34
import Swift
45

56
class C {}
7+
8+
enum Never {}
9+
10+
struct Pointer {
11+
var rawValue: Builtin.RawPointer
12+
}
13+
14+
struct Int32 {
15+
var int: Builtin.Int32
16+
}
17+
sil @run : $@convention(thin) () -> Never
618
sil @getOwned : $@convention(thin) () -> @owned C
719
sil @barrier : $@convention(thin) () -> ()
820
sil [ossa] @getC : $@convention(thin) () -> @owned C
921
sil [ossa] @borrowC : $@convention(thin) (@guaranteed C) -> ()
1022
sil [ossa] @takeC : $@convention(thin) (@owned C) -> ()
23+
sil @getPointer : $@convention(thin) () -> (@owned Pointer)
24+
1125
struct S {}
1226

1327
struct MoS: ~Copyable {}
@@ -885,6 +899,33 @@ die:
885899
unreachable
886900
}
887901

902+
// CHECK-LABEL: begin running test {{.*}} on consume_copy_before_use_in_dead_end_2
903+
// CHECK-LABEL: sil [ossa] @consume_copy_before_use_in_dead_end_2 : {{.*}} {
904+
// CHECK-NOT: destroy_value [dead_end]
905+
// CHECK-LABEL: } // end sil function 'consume_copy_before_use_in_dead_end_2'
906+
// CHECK-LABEL: end running test {{.*}} on consume_copy_before_use_in_dead_end_2
907+
sil [ossa] @consume_copy_before_use_in_dead_end_2 : $@convention(thin) (@owned C) -> () {
908+
entry(%c : @owned $C):
909+
cond_br undef, exit, die
910+
911+
exit:
912+
destroy_value %c
913+
%retval = tuple ()
914+
return %retval
915+
916+
die:
917+
%move = move_value %c
918+
%copy = copy_value %move
919+
specify_test "canonicalize_ossa_lifetime true false true %move"
920+
apply undef(%move) : $@convention(thin) (@owned C) -> ()
921+
%addr = alloc_stack $C
922+
%token = store_borrow %copy to %addr
923+
apply undef() : $@convention(thin) () -> ()
924+
%reload = load_borrow %token
925+
apply undef(%reload) : $@convention(thin) (@guaranteed C) -> ()
926+
unreachable
927+
}
928+
888929
// The destroy of a value must not be hoisted over a destroy of a copy of a
889930
// partial_apply [on_stack] which captures the value.
890931
// CHECK-LABEL: begin running test {{.*}} on destroy_after_pa_copy_destroy
@@ -918,3 +959,43 @@ entry(%a: @owned $C):
918959
%retval = tuple ()
919960
return %retval
920961
}
962+
963+
// CHECK-LABEL: begin running test {{.*}} on extend_lifetime_to_deadend_despite_copy_consume
964+
// CHECK-LABEL: sil [ossa] @extend_lifetime_to_deadend_despite_copy_consume : {{.*}} {
965+
// CHECK: copy_value
966+
// CHECK-LABEL: } // end sil function 'extend_lifetime_to_deadend_despite_copy_consume'
967+
// CHECK-LABEL: end running test {{.*}} on extend_lifetime_to_deadend_despite_copy_consume
968+
sil [ossa] @extend_lifetime_to_deadend_despite_copy_consume : $@convention(thin) (@owned C) -> () {
969+
entry(%c : @owned $C):
970+
specify_test "canonicalize_ossa_lifetime true false true @argument"
971+
%cc = copy_value %c
972+
%takeC = function_ref @takeC : $@convention(thin) (@owned C) -> ()
973+
apply %takeC(%cc) : $@convention(thin) (@owned C) -> ()
974+
%run = function_ref @run : $@convention(thin) () -> Never
975+
unreachable
976+
}
977+
978+
/// If there's a pointer escape (here, mark_dependence) of the value, we can't
979+
/// canonicalize its lifetime.
980+
// CHECK-LABEL: begin running test {{.*}} on dont_canonicalize_on_pointer_escape
981+
// CHECK-LABEL: sil [ossa] @dont_canonicalize_on_pointer_escape : {{.*}} {
982+
// CHECK: load
983+
// CHECK-NEXT: destroy_value
984+
// CHECK-LABEL: } // end sil function 'dont_canonicalize_on_pointer_escape'
985+
// CHECK-LABEL: end running test {{.*}} on dont_canonicalize_on_pointer_escape
986+
sil [ossa] @dont_canonicalize_on_pointer_escape : $@convention(thin) () -> () {
987+
entry:
988+
%getC = function_ref @getC : $@convention(thin) () -> (@owned C)
989+
%getPointer = function_ref @getPointer : $@convention(thin) () -> (@owned Pointer)
990+
%c = apply %getC() : $@convention(thin) () -> (@owned C)
991+
%pointer = apply %getPointer() : $@convention(thin) () -> (@owned Pointer)
992+
specify_test "canonicalize_ossa_lifetime true false true %c"
993+
%rawPointer = struct_extract %pointer, #Pointer.rawValue
994+
%o = pointer_to_address %rawPointer to [strict] $*Int32
995+
%dependent = mark_dependence [nonescaping] %o on %c
996+
%int_addr = struct_element_addr %dependent, #Int32.int
997+
%int = load [trivial] %int_addr
998+
destroy_value %c
999+
%retval = tuple ()
1000+
return %retval : $()
1001+
}

0 commit comments

Comments
 (0)