Skip to content
Closed
Show file tree
Hide file tree
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 Jul 15, 2025
e2ada85
Complete instruction extraction in analyzeLoop. Add more filtering af…
MAJKFL Jul 15, 2025
23a0026
Implement the remainder of besides .
MAJKFL Jul 17, 2025
2f8a0cd
Implement the remainder of .
MAJKFL Jul 17, 2025
9a3b85c
Add sinking and hoisting instructions.
MAJKFL Jul 18, 2025
27db055
Format.
MAJKFL Jul 18, 2025
ea81e32
Add traversal of dom children.
MAJKFL Jul 21, 2025
45a02a6
Minor bug fixes.
MAJKFL Jul 21, 2025
95b89ce
Minor bug fixes.
MAJKFL Jul 22, 2025
12ae07b
Add generalized cloner. Add initial implementation of .
MAJKFL Jul 24, 2025
d9f59c2
Add . Minor fixes.
MAJKFL Jul 24, 2025
dce2b7c
Fix SSA updater not introducing phi nodes in hoistLoadsAndStores.
MAJKFL Jul 28, 2025
b2acc8a
Fix loop re-running for some transformation. Fix copying for values w…
MAJKFL Jul 28, 2025
13d9b1e
Add recursive load spliting.
MAJKFL Jul 29, 2025
fdccfca
Add trySplit along an access path. Other minor fixes.
MAJKFL Jul 30, 2025
153df29
Refactor loop analysis.
MAJKFL Jul 30, 2025
cf5b9b8
Restructure licm algorithm into extensions.
MAJKFL Jul 31, 2025
cf13e6f
Add removal of duplicates when hoisting instructions. Removes the nec…
MAJKFL Jul 31, 2025
5438deb
Add simple hoist.
MAJKFL Jul 31, 2025
bb5fdcb
Fix end access hoisting by checking whether instructions are inside t…
MAJKFL Jul 31, 2025
11f1e77
Fix side effect handling. Reintroduce non-standard projection test case.
MAJKFL Aug 1, 2025
d92a2cc
Merge branch 'main'
MAJKFL Aug 1, 2025
1a7bf6d
Fix merge artifacts.
MAJKFL Aug 1, 2025
cd4bcdf
Fix build artifacts related to the new Cloner.
MAJKFL Aug 4, 2025
f974cc3
Enable new licm in pass pipeline.
MAJKFL Aug 4, 2025
2db5711
Revert back deleted vim files.
MAJKFL Aug 4, 2025
448e7f7
Add simple formatting suggested changes.
MAJKFL Aug 5, 2025
8f6edc9
Add more efficient lookup of blocks inside a loop. Use block markers …
MAJKFL Aug 5, 2025
5cee03e
Add swift:: namespace before ArraySemanticsCall.
MAJKFL Aug 5, 2025
1519482
Simplify load splitting during loop analysis. Revert loop block data …
MAJKFL Aug 6, 2025
6f2707b
Add instruction merging in loop preheader during analysis.
MAJKFL Aug 8, 2025
688881c
Disable operation merging when hoisting. More refactoring.
MAJKFL Aug 8, 2025
c95838c
Refactoring.
MAJKFL Aug 8, 2025
f23e65c
Fix read only apply handling. Refactoring.
MAJKFL Aug 12, 2025
7e9f92c
Fix handling of FixLifetimeInst. Refactor.
MAJKFL Aug 13, 2025
611471d
Minor fixes. Refactoring.
MAJKFL Aug 15, 2025
af189a4
Move cloner code to SIL module. Fix minor bug in trySplit.
MAJKFL Aug 18, 2025
acf3633
Refactoring. Adjust inline array bounds check tests.
MAJKFL Aug 19, 2025
d08d670
Simplify cloner.
MAJKFL Aug 19, 2025
de9ec67
Add stopCloning to cloner checkBase closure. Refactor.
MAJKFL Aug 20, 2025
f1b1502
Refactor.
MAJKFL Aug 21, 2025
15e9365
Move remaining comments from the old to new implementation. Refactor.
MAJKFL Aug 27, 2025
b8b6b48
Merge branch 'main' into new-sil-licm-pass
MAJKFL Aug 27, 2025
7e27996
Fix merge artifacts.
MAJKFL Aug 27, 2025
71f9b05
Refactor.
MAJKFL Aug 28, 2025
fe04718
Remove the call to .count(where: )
MAJKFL Aug 28, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
swift_compiler_sources(Optimizer
AliasAnalysis.swift
CalleeAnalysis.swift
LoopTree.swift
DeadEndBlocksAnalysis.swift
DominatorTree.swift
PostDominatorTree.swift)
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,31 @@ import OptimizerBridging

struct DominatorTree {
let bridged: BridgedDomTree

func getChildren(of block: BasicBlock) -> DomChildren {
return DomChildren(bridgedDomTree: bridged, bb: block)
}
}

struct DomChildren: BridgedRandomAccessCollection {
private let bridgedDomTree: BridgedDomTree
private let block: BasicBlock

let count: Int

var startIndex: Int { return 0 }
var endIndex: Int { return count }

init(bridgedDomTree: BridgedDomTree, bb: BasicBlock) {
self.bridgedDomTree = bridgedDomTree
self.block = bb
self.count = bridgedDomTree.getNumberOfChildren(bb.bridged)
}

subscript(_ index: Int) -> BasicBlock {
assert(index >= startIndex && index < endIndex)
return bridgedDomTree.getChildAt(block.bridged, index).block
}
}

extension BasicBlock {
Expand Down
213 changes: 213 additions & 0 deletions SwiftCompilerSources/Sources/Optimizer/Analysis/LoopTree.swift
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)
}
Original file line number Diff line number Diff line change
Expand Up @@ -311,10 +311,9 @@ private struct FunctionSpecializations {
// This can happen if a previous run of the pass already created this specialization.
return
}
let cloner = SpecializationCloner(emptySpecializedFunction: specializedFunc, context)
cloner.cloneFunctionBody(from: original)

context.buildSpecializedFunction(specializedFunction: specializedFunc) { (specializedFunc, specContext) in
cloneFunction(from: original, toEmpty: specializedFunc, specContext)

replaceBoxWithStackArguments(in: specializedFunc, original: original, specContext)
}
context.notifyNewFunction(function: specializedFunc, derivedFrom: original)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ swift_compiler_sources(Optimizer
LifetimeDependenceDiagnostics.swift
LifetimeDependenceInsertion.swift
LifetimeDependenceScopeFixup.swift
LoopInvariantCodeMotion.swift
ObjectOutliner.swift
ObjCBridgingOptimization.swift
MergeCondFails.swift
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -247,9 +247,10 @@ private func getOrCreateSpecializedFunction(
context.buildSpecializedFunction(
specializedFunction: specializedFunction,
buildFn: { (emptySpecializedFunction, functionPassContext) in
let closureSpecCloner = SpecializationCloner(
emptySpecializedFunction: emptySpecializedFunction, functionPassContext)
var closureSpecCloner = Cloner(
cloneToEmptyFunction: emptySpecializedFunction, functionPassContext)
closureSpecCloner.cloneAndSpecializeFunctionBody(using: pullbackClosureInfo)
closureSpecCloner.deinitialize()
})

return (specializedFunction, false)
Expand Down Expand Up @@ -731,15 +732,14 @@ private func markConvertedAndReabstractedClosuresAsUsed(
}
}

extension SpecializationCloner {
extension Cloner where Context == FunctionPassContext {
fileprivate func cloneAndSpecializeFunctionBody(using pullbackClosureInfo: PullbackClosureInfo) {
self.cloneEntryBlockArgsWithoutOrigClosures(usingOrigCalleeAt: pullbackClosureInfo)

let (allSpecializedEntryBlockArgs, closureArgIndexToAllClonedReleasableClosures) =
cloneAllClosures(at: pullbackClosureInfo)

self.cloneFunctionBody(
from: pullbackClosureInfo.pullbackFn, entryBlockArguments: allSpecializedEntryBlockArgs)
self.cloneFunctionBody(from: pullbackClosureInfo.pullbackFn, entryBlockArguments: allSpecializedEntryBlockArgs)

self.insertCleanupCodeForClonedReleasableClosures(
from: pullbackClosureInfo,
Expand All @@ -750,8 +750,8 @@ extension SpecializationCloner {
usingOrigCalleeAt pullbackClosureInfo: PullbackClosureInfo
) {
let originalEntryBlock = pullbackClosureInfo.pullbackFn.entryBlock
let clonedFunction = self.cloned
let clonedEntryBlock = self.entryBlock
let clonedFunction = self.targetFunction
let clonedEntryBlock = self.getOrCreateEntryBlock()

originalEntryBlock.arguments
.enumerated()
Expand Down Expand Up @@ -782,7 +782,8 @@ extension SpecializationCloner {
)
{
func entryBlockArgsWithOrigClosuresSkipped() -> [Value?] {
var clonedNonClosureEntryBlockArgs = self.entryBlock.arguments.makeIterator()
let clonedEntryBlock = self.getOrCreateEntryBlock()
var clonedNonClosureEntryBlockArgs = clonedEntryBlock.arguments.makeIterator()

return pullbackClosureInfo.pullbackFn
.entryBlock
Expand Down Expand Up @@ -823,8 +824,8 @@ extension SpecializationCloner {
{
let (origToClonedValueMap, capturedArgRange) = self.addEntryBlockArgs(
forValuesCapturedBy: closureArgDesc)
let clonedFunction = self.cloned
let clonedEntryBlock = self.entryBlock
let clonedFunction = self.targetFunction
let clonedEntryBlock = self.getOrCreateEntryBlock()
let clonedClosureArgs = Array(clonedEntryBlock.arguments[capturedArgRange])

let builder =
Expand Down Expand Up @@ -853,8 +854,8 @@ extension SpecializationCloner {
-> (origToClonedValueMap: [HashableValue: Value], capturedArgRange: Range<Int>)
{
var origToClonedValueMap: [HashableValue: Value] = [:]
let clonedFunction = self.cloned
let clonedEntryBlock = self.entryBlock
let clonedFunction = self.targetFunction
let clonedEntryBlock = self.getOrCreateEntryBlock()

let capturedArgRangeStart = clonedEntryBlock.arguments.count

Expand Down Expand Up @@ -908,8 +909,8 @@ extension SpecializationCloner {
}
}

if self.context.needFixStackNesting {
self.context.fixStackNesting(in: self.cloned)
if (self.context.needFixStackNesting) {
self.context.fixStackNesting(in: targetFunction)
}
}
}
Expand Down Expand Up @@ -1425,12 +1426,10 @@ private struct PullbackClosureInfo {
}

func specializedCalleeName(_ context: FunctionPassContext) -> String {
let closureArgs = Array(self.closureArgDescriptors.map { $0.closure })
let closureIndices = Array(self.closureArgDescriptors.map { $0.closureArgIndex })

return context.mangle(
withClosureArguments: closureArgs, closureArgIndices: closureIndices,
from: pullbackFn)
let closureArgs = Array(self.closureArgDescriptors.map {
(argumentIndex: $0.closureArgIndex, argumentValue: $0.closure)
})
return context.mangle(withClosureArguments: closureArgs, from: pullbackFn)
}
}

Expand Down
Loading