Skip to content

Commit cb67372

Browse files
authored
Merge pull request #62480 from eeckstein/instruction-iteration
SIL: simplify deleting instructions while iterating over instructions.
2 parents 4ca5d1e + d4d1620 commit cb67372

34 files changed

+642
-476
lines changed

SwiftCompilerSources/Sources/Optimizer/DataStructures/InstructionRange.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ struct InstructionRange : CustomStringConvertible, NoReflectionChildren {
9494
var isValid: Bool {
9595
blockRange.isValid &&
9696
// Check if there are any inserted instructions before the begin instruction in its block.
97-
!ReverseList(first: begin).dropFirst().contains { insertedInsts.contains($0) }
97+
!ReverseInstructionList(first: begin).dropFirst().contains { insertedInsts.contains($0) }
9898
}
9999

100100
/// Returns the end instructions.
@@ -115,7 +115,7 @@ struct InstructionRange : CustomStringConvertible, NoReflectionChildren {
115115
/// Returns the interior instructions.
116116
var interiors: LazySequence<FlattenSequence<
117117
LazyMapSequence<Stack<BasicBlock>,
118-
LazyFilterSequence<ReverseList<Instruction>>>>> {
118+
LazyFilterSequence<ReverseInstructionList>>>> {
119119
blockRange.inserted.lazy.flatMap {
120120
var include = blockRange.contains($0)
121121
return $0.instructions.reversed().lazy.filter {

SwiftCompilerSources/Sources/Optimizer/PassManager/PassRegistration.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ private func registerSwiftPasses() {
7575
registerPass(deadEndBlockDumper, { deadEndBlockDumper.run($0) })
7676
registerPass(rangeDumper, { rangeDumper.run($0) })
7777
registerPass(runUnitTests, { runUnitTests.run($0) })
78+
registerPass(testInstructionIteration, { testInstructionIteration.run($0) })
7879
}
7980

8081
private func registerSwiftAnalyses() {

SwiftCompilerSources/Sources/Optimizer/TestPasses/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,5 @@ swift_compiler_sources(Optimizer
1414
SILPrinter.swift
1515
RangeDumper.swift
1616
RunUnitTests.swift
17+
TestInstructionIteration.swift
1718
)
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
//===--- TestInstructionIteration.swift -----------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2022 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
import SIL
14+
15+
/// Tests instruction iteration while modifying the instruction list.
16+
///
17+
/// This pass iterates over the instruction list of the function's block and performs
18+
/// modifications of the instruction list - mostly deleting instructions.
19+
/// Modifications are triggered by `string_literal` instructions with known "commands".
20+
/// E.g. if a
21+
/// ```
22+
/// %1 = string_literal utf8 "delete_strings"
23+
/// ```
24+
/// is encountered during the iteration, it triggers the deletion of all `string_literal`
25+
/// instructions of the basic block (including the current one).
26+
let testInstructionIteration = FunctionPass(name: "test-instruction-iteration") {
27+
(function: Function, context: PassContext) in
28+
29+
print("Test instruction iteration in \(function.name):")
30+
31+
let reverse = function.name.string.hasSuffix("backward")
32+
33+
for block in function.blocks {
34+
print("\(block.name):")
35+
let termLoc = block.terminator.location
36+
if reverse {
37+
for inst in block.instructions.reversed() {
38+
handle(instruction: inst, context)
39+
}
40+
} else {
41+
for inst in block.instructions {
42+
handle(instruction: inst, context)
43+
}
44+
}
45+
if block.instructions.isEmpty || !(block.instructions.reversed().first is TermInst) {
46+
let builder = Builder(atEndOf: block, location: termLoc, context)
47+
builder.createUnreachable()
48+
}
49+
}
50+
print("End function \(function.name):")
51+
}
52+
53+
private func handle(instruction: Instruction, _ context: PassContext) {
54+
print(instruction)
55+
if let sl = instruction as? StringLiteralInst {
56+
switch sl.string {
57+
case "delete_strings":
58+
deleteAllInstructions(ofType: StringLiteralInst.self, in: instruction.block, context)
59+
case "delete_ints":
60+
deleteAllInstructions(ofType: IntegerLiteralInst.self, in: instruction.block, context)
61+
case "delete_branches":
62+
deleteAllInstructions(ofType: BranchInst.self, in: instruction.block, context)
63+
case "split_block":
64+
_ = context.splitBlock(at: instruction)
65+
default:
66+
break
67+
}
68+
}
69+
}
70+
71+
private func deleteAllInstructions<InstType: Instruction>(ofType: InstType.Type, in block: BasicBlock, _ context: PassContext) {
72+
for inst in block.instructions {
73+
if inst is InstType {
74+
context.erase(instruction: inst)
75+
}
76+
}
77+
}

SwiftCompilerSources/Sources/SIL/BasicBlock.swift

Lines changed: 63 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,10 @@
1313
import Basic
1414
import SILBridging
1515

16-
final public class BasicBlock : ListNode, CustomStringConvertible, HasShortDescription {
16+
final public class BasicBlock : CustomStringConvertible, HasShortDescription {
1717
public var next: BasicBlock? { SILBasicBlock_next(bridged).block }
1818
public var previous: BasicBlock? { SILBasicBlock_previous(bridged).block }
1919

20-
// Needed for ReverseList<BasicBlock>.reversed(). Never use directly.
21-
public var _firstInList: BasicBlock { SILFunction_firstBlock(function.bridged).block! }
22-
// Needed for List<BasicBlock>.reversed(). Never use directly.
23-
public var _lastInList: BasicBlock { SILFunction_lastBlock(function.bridged).block! }
24-
2520
public var function: Function { SILBasicBlock_getFunction(bridged).function }
2621

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

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

35-
public var instructions: List<Instruction> {
36-
List(first: SILBasicBlock_firstInst(bridged).instruction)
30+
public var instructions: InstructionList {
31+
InstructionList(first: SILBasicBlock_firstInst(bridged).instruction)
3732
}
3833

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

73+
/// The list of instructions in a BasicBlock.
74+
///
75+
/// It's allowed to delete the current, next or any other instructions while
76+
/// iterating over the instruction list.
77+
public struct InstructionList : CollectionLikeSequence, IteratorProtocol {
78+
private var currentInstruction: Instruction?
79+
80+
public init(first: Instruction?) { currentInstruction = first }
81+
82+
public mutating func next() -> Instruction? {
83+
if var inst = currentInstruction {
84+
while inst.isDeleted {
85+
guard let nextInst = inst.next else {
86+
return nil
87+
}
88+
inst = nextInst
89+
}
90+
currentInstruction = inst.next
91+
return inst
92+
}
93+
return nil
94+
}
95+
96+
public var first: Instruction? { currentInstruction }
97+
98+
public func reversed() -> ReverseInstructionList {
99+
if let inst = currentInstruction {
100+
let lastInst = SILBasicBlock_lastInst(inst.block.bridged).instruction
101+
return ReverseInstructionList(first: lastInst)
102+
}
103+
return ReverseInstructionList(first: nil)
104+
}
105+
}
106+
107+
/// The list of instructions in a BasicBlock in reverse order.
108+
///
109+
/// It's allowed to delete the current, next or any other instructions while
110+
/// iterating over the instruction list.
111+
public struct ReverseInstructionList : CollectionLikeSequence, IteratorProtocol {
112+
private var currentInstruction: Instruction?
113+
114+
public init(first: Instruction?) { currentInstruction = first }
115+
116+
public mutating func next() -> Instruction? {
117+
if var inst = currentInstruction {
118+
while inst.isDeleted {
119+
guard let nextInst = inst.previous else {
120+
return nil
121+
}
122+
inst = nextInst
123+
}
124+
currentInstruction = inst.previous
125+
return inst
126+
}
127+
return nil
128+
}
129+
130+
public var first: Instruction? { currentInstruction }
131+
}
132+
78133
public struct ArgumentArray : RandomAccessCollection {
79134
fileprivate let block: BasicBlock
80135

SwiftCompilerSources/Sources/SIL/Builder.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,4 +187,12 @@ public struct Builder {
187187
return bi.getAs(BranchInst.self)
188188
}
189189
}
190+
191+
@discardableResult
192+
public func createUnreachable() -> UnreachableInst {
193+
notifyInstructionsChanged()
194+
notifyBranchesChanged()
195+
let ui = SILBuilder_createUnreachable(bridged)
196+
return ui.getAs(UnreachableInst.self)
197+
}
190198
}

SwiftCompilerSources/Sources/SIL/Function.swift

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,16 +42,16 @@ final public class Function : CustomStringConvertible, HasShortDescription, Hash
4242
SILFunction_firstBlock(bridged).block!
4343
}
4444

45-
public var blocks : List<BasicBlock> {
46-
return List(first: SILFunction_firstBlock(bridged).block)
45+
public var blocks : BasicBlockList {
46+
BasicBlockList(first: SILFunction_firstBlock(bridged).block)
4747
}
4848

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

5353
/// All instructions of all blocks.
54-
public var instructions: LazySequence<FlattenSequence<LazyMapSequence<List<BasicBlock>, List<Instruction>>>> {
54+
public var instructions: LazySequence<FlattenSequence<LazyMapSequence<BasicBlockList, InstructionList>>> {
5555
blocks.lazy.flatMap { $0.instructions }
5656
}
5757

@@ -358,3 +358,43 @@ public extension SideEffects.GlobalEffects {
358358
}
359359
}
360360
}
361+
362+
public struct BasicBlockList : CollectionLikeSequence, IteratorProtocol {
363+
private var currentBlock: BasicBlock?
364+
365+
public init(first: BasicBlock?) { currentBlock = first }
366+
367+
public mutating func next() -> BasicBlock? {
368+
if let block = currentBlock {
369+
currentBlock = block.next
370+
return block
371+
}
372+
return nil
373+
}
374+
375+
public var first: BasicBlock? { currentBlock }
376+
377+
public func reversed() -> ReverseBasicBlockList {
378+
if let block = currentBlock {
379+
let lastBlock = SILFunction_lastBlock(block.function.bridged).block
380+
return ReverseBasicBlockList(first: lastBlock)
381+
}
382+
return ReverseBasicBlockList(first: nil)
383+
}
384+
}
385+
386+
public struct ReverseBasicBlockList : CollectionLikeSequence, IteratorProtocol {
387+
private var currentBlock: BasicBlock?
388+
389+
public init(first: BasicBlock?) { currentBlock = first }
390+
391+
public mutating func next() -> BasicBlock? {
392+
if let block = currentBlock {
393+
currentBlock = block.previous
394+
return block
395+
}
396+
return nil
397+
}
398+
399+
public var first: BasicBlock? { currentBlock }
400+
}

SwiftCompilerSources/Sources/SIL/Instruction.swift

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import SILBridging
1717
// Instruction base classes
1818
//===----------------------------------------------------------------------===//
1919

20-
public class Instruction : ListNode, CustomStringConvertible, Hashable {
20+
public class Instruction : CustomStringConvertible, Hashable {
2121
final public var next: Instruction? {
2222
SILInstruction_next(bridged).instruction
2323
}
@@ -26,11 +26,6 @@ public class Instruction : ListNode, CustomStringConvertible, Hashable {
2626
SILInstruction_previous(bridged).instruction
2727
}
2828

29-
// Needed for ReverseList<Instruction>.reversed(). Never use directly.
30-
public var _firstInList: Instruction { SILBasicBlock_firstInst(block.bridged).instruction! }
31-
// Needed for List<Instruction>.reversed(). Never use directly.
32-
public var _lastInList: Instruction { SILBasicBlock_lastInst(block.bridged).instruction! }
33-
3429
final public var block: BasicBlock {
3530
SILInstruction_getParent(bridged).block
3631
}
@@ -42,6 +37,10 @@ public class Instruction : ListNode, CustomStringConvertible, Hashable {
4237
return String(_cxxString: stdString)
4338
}
4439

40+
final public var isDeleted: Bool {
41+
return SILInstruction_isDeleted(bridged)
42+
}
43+
4544
final public var operands: OperandArray {
4645
return OperandArray(opArray: SILInstruction_getOperands(bridged))
4746
}

SwiftCompilerSources/Sources/SIL/Utils.swift

Lines changed: 0 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -17,71 +17,6 @@ import SILBridging
1717
// Otherwise The Optimizer would fall back to Swift's assert implementation.
1818
@_exported import Basic
1919

20-
//===----------------------------------------------------------------------===//
21-
// Lists
22-
//===----------------------------------------------------------------------===//
23-
24-
public protocol ListNode : AnyObject {
25-
associatedtype Element
26-
var next: Element? { get }
27-
var previous: Element? { get }
28-
29-
/// The first node in the list. Used to implement `reversed()`.
30-
var _firstInList: Element { get }
31-
32-
/// The last node in the list. Used to implement `reversed()`.
33-
var _lastInList: Element { get }
34-
}
35-
36-
public struct List<NodeType: ListNode> :
37-
CollectionLikeSequence, IteratorProtocol where NodeType.Element == NodeType {
38-
private var currentNode: NodeType?
39-
40-
public init(first: NodeType?) { currentNode = first }
41-
42-
public mutating func next() -> NodeType? {
43-
if let node = currentNode {
44-
currentNode = node.next
45-
return node
46-
}
47-
return nil
48-
}
49-
50-
public var first: NodeType? { currentNode }
51-
52-
public func reversed() -> ReverseList<NodeType> {
53-
if let node = first {
54-
return ReverseList(first: node._lastInList)
55-
}
56-
return ReverseList(first: nil)
57-
}
58-
}
59-
60-
public struct ReverseList<NodeType: ListNode> :
61-
CollectionLikeSequence, IteratorProtocol where NodeType.Element == NodeType {
62-
private var currentNode: NodeType?
63-
64-
public init(first: NodeType?) { currentNode = first }
65-
66-
public mutating func next() -> NodeType? {
67-
if let node = currentNode {
68-
currentNode = node.previous
69-
return node
70-
}
71-
return nil
72-
}
73-
74-
public var first: NodeType? { currentNode }
75-
76-
public func reversed() -> ReverseList<NodeType> {
77-
if let node = first {
78-
return ReverseList(first: node._firstInList)
79-
}
80-
return ReverseList(first: nil)
81-
}
82-
}
83-
84-
8520
//===----------------------------------------------------------------------===//
8621
// Sequence Utilities
8722
//===----------------------------------------------------------------------===//

0 commit comments

Comments
 (0)