Skip to content

Commit 3514b2b

Browse files
committed
Bridge array semantics call and loop tree
1 parent e3140e0 commit 3514b2b

File tree

10 files changed

+416
-23
lines changed

10 files changed

+416
-23
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/PassManager/ContextCommon.swift

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,22 @@ extension MutatingContext {
152152
bridgedPassContext.notifyDependencyOnBodyOf(otherFunction.bridged)
153153
}
154154
}
155+
156+
extension Instruction {
157+
var arraySemanticsCallKind: ArrayCallKind {
158+
return BridgedPassContext.getArraySemanticsCallKind(self.bridged)
159+
}
160+
161+
func canHoistArraySemanticsCall(to toInst: Instruction, _ context: FunctionPassContext) -> Bool {
162+
return context.bridgedPassContext.canHoistArraySemanticsCall(self.bridged, toInst.bridged)
163+
}
164+
165+
func hoistArraySemanticsCall(before toInst: Instruction, _ context: some MutatingContext) {
166+
context.bridgedPassContext.hoistArraySemanticsCall(self.bridged, toInst.bridged) // Internally updates dom tree.
167+
context.notifyInstructionsChanged()
168+
}
169+
}
170+
155171
extension Cloner where Context == FunctionPassContext {
156172
func getOrCreateEntryBlock() -> BasicBlock {
157173
if let entryBlock = targetFunction.blocks.first {

SwiftCompilerSources/Sources/Optimizer/PassManager/FunctionPassContext.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,11 @@ struct FunctionPassContext : MutatingContext {
4545
let bridgedPDT = bridgedPassContext.getPostDomTree()
4646
return PostDominatorTree(bridged: bridgedPDT)
4747
}
48+
49+
var loopTree: LoopTree {
50+
let bridgedLT = bridgedPassContext.getLoopTree()
51+
return LoopTree(bridged: bridgedLT, context: self)
52+
}
4853

4954
func loadFunction(name: StaticString, loadCalleesRecursively: Bool) -> Function? {
5055
return name.withUTF8Buffer { (nameBuffer: UnsafeBufferPointer<UInt8>) in

SwiftCompilerSources/Sources/SIL/BasicBlock.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,14 @@ final public class BasicBlock : CustomStringConvertible, HasShortDescription, Ha
153153
return false
154154
}
155155
}
156+
157+
public func isCriticalEdge(edgeIndex: Int) -> Bool {
158+
if terminator.successors.count <= 1 {
159+
return false
160+
} else {
161+
return !terminator.successors[edgeIndex].hasSinglePredecessor
162+
}
163+
}
156164
}
157165

158166
/// The list of instructions in a BasicBlock.

include/swift/SIL/SILBridging.h

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,8 @@ class SymbolicValueBumpAllocator;
6060
class ConstExprEvaluator;
6161
class SILWitnessTable;
6262
class SILDefaultWitnessTable;
63+
class SILLoopInfo;
64+
class SILLoop;
6365
class BridgedClonerImpl;
6466
class SILDebugLocation;
6567
class NominalTypeDecl;
@@ -76,11 +78,25 @@ class SILLocation;
7678
class BasicBlockSet;
7779
class NodeSet;
7880
class OperandSet;
79-
class ClonerWithFixedLocation;
8081
class FixedSizeSlabPayload;
8182
class FixedSizeSlab;
8283
}
8384

85+
struct BridgedLoop {
86+
swift::SILLoop * _Nonnull l;
87+
88+
BRIDGED_INLINE SwiftInt getInnerLoopCount() const;
89+
BRIDGED_INLINE BridgedLoop getInnerLoop(SwiftInt index) const;
90+
91+
BRIDGED_INLINE SwiftInt getBasicBlockCount() const;
92+
BRIDGED_INLINE BridgedBasicBlock getBasicBlock(SwiftInt index) const;
93+
94+
BRIDGED_INLINE OptionalBridgedBasicBlock getPreheader() const;
95+
BRIDGED_INLINE BridgedBasicBlock getHeader() const;
96+
97+
BRIDGED_INLINE bool contains(BridgedBasicBlock block) const;
98+
};
99+
84100
bool swiftModulesInitialized();
85101
void registerBridgedClass(BridgedStringRef className, SwiftMetatype metatype);
86102

@@ -679,6 +695,7 @@ struct BridgedInstruction {
679695
bool maySynchronize() const;
680696
bool mayBeDeinitBarrierNotConsideringSideEffects() const;
681697
BRIDGED_INLINE bool shouldBeForwarding() const;
698+
BRIDGED_INLINE bool isIdenticalTo(BridgedInstruction inst) const;
682699

683700
// =========================================================================//
684701
// Generalized instruction subclasses
@@ -1320,7 +1337,7 @@ struct BridgedBuilder{
13201337
BridgedASTType::MetatypeRepresentation representation) const;
13211338
SWIFT_IMPORT_UNSAFE BRIDGED_INLINE BridgedInstruction createEndCOWMutation(BridgedValue instance,
13221339
bool keepUnique) const;
1323-
SWIFT_IMPORT_UNSAFE BRIDGED_INLINE BridgedInstruction createEndCOWMutationAddr(BridgedValue instance) const;
1340+
SWIFT_IMPORT_UNSAFE BRIDGED_INLINE BridgedInstruction createEndCOWMutationAddr(BridgedValue instance) const;
13241341
SWIFT_IMPORT_UNSAFE BRIDGED_INLINE BridgedInstruction createMarkDependence(
13251342
BridgedValue value, BridgedValue base, BridgedInstruction::MarkDependenceKind dependenceKind) const;
13261343

@@ -1454,6 +1471,7 @@ struct BridgedContext {
14541471
BRIDGED_INLINE void eraseInstruction(BridgedInstruction inst, bool salvageDebugInfo) const;
14551472
BRIDGED_INLINE void eraseBlock(BridgedBasicBlock block) const;
14561473
static BRIDGED_INLINE void moveInstructionBefore(BridgedInstruction inst, BridgedInstruction beforeInst);
1474+
static BRIDGED_INLINE void copyInstructionBefore(BridgedInstruction inst, BridgedInstruction beforeInst);
14571475

14581476
// SSAUpdater
14591477

0 commit comments

Comments
 (0)