Skip to content

Add some swift SIL+Optimizer infrastructure #60745

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 17 commits into from
Aug 25, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
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
1 change: 1 addition & 0 deletions SwiftCompilerSources/Sources/Optimizer/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,5 @@ add_subdirectory(DataStructures)
add_subdirectory(InstructionPasses)
add_subdirectory(PassManager)
add_subdirectory(FunctionPasses)
add_subdirectory(TestPasses)
add_subdirectory(Utilities)
Original file line number Diff line number Diff line change
Expand Up @@ -78,8 +78,7 @@ struct BasicBlockRange : CustomStringConvertible, CustomReflectable {

/// Insert a potential end block.
mutating func insert(_ block: BasicBlock) {
if !wasInserted.contains(block) {
wasInserted.insert(block)
if wasInserted.insert(block) {
inserted.append(block)
}
worklist.pushIfNotVisited(block)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,7 @@ struct BasicBlockWorklist : CustomStringConvertible, CustomReflectable {

/// Pushes \p block onto the worklist if \p block has never been pushed before.
mutating func pushIfNotVisited(_ block: BasicBlock) {
if !pushedBlocks.contains(block) {
pushedBlocks.insert(block);
if pushedBlocks.insert(block) {
worklist.append(block)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
swift_compiler_sources(Optimizer
BasicBlockRange.swift
BasicBlockWorklist.swift
FunctionUses.swift
InstructionRange.swift
Set.swift
Stack.swift)
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
//===--- FunctionUses.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

/// Provides a list of instructions, which reference a function.
///
/// A function "use" is an instruction in another (or the same) function which
/// references the function. In most cases those are `function_ref` instructions,
/// but can also be e.g. `keypath` instructions.
///
/// 'FunctionUses' performs an analysis of all functions in the module and collects
/// instructions which reference other functions. This utility can be used to do
/// inter-procedural caller-analysis.
///
/// In order to use `FunctionUses`, first call `collect()` and then get use-lists of
/// functions with `getUses(of:)`.
struct FunctionUses {

// Function uses are stored in a single linked list, whereas the "next" is not a pointer
// but an index into `FunctionUses.useStorage`.
fileprivate struct Use {
// The index of the next use in `FunctionUses.useStorage`.
let next: Int?

// The instruction which references the function.
let usingInstruction: Instruction
}

// The head of the single-linked list of function uses.
fileprivate struct FirstUse {
// The head of the use-list.
var first: Int?

// True if the function has unknown uses
var hasUnknownUses: Bool

init(of function: Function) {
self.hasUnknownUses = function.isPossiblyUsedExternally || function.isAvailableExternally
}

mutating func insert(_ inst: Instruction, _ uses: inout [Use]) {
let newFirst = uses.count
uses.append(Use(next: first, usingInstruction: inst))
first = newFirst
}
}

/// The list of uses of a function.
struct UseList : CollectionLikeSequence, CustomStringConvertible {
struct Iterator : IteratorProtocol {
fileprivate let useStorage: [Use]
fileprivate var currentUseIdx: Int?

mutating func next() -> Instruction? {
if let useIdx = currentUseIdx {
let use = useStorage[useIdx]
currentUseIdx = use.next
return use.usingInstruction
}
return nil
}
}

// The "storage" for all function uses.
fileprivate let useStorage: [Use]

// The head of the single-linked use list.
fileprivate let firstUse: FirstUse

/// True if the function has unknown uses in addition to the list of referencing instructions.
///
/// This is the case, e.g. if the function has public linkage or if the function
/// is referenced from a vtable or witness table.
var hasUnknownUses: Bool { firstUse.hasUnknownUses }

func makeIterator() -> Iterator {
return Iterator(useStorage: useStorage, currentUseIdx: firstUse.first)
}

var description: String {
var result = "[\n"
if hasUnknownUses {
result += "<unknown uses>\n"
}
for inst in self {
result += "@\(inst.function.name): \(inst)\n"

}
result += "]"
return result
}

var customMirror: Mirror { Mirror(self, children: []) }
}

// The "storage" for all function uses.
private var useStorage: [Use] = []

// The use-list head for each function.
private var uses: [Function: FirstUse] = [:]

init() {
// Already start with a reasonable big capacity to reduce the number of
// re-allocations when appending to the data structures.
useStorage.reserveCapacity(128)
uses.reserveCapacity(64)
}

/// Returns the use-list of `function`.
///
/// Note that `collect` must be called before `getUses` can be used.
func getUses(of function: Function) -> UseList {
UseList(useStorage: useStorage, firstUse: uses[function, default: FirstUse(of: function)])
}

/// Collects all uses of all function in the module.
mutating func collect(context: ModulePassContext) {

// Mark all functions, which are referenced from tables, to have "unknown" uses.

for vTable in context.vTables {
for entry in vTable.entries {
markUnknown(entry.function)
}
}

for witnessTable in context.witnessTables {
for entry in witnessTable.entries {
if entry.kind == .method, let f = entry.methodFunction {
markUnknown(f)
}
}
}

for witnessTable in context.defaultWitnessTables {
for entry in witnessTable.entries {
if entry.kind == .method, let f = entry.methodFunction {
markUnknown(f)
}
}
}

// Collect all instructions, which reference functions, in the module.
for function in context.functions {
for inst in function.instructions {
inst.visitReferencedFunctions { referencedFunc in
uses[referencedFunc, default: FirstUse(of: referencedFunc)].insert(inst, &useStorage)
}
}
}
}

private mutating func markUnknown(_ function: Function) {
uses[function, default: FirstUse(of: function)].hasUnknownUses = true
}
}
26 changes: 15 additions & 11 deletions SwiftCompilerSources/Sources/Optimizer/DataStructures/Set.swift
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,10 @@ struct BasicBlockSet : CustomStringConvertible, CustomReflectable {
BasicBlockSet_contains(bridged, block.bridged) != 0
}

mutating func insert(_ block: BasicBlock) {
BasicBlockSet_insert(bridged, block.bridged)
/// Returns true if `block` was not contained in the set before inserting.
@discardableResult
mutating func insert(_ block: BasicBlock) -> Bool {
BasicBlockSet_insert(bridged, block.bridged) != 0
}

mutating func erase(_ block: BasicBlock) {
Expand Down Expand Up @@ -80,8 +82,10 @@ struct ValueSet : CustomStringConvertible, CustomReflectable {
NodeSet_containsValue(bridged, value.bridged) != 0
}

mutating func insert(_ value: Value) {
NodeSet_insertValue(bridged, value.bridged)
/// Returns true if `value` was not contained in the set before inserting.
@discardableResult
mutating func insert(_ value: Value) -> Bool {
NodeSet_insertValue(bridged, value.bridged) != 0
}

mutating func erase(_ value: Value) {
Expand Down Expand Up @@ -139,8 +143,10 @@ struct InstructionSet : CustomStringConvertible, CustomReflectable {
NodeSet_containsInstruction(bridged, inst.bridged) != 0
}

mutating func insert(_ inst: Instruction) {
NodeSet_insertInstruction(bridged, inst.bridged)
/// Returns true if `inst` was not contained in the set before inserting.
@discardableResult
mutating func insert(_ inst: Instruction) -> Bool {
NodeSet_insertInstruction(bridged, inst.bridged) != 0
}

mutating func erase(_ inst: Instruction) {
Expand All @@ -150,11 +156,9 @@ struct InstructionSet : CustomStringConvertible, CustomReflectable {
var description: String {
let function = NodeSet_getFunction(bridged).function
var d = "{\n"
for block in function.blocks {
for inst in block.instructions {
if contains(inst) {
d += inst.description
}
for inst in function.instructions {
if contains(inst) {
d += inst.description
}
}
d += "}\n"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import SIL
/// destruct this data structure, e.g. in a `defer {}` block.
struct Stack<Element> : CollectionLikeSequence {

private let context: PassContext
private let bridgedContext: BridgedPassContext
private var firstSlab = BridgedSlab(data: nil)
private var lastSlab = BridgedSlab(data: nil)
private var endIndex: Int = 0
Expand Down Expand Up @@ -61,7 +61,8 @@ struct Stack<Element> : CollectionLikeSequence {
}
}

init(_ context: PassContext) { self.context = context }
init(_ context: PassContext) { self.bridgedContext = context._bridged }
init(_ context: ModulePassContext) { self.bridgedContext = context._bridged }

func makeIterator() -> Iterator {
return Iterator(slab: firstSlab, index: 0, lastSlab: lastSlab, endIndex: endIndex)
Expand All @@ -77,11 +78,11 @@ struct Stack<Element> : CollectionLikeSequence {

mutating func push(_ element: Element) {
if endIndex >= Stack.slabCapacity {
lastSlab = PassContext_allocSlab(context._bridged, lastSlab)
lastSlab = PassContext_allocSlab(bridgedContext, lastSlab)
endIndex = 0
} else if firstSlab.data == nil {
assert(endIndex == 0)
firstSlab = PassContext_allocSlab(context._bridged, lastSlab)
firstSlab = PassContext_allocSlab(bridgedContext, lastSlab)
lastSlab = firstSlab
}
(Stack.bind(lastSlab) + endIndex).initialize(to: element)
Expand Down Expand Up @@ -109,12 +110,12 @@ struct Stack<Element> : CollectionLikeSequence {

if endIndex == 0 {
if lastSlab.data == firstSlab.data {
_ = PassContext_freeSlab(context._bridged, lastSlab)
_ = PassContext_freeSlab(bridgedContext, lastSlab)
firstSlab.data = nil
lastSlab.data = nil
endIndex = 0
} else {
lastSlab = PassContext_freeSlab(context._bridged, lastSlab)
lastSlab = PassContext_freeSlab(bridgedContext, lastSlab)
endIndex = Stack.slabCapacity
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,10 @@ import SIL

let assumeSingleThreadedPass = FunctionPass(
name: "sil-assume-single-threaded", { function, context in
for block in function.blocks {
for inst in block.instructions {
guard let rcInst = inst as? RefCountingInst else { continue }
for inst in function.instructions {
guard let rcInst = inst as? RefCountingInst else { continue }

rcInst.setAtomicity(isAtomic: false, context)
}
rcInst.setAtomicity(isAtomic: false, context)
}
}
)
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,9 @@

swift_compiler_sources(Optimizer
AssumeSingleThreaded.swift
AccessDumper.swift
ComputeEffects.swift
EscapeInfoDumper.swift
ObjCBridgingOptimization.swift
SILPrinter.swift
MergeCondFails.swift
RangeDumper.swift
ReleaseDevirtualizer.swift
RunUnitTests.swift
StackPromotion.swift
)
Original file line number Diff line number Diff line change
Expand Up @@ -48,12 +48,10 @@ let objCBridgingOptimization = FunctionPass(name: "objc-bridging-opt", {
}

// Now try to optimize non-optional and optional -> non-optional bridging.
for block in function.blocks {
for inst in block.instructions {
if let apply = inst as? ApplyInst {
if !optimizeNonOptionalBridging(apply, context) {
return
}
for inst in function.instructions {
if let apply = inst as? ApplyInst {
if !optimizeNonOptionalBridging(apply, context) {
return
}
}
}
Expand Down
Loading