Files
Erik Eckstein 18063707b5 Optimizer: enable complete OSSA lifetimes throughout the pass pipeline
This new OSSA invariant simplifies many optimizations because they don't have to take care of the corner case of incomplete lifetimes in dead-end blocks.

The implementation basically consists of these changes:
* add the lifetime completion utility
* add a flag in SILFunction which tells optimization that they need to run the lifetime completion utility
* let all optimizations complete lifetimes if necessary
* enable the ownership verifier to check complete lifetimes
2026-01-22 17:41:48 +01:00

133 lines
4.6 KiB
Swift

//===----------- Test.swift -----------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2023 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
//
//===----------------------------------------------------------------------===//
//
// Optimizer test infrastructure.
//
// For general documentation on how to write tests see `Test.swift` in the
// SIL module.
// To add an optimizer test, use `FunctionTest` or `ModuleTest` instead of `Test`
// and register it in `registerOptimizerTests`.
//
//===----------------------------------------------------------------------===//
import SIL
import OptimizerBridging
/// Like `SIL.Test`, but provides a `FunctionPassContext` to the invocation closure.
struct FunctionTest {
let name: String
let invocation: FunctionTestInvocation
public init(_ name: String, invocation: @escaping FunctionTestInvocation) {
self.name = name
self.invocation = invocation
}
}
/// Like `SIL.Test`, but provides a `ModulePassContext` to the invocation closure.
/// This is just a wrapper around a `ModulePass` with the name "test-<testname>".
/// Therefore, module tests must also be added to `Passes.def`.
struct ModuleTest {
let pass: ModulePass
public init(_ name: String, invocation: @escaping (ModulePassContext) -> ()) {
self.pass = ModulePass(name: "test-" + name, invocation)
}
}
/// The type of the closure passed to a FunctionTest.
typealias FunctionTestInvocation = @convention(thin) (Function, TestArguments, FunctionPassContext) -> ()
public func registerOptimizerTests() {
SIL.registerTests()
// Register each test.
registerFunctionTests(
addressOwnershipLiveRangeTest,
argumentConventionsTest,
breakInfiniteLoopsTest,
domtreeTest,
getAutoDiffSpecializationInfoTest,
interiorLivenessTest,
lifetimeComletionTest,
lifetimeDependenceRootTest,
lifetimeDependenceScopeTest,
lifetimeDependenceUseTest,
linearLivenessTest,
localVariableReachableUsesTest,
localVariableReachingAssignmentsTest,
rangeOverlapsPathTest,
specializeBranchTracingEnums,
specializeBTEArgInVjpBB,
specializePayloadArgInPullbackBB,
variableIntroducerTest,
destroyBarrierTest,
deadEndBlockTest,
escapeInfoTest,
addressEscapeInfoTest,
aliasingTest,
memoryEffectsTest
)
registerModuleTest(functionUsesTest, { functionUsesTest.pass.run($0) })
// Finally register the thunk they all call through.
registerFunctionTestThunk(functionTestThunk)
}
private func registerFunctionTests(_ tests: FunctionTest...) {
tests.forEach { registerFunctionTest($0) }
}
private func registerFunctionTest(_ test: FunctionTest) {
test.name._withBridgedStringRef { ref in
registerFunctionTest(ref, castToOpaquePointer(fromInvocation: test.invocation))
}
}
private func registerModuleTest(_ test: ModuleTest, _ runFn: @escaping (@convention(c) (BridgedContext) -> ())) {
registerPass(test.pass, runFn)
}
/// The function called by the swift::test::FunctionTest which invokes the
/// actual test function.
///
/// This function is necessary because tests need to be written in terms of
/// native Swift types (Function, TestArguments, FunctionPassContext)
/// rather than their bridged variants, but such a function isn't representable
/// in C++. This thunk unwraps the bridged types and invokes the real function.
private func functionTestThunk(
_ erasedInvocation: UnsafeMutableRawPointer,
_ function: BridgedFunction,
_ arguments: BridgedTestArguments,
_ bridgedContext: BridgedContext) {
let invocation = castToInvocation(fromOpaquePointer: erasedInvocation)
let context = FunctionPassContext(_bridged: bridgedContext)
invocation(function.function, arguments.native, context)
}
/// Bitcast a thin test closure to void *.
///
/// Needed so that the closure can be represented in C++ for storage in the test
/// registry.
private func castToOpaquePointer(fromInvocation invocation: FunctionTestInvocation) -> UnsafeMutableRawPointer {
return unsafeBitCast(invocation, to: UnsafeMutableRawPointer.self)
}
/// Bitcast a void * to a thin test closure.
///
/// Needed so that the closure stored in the C++ test registry can be invoked
/// via the functionTestThunk.
private func castToInvocation(fromOpaquePointer erasedInvocation: UnsafeMutableRawPointer) -> FunctionTestInvocation {
return unsafeBitCast(erasedInvocation, to: FunctionTestInvocation.self)
}