SwiftCompilerSources: move SIL-related datastructures from the Optimizer to the SIL module

This commit is contained in:
Erik Eckstein
2025-07-25 16:39:16 +02:00
parent e2129b50ce
commit e89fdb56ba
10 changed files with 174 additions and 164 deletions

View File

@@ -7,11 +7,6 @@
# See http://swift.org/CONTRIBUTORS.txt for Swift project authors
swift_compiler_sources(Optimizer
BasicBlockRange.swift
DeadEndBlocks.swift
FunctionUses.swift
InstructionRange.swift
ReachableBlocks.swift
Set.swift
Stack.swift
Worklist.swift)
ReachableBlocks.swift)

View File

@@ -14,6 +14,8 @@
//
//===----------------------------------------------------------------------===//
import SIL
let getAccessBaseTest = FunctionTest("swift_get_access_base") {
function, arguments, context in
let address = arguments.takeValue()

View File

@@ -904,3 +904,29 @@ let interiorLivenessTest = FunctionTest("interior_liveness_swift") {
defer { boundary.deinitialize() }
print(boundary)
}
//
// TODO: Move this to InstructionRange.swift when computeLinearLiveness is in the SIL module.
//
let rangeOverlapsPathTest = FunctionTest("range_overlaps_path") {
function, arguments, context in
let rangeValue = arguments.takeValue()
print("Range of: \(rangeValue)")
var range = computeLinearLiveness(for: rangeValue, context)
defer { range.deinitialize() }
let pathInst = arguments.takeInstruction()
print("Path begin: \(pathInst)")
if let pathBegin = pathInst as? ScopedInstruction {
for end in pathBegin.endInstructions {
print("Overlap kind:", range.overlaps(pathBegin: pathInst, pathEnd: end, context))
}
return
}
if let pathValue = pathInst as? SingleValueInstruction, pathValue.ownership == .owned {
for end in pathValue.uses.endingLifetime {
print("Overlap kind:", range.overlaps(pathBegin: pathInst, pathEnd: end.instruction, context))
}
return
}
print("Test specification error: not a scoped or owned instruction: \(pathInst)")
}

View File

@@ -35,5 +35,6 @@ add_swift_compiler_module(SIL
VTable.swift
WitnessTable.swift)
add_subdirectory(DataStructures)
add_subdirectory(Utilities)

View File

@@ -10,8 +10,6 @@
//
//===----------------------------------------------------------------------===//
import SIL
/// A range of basic blocks.
///
/// The `BasicBlockRange` defines a range from a dominating "begin" block to one or more "end" blocks.
@@ -46,16 +44,16 @@ import SIL
/// This type should be a move-only type, but unfortunately we don't have move-only
/// types yet. Therefore it's needed to call `deinitialize()` explicitly to
/// destruct this data structure, e.g. in a `defer {}` block.
struct BasicBlockRange : CustomStringConvertible, NoReflectionChildren {
public struct BasicBlockRange : CustomStringConvertible, NoReflectionChildren {
/// The dominating begin block.
let begin: BasicBlock
public let begin: BasicBlock
/// The inclusive range, i.e. the exclusive range plus the end blocks.
private(set) var inclusiveRange: Stack<BasicBlock>
public private(set) var inclusiveRange: Stack<BasicBlock>
/// The exclusive range, i.e. not containing the end blocks.
var range: LazyFilterSequence<Stack<BasicBlock>> {
public var range: LazyFilterSequence<Stack<BasicBlock>> {
inclusiveRange.lazy.filter { contains($0) }
}
@@ -66,7 +64,7 @@ struct BasicBlockRange : CustomStringConvertible, NoReflectionChildren {
private var inExclusiveRange: BasicBlockSet
private var worklist: BasicBlockWorklist
init(begin: BasicBlock, _ context: some Context) {
public init(begin: BasicBlock, _ context: some Context) {
self.begin = begin
self.inclusiveRange = Stack(context)
self.inserted = Stack(context)
@@ -81,7 +79,7 @@ struct BasicBlockRange : CustomStringConvertible, NoReflectionChildren {
/// Returns true if the begin-block is reached during backward propagation.
/// Usually this is not relevant, but InstructionRange needs this information.
@discardableResult
mutating func insert(_ block: BasicBlock) -> Bool {
public mutating func insert(_ block: BasicBlock) -> Bool {
if wasInserted.insert(block) {
inserted.append(block)
}
@@ -103,22 +101,22 @@ struct BasicBlockRange : CustomStringConvertible, NoReflectionChildren {
}
/// Insert a sequence of potential end blocks.
mutating func insert<S: Sequence>(contentsOf other: S) where S.Element == BasicBlock {
public mutating func insert<S: Sequence>(contentsOf other: S) where S.Element == BasicBlock {
for block in other {
insert(block)
}
}
/// Returns true if the exclusive range contains `block`.
func contains(_ block: BasicBlock) -> Bool { inExclusiveRange.contains(block) }
public func contains(_ block: BasicBlock) -> Bool { inExclusiveRange.contains(block) }
/// Returns true if the inclusive range contains `block`.
func inclusiveRangeContains (_ block: BasicBlock) -> Bool {
public func inclusiveRangeContains (_ block: BasicBlock) -> Bool {
worklist.hasBeenPushed(block)
}
/// Returns true if the range is valid and that's iff the begin block dominates all blocks of the range.
var isValid: Bool {
public var isValid: Bool {
let entry = begin.parentFunction.entryBlock
return begin == entry ||
// If any block in the range is not dominated by `begin`, the range propagates back to the entry block.
@@ -126,12 +124,12 @@ struct BasicBlockRange : CustomStringConvertible, NoReflectionChildren {
}
/// Returns the end blocks.
var ends: LazyFilterSequence<Stack<BasicBlock>> {
public var ends: LazyFilterSequence<Stack<BasicBlock>> {
inserted.lazy.filter { !contains($0) }
}
/// Returns the exit blocks.
var exits: LazySequence<FlattenSequence<
public var exits: LazySequence<FlattenSequence<
LazyMapSequence<LazyFilterSequence<Stack<BasicBlock>>,
LazyFilterSequence<SuccessorArray>>>> {
range.flatMap {
@@ -142,11 +140,11 @@ struct BasicBlockRange : CustomStringConvertible, NoReflectionChildren {
}
/// Returns the interior blocks.
var interiors: LazyFilterSequence<Stack<BasicBlock>> {
public var interiors: LazyFilterSequence<Stack<BasicBlock>> {
inserted.lazy.filter { contains($0) && $0 != begin }
}
var description: String {
public var description: String {
return (isValid ? "" : "<invalid>\n") +
"""
begin: \(begin.name)
@@ -159,7 +157,7 @@ struct BasicBlockRange : CustomStringConvertible, NoReflectionChildren {
}
/// TODO: once we have move-only types, make this a real deinit.
mutating func deinitialize() {
public mutating func deinitialize() {
worklist.deinitialize()
inExclusiveRange.deinitialize()
wasInserted.deinitialize()

View File

@@ -0,0 +1,14 @@
# This source file is part of the Swift.org open source project
#
# Copyright (c) 2014 - 2021 Apple Inc. and the Swift project authors
# Licensed under Apache License v2.0 with Runtime Library Exception
#
# See http://swift.org/LICENSE.txt for license information
# See http://swift.org/CONTRIBUTORS.txt for Swift project authors
swift_compiler_sources(SIL
BasicBlockRange.swift
InstructionRange.swift
Set.swift
Stack.swift
Worklist.swift)

View File

@@ -10,8 +10,6 @@
//
//===----------------------------------------------------------------------===//
import SIL
/// A range of instructions.
///
/// The `InstructionRange` defines a range from a dominating "begin" instruction to one or more "end" instructions.
@@ -38,29 +36,29 @@ import SIL
/// This type should be a move-only type, but unfortunately we don't have move-only
/// types yet. Therefore it's needed to call `deinitialize()` explicitly to
/// destruct this data structure, e.g. in a `defer {}` block.
struct InstructionRange : CustomStringConvertible, NoReflectionChildren {
public struct InstructionRange : CustomStringConvertible, NoReflectionChildren {
/// The underlying block range.
private(set) var blockRange: BasicBlockRange
public private(set) var blockRange: BasicBlockRange
private var insertedInsts: InstructionSet
// For efficiency, this set does not include instructions in blocks which are not the begin or any end block.
private var inExclusiveRange: InstructionSet
init(begin beginInst: Instruction, _ context: some Context) {
public init(begin beginInst: Instruction, _ context: some Context) {
self = InstructionRange(beginBlock: beginInst.parentBlock, context)
self.inExclusiveRange.insert(beginInst)
}
// Note: 'ends' are simply the instructions to insert in the range. 'self.ends' might not return the same sequence
// as this 'ends' argument because 'self.ends' will not include block exits.
init<S: Sequence>(begin beginInst: Instruction, ends: S, _ context: some Context) where S.Element: Instruction {
public init<S: Sequence>(begin beginInst: Instruction, ends: S, _ context: some Context) where S.Element: Instruction {
self = InstructionRange(begin: beginInst, context)
insert(contentsOf: ends)
}
init(for value: Value, _ context: some Context) {
public init(for value: Value, _ context: some Context) {
if let inst = value.definingInstruction {
self = InstructionRange(begin: inst, context)
} else if let arg = value as? Argument {
@@ -77,7 +75,7 @@ struct InstructionRange : CustomStringConvertible, NoReflectionChildren {
}
/// Insert a potential end instruction.
mutating func insert(_ inst: Instruction) {
public mutating func insert(_ inst: Instruction) {
insertedInsts.insert(inst)
insertIntoRange(instructions: ReverseInstructionList(first: inst.previous))
if blockRange.insert(inst.parentBlock) {
@@ -90,14 +88,14 @@ struct InstructionRange : CustomStringConvertible, NoReflectionChildren {
}
/// Insert a sequence of potential end instructions.
mutating func insert<S: Sequence>(contentsOf other: S) where S.Element: Instruction {
public mutating func insert<S: Sequence>(contentsOf other: S) where S.Element: Instruction {
for inst in other {
insert(inst)
}
}
/// Returns true if the exclusive range contains `inst`.
func contains(_ inst: Instruction) -> Bool {
public func contains(_ inst: Instruction) -> Bool {
if inExclusiveRange.contains(inst) {
return true
}
@@ -106,37 +104,37 @@ struct InstructionRange : CustomStringConvertible, NoReflectionChildren {
}
/// Returns true if the inclusive range contains `inst`.
func inclusiveRangeContains (_ inst: Instruction) -> Bool {
public func inclusiveRangeContains (_ inst: Instruction) -> Bool {
contains(inst) || insertedInsts.contains(inst)
}
/// Returns the end instructions.
///
/// Warning: this returns `begin` if no instructions were inserted.
var ends: LazyMapSequence<LazyFilterSequence<Stack<BasicBlock>>, Instruction> {
public var ends: LazyMapSequence<LazyFilterSequence<Stack<BasicBlock>>, Instruction> {
blockRange.ends.map {
$0.instructions.reversed().first(where: { insertedInsts.contains($0)})!
}
}
// Returns the exit blocks.
var exitBlocks: LazySequence<FlattenSequence<
LazyMapSequence<LazyFilterSequence<Stack<BasicBlock>>,
public var exitBlocks: LazySequence<FlattenSequence<
LazyMapSequence<LazyFilterSequence<Stack<BasicBlock>>,
LazyFilterSequence<SuccessorArray>>>> {
blockRange.exits
}
/// Returns the exit instructions.
var exits: LazyMapSequence<LazySequence<FlattenSequence<
LazyMapSequence<LazyFilterSequence<Stack<BasicBlock>>,
public var exits: LazyMapSequence<LazySequence<FlattenSequence<
LazyMapSequence<LazyFilterSequence<Stack<BasicBlock>>,
LazyFilterSequence<SuccessorArray>>>>,
Instruction> {
blockRange.exits.lazy.map { $0.instructions.first! }
}
/// Returns the interior instructions.
var interiors: LazySequence<FlattenSequence<
LazyMapSequence<Stack<BasicBlock>,
public var interiors: LazySequence<FlattenSequence<
LazyMapSequence<Stack<BasicBlock>,
LazyFilterSequence<ReverseInstructionList>>>> {
blockRange.inserted.lazy.flatMap {
var include = blockRange.contains($0)
@@ -151,7 +149,7 @@ struct InstructionRange : CustomStringConvertible, NoReflectionChildren {
}
}
var begin: Instruction? {
public var begin: Instruction? {
blockRange.begin.instructions.first(where: inExclusiveRange.contains)
}
@@ -163,7 +161,7 @@ struct InstructionRange : CustomStringConvertible, NoReflectionChildren {
}
}
var description: String {
public var description: String {
return (blockRange.isValid ? "" : "<invalid>\n") +
"""
begin: \(begin?.description ?? blockRange.begin.name)
@@ -174,7 +172,7 @@ struct InstructionRange : CustomStringConvertible, NoReflectionChildren {
}
/// TODO: once we have move-only types, make this a real deinit.
mutating func deinitialize() {
public mutating func deinitialize() {
inExclusiveRange.deinitialize()
insertedInsts.deinitialize()
blockRange.deinitialize()
@@ -182,7 +180,7 @@ struct InstructionRange : CustomStringConvertible, NoReflectionChildren {
}
extension InstructionRange {
enum PathOverlap {
public enum PathOverlap {
// range: ---
// | pathBegin
// | |
@@ -226,7 +224,7 @@ extension InstructionRange {
/// Returns .containsBegin, if this range has the same begin and end as the path.
///
/// Precondition: `begin` dominates `end`.
func overlaps(pathBegin: Instruction, pathEnd: Instruction, _ context: some Context) -> PathOverlap {
public func overlaps(pathBegin: Instruction, pathEnd: Instruction, _ context: some Context) -> PathOverlap {
assert(pathBegin != pathEnd, "expect an exclusive path")
if contains(pathBegin) {
// Note: pathEnd != self.begin here since self.contains(pathBegin)
@@ -277,25 +275,3 @@ extension InstructionRange {
}
}
let rangeOverlapsPathTest = FunctionTest("range_overlaps_path") {
function, arguments, context in
let rangeValue = arguments.takeValue()
print("Range of: \(rangeValue)")
var range = computeLinearLiveness(for: rangeValue, context)
defer { range.deinitialize() }
let pathInst = arguments.takeInstruction()
print("Path begin: \(pathInst)")
if let pathBegin = pathInst as? ScopedInstruction {
for end in pathBegin.endInstructions {
print("Overlap kind:", range.overlaps(pathBegin: pathInst, pathEnd: end, context))
}
return
}
if let pathValue = pathInst as? SingleValueInstruction, pathValue.ownership == .owned {
for end in pathValue.uses.endingLifetime {
print("Overlap kind:", range.overlaps(pathBegin: pathInst, pathEnd: end.instruction, context))
}
return
}
print("Test specification error: not a scoped or owned instruction: \(pathInst)")
}

View File

@@ -10,10 +10,9 @@
//
//===----------------------------------------------------------------------===//
import SIL
import OptimizerBridging
import SILBridging
protocol IntrusiveSet : CustomStringConvertible, NoReflectionChildren {
public protocol IntrusiveSet : CustomStringConvertible, NoReflectionChildren {
associatedtype Element
init(_ context: some Context)
@@ -31,31 +30,31 @@ protocol IntrusiveSet : CustomStringConvertible, NoReflectionChildren {
/// This type should be a move-only type, but unfortunately we don't have move-only
/// types yet. Therefore it's needed to call `deinitialize()` explicitly to
/// destruct this data structure, e.g. in a `defer {}` block.
struct BasicBlockSet : IntrusiveSet {
public struct BasicBlockSet : IntrusiveSet {
private let context: BridgedContext
private let bridged: BridgedBasicBlockSet
init(_ context: some Context) {
public init(_ context: some Context) {
self.context = context._bridged
self.bridged = self.context.allocBasicBlockSet()
}
func contains(_ block: BasicBlock) -> Bool {
public func contains(_ block: BasicBlock) -> Bool {
bridged.contains(block.bridged)
}
/// Returns true if `block` was not contained in the set before inserting.
@discardableResult
mutating func insert(_ block: BasicBlock) -> Bool {
public mutating func insert(_ block: BasicBlock) -> Bool {
bridged.insert(block.bridged)
}
mutating func erase(_ block: BasicBlock) {
public mutating func erase(_ block: BasicBlock) {
bridged.erase(block.bridged)
}
var description: String {
public var description: String {
let function = bridged.getFunction().function
let blockNames = function.blocks.enumerated().filter { contains($0.1) }
.map { "bb\($0.0)"}
@@ -63,7 +62,7 @@ struct BasicBlockSet : IntrusiveSet {
}
/// TODO: once we have move-only types, make this a real deinit.
mutating func deinitialize() {
public mutating func deinitialize() {
context.freeBasicBlockSet(bridged)
}
}
@@ -76,31 +75,31 @@ struct BasicBlockSet : IntrusiveSet {
/// This type should be a move-only type, but unfortunately we don't have move-only
/// types yet. Therefore it's needed to call `deinitialize()` explicitly to
/// destruct this data structure, e.g. in a `defer {}` block.
struct ValueSet : IntrusiveSet {
public struct ValueSet : IntrusiveSet {
private let context: BridgedContext
private let bridged: BridgedNodeSet
init(_ context: some Context) {
public init(_ context: some Context) {
self.context = context._bridged
self.bridged = self.context.allocNodeSet()
}
func contains(_ value: Value) -> Bool {
public func contains(_ value: Value) -> Bool {
bridged.containsValue(value.bridged)
}
/// Returns true if `value` was not contained in the set before inserting.
@discardableResult
mutating func insert(_ value: Value) -> Bool {
public mutating func insert(_ value: Value) -> Bool {
bridged.insertValue(value.bridged)
}
mutating func erase(_ value: Value) {
public mutating func erase(_ value: Value) {
bridged.eraseValue(value.bridged)
}
var description: String {
public var description: String {
let function = bridged.getFunction().function
var d = "{\n"
for block in function.blocks {
@@ -122,7 +121,7 @@ struct ValueSet : IntrusiveSet {
}
/// TODO: once we have move-only types, make this a real deinit.
mutating func deinitialize() {
public mutating func deinitialize() {
context.freeNodeSet(bridged)
}
}
@@ -135,31 +134,31 @@ struct ValueSet : IntrusiveSet {
/// This type should be a move-only type, but unfortunately we don't have move-only
/// types yet. Therefore it's needed to call `deinitialize()` explicitly to
/// destruct this data structure, e.g. in a `defer {}` block.
struct SpecificInstructionSet<InstType: Instruction> : IntrusiveSet {
public struct SpecificInstructionSet<InstType: Instruction> : IntrusiveSet {
private let context: BridgedContext
private let bridged: BridgedNodeSet
init(_ context: some Context) {
public init(_ context: some Context) {
self.context = context._bridged
self.bridged = self.context.allocNodeSet()
}
func contains(_ inst: InstType) -> Bool {
public func contains(_ inst: InstType) -> Bool {
bridged.containsInstruction(inst.bridged)
}
/// Returns true if `inst` was not contained in the set before inserting.
@discardableResult
mutating func insert(_ inst: InstType) -> Bool {
public mutating func insert(_ inst: InstType) -> Bool {
bridged.insertInstruction(inst.bridged)
}
mutating func erase(_ inst: InstType) {
public mutating func erase(_ inst: InstType) {
bridged.eraseInstruction(inst.bridged)
}
var description: String {
public var description: String {
let function = bridged.getFunction().function
var d = "{\n"
for i in function.instructions {
@@ -172,27 +171,27 @@ struct SpecificInstructionSet<InstType: Instruction> : IntrusiveSet {
}
/// TODO: once we have move-only types, make this a real deinit.
mutating func deinitialize() {
public mutating func deinitialize() {
context.freeNodeSet(bridged)
}
}
/// An `InstructionSet` which also provides a `count` property.
struct SpecificInstructionSetWithCount<InstType: Instruction> : IntrusiveSet {
private(set) var count = 0
public struct SpecificInstructionSetWithCount<InstType: Instruction> : IntrusiveSet {
public private(set) var count = 0
private var underlyingSet: SpecificInstructionSet<InstType>
init(_ context: some Context) {
public init(_ context: some Context) {
self.underlyingSet = SpecificInstructionSet(context)
}
func contains(_ inst: InstType) -> Bool { underlyingSet.contains(inst) }
public func contains(_ inst: InstType) -> Bool { underlyingSet.contains(inst) }
var isEmpty: Bool { count == 0 }
public var isEmpty: Bool { count == 0 }
/// Returns true if `inst` was not contained in the set before inserting.
@discardableResult
mutating func insert(_ inst: InstType) -> Bool {
public mutating func insert(_ inst: InstType) -> Bool {
if underlyingSet.insert(inst) {
count += 1
return true
@@ -200,7 +199,7 @@ struct SpecificInstructionSetWithCount<InstType: Instruction> : IntrusiveSet {
return false
}
mutating func erase(_ inst: InstType) {
public mutating func erase(_ inst: InstType) {
if underlyingSet.contains(inst) {
count -= 1
assert(count >= 0)
@@ -208,13 +207,13 @@ struct SpecificInstructionSetWithCount<InstType: Instruction> : IntrusiveSet {
underlyingSet.erase(inst)
}
var description: String { underlyingSet.description }
public var description: String { underlyingSet.description }
mutating func deinitialize() { underlyingSet.deinitialize() }
public mutating func deinitialize() { underlyingSet.deinitialize() }
}
typealias InstructionSet = SpecificInstructionSet<Instruction>
typealias InstructionSetWithCount = SpecificInstructionSetWithCount<Instruction>
public typealias InstructionSet = SpecificInstructionSet<Instruction>
public typealias InstructionSetWithCount = SpecificInstructionSetWithCount<Instruction>
/// A set of operands.
///
@@ -224,31 +223,31 @@ typealias InstructionSetWithCount = SpecificInstructionSetWithCount<Instruction>
/// This type should be a move-only type, but unfortunately we don't have move-only
/// types yet. Therefore it's needed to call `deinitialize()` explicitly to
/// destruct this data structure, e.g. in a `defer {}` block.
struct OperandSet : IntrusiveSet {
public struct OperandSet : IntrusiveSet {
private let context: BridgedContext
private let bridged: BridgedOperandSet
init(_ context: some Context) {
public init(_ context: some Context) {
self.context = context._bridged
self.bridged = self.context.allocOperandSet()
}
func contains(_ operand: Operand) -> Bool {
public func contains(_ operand: Operand) -> Bool {
bridged.contains(operand.bridged)
}
/// Returns true if `inst` was not contained in the set before inserting.
@discardableResult
mutating func insert(_ operand: Operand) -> Bool {
public mutating func insert(_ operand: Operand) -> Bool {
bridged.insert(operand.bridged)
}
mutating func erase(_ operand: Operand) {
public mutating func erase(_ operand: Operand) {
bridged.erase(operand.bridged)
}
var description: String {
public var description: String {
let function = bridged.getFunction().function
var d = "{\n"
for inst in function.instructions {
@@ -263,13 +262,13 @@ struct OperandSet : IntrusiveSet {
}
/// TODO: once we have move-only types, make this a real deinit.
mutating func deinitialize() {
public mutating func deinitialize() {
context.freeOperandSet(bridged)
}
}
extension InstructionSet {
mutating func insert<I: Instruction>(contentsOf source: some Sequence<I>) {
public mutating func insert<I: Instruction>(contentsOf source: some Sequence<I>) {
for inst in source {
_ = insert(inst)
}
@@ -277,13 +276,13 @@ extension InstructionSet {
}
extension IntrusiveSet {
mutating func insert(contentsOf source: some Sequence<Element>) {
public mutating func insert(contentsOf source: some Sequence<Element>) {
for element in source {
_ = insert(element)
}
}
init(insertContentsOf source: some Sequence<Element>, _ context: some Context) {
public init(insertContentsOf source: some Sequence<Element>, _ context: some Context) {
self.init(context)
insert(contentsOf: source)
}

View File

@@ -10,8 +10,7 @@
//
//===----------------------------------------------------------------------===//
import OptimizerBridging
import SIL
import SILBridging
/// A very efficient implementation of a stack, which can also be iterated over.
///
@@ -24,7 +23,7 @@ import SIL
/// This type should be a move-only type, but unfortunately we don't have move-only
/// types yet. Therefore it's needed to call `deinitialize()` explicitly to
/// destruct this data structure, e.g. in a `defer {}` block.
struct Stack<Element> : CollectionLikeSequence {
public struct Stack<Element> : CollectionLikeSequence {
private let bridgedContext: BridgedContext
private var firstSlab = BridgedContext.Slab(nil)
@@ -50,13 +49,13 @@ struct Stack<Element> : CollectionLikeSequence {
return UnsafeMutableRawPointer(slab.data!).assumingMemoryBound(to: Element.self) + index
}
struct Iterator : IteratorProtocol {
public struct Iterator : IteratorProtocol {
var slab: BridgedContext.Slab
var index: Int
let lastSlab: BridgedContext.Slab
let endIndex: Int
mutating func next() -> Element? {
public mutating func next() -> Element? {
let end = (slab.data == lastSlab.data ? endIndex : slabCapacity)
guard index < end else { return nil }
@@ -72,21 +71,21 @@ struct Stack<Element> : CollectionLikeSequence {
}
}
init(_ context: some Context) { self.bridgedContext = context._bridged }
public init(_ context: some Context) { self.bridgedContext = context._bridged }
func makeIterator() -> Iterator {
public func makeIterator() -> Iterator {
return Iterator(slab: firstSlab, index: 0, lastSlab: lastSlab, endIndex: endIndex)
}
var first: Element? {
public var first: Element? {
isEmpty ? nil : Stack.element(in: firstSlab, at: 0)
}
var last: Element? {
public var last: Element? {
isEmpty ? nil : Stack.element(in: lastSlab, at: endIndex &- 1)
}
mutating func push(_ element: Element) {
public mutating func push(_ element: Element) {
if endIndex >= Stack.slabCapacity {
lastSlab = allocate(after: lastSlab)
endIndex = 0
@@ -100,17 +99,17 @@ struct Stack<Element> : CollectionLikeSequence {
}
/// The same as `push` to provide an Array-like append API.
mutating func append(_ element: Element) { push(element) }
public mutating func append(_ element: Element) { push(element) }
mutating func append<S: Sequence>(contentsOf other: S) where S.Element == Element {
public mutating func append<S: Sequence>(contentsOf other: S) where S.Element == Element {
for elem in other {
append(elem)
}
}
var isEmpty: Bool { return endIndex == 0 }
mutating func pop() -> Element? {
public var isEmpty: Bool { return endIndex == 0 }
public mutating func pop() -> Element? {
if isEmpty {
return nil
}
@@ -133,12 +132,12 @@ struct Stack<Element> : CollectionLikeSequence {
return elem
}
mutating func removeAll() {
public mutating func removeAll() {
while pop() != nil { }
}
/// TODO: once we have move-only types, make this a real deinit.
mutating func deinitialize() { removeAll() }
public mutating func deinitialize() { removeAll() }
}
extension Stack {

View File

@@ -10,8 +10,6 @@
//
//===----------------------------------------------------------------------===//
import SIL
/// A utility for processing entities in a worklist.
///
/// A `Worklist` is basically a combination of a stack and a set.
@@ -20,20 +18,20 @@ import SIL
/// This type should be a move-only type, but unfortunately we don't have move-only
/// types yet. Therefore it's needed to call `deinitialize()` explicitly to
/// destruct this data structure, e.g. in a `defer {}` block.
struct Worklist<Set: IntrusiveSet> : CustomStringConvertible, NoReflectionChildren {
typealias Element = Set.Element
public struct Worklist<Set: IntrusiveSet> : CustomStringConvertible, NoReflectionChildren {
public typealias Element = Set.Element
private var worklist: Stack<Element>
private var pushedElements: Set
init(_ context: some Context) {
public init(_ context: some Context) {
self.worklist = Stack(context)
self.pushedElements = Set(context)
}
mutating func pop() -> Element? { return worklist.pop() }
public mutating func pop() -> Element? { return worklist.pop() }
/// Pop and allow the popped element to be pushed again to the worklist.
mutating func popAndForget() -> Element? {
public mutating func popAndForget() -> Element? {
if let element = worklist.pop() {
pushedElements.erase(element)
return element
@@ -41,24 +39,24 @@ struct Worklist<Set: IntrusiveSet> : CustomStringConvertible, NoReflectionChildr
return nil
}
mutating func pushIfNotVisited(_ element: Element) {
public mutating func pushIfNotVisited(_ element: Element) {
if pushedElements.insert(element) {
worklist.append(element)
}
}
mutating func pushIfNotVisited<S: Sequence>(contentsOf other: S) where S.Element == Element {
public mutating func pushIfNotVisited<S: Sequence>(contentsOf other: S) where S.Element == Element {
for element in other {
pushIfNotVisited(element)
}
}
/// Returns true if \p element was pushed to the worklist, regardless if it's already popped or not.
func hasBeenPushed(_ element: Element) -> Bool { pushedElements.contains(element) }
public func hasBeenPushed(_ element: Element) -> Bool { pushedElements.contains(element) }
var isEmpty: Bool { worklist.isEmpty }
public var isEmpty: Bool { worklist.isEmpty }
var description: String {
public var description: String {
"""
worklist: \(worklist)
pushed: \(pushedElements)
@@ -66,20 +64,20 @@ struct Worklist<Set: IntrusiveSet> : CustomStringConvertible, NoReflectionChildr
}
/// TODO: once we have move-only types, make this a real deinit.
mutating func deinitialize() {
public mutating func deinitialize() {
pushedElements.deinitialize()
worklist.deinitialize()
}
}
typealias BasicBlockWorklist = Worklist<BasicBlockSet>
typealias InstructionWorklist = Worklist<InstructionSet>
typealias SpecificInstructionWorklist<InstType: Instruction> = Worklist<SpecificInstructionSet<InstType>>
typealias ValueWorklist = Worklist<ValueSet>
typealias OperandWorklist = Worklist<OperandSet>
public typealias BasicBlockWorklist = Worklist<BasicBlockSet>
public typealias InstructionWorklist = Worklist<InstructionSet>
public typealias SpecificInstructionWorklist<InstType: Instruction> = Worklist<SpecificInstructionSet<InstType>>
public typealias ValueWorklist = Worklist<ValueSet>
public typealias OperandWorklist = Worklist<OperandSet>
extension InstructionWorklist {
mutating func pushPredecessors(of inst: Instruction, ignoring ignoreInst: Instruction) {
public mutating func pushPredecessors(of inst: Instruction, ignoring ignoreInst: Instruction) {
if let prev = inst.previous {
if prev != ignoreInst {
pushIfNotVisited(prev)
@@ -94,7 +92,7 @@ extension InstructionWorklist {
}
}
mutating func pushSuccessors(of inst: Instruction, ignoring ignoreInst: Instruction) {
public mutating func pushSuccessors(of inst: Instruction, ignoring ignoreInst: Instruction) {
if let succ = inst.next {
if succ != ignoreInst {
pushIfNotVisited(succ)
@@ -111,34 +109,36 @@ extension InstructionWorklist {
}
/// A worklist for `Function`s.
struct FunctionWorklist {
public struct FunctionWorklist {
// The current functions in the worklist.
private(set) var functions = Array<Function>()
public private(set) var functions = Array<Function>()
// All functions which were ever pushed to the worklist.
private var pushedFunctions = Set<Function>()
mutating func pushIfNotVisited(_ function: Function) {
public init() {}
public mutating func pushIfNotVisited(_ function: Function) {
if pushedFunctions.insert(function).inserted {
functions.append(function)
}
}
mutating func pushIfNotVisited<S: Sequence>(contentsOf functions: S) where S.Element == Function {
public mutating func pushIfNotVisited<S: Sequence>(contentsOf functions: S) where S.Element == Function {
for f in functions {
pushIfNotVisited(f)
}
}
mutating func pop() -> Function? {
public mutating func pop() -> Function? {
return functions.popLast()
}
}
/// Like `ValueWorklist`, but allows pushing `Value`s from different functions -
/// at the cost of a less efficient implementation.
struct CrossFunctionValueWorklist {
public struct CrossFunctionValueWorklist {
// The current values in the worklist.
private(set) var values = Array<Value>()
@@ -146,27 +146,27 @@ struct CrossFunctionValueWorklist {
// All values which were ever pushed to the worklist.
private var pushedValues = Set<ObjectIdentifier>(minimumCapacity: 8)
init() {
public init() {
values.reserveCapacity(8)
}
mutating func pop() -> Value? {
public mutating func pop() -> Value? {
return values.popLast()
}
mutating func pushIfNotVisited(_ value: Value) {
public mutating func pushIfNotVisited(_ value: Value) {
if pushedValues.insert(ObjectIdentifier(value)).inserted {
values.append(value)
}
}
mutating func pushIfNotVisited<S: Sequence>(contentsOf values: S) where S.Element == Value {
public mutating func pushIfNotVisited<S: Sequence>(contentsOf values: S) where S.Element == Value {
for value in values {
pushIfNotVisited(value)
}
}
func hasBeenPushed(_ value: Value) -> Bool {
public func hasBeenPushed(_ value: Value) -> Bool {
return pushedValues.contains(ObjectIdentifier(value))
}
}