Skip to content

Commit d64d839

Browse files
committed
Add store_borrow support.
1 parent ad6028f commit d64d839

File tree

6 files changed

+100
-35
lines changed

6 files changed

+100
-35
lines changed

SwiftCompilerSources/Sources/Optimizer/FunctionPasses/InitializeStaticGlobals.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -289,7 +289,7 @@ private indirect enum GlobalInitValue {
289289
fatalError("cannot materialize undefined init value")
290290

291291
case .constant(let value):
292-
return cloner.cloneRecursivelyToGlobal(value: value)
292+
return cloner.cloneRecursively(globalInitValue: value)
293293

294294
case .aggregate(let fields):
295295
if type.isStruct {

SwiftCompilerSources/Sources/Optimizer/FunctionPasses/LoopInvariantCodeMotion.swift

Lines changed: 67 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -649,6 +649,19 @@ private extension MovableInstructions {
649649
var changed = false
650650

651651
for scopedInst in scopedInsts {
652+
if let storeBorrowInst = scopedInst as? StoreBorrowInst {
653+
_ = storeBorrowInst.allocStack.hoist(outOf: loop, context)
654+
655+
var sankFirst = false
656+
for deallocStack in storeBorrowInst.allocStack.deallocations {
657+
if sankFirst {
658+
context.erase(instruction: deallocStack)
659+
} else {
660+
sankFirst = deallocStack.sink(outOf: loop, context)
661+
}
662+
}
663+
}
664+
652665
guard scopedInst.hoist(outOf: loop, context) else {
653666
continue
654667
}
@@ -706,7 +719,7 @@ private extension MovableInstructions {
706719
var ssaUpdater = SSAUpdater(
707720
function: firstStore.parentFunction,
708721
type: firstStore.destination.type.objectType,
709-
ownership: firstStore.storeOwnership == .initialize ? .owned : .none,
722+
ownership: firstStore.source.ownership,
710723
context
711724
)
712725

@@ -868,7 +881,7 @@ private extension Instruction {
868881
}
869882

870883
if let singleValueInst = self as? SingleValueInstruction,
871-
!(self is BeginAccessInst),
884+
!(self is ScopedInstruction),
872885
let identicalInst = (loop.preheader!.instructions.first { otherInst in
873886
return singleValueInst != otherInst && singleValueInst.isIdenticalTo(otherInst)
874887
}) {
@@ -1070,43 +1083,70 @@ private extension ScopedInstruction where Self: UnaryInstruction {
10701083
return false
10711084
}
10721085

1073-
let areBeginAccessesSafe = analyzedInstructions.scopedInsts
1074-
.allSatisfy { otherScopedInst in
1075-
guard self != otherScopedInst else { return true }
1086+
// Instruction specific preconditions
1087+
switch self {
1088+
case is BeginAccessInst, is LoadBorrowInst:
1089+
guard (analyzedInstructions.scopedInsts
1090+
.allSatisfy { otherScopedInst in
1091+
guard self != otherScopedInst else { return true }
10761092

1077-
return operand.value.accessPath.isDistinct(from: otherScopedInst.operand.value.accessPath)
1093+
return operand.value.accessPath.isDistinct(from: otherScopedInst.operand.value.accessPath)
1094+
}) else {
1095+
return false
10781096
}
1079-
1080-
guard areBeginAccessesSafe else { return false }
1097+
default:
1098+
break
1099+
}
10811100

10821101
var scope = InstructionRange(begin: self, ends: endInstructions, context)
10831102
defer { scope.deinitialize() }
1084-
1085-
for fullApplyInst in analyzedInstructions.fullApplies {
1086-
guard mayWriteToMemory && fullApplyInst.mayReadOrWrite(address: operand.value, context.aliasAnalysis) ||
1087-
!mayWriteToMemory && fullApplyInst.mayWrite(toAddress: operand.value, context.aliasAnalysis) else {
1088-
continue
1089-
}
1090-
1091-
// After hoisting the begin/end_access the apply will be within the scope, so it must not have a conflicting access.
1092-
if !scope.contains(fullApplyInst) {
1093-
return false
1103+
1104+
// Instruction specific range related conditions
1105+
switch self {
1106+
case is BeginApplyInst:
1107+
return false // TODO: Add support. Like read-only apply hoist + end_apply sink.
1108+
case is LoadBorrowInst:
1109+
for storeInst in analyzedInstructions.stores {
1110+
if storeInst.mayWrite(toAddress: operand.value, context.aliasAnalysis) {
1111+
if !scope.contains(storeInst) {
1112+
return false
1113+
}
1114+
}
10941115
}
1095-
}
1116+
1117+
fallthrough
1118+
case is BeginAccessInst:
1119+
for fullApplyInst in analyzedInstructions.fullApplies {
1120+
guard mayWriteToMemory && fullApplyInst.mayReadOrWrite(address: operand.value, context.aliasAnalysis) ||
1121+
!mayWriteToMemory && fullApplyInst.mayWrite(toAddress: operand.value, context.aliasAnalysis) else {
1122+
continue
1123+
}
10961124

1097-
switch operand.value.accessPath.base {
1098-
case .class, .global:
1099-
for sideEffect in analyzedInstructions.loopSideEffects where sideEffect.mayRelease {
1100-
// Since a class might have a deinitializer, hoisting begin/end_access pair could violate
1101-
// exclusive access if the deinitializer accesses address used by begin_access.
1102-
if !scope.contains(sideEffect) {
1125+
// After hoisting the begin/end_access the apply will be within the scope, so it must not have a conflicting access.
1126+
if !scope.contains(fullApplyInst) {
11031127
return false
11041128
}
11051129
}
1130+
1131+
switch operand.value.accessPath.base {
1132+
case .class, .global:
1133+
for sideEffect in analyzedInstructions.loopSideEffects where sideEffect.mayRelease {
1134+
// Since a class might have a deinitializer, hoisting begin/end_access pair could violate
1135+
// exclusive access if the deinitializer accesses address used by begin_access.
1136+
if !scope.contains(sideEffect) {
1137+
return false
1138+
}
1139+
}
11061140

1107-
return true
1141+
return true
1142+
default:
1143+
return true
1144+
}
1145+
case is BeginBorrowInst, is StoreBorrowInst:
1146+
// Ensure the value is produced outside the loop.
1147+
return !loop.contains(block: operand.value.parentBlock)
11081148
default:
1109-
return true
1149+
return false
11101150
}
11111151
}
11121152
}

SwiftCompilerSources/Sources/Optimizer/FunctionPasses/ObjectOutliner.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -370,7 +370,7 @@ private func constructObject(of allocRef: AllocRefInstBase,
370370
// Create the initializers for the fields
371371
var objectArgs = [Value]()
372372
for store in storesToClassFields {
373-
objectArgs.append(cloner.cloneRecursivelyToGlobal(value: store.source as! SingleValueInstruction))
373+
objectArgs.append(cloner.cloneRecursively(globalInitValue: store.source as! SingleValueInstruction))
374374
}
375375
let globalBuilder = Builder(staticInitializerOf: global, context)
376376

@@ -382,15 +382,15 @@ private func constructObject(of allocRef: AllocRefInstBase,
382382
for elementIdx in 0..<allocRef.numTailElements! {
383383
let tupleElems = (0..<numTailTupleElems).map { tupleIdx in
384384
let store = storesToTailElements[elementIdx * numTailTupleElems + tupleIdx]
385-
return cloner.cloneRecursivelyToGlobal(value: store.source as! SingleValueInstruction)
385+
return cloner.cloneRecursively(globalInitValue: store.source as! SingleValueInstruction)
386386
}
387387
let tuple = globalBuilder.createTuple(type: allocRef.tailAllocatedTypes[0], elements: tupleElems)
388388
objectArgs.append(tuple)
389389
}
390390
} else {
391391
// The non-tuple element case.
392392
for store in storesToTailElements {
393-
objectArgs.append(cloner.cloneRecursivelyToGlobal(value: store.source as! SingleValueInstruction))
393+
objectArgs.append(cloner.cloneRecursively(globalInitValue: store.source as! SingleValueInstruction))
394394
}
395395
}
396396
}

SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyLoad.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ extension LoadInst : OnoneSimplifiable, SILCombineSimplifiable {
107107
var cloner = Cloner(cloneBefore: self, context)
108108
defer { cloner.deinitialize() }
109109

110-
let initVal = cloner.cloneRecursivelyToGlobal(value: globalInitVal)
110+
let initVal = cloner.cloneRecursively(globalInitValue: globalInitVal)
111111

112112
uses.replaceAll(with: initVal, context)
113113
// Also erases a builtin "once" on which the global_addr depends on. This is fine

SwiftCompilerSources/Sources/SIL/Utilities/Cloner.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -72,14 +72,14 @@ public struct Cloner<Context: MutatingContext> {
7272
return cloned
7373
}
7474

75-
public mutating func cloneRecursivelyToGlobal(value: Value) -> Value {
76-
guard let cloned = cloneRecursively(value: value, customGetCloned: { value, cloner in
75+
public mutating func cloneRecursively(globalInitValue: Value) -> Value {
76+
guard let cloned = cloneRecursively(value: globalInitValue, customGetCloned: { value, cloner in
7777
guard let beginAccess = value as? BeginAccessInst else {
7878
return .defaultValue
7979
}
8080

8181
// Skip access instructions, which might be generated for UnsafePointer globals which point to other globals.
82-
let clonedOperand = cloner.cloneRecursivelyToGlobal(value: beginAccess.address)
82+
let clonedOperand = cloner.cloneRecursively(globalInitValue: beginAccess.address)
8383
cloner.recordFoldedValue(beginAccess, mappedTo: clonedOperand)
8484
return .customValue(clonedOperand)
8585
}) else {

test/SILOptimizer/licm.sil

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1643,6 +1643,31 @@ bb3:
16431643
return %r : $()
16441644
}
16451645

1646+
// CHECK-LABEL: sil [ossa] @hoist_store_borrow :
1647+
// CHECK: bb0(%0 : @guaranteed $S):
1648+
// CHECK-NEXT: alloc_stack
1649+
// CHECK-NEXT: store_borrow
1650+
// CHECK: bb3:
1651+
// CHECK-NEXT: end_borrow
1652+
// CHECK-NEXT: dealloc_stack
1653+
// CHECK: } // end sil function 'hoist_store_borrow'
1654+
sil [ossa] @hoist_store_borrow : $@convention(thin) (@guaranteed S) -> () {
1655+
bb0(%0 : @guaranteed $S):
1656+
br bb1
1657+
bb1:
1658+
%s = alloc_stack $S
1659+
%2 = store_borrow %0 to %s
1660+
fix_lifetime %2
1661+
end_borrow %2
1662+
dealloc_stack %s
1663+
cond_br undef, bb2, bb3
1664+
bb2:
1665+
br bb1
1666+
bb3:
1667+
%r = tuple ()
1668+
return %r : $()
1669+
}
1670+
16461671
sil @foo : $@convention(thin) (@guaranteed { var Int }) -> ()
16471672

16481673
// CHECK-LABEL: sil [ossa] @test_begin_access : $@convention(thin) (Int) -> () {

0 commit comments

Comments
 (0)