Files
swift-mirror/SwiftCompilerSources/Sources/Optimizer/TestPasses/TestInstructionIteration.swift
Erik Eckstein 48db89031a SIL: be more tolerant when a user is deleted during use-list iteration.
Check if an operand's instruction has been deleted in `UseList.next()`.
This allows to delete an instruction, which has two uses of a value, during use-list iteration.
2025-03-07 15:59:32 +01:00

96 lines
3.3 KiB
Swift

//===--- TestInstructionIteration.swift -----------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2022 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
/// Tests instruction iteration while modifying the instruction list.
///
/// This pass iterates over the instruction list of the function's block and performs
/// modifications of the instruction list - mostly deleting instructions.
/// Modifications are triggered by `string_literal` instructions with known "commands".
/// E.g. if a
/// ```
/// %1 = string_literal utf8 "delete_strings"
/// ```
/// is encountered during the iteration, it triggers the deletion of all `string_literal`
/// instructions of the basic block (including the current one).
let testInstructionIteration = FunctionPass(name: "test-instruction-iteration") {
(function: Function, context: FunctionPassContext) in
print("Test instruction iteration in \(function.name):")
let reverse = function.name.string.hasSuffix("backward")
for block in function.blocks {
print("\(block.name):")
let termLoc = block.terminator.location
if reverse {
for inst in block.instructions.reversed() {
handle(instruction: inst, context)
}
} else {
for inst in block.instructions {
handle(instruction: inst, context)
}
}
if block.instructions.isEmpty || !(block.instructions.reversed().first is TermInst) {
let builder = Builder(atEndOf: block, location: termLoc, context)
builder.createUnreachable()
}
}
print("End function \(function.name):")
}
private func handle(instruction: Instruction, _ context: FunctionPassContext) {
print(instruction)
if let sl = instruction as? StringLiteralInst {
switch sl.value {
case "delete_strings":
deleteAllInstructions(ofType: StringLiteralInst.self, in: instruction.parentBlock, context)
case "delete_ints":
deleteAllInstructions(ofType: IntegerLiteralInst.self, in: instruction.parentBlock, context)
case "delete_branches":
deleteAllInstructions(ofType: BranchInst.self, in: instruction.parentBlock, context)
case "split_block":
_ = context.splitBlock(before: instruction)
case "print_uses":
for use in sl.uses {
print("use: \(use)")
}
case "delete_first_user":
deleteUser(of: sl, at: 0, context)
case "delete_second_user":
deleteUser(of: sl, at: 1, context)
default:
break
}
}
}
private func deleteAllInstructions<InstType: Instruction>(ofType: InstType.Type, in block: BasicBlock, _ context: FunctionPassContext) {
for inst in block.instructions {
if inst is InstType {
context.erase(instruction: inst)
}
}
}
private func deleteUser(of value: Value, at deleteIndex: Int, _ context: FunctionPassContext) {
for (idx, use) in value.uses.enumerated() {
if idx == deleteIndex {
context.erase(instruction: use.instruction)
} else {
print("use: \(use)")
}
}
}