-
Notifications
You must be signed in to change notification settings - Fork 10.6k
New SIL Loop Invariant Code Motion pass #83512
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Closed
Closed
Changes from all commits
Commits
Show all changes
46 commits
Select commit
Hold shift + click to select a range
2a305e1
Add initial structure for the new licm pass.
MAJKFL e2ada85
Complete instruction extraction in analyzeLoop. Add more filtering af…
MAJKFL 23a0026
Implement the remainder of besides .
MAJKFL 2f8a0cd
Implement the remainder of .
MAJKFL 9a3b85c
Add sinking and hoisting instructions.
MAJKFL 27db055
Format.
MAJKFL ea81e32
Add traversal of dom children.
MAJKFL 45a02a6
Minor bug fixes.
MAJKFL 95b89ce
Minor bug fixes.
MAJKFL 12ae07b
Add generalized cloner. Add initial implementation of .
MAJKFL d9f59c2
Add . Minor fixes.
MAJKFL dce2b7c
Fix SSA updater not introducing phi nodes in hoistLoadsAndStores.
MAJKFL b2acc8a
Fix loop re-running for some transformation. Fix copying for values w…
MAJKFL 13d9b1e
Add recursive load spliting.
MAJKFL fdccfca
Add trySplit along an access path. Other minor fixes.
MAJKFL 153df29
Refactor loop analysis.
MAJKFL cf5b9b8
Restructure licm algorithm into extensions.
MAJKFL cf13e6f
Add removal of duplicates when hoisting instructions. Removes the nec…
MAJKFL 5438deb
Add simple hoist.
MAJKFL bb5fdcb
Fix end access hoisting by checking whether instructions are inside t…
MAJKFL 11f1e77
Fix side effect handling. Reintroduce non-standard projection test case.
MAJKFL d92a2cc
Merge branch 'main'
MAJKFL 1a7bf6d
Fix merge artifacts.
MAJKFL cd4bcdf
Fix build artifacts related to the new Cloner.
MAJKFL f974cc3
Enable new licm in pass pipeline.
MAJKFL 2db5711
Revert back deleted vim files.
MAJKFL 448e7f7
Add simple formatting suggested changes.
MAJKFL 8f6edc9
Add more efficient lookup of blocks inside a loop. Use block markers …
MAJKFL 5cee03e
Add swift:: namespace before ArraySemanticsCall.
MAJKFL 1519482
Simplify load splitting during loop analysis. Revert loop block data …
MAJKFL 6f2707b
Add instruction merging in loop preheader during analysis.
MAJKFL 688881c
Disable operation merging when hoisting. More refactoring.
MAJKFL c95838c
Refactoring.
MAJKFL f23e65c
Fix read only apply handling. Refactoring.
MAJKFL 7e9f92c
Fix handling of FixLifetimeInst. Refactor.
MAJKFL 611471d
Minor fixes. Refactoring.
MAJKFL af189a4
Move cloner code to SIL module. Fix minor bug in trySplit.
MAJKFL acf3633
Refactoring. Adjust inline array bounds check tests.
MAJKFL d08d670
Simplify cloner.
MAJKFL de9ec67
Add stopCloning to cloner checkBase closure. Refactor.
MAJKFL f1b1502
Refactor.
MAJKFL 15e9365
Move remaining comments from the old to new implementation. Refactor.
MAJKFL b8b6b48
Merge branch 'main' into new-sil-licm-pass
MAJKFL 7e27996
Fix merge artifacts.
MAJKFL 71f9b05
Refactor.
MAJKFL fe04718
Remove the call to .count(where: )
MAJKFL File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
213 changes: 213 additions & 0 deletions
213
SwiftCompilerSources/Sources/Optimizer/Analysis/LoopTree.swift
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,213 @@ | ||
//===--- LoopTree.swift ---------------------------------------------------===// | ||
// | ||
// This source file is part of the Swift.org open source project | ||
// | ||
// Copyright (c) 2014 - 2025 Apple Inc. and the Swift project authors | ||
// Licensed under Apache License v2.0 with Runtime Library Exception | ||
// | ||
// See https://swift.org/LICENSE.txt for license information | ||
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors | ||
// | ||
//===----------------------------------------------------------------------===// | ||
|
||
import SIL | ||
import OptimizerBridging | ||
|
||
/// Describes top level loops. | ||
struct LoopTree { | ||
fileprivate let bridged: BridgedLoopTree | ||
|
||
let loops: TopLevelLoopArray | ||
|
||
init(bridged: BridgedLoopTree, context: FunctionPassContext) { | ||
self.bridged = bridged | ||
self.loops = TopLevelLoopArray(bridged) | ||
} | ||
} | ||
|
||
/// Describes a loop with its children. | ||
struct Loop { | ||
private let bridged: BridgedLoop | ||
|
||
let innerLoops: LoopArray | ||
let loopBlocks: LoopBlocks | ||
|
||
var exitingAndLatchBlocks: some Sequence<BasicBlock> { | ||
return header.predecessors.lazy | ||
.filter { predecessor in | ||
contains(block: predecessor) && !isLoopExiting(loopBlock: predecessor) | ||
} + exitingBlocks | ||
} | ||
|
||
/// Exit blocks of the loop. | ||
/// | ||
/// - Note: Some exit blocks will be duplicated if the loop has critical edges. | ||
var exitBlocks: some Sequence<BasicBlock> { | ||
return loopBlocks.lazy | ||
.flatMap(\.successors) | ||
.filter { !contains(block: $0) } | ||
} | ||
|
||
var exitingBlocks: some Sequence<BasicBlock> { | ||
return loopBlocks.lazy | ||
.filter { isLoopExiting(loopBlock: $0) } | ||
} | ||
|
||
init(bridged: BridgedLoop) { | ||
self.bridged = bridged | ||
self.innerLoops = LoopArray(bridged) | ||
self.loopBlocks = LoopBlocks(bridged) | ||
} | ||
|
||
var preheader: BasicBlock? { | ||
bridged.getPreheader().block | ||
} | ||
|
||
var header: BasicBlock { | ||
bridged.getHeader().block | ||
} | ||
|
||
/// Returns `true` if the loop has exactly one exit block. | ||
var hasSingleExitBlock: Bool { | ||
return exitBlocks.singleElement != nil | ||
} | ||
|
||
var hasNoExitBlocks: Bool { | ||
return exitBlocks.isEmpty | ||
} | ||
|
||
private func isLoopExiting(loopBlock: BasicBlock) -> Bool { | ||
return loopBlock.successors.contains { !contains(block: $0) } | ||
} | ||
|
||
func getBlocksThatDominateAllExitingAndLatchBlocks(_ context: FunctionPassContext) -> [BasicBlock] { | ||
var result: [BasicBlock] = [] | ||
var cachedExitingAndLatchBlocks = Stack<BasicBlock>(context) | ||
var workList = BasicBlockWorklist(context) | ||
defer { | ||
cachedExitingAndLatchBlocks.deinitialize() | ||
workList.deinitialize() | ||
} | ||
|
||
cachedExitingAndLatchBlocks.append(contentsOf: exitingAndLatchBlocks) | ||
workList.pushIfNotVisited(header) | ||
|
||
while let block = workList.pop() { | ||
guard cachedExitingAndLatchBlocks.allSatisfy({ exitBlock in | ||
return block.dominates(exitBlock, context.dominatorTree) | ||
}) else { | ||
continue | ||
} | ||
|
||
result.append(block) | ||
|
||
workList.pushIfNotVisited(contentsOf: context.dominatorTree.getChildren(of: block)) | ||
} | ||
|
||
return result | ||
} | ||
|
||
func contains(block: BasicBlock) -> Bool { | ||
return bridged.contains(block.bridged) | ||
} | ||
|
||
func splitCriticalExitingAndBackEdges(_ context: FunctionPassContext) { | ||
for exitingOrLatchBlock in exitingAndLatchBlocks { | ||
for (index, succesor) in exitingOrLatchBlock.successors.enumerated() where !contains(block: succesor) { | ||
splitCriticalEdge( | ||
from: exitingOrLatchBlock.terminator.parentBlock, | ||
toEdgeIndex: index, | ||
dominatorTree: context.dominatorTree, | ||
loopTree: context.loopTree, | ||
context | ||
) | ||
} | ||
} | ||
} | ||
} | ||
|
||
struct TopLevelLoopArray: BridgedRandomAccessCollection { | ||
private let bridgedLoopTree: BridgedLoopTree | ||
|
||
let count: Int | ||
|
||
var startIndex: Int { return 0 } | ||
var endIndex: Int { return count } | ||
|
||
init(_ bridgedLoopTree: BridgedLoopTree) { | ||
self.bridgedLoopTree = bridgedLoopTree | ||
self.count = bridgedLoopTree.getTopLevelLoopCount() | ||
} | ||
|
||
subscript(_ index: Int) -> Loop { | ||
assert(index >= startIndex && index < endIndex) | ||
return Loop(bridged: bridgedLoopTree.getLoop(index)) | ||
} | ||
} | ||
|
||
struct LoopArray: BridgedRandomAccessCollection { | ||
private let bridgedLoop: BridgedLoop | ||
|
||
let count: Int | ||
|
||
var startIndex: Int { return 0 } | ||
var endIndex: Int { return count } | ||
|
||
init(_ bridgedLoop: BridgedLoop) { | ||
self.bridgedLoop = bridgedLoop | ||
self.count = bridgedLoop.getInnerLoopCount() | ||
} | ||
|
||
subscript(_ index: Int) -> Loop { | ||
assert(index >= startIndex && index < endIndex) | ||
return Loop(bridged: bridgedLoop.getInnerLoop(index)) | ||
} | ||
} | ||
|
||
struct LoopBlocks: BridgedRandomAccessCollection { | ||
private let bridgedLoop: BridgedLoop | ||
|
||
let count: Int | ||
|
||
var startIndex: Int { return 0 } | ||
var endIndex: Int { return count } | ||
|
||
init(_ bridgedLoop: BridgedLoop) { | ||
self.bridgedLoop = bridgedLoop | ||
self.count = bridgedLoop.getBasicBlockCount() | ||
} | ||
|
||
subscript(_ index: Int) -> BasicBlock { | ||
assert(index >= startIndex && index < endIndex) | ||
return bridgedLoop.getBasicBlock(index).block | ||
} | ||
} | ||
|
||
func splitEdge( | ||
from block: BasicBlock, | ||
toEdgeIndex: Int, | ||
dominatorTree: DominatorTree, | ||
loopTree: LoopTree, | ||
_ context: some MutatingContext | ||
) -> BasicBlock { | ||
let result = loopTree.bridged.splitEdge(block.bridged, toEdgeIndex, dominatorTree.bridged).block | ||
|
||
context.notifyBranchesChanged() | ||
return result | ||
} | ||
|
||
/// If the specified edge is critical, the function returns inserted block. Otherwise returns `nil`. | ||
@discardableResult | ||
func splitCriticalEdge( | ||
from block: BasicBlock, | ||
toEdgeIndex: Int, | ||
dominatorTree: DominatorTree, | ||
loopTree: LoopTree, | ||
_ context: some MutatingContext | ||
) -> BasicBlock? { | ||
guard block.isCriticalEdge(edgeIndex: toEdgeIndex) else { | ||
return nil | ||
} | ||
|
||
return splitEdge(from: block, toEdgeIndex: toEdgeIndex, dominatorTree: dominatorTree, loopTree: loopTree, context) | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.