Files
swift-mirror/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/SimplificationPasses.swift
2023-12-09 18:48:50 +01:00

142 lines
4.9 KiB
Swift

//===--- SimplificationPasses.swift ----------------------------------------==//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2023 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
//===--------------------------------------------------------------------===//
// Instruction protocols
//===--------------------------------------------------------------------===//
/// Instructions which can be simplified at all optimization levels
protocol Simplifyable : Instruction {
func simplify(_ context: SimplifyContext)
}
/// Instructions which can be simplified at -Onone
protocol OnoneSimplifyable : Simplifyable {
}
/// Instructions which can only be simplified at the end of the -Onone pipeline
protocol LateOnoneSimplifyable : Instruction {
func simplifyLate(_ context: SimplifyContext)
}
//===--------------------------------------------------------------------===//
// Simplification passes
//===--------------------------------------------------------------------===//
let ononeSimplificationPass = FunctionPass(name: "onone-simplification") {
(function: Function, context: FunctionPassContext) in
runSimplification(on: function, context, preserveDebugInfo: true) {
if let i = $0 as? OnoneSimplifyable {
i.simplify($1)
}
}
}
let simplificationPass = FunctionPass(name: "simplification") {
(function: Function, context: FunctionPassContext) in
runSimplification(on: function, context, preserveDebugInfo: false) {
if let i = $0 as? Simplifyable {
i.simplify($1)
}
}
}
let lateOnoneSimplificationPass = FunctionPass(name: "late-onone-simplification") {
(function: Function, context: FunctionPassContext) in
runSimplification(on: function, context, preserveDebugInfo: true) {
if let i = $0 as? LateOnoneSimplifyable {
i.simplifyLate($1)
} else if let i = $0 as? OnoneSimplifyable {
i.simplify($1)
}
}
}
//===--------------------------------------------------------------------===//
// Pass implementation
//===--------------------------------------------------------------------===//
@discardableResult
func runSimplification(on function: Function, _ context: FunctionPassContext,
preserveDebugInfo: Bool,
_ simplify: (Instruction, SimplifyContext) -> ()) -> Bool {
var worklist = InstructionWorklist(context)
defer { worklist.deinitialize() }
var changed = false
let simplifyCtxt = context.createSimplifyContext(preserveDebugInfo: preserveDebugInfo,
notifyInstructionChanged: {
worklist.pushIfNotVisited($0)
changed = true
})
// Push in reverse order so that popping from the tail of the worklist visits instruction in forward order again.
worklist.pushIfNotVisited(contentsOf: function.reversedInstructions)
// Run multiple iterations because cleanupDeadCode can add new candidates to the worklist.
repeat {
// The core worklist-loop.
while let instruction = worklist.popAndForget() {
if instruction.isDeleted {
continue
}
if !context.options.enableSimplification(for: instruction) {
continue
}
if !context.continueWithNextSubpassRun(for: instruction) {
return changed
}
simplify(instruction, simplifyCtxt)
}
cleanupDeadInstructions(in: function, preserveDebugInfo, context)
cleanupDeadBlocks(in: function, pushNewCandidatesTo: &worklist, context)
} while !worklist.isEmpty
if context.needFixStackNesting {
function.fixStackNesting(context)
}
return changed
}
private func cleanupDeadInstructions(in function: Function,
_ preserveDebugInfo: Bool,
_ context: FunctionPassContext) {
if preserveDebugInfo {
context.removeTriviallyDeadInstructionsPreservingDebugInfo(in: function)
} else {
context.removeTriviallyDeadInstructionsIgnoringDebugUses(in: function)
}
}
private func cleanupDeadBlocks(in function: Function,
pushNewCandidatesTo worklist: inout InstructionWorklist,
_ context: FunctionPassContext) {
if context.removeDeadBlocks(in: function) {
// After deleting dead blocks their (still alive) successor blocks may become eligible for block merging.
// Therefore we re-run simplification for all branch instructions.
for block in function.blocks.reversed() {
if let bi = block.terminator as? BranchInst {
worklist.pushIfNotVisited(bi)
}
}
}
}