Skip to content

Commit ad6028f

Browse files
committed
Add support for hoisting load take - store init pairs.
1 parent aa10de2 commit ad6028f

File tree

2 files changed

+46
-27
lines changed

2 files changed

+46
-27
lines changed

SwiftCompilerSources/Sources/Optimizer/FunctionPasses/LoopInvariantCodeMotion.swift

Lines changed: 18 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -421,7 +421,7 @@ private extension AnalyzedInstructions {
421421
if (loopSideEffects.contains { sideEffect in
422422
switch sideEffect {
423423
case let storeInst as StoreInst:
424-
if storeInst.isNonInitializingStoreTo(accessPath) {
424+
if storeInst.storesTo(accessPath) {
425425
return false
426426
}
427427
case let loadInst as LoadInst:
@@ -438,13 +438,13 @@ private extension AnalyzedInstructions {
438438
}
439439

440440
if (loads.contains { loadInst in
441-
loadInst.mayRead(fromAddress: storeAddr, aliasAnalysis) && loadInst.loadOwnership != .take && !loadInst.overlaps(accessPath: accessPath)
441+
loadInst.mayRead(fromAddress: storeAddr, aliasAnalysis) && !loadInst.overlaps(accessPath: accessPath)
442442
}) {
443443
return false
444444
}
445445

446446
if (stores.contains { storeInst in
447-
storeInst.mayWrite(toAddress: storeAddr, aliasAnalysis) && !storeInst.isNonInitializingStoreTo(accessPath)
447+
storeInst.mayWrite(toAddress: storeAddr, aliasAnalysis) && !storeInst.storesTo(accessPath)
448448
}) {
449449
return false
450450
}
@@ -676,10 +676,10 @@ private extension MovableInstructions {
676676
) -> Bool {
677677
// Initially load the value in the loop pre header.
678678
let builder = Builder(before: loop.preheader!.terminator, context)
679-
var storeAddr: Value?
679+
var firstStore: StoreInst?
680680

681681
// If there are multiple stores in a block, only the last one counts.
682-
for case let storeInst as StoreInst in loadsAndStores where storeInst.isNonInitializingStoreTo(accessPath) {
682+
for case let storeInst as StoreInst in loadsAndStores where storeInst.storesTo(accessPath) {
683683
// If a store just stores the loaded value, bail. The operand (= the load)
684684
// will be removed later, so it cannot be used as available value.
685685
// This corner case is surprisingly hard to handle, so we just give up.
@@ -688,9 +688,9 @@ private extension MovableInstructions {
688688
return false
689689
}
690690

691-
if storeAddr == nil {
692-
storeAddr = storeInst.destination
693-
} else if storeInst.destination.type != storeAddr!.type {
691+
if firstStore == nil {
692+
firstStore = storeInst
693+
} else if storeInst.destination.type != firstStore!.destination.type {
694694
// This transformation assumes that the values of all stores in the loop
695695
// must be interchangeable. It won't work if stores different types
696696
// because of casting or payload extraction even though they have the
@@ -699,26 +699,26 @@ private extension MovableInstructions {
699699
}
700700
}
701701

702-
guard let storeAddr else {
702+
guard let firstStore else {
703703
return false
704704
}
705705

706706
var ssaUpdater = SSAUpdater(
707-
function: storeAddr.parentFunction,
708-
type: storeAddr.type.objectType,
709-
ownership: .none,
707+
function: firstStore.parentFunction,
708+
type: firstStore.destination.type.objectType,
709+
ownership: firstStore.storeOwnership == .initialize ? .owned : .none,
710710
context
711711
)
712712

713713
// Set all stored values as available values in the ssaUpdater.
714-
for case let storeInst as StoreInst in loadsAndStores where storeInst.isNonInitializingStoreTo(accessPath) {
714+
for case let storeInst as StoreInst in loadsAndStores where storeInst.storesTo(accessPath) {
715715
ssaUpdater.addAvailableValue(storeInst.source, in: storeInst.parentBlock)
716716
}
717717

718718
var cloner = Cloner(cloneBefore: loop.preheader!.terminator, context)
719719
defer { cloner.deinitialize() }
720720

721-
guard let initialAddr = (cloner.cloneRecursively(value: storeAddr) { srcAddr, cloner in
721+
guard let initialAddr = (cloner.cloneRecursively(value: firstStore.destination) { srcAddr, cloner in
722722
switch srcAddr {
723723
case is AllocStackInst, is BeginBorrowInst, is MarkDependenceInst:
724724
return .stopCloning
@@ -737,7 +737,7 @@ private extension MovableInstructions {
737737
return false
738738
}
739739

740-
let ownership: LoadInst.LoadOwnership = loop.preheader!.terminator.parentFunction.hasOwnership ? .trivial : .unqualified
740+
let ownership: LoadInst.LoadOwnership = firstStore.parentFunction.hasOwnership ? (firstStore.storeOwnership == .initialize ? .take : .trivial) : .unqualified
741741

742742
let initialLoad = builder.createLoad(fromAddress: initialAddr, ownership: ownership)
743743
ssaUpdater.addAvailableValue(initialLoad, in: loop.preheader!)
@@ -757,7 +757,7 @@ private extension MovableInstructions {
757757
currentVal = nil
758758
}
759759

760-
if let storeInst = inst as? StoreInst, storeInst.isNonInitializingStoreTo(accessPath) {
760+
if let storeInst = inst as? StoreInst, storeInst.storesTo(accessPath) {
761761
currentVal = storeInst.source
762762
context.erase(instruction: storeInst)
763763
changed = true
@@ -797,11 +797,10 @@ private extension MovableInstructions {
797797

798798
let builder = Builder(before: exitBlock.instructions.first!, context)
799799

800-
let ownership: StoreInst.StoreOwnership = exitBlock.instructions.first!.parentFunction.hasOwnership ? .trivial : .unqualified
801800
builder.createStore(
802801
source: ssaUpdater.getValue(inMiddleOf: exitBlock),
803802
destination: initialAddr,
804-
ownership: ownership
803+
ownership: firstStore.storeOwnership
805804
)
806805
changed = true
807806
}
@@ -982,11 +981,7 @@ private extension Instruction {
982981

983982
private extension StoreInst {
984983
/// Returns a `true` if this store is a store to `accessPath`.
985-
func isNonInitializingStoreTo(_ accessPath: AccessPath) -> Bool {
986-
if self.storeOwnership == .initialize {
987-
return false
988-
}
989-
984+
func storesTo(_ accessPath: AccessPath) -> Bool {
990985
return accessPath == self.destination.accessPath
991986
}
992987
}

test/SILOptimizer/licm.sil

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1539,14 +1539,14 @@ bb2:
15391539
return %r1 : $()
15401540
}
15411541

1542-
// CHECK-LABEL: sil [ossa] @dont_hoist_non_trivial_load :
1543-
// CHECK: %1 = load_borrow %0
1542+
// CHECK-LABEL: sil [ossa] @hoist_load_copy :
1543+
// CHECK: %1 = load_borrow %0
15441544
// CHECK: bb1:
15451545
// CHECK-NEXT: %3 = copy_value %1
15461546
// CHECK: bb3:
15471547
// CHECK-NEXT: end_borrow %1
1548-
// CHECK: } // end sil function 'dont_hoist_non_trivial_load'
1549-
sil [ossa] @dont_hoist_non_trivial_load : $@convention(thin) (@inout Builtin.NativeObject) -> () {
1548+
// CHECK: } // end sil function 'hoist_load_copy'
1549+
sil [ossa] @hoist_load_copy : $@convention(thin) (@inout Builtin.NativeObject) -> () {
15501550
bb0(%0 : $*Builtin.NativeObject):
15511551
br bb1
15521552
bb1:
@@ -1561,6 +1561,30 @@ bb3:
15611561
return %r : $()
15621562
}
15631563

1564+
// CHECK-LABEL: sil [ossa] @hoist_load_take_store_init :
1565+
// CHECK: %2 = load [take] %0
1566+
// CHECK: bb1(%4 : @owned $S):
1567+
// CHECK-NEXT: fix_lifetime %4
1568+
// CHECK: bb3:
1569+
// CHECK-NEXT: store %7 to [init] %0
1570+
// CHECK: } // end sil function 'hoist_load_take_store_init'
1571+
sil [ossa] @hoist_load_take_store_init : $@convention(thin) (@inout S, @guaranteed S) -> () {
1572+
bb0(%0 : $*S, %new_val : @guaranteed $S):
1573+
br bb1
1574+
bb1:
1575+
%2 = load [take] %0
1576+
fix_lifetime %2
1577+
destroy_value %2
1578+
%copied = copy_value %new_val
1579+
store %copied to [init] %0
1580+
cond_br undef, bb2, bb3
1581+
bb2:
1582+
br bb1
1583+
bb3:
1584+
%r = tuple ()
1585+
return %r : $()
1586+
}
1587+
15641588
// CHECK-LABEL: sil [ossa] @hoist_trivial_load :
15651589
// CHECK: load [trivial]
15661590
// CHECK-NEXT: br bb1

0 commit comments

Comments
 (0)