mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
Although empty blocks are cleaned up at the end of every Simplification pass, it's not legal SIL to have empty blocks and subsequent simplification passes may crash because of this. rdar://115169880
116 lines
3.1 KiB
Swift
116 lines
3.1 KiB
Swift
//===--- SimplifyBranch.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
|
|
|
|
extension BranchInst : OnoneSimplifyable {
|
|
func simplify(_ context: SimplifyContext) {
|
|
tryMergeWithTargetBlock(context)
|
|
}
|
|
}
|
|
|
|
private extension BranchInst {
|
|
func tryMergeWithTargetBlock(_ context: SimplifyContext) {
|
|
if canMergeWithTargetBlock {
|
|
mergeWithTargetBlock(context)
|
|
}
|
|
}
|
|
|
|
var canMergeWithTargetBlock: Bool {
|
|
// We can only merge if there is a 1:1 relation to the target block.
|
|
guard let pred = targetBlock.singlePredecessor else {
|
|
return false
|
|
}
|
|
assert(pred == parentBlock)
|
|
|
|
// Ignore self cycles
|
|
if targetBlock == parentBlock {
|
|
return false
|
|
}
|
|
|
|
if hasInvalidDominanceCycle {
|
|
return false
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
func mergeWithTargetBlock(_ context: SimplifyContext) {
|
|
let targetBB = targetBlock
|
|
let parentBB = parentBlock
|
|
|
|
for (argIdx, op) in operands.enumerated() {
|
|
targetBB.arguments[argIdx].uses.replaceAll(with: op.value, context)
|
|
}
|
|
targetBB.eraseAllArguments(context)
|
|
|
|
if context.preserveDebugInfo {
|
|
let builder = Builder(before: self, context)
|
|
builder.createDebugStep()
|
|
}
|
|
context.erase(instruction: self)
|
|
|
|
// Move instruction from the smaller block to the larger block.
|
|
// The order is essential because if many blocks are merged and this is done
|
|
// in the wrong order, we end up with quadratic complexity.
|
|
//
|
|
if parentBB.hasLessInstructions(than: targetBB) &&
|
|
parentBB != parentBB.parentFunction.entryBlock {
|
|
for pred in parentBB.predecessors {
|
|
pred.terminator.replaceBranchTarget(from: parentBB, to: targetBB, context)
|
|
}
|
|
parentBB.moveAllInstructions(toBeginOf: targetBB, context)
|
|
parentBB.moveAllArguments(to: targetBB, context)
|
|
context.erase(block: parentBB)
|
|
} else {
|
|
targetBB.moveAllInstructions(toEndOf: parentBB, context)
|
|
context.erase(block: targetBB)
|
|
}
|
|
}
|
|
}
|
|
|
|
private extension BasicBlock {
|
|
func hasLessInstructions(than otherBlock: BasicBlock) -> Bool {
|
|
var insts = instructions
|
|
var otherInsts = otherBlock.instructions
|
|
while true {
|
|
if otherInsts.next() == nil {
|
|
return false
|
|
}
|
|
if insts.next() == nil {
|
|
return true
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private extension BranchInst {
|
|
|
|
// True if this block is part of an unreachable cfg cycle, where an argument dominates itself.
|
|
// For example:
|
|
// ```
|
|
// bb1(arg1): // preds: bb2
|
|
// br bb2
|
|
//
|
|
// bb2: // preds: bb1
|
|
// br bb1(arg1)
|
|
// ```
|
|
var hasInvalidDominanceCycle: Bool {
|
|
for (argIdx, op) in operands.enumerated() {
|
|
if targetBlock.arguments[argIdx] == op.value {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
}
|