Skip to content

SIL: simplify deleting instructions while iterating over instructions. #62480

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

Merged
merged 8 commits into from
Dec 13, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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 @@ -94,7 +94,7 @@ struct InstructionRange : CustomStringConvertible, NoReflectionChildren {
var isValid: Bool {
blockRange.isValid &&
// Check if there are any inserted instructions before the begin instruction in its block.
!ReverseList(first: begin).dropFirst().contains { insertedInsts.contains($0) }
!ReverseInstructionList(first: begin).dropFirst().contains { insertedInsts.contains($0) }
}

/// Returns the end instructions.
Expand All @@ -115,7 +115,7 @@ struct InstructionRange : CustomStringConvertible, NoReflectionChildren {
/// Returns the interior instructions.
var interiors: LazySequence<FlattenSequence<
LazyMapSequence<Stack<BasicBlock>,
LazyFilterSequence<ReverseList<Instruction>>>>> {
LazyFilterSequence<ReverseInstructionList>>>> {
blockRange.inserted.lazy.flatMap {
var include = blockRange.contains($0)
return $0.instructions.reversed().lazy.filter {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ private func registerSwiftPasses() {
registerPass(deadEndBlockDumper, { deadEndBlockDumper.run($0) })
registerPass(rangeDumper, { rangeDumper.run($0) })
registerPass(runUnitTests, { runUnitTests.run($0) })
registerPass(testInstructionIteration, { testInstructionIteration.run($0) })
}

private func registerSwiftAnalyses() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,5 @@ swift_compiler_sources(Optimizer
SILPrinter.swift
RangeDumper.swift
RunUnitTests.swift
TestInstructionIteration.swift
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
//===--- 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: PassContext) 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: PassContext) {
print(instruction)
if let sl = instruction as? StringLiteralInst {
switch sl.string {
case "delete_strings":
deleteAllInstructions(ofType: StringLiteralInst.self, in: instruction.block, context)
case "delete_ints":
deleteAllInstructions(ofType: IntegerLiteralInst.self, in: instruction.block, context)
case "delete_branches":
deleteAllInstructions(ofType: BranchInst.self, in: instruction.block, context)
case "split_block":
_ = context.splitBlock(at: instruction)
default:
break
}
}
}

private func deleteAllInstructions<InstType: Instruction>(ofType: InstType.Type, in block: BasicBlock, _ context: PassContext) {
for inst in block.instructions {
if inst is InstType {
context.erase(instruction: inst)
}
}
}
71 changes: 63 additions & 8 deletions SwiftCompilerSources/Sources/SIL/BasicBlock.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,10 @@
import Basic
import SILBridging

final public class BasicBlock : ListNode, CustomStringConvertible, HasShortDescription {
final public class BasicBlock : CustomStringConvertible, HasShortDescription {
public var next: BasicBlock? { SILBasicBlock_next(bridged).block }
public var previous: BasicBlock? { SILBasicBlock_previous(bridged).block }

// Needed for ReverseList<BasicBlock>.reversed(). Never use directly.
public var _firstInList: BasicBlock { SILFunction_firstBlock(function.bridged).block! }
// Needed for List<BasicBlock>.reversed(). Never use directly.
public var _lastInList: BasicBlock { SILFunction_lastBlock(function.bridged).block! }

public var function: Function { SILBasicBlock_getFunction(bridged).function }

public var description: String {
Expand All @@ -32,8 +27,8 @@ final public class BasicBlock : ListNode, CustomStringConvertible, HasShortDescr

public var arguments: ArgumentArray { ArgumentArray(block: self) }

public var instructions: List<Instruction> {
List(first: SILBasicBlock_firstInst(bridged).instruction)
public var instructions: InstructionList {
InstructionList(first: SILBasicBlock_firstInst(bridged).instruction)
}

public var terminator: TermInst {
Expand Down Expand Up @@ -75,6 +70,66 @@ final public class BasicBlock : ListNode, CustomStringConvertible, HasShortDescr
public func == (lhs: BasicBlock, rhs: BasicBlock) -> Bool { lhs === rhs }
public func != (lhs: BasicBlock, rhs: BasicBlock) -> Bool { lhs !== rhs }

/// The list of instructions in a BasicBlock.
///
/// It's allowed to delete the current, next or any other instructions while
/// iterating over the instruction list.
public struct InstructionList : CollectionLikeSequence, IteratorProtocol {
private var currentInstruction: Instruction?

public init(first: Instruction?) { currentInstruction = first }

public mutating func next() -> Instruction? {
if var inst = currentInstruction {
while inst.isDeleted {
guard let nextInst = inst.next else {
return nil
}
inst = nextInst
}
currentInstruction = inst.next
return inst
}
return nil
}

public var first: Instruction? { currentInstruction }

public func reversed() -> ReverseInstructionList {
if let inst = currentInstruction {
let lastInst = SILBasicBlock_lastInst(inst.block.bridged).instruction
return ReverseInstructionList(first: lastInst)
}
return ReverseInstructionList(first: nil)
}
}

/// The list of instructions in a BasicBlock in reverse order.
///
/// It's allowed to delete the current, next or any other instructions while
/// iterating over the instruction list.
public struct ReverseInstructionList : CollectionLikeSequence, IteratorProtocol {
private var currentInstruction: Instruction?

public init(first: Instruction?) { currentInstruction = first }

public mutating func next() -> Instruction? {
if var inst = currentInstruction {
while inst.isDeleted {
guard let nextInst = inst.previous else {
return nil
}
inst = nextInst
}
currentInstruction = inst.previous
return inst
}
return nil
}

public var first: Instruction? { currentInstruction }
}

public struct ArgumentArray : RandomAccessCollection {
fileprivate let block: BasicBlock

Expand Down
8 changes: 8 additions & 0 deletions SwiftCompilerSources/Sources/SIL/Builder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -187,4 +187,12 @@ public struct Builder {
return bi.getAs(BranchInst.self)
}
}

@discardableResult
public func createUnreachable() -> UnreachableInst {
notifyInstructionsChanged()
notifyBranchesChanged()
let ui = SILBuilder_createUnreachable(bridged)
return ui.getAs(UnreachableInst.self)
}
}
46 changes: 43 additions & 3 deletions SwiftCompilerSources/Sources/SIL/Function.swift
Original file line number Diff line number Diff line change
Expand Up @@ -42,16 +42,16 @@ final public class Function : CustomStringConvertible, HasShortDescription, Hash
SILFunction_firstBlock(bridged).block!
}

public var blocks : List<BasicBlock> {
return List(first: SILFunction_firstBlock(bridged).block)
public var blocks : BasicBlockList {
BasicBlockList(first: SILFunction_firstBlock(bridged).block)
}

public var arguments: LazyMapSequence<ArgumentArray, FunctionArgument> {
entryBlock.arguments.lazy.map { $0 as! FunctionArgument }
}

/// All instructions of all blocks.
public var instructions: LazySequence<FlattenSequence<LazyMapSequence<List<BasicBlock>, List<Instruction>>>> {
public var instructions: LazySequence<FlattenSequence<LazyMapSequence<BasicBlockList, InstructionList>>> {
blocks.lazy.flatMap { $0.instructions }
}

Expand Down Expand Up @@ -358,3 +358,43 @@ public extension SideEffects.GlobalEffects {
}
}
}

public struct BasicBlockList : CollectionLikeSequence, IteratorProtocol {
private var currentBlock: BasicBlock?

public init(first: BasicBlock?) { currentBlock = first }

public mutating func next() -> BasicBlock? {
if let block = currentBlock {
currentBlock = block.next
return block
}
return nil
}

public var first: BasicBlock? { currentBlock }

public func reversed() -> ReverseBasicBlockList {
if let block = currentBlock {
let lastBlock = SILFunction_lastBlock(block.function.bridged).block
return ReverseBasicBlockList(first: lastBlock)
}
return ReverseBasicBlockList(first: nil)
}
}

public struct ReverseBasicBlockList : CollectionLikeSequence, IteratorProtocol {
private var currentBlock: BasicBlock?

public init(first: BasicBlock?) { currentBlock = first }

public mutating func next() -> BasicBlock? {
if let block = currentBlock {
currentBlock = block.previous
return block
}
return nil
}

public var first: BasicBlock? { currentBlock }
}
11 changes: 5 additions & 6 deletions SwiftCompilerSources/Sources/SIL/Instruction.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import SILBridging
// Instruction base classes
//===----------------------------------------------------------------------===//

public class Instruction : ListNode, CustomStringConvertible, Hashable {
public class Instruction : CustomStringConvertible, Hashable {
final public var next: Instruction? {
SILInstruction_next(bridged).instruction
}
Expand All @@ -26,11 +26,6 @@ public class Instruction : ListNode, CustomStringConvertible, Hashable {
SILInstruction_previous(bridged).instruction
}

// Needed for ReverseList<Instruction>.reversed(). Never use directly.
public var _firstInList: Instruction { SILBasicBlock_firstInst(block.bridged).instruction! }
// Needed for List<Instruction>.reversed(). Never use directly.
public var _lastInList: Instruction { SILBasicBlock_lastInst(block.bridged).instruction! }

final public var block: BasicBlock {
SILInstruction_getParent(bridged).block
}
Expand All @@ -42,6 +37,10 @@ public class Instruction : ListNode, CustomStringConvertible, Hashable {
return String(_cxxString: stdString)
}

final public var isDeleted: Bool {
return SILInstruction_isDeleted(bridged)
}

final public var operands: OperandArray {
return OperandArray(opArray: SILInstruction_getOperands(bridged))
}
Expand Down
65 changes: 0 additions & 65 deletions SwiftCompilerSources/Sources/SIL/Utils.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,71 +17,6 @@ import SILBridging
// Otherwise The Optimizer would fall back to Swift's assert implementation.
@_exported import Basic

//===----------------------------------------------------------------------===//
// Lists
//===----------------------------------------------------------------------===//

public protocol ListNode : AnyObject {
associatedtype Element
var next: Element? { get }
var previous: Element? { get }

/// The first node in the list. Used to implement `reversed()`.
var _firstInList: Element { get }

/// The last node in the list. Used to implement `reversed()`.
var _lastInList: Element { get }
}

public struct List<NodeType: ListNode> :
CollectionLikeSequence, IteratorProtocol where NodeType.Element == NodeType {
private var currentNode: NodeType?

public init(first: NodeType?) { currentNode = first }

public mutating func next() -> NodeType? {
if let node = currentNode {
currentNode = node.next
return node
}
return nil
}

public var first: NodeType? { currentNode }

public func reversed() -> ReverseList<NodeType> {
if let node = first {
return ReverseList(first: node._lastInList)
}
return ReverseList(first: nil)
}
}

public struct ReverseList<NodeType: ListNode> :
CollectionLikeSequence, IteratorProtocol where NodeType.Element == NodeType {
private var currentNode: NodeType?

public init(first: NodeType?) { currentNode = first }

public mutating func next() -> NodeType? {
if let node = currentNode {
currentNode = node.previous
return node
}
return nil
}

public var first: NodeType? { currentNode }

public func reversed() -> ReverseList<NodeType> {
if let node = first {
return ReverseList(first: node._firstInList)
}
return ReverseList(first: nil)
}
}


//===----------------------------------------------------------------------===//
// Sequence Utilities
//===----------------------------------------------------------------------===//
Expand Down
Loading