Skip to content

Commit eae7864

Browse files
authored
Merge pull request #83988 from MAJKFL/new-sil-licm-pass-copy
New SIL LICM pass
2 parents 57ae9de + bab0011 commit eae7864

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

47 files changed

+2195
-2035
lines changed

SwiftCompilerSources/Sources/Optimizer/Analysis/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
swift_compiler_sources(Optimizer
1010
AliasAnalysis.swift
1111
CalleeAnalysis.swift
12+
LoopTree.swift
1213
DeadEndBlocksAnalysis.swift
1314
DominatorTree.swift
1415
PostDominatorTree.swift)

SwiftCompilerSources/Sources/Optimizer/Analysis/DominatorTree.swift

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,31 @@ import OptimizerBridging
1515

1616
struct DominatorTree {
1717
let bridged: BridgedDomTree
18+
19+
func getChildren(of block: BasicBlock) -> DomChildren {
20+
return DomChildren(bridgedDomTree: bridged, bb: block)
21+
}
22+
}
23+
24+
struct DomChildren: BridgedRandomAccessCollection {
25+
private let bridgedDomTree: BridgedDomTree
26+
private let block: BasicBlock
27+
28+
let count: Int
29+
30+
var startIndex: Int { return 0 }
31+
var endIndex: Int { return count }
32+
33+
init(bridgedDomTree: BridgedDomTree, bb: BasicBlock) {
34+
self.bridgedDomTree = bridgedDomTree
35+
self.block = bb
36+
self.count = bridgedDomTree.getNumberOfChildren(bb.bridged)
37+
}
38+
39+
subscript(_ index: Int) -> BasicBlock {
40+
assert(index >= startIndex && index < endIndex)
41+
return bridgedDomTree.getChildAt(block.bridged, index).block
42+
}
1843
}
1944

2045
extension BasicBlock {
Lines changed: 213 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,213 @@
1+
//===--- LoopTree.swift ---------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2025 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
import SIL
14+
import OptimizerBridging
15+
16+
/// Describes top level loops.
17+
struct LoopTree {
18+
fileprivate let bridged: BridgedLoopTree
19+
20+
let loops: TopLevelLoopArray
21+
22+
init(bridged: BridgedLoopTree, context: FunctionPassContext) {
23+
self.bridged = bridged
24+
self.loops = TopLevelLoopArray(bridged)
25+
}
26+
}
27+
28+
/// Describes a loop with its children.
29+
struct Loop {
30+
private let bridged: BridgedLoop
31+
32+
let innerLoops: LoopArray
33+
let loopBlocks: LoopBlocks
34+
35+
var exitingAndLatchBlocks: some Sequence<BasicBlock> {
36+
return header.predecessors.lazy
37+
.filter { predecessor in
38+
contains(block: predecessor) && !isLoopExiting(loopBlock: predecessor)
39+
} + exitingBlocks
40+
}
41+
42+
/// Exit blocks of the loop.
43+
///
44+
/// - Note: Some exit blocks will be duplicated if the loop has critical edges.
45+
var exitBlocks: some Sequence<BasicBlock> {
46+
return loopBlocks.lazy
47+
.flatMap(\.successors)
48+
.filter { !contains(block: $0) }
49+
}
50+
51+
var exitingBlocks: some Sequence<BasicBlock> {
52+
return loopBlocks.lazy
53+
.filter { isLoopExiting(loopBlock: $0) }
54+
}
55+
56+
init(bridged: BridgedLoop) {
57+
self.bridged = bridged
58+
self.innerLoops = LoopArray(bridged)
59+
self.loopBlocks = LoopBlocks(bridged)
60+
}
61+
62+
var preheader: BasicBlock? {
63+
bridged.getPreheader().block
64+
}
65+
66+
var header: BasicBlock {
67+
bridged.getHeader().block
68+
}
69+
70+
/// Returns `true` if the loop has exactly one exit block.
71+
var hasSingleExitBlock: Bool {
72+
return exitBlocks.singleElement != nil
73+
}
74+
75+
var hasNoExitBlocks: Bool {
76+
return exitBlocks.isEmpty
77+
}
78+
79+
private func isLoopExiting(loopBlock: BasicBlock) -> Bool {
80+
return loopBlock.successors.contains { !contains(block: $0) }
81+
}
82+
83+
func getBlocksThatDominateAllExitingAndLatchBlocks(_ context: FunctionPassContext) -> [BasicBlock] {
84+
var result: [BasicBlock] = []
85+
var cachedExitingAndLatchBlocks = Stack<BasicBlock>(context)
86+
var workList = BasicBlockWorklist(context)
87+
defer {
88+
cachedExitingAndLatchBlocks.deinitialize()
89+
workList.deinitialize()
90+
}
91+
92+
cachedExitingAndLatchBlocks.append(contentsOf: exitingAndLatchBlocks)
93+
workList.pushIfNotVisited(header)
94+
95+
while let block = workList.pop() {
96+
guard cachedExitingAndLatchBlocks.allSatisfy({ exitBlock in
97+
return block.dominates(exitBlock, context.dominatorTree)
98+
}) else {
99+
continue
100+
}
101+
102+
result.append(block)
103+
104+
workList.pushIfNotVisited(contentsOf: context.dominatorTree.getChildren(of: block))
105+
}
106+
107+
return result
108+
}
109+
110+
func contains(block: BasicBlock) -> Bool {
111+
return bridged.contains(block.bridged)
112+
}
113+
114+
func splitCriticalExitingAndBackEdges(_ context: FunctionPassContext) {
115+
for exitingOrLatchBlock in exitingAndLatchBlocks {
116+
for (index, succesor) in exitingOrLatchBlock.successors.enumerated() where !contains(block: succesor) {
117+
splitCriticalEdge(
118+
from: exitingOrLatchBlock.terminator.parentBlock,
119+
toEdgeIndex: index,
120+
dominatorTree: context.dominatorTree,
121+
loopTree: context.loopTree,
122+
context
123+
)
124+
}
125+
}
126+
}
127+
}
128+
129+
struct TopLevelLoopArray: BridgedRandomAccessCollection {
130+
private let bridgedLoopTree: BridgedLoopTree
131+
132+
let count: Int
133+
134+
var startIndex: Int { return 0 }
135+
var endIndex: Int { return count }
136+
137+
init(_ bridgedLoopTree: BridgedLoopTree) {
138+
self.bridgedLoopTree = bridgedLoopTree
139+
self.count = bridgedLoopTree.getTopLevelLoopCount()
140+
}
141+
142+
subscript(_ index: Int) -> Loop {
143+
assert(index >= startIndex && index < endIndex)
144+
return Loop(bridged: bridgedLoopTree.getLoop(index))
145+
}
146+
}
147+
148+
struct LoopArray: BridgedRandomAccessCollection {
149+
private let bridgedLoop: BridgedLoop
150+
151+
let count: Int
152+
153+
var startIndex: Int { return 0 }
154+
var endIndex: Int { return count }
155+
156+
init(_ bridgedLoop: BridgedLoop) {
157+
self.bridgedLoop = bridgedLoop
158+
self.count = bridgedLoop.getInnerLoopCount()
159+
}
160+
161+
subscript(_ index: Int) -> Loop {
162+
assert(index >= startIndex && index < endIndex)
163+
return Loop(bridged: bridgedLoop.getInnerLoop(index))
164+
}
165+
}
166+
167+
struct LoopBlocks: BridgedRandomAccessCollection {
168+
private let bridgedLoop: BridgedLoop
169+
170+
let count: Int
171+
172+
var startIndex: Int { return 0 }
173+
var endIndex: Int { return count }
174+
175+
init(_ bridgedLoop: BridgedLoop) {
176+
self.bridgedLoop = bridgedLoop
177+
self.count = bridgedLoop.getBasicBlockCount()
178+
}
179+
180+
subscript(_ index: Int) -> BasicBlock {
181+
assert(index >= startIndex && index < endIndex)
182+
return bridgedLoop.getBasicBlock(index).block
183+
}
184+
}
185+
186+
func splitEdge(
187+
from block: BasicBlock,
188+
toEdgeIndex: Int,
189+
dominatorTree: DominatorTree,
190+
loopTree: LoopTree,
191+
_ context: some MutatingContext
192+
) -> BasicBlock {
193+
let result = loopTree.bridged.splitEdge(block.bridged, toEdgeIndex, dominatorTree.bridged).block
194+
195+
context.notifyBranchesChanged()
196+
return result
197+
}
198+
199+
/// If the specified edge is critical, the function returns inserted block. Otherwise returns `nil`.
200+
@discardableResult
201+
func splitCriticalEdge(
202+
from block: BasicBlock,
203+
toEdgeIndex: Int,
204+
dominatorTree: DominatorTree,
205+
loopTree: LoopTree,
206+
_ context: some MutatingContext
207+
) -> BasicBlock? {
208+
guard block.isCriticalEdge(edgeIndex: toEdgeIndex) else {
209+
return nil
210+
}
211+
212+
return splitEdge(from: block, toEdgeIndex: toEdgeIndex, dominatorTree: dominatorTree, loopTree: loopTree, context)
213+
}

SwiftCompilerSources/Sources/Optimizer/FunctionPasses/AllocBoxToStack.swift

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -322,10 +322,9 @@ private struct FunctionSpecializations {
322322
// This can happen if a previous run of the pass already created this specialization.
323323
return
324324
}
325-
let cloner = SpecializationCloner(emptySpecializedFunction: specializedFunc, context)
326-
cloner.cloneFunctionBody(from: original)
327-
328325
context.buildSpecializedFunction(specializedFunction: specializedFunc) { (specializedFunc, specContext) in
326+
cloneFunction(from: original, toEmpty: specializedFunc, specContext)
327+
329328
replaceBoxWithStackArguments(in: specializedFunc, original: original, specContext)
330329
}
331330
context.notifyNewFunction(function: specializedFunc, derivedFrom: original)

SwiftCompilerSources/Sources/Optimizer/FunctionPasses/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ swift_compiler_sources(Optimizer
2525
LifetimeDependenceDiagnostics.swift
2626
LifetimeDependenceInsertion.swift
2727
LifetimeDependenceScopeFixup.swift
28+
LoopInvariantCodeMotion.swift
2829
ObjectOutliner.swift
2930
ObjCBridgingOptimization.swift
3031
MergeCondFails.swift

SwiftCompilerSources/Sources/Optimizer/FunctionPasses/ClosureSpecialization.swift

Lines changed: 19 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -247,9 +247,10 @@ private func getOrCreateSpecializedFunction(
247247
context.buildSpecializedFunction(
248248
specializedFunction: specializedFunction,
249249
buildFn: { (emptySpecializedFunction, functionPassContext) in
250-
let closureSpecCloner = SpecializationCloner(
251-
emptySpecializedFunction: emptySpecializedFunction, functionPassContext)
250+
var closureSpecCloner = Cloner(
251+
cloneToEmptyFunction: emptySpecializedFunction, functionPassContext)
252252
closureSpecCloner.cloneAndSpecializeFunctionBody(using: pullbackClosureInfo)
253+
closureSpecCloner.deinitialize()
253254
})
254255

255256
return (specializedFunction, false)
@@ -731,15 +732,14 @@ private func markConvertedAndReabstractedClosuresAsUsed(
731732
}
732733
}
733734

734-
extension SpecializationCloner {
735+
extension Cloner where Context == FunctionPassContext {
735736
fileprivate func cloneAndSpecializeFunctionBody(using pullbackClosureInfo: PullbackClosureInfo) {
736737
self.cloneEntryBlockArgsWithoutOrigClosures(usingOrigCalleeAt: pullbackClosureInfo)
737738

738739
let (allSpecializedEntryBlockArgs, closureArgIndexToAllClonedReleasableClosures) =
739740
cloneAllClosures(at: pullbackClosureInfo)
740741

741-
self.cloneFunctionBody(
742-
from: pullbackClosureInfo.pullbackFn, entryBlockArguments: allSpecializedEntryBlockArgs)
742+
self.cloneFunctionBody(from: pullbackClosureInfo.pullbackFn, entryBlockArguments: allSpecializedEntryBlockArgs)
743743

744744
self.insertCleanupCodeForClonedReleasableClosures(
745745
from: pullbackClosureInfo,
@@ -750,8 +750,8 @@ extension SpecializationCloner {
750750
usingOrigCalleeAt pullbackClosureInfo: PullbackClosureInfo
751751
) {
752752
let originalEntryBlock = pullbackClosureInfo.pullbackFn.entryBlock
753-
let clonedFunction = self.cloned
754-
let clonedEntryBlock = self.entryBlock
753+
let clonedFunction = self.targetFunction
754+
let clonedEntryBlock = self.getOrCreateEntryBlock()
755755

756756
originalEntryBlock.arguments
757757
.enumerated()
@@ -782,7 +782,8 @@ extension SpecializationCloner {
782782
)
783783
{
784784
func entryBlockArgsWithOrigClosuresSkipped() -> [Value?] {
785-
var clonedNonClosureEntryBlockArgs = self.entryBlock.arguments.makeIterator()
785+
let clonedEntryBlock = self.getOrCreateEntryBlock()
786+
var clonedNonClosureEntryBlockArgs = clonedEntryBlock.arguments.makeIterator()
786787

787788
return pullbackClosureInfo.pullbackFn
788789
.entryBlock
@@ -823,8 +824,8 @@ extension SpecializationCloner {
823824
{
824825
let (origToClonedValueMap, capturedArgRange) = self.addEntryBlockArgs(
825826
forValuesCapturedBy: closureArgDesc)
826-
let clonedFunction = self.cloned
827-
let clonedEntryBlock = self.entryBlock
827+
let clonedFunction = self.targetFunction
828+
let clonedEntryBlock = self.getOrCreateEntryBlock()
828829
let clonedClosureArgs = Array(clonedEntryBlock.arguments[capturedArgRange])
829830

830831
let builder =
@@ -853,8 +854,8 @@ extension SpecializationCloner {
853854
-> (origToClonedValueMap: [HashableValue: Value], capturedArgRange: Range<Int>)
854855
{
855856
var origToClonedValueMap: [HashableValue: Value] = [:]
856-
let clonedFunction = self.cloned
857-
let clonedEntryBlock = self.entryBlock
857+
let clonedFunction = self.targetFunction
858+
let clonedEntryBlock = self.getOrCreateEntryBlock()
858859

859860
let capturedArgRangeStart = clonedEntryBlock.arguments.count
860861

@@ -908,8 +909,8 @@ extension SpecializationCloner {
908909
}
909910
}
910911

911-
if self.context.needFixStackNesting {
912-
self.context.fixStackNesting(in: self.cloned)
912+
if (self.context.needFixStackNesting) {
913+
self.context.fixStackNesting(in: targetFunction)
913914
}
914915
}
915916
}
@@ -1425,12 +1426,10 @@ private struct PullbackClosureInfo {
14251426
}
14261427

14271428
func specializedCalleeName(_ context: FunctionPassContext) -> String {
1428-
let closureArgs = Array(self.closureArgDescriptors.map { $0.closure })
1429-
let closureIndices = Array(self.closureArgDescriptors.map { $0.closureArgIndex })
1430-
1431-
return context.mangle(
1432-
withClosureArguments: closureArgs, closureArgIndices: closureIndices,
1433-
from: pullbackFn)
1429+
let closureArgs = Array(self.closureArgDescriptors.map {
1430+
(argumentIndex: $0.closureArgIndex, argumentValue: $0.closure)
1431+
})
1432+
return context.mangle(withClosureArguments: closureArgs, from: pullbackFn)
14341433
}
14351434
}
14361435

0 commit comments

Comments
 (0)