mirror of
https://github.com/apple/swift.git
synced 2026-06-20 15:42:51 +02:00
784da0a090
Keypath simplification can create new basic blocks when projecting optional chain components (OptionalChainProjector splits the block and creates new conditional branches). However, the Swift-side tryOptimizeKeypath was not notifying the pass manager about CFG or instruction changes, leaving analyses like the dominator tree stale. Fix this by calling notifyBranchesChanged() and notifyInstructionsChanged() when tryOptimizeKeypath succeeds.
184 lines
6.0 KiB
Swift
184 lines
6.0 KiB
Swift
//===--- Context.swift - defines the context types ------------------------===//
|
|
//
|
|
// 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 AST
|
|
import SIL
|
|
import OptimizerBridging
|
|
|
|
extension Context {
|
|
var bridgedPassContext: BridgedPassContext {
|
|
BridgedPassContext(_bridged)
|
|
}
|
|
|
|
var options: Options { Options(_bridged: bridgedPassContext) }
|
|
|
|
var diagnosticEngine: DiagnosticEngine {
|
|
return DiagnosticEngine(bridged: bridgedPassContext.getDiagnosticEngine())
|
|
}
|
|
|
|
// The calleeAnalysis is not specific to a function and therefore can be provided in
|
|
// all contexts.
|
|
var calleeAnalysis: CalleeAnalysis {
|
|
let bridgeCA = bridgedPassContext.getCalleeAnalysis()
|
|
return CalleeAnalysis(bridged: bridgeCA)
|
|
}
|
|
|
|
var hadError: Bool { bridgedPassContext.hadError() }
|
|
|
|
/// Enable diagnostics requiring WMO (for @noLocks, @noAllocation
|
|
/// annotations, Embedded Swift, and class specialization). SourceKit is the
|
|
/// only consumer that has this disabled today (as it disables WMO
|
|
/// explicitly).
|
|
var enableWMORequiredDiagnostics: Bool {
|
|
bridgedPassContext.enableWMORequiredDiagnostics()
|
|
}
|
|
|
|
func canMakeStaticObjectReadOnly(objectType: Type) -> Bool {
|
|
bridgedPassContext.canMakeStaticObjectReadOnly(objectType.bridged)
|
|
}
|
|
|
|
/// True if the current compilation is in whole-module mode, i.e. the SIL of all source files
|
|
/// of the module is available.
|
|
var isWholeModule: Bool {
|
|
bridgedPassContext.isWholeModule()
|
|
}
|
|
|
|
/// The ModuleDecl of the currently compiled module.
|
|
var moduleDecl: ModuleDecl {
|
|
bridgedPassContext.getModuleDecl().getAs(ModuleDecl.self)
|
|
}
|
|
}
|
|
|
|
extension MutatingContext {
|
|
func notifyInvalidatedStackNesting() { bridgedPassContext.notifyInvalidatedStackNesting() }
|
|
|
|
func tryOptimizeApplyOfPartialApply(closure: PartialApplyInst) -> Bool {
|
|
if bridgedPassContext.tryOptimizeApplyOfPartialApply(closure.bridged) {
|
|
notifyInstructionsChanged()
|
|
notifyCallsChanged()
|
|
|
|
for use in closure.callee.uses {
|
|
if use.instruction is FullApplySite {
|
|
notifyInstructionChanged(use.instruction)
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
func tryDeleteDeadClosure(closure: SingleValueInstruction, needKeepArgsAlive: Bool = true) -> Bool {
|
|
if bridgedPassContext.tryDeleteDeadClosure(closure.bridged, needKeepArgsAlive) {
|
|
notifyInstructionsChanged()
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
func tryDevirtualize(apply: FullApplySite, isMandatory: Bool) -> ApplySite? {
|
|
let result = bridgedPassContext.tryDevirtualizeApply(apply.bridged, isMandatory)
|
|
if let newApply = result.newApply.instruction {
|
|
erase(instruction: apply)
|
|
notifyInstructionsChanged()
|
|
notifyCallsChanged()
|
|
if result.cfgChanged {
|
|
notifyBranchesChanged()
|
|
}
|
|
notifyInstructionChanged(newApply)
|
|
return newApply as! FullApplySite
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func tryOptimizeKeypath(apply: FullApplySite) -> Bool {
|
|
if bridgedPassContext.tryOptimizeKeypath(apply.bridged) {
|
|
notifyBranchesChanged()
|
|
notifyInstructionsChanged()
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
func inlineFunction(apply: FullApplySite, mandatoryInline: Bool) {
|
|
// This is only a best-effort attempt to notify the new cloned instructions as changed.
|
|
// TODO: get a list of cloned instructions from the `inlineFunction`
|
|
let instBeforeInlining = apply.previous
|
|
let instAfterInlining: Instruction?
|
|
switch apply {
|
|
case is ApplyInst:
|
|
instAfterInlining = apply.next
|
|
case let beginApply as BeginApplyInst:
|
|
let next = beginApply.next!
|
|
instAfterInlining = (next is EndApplyInst ? nil : next)
|
|
case is TryApplyInst:
|
|
instAfterInlining = apply.parentBlock.next?.instructions.first
|
|
default:
|
|
instAfterInlining = nil
|
|
}
|
|
|
|
bridgedPassContext.inlineFunction(apply.bridged, mandatoryInline)
|
|
|
|
if let instBeforeInlining = instBeforeInlining?.next,
|
|
let instAfterInlining = instAfterInlining,
|
|
!instAfterInlining.isDeleted {
|
|
notifyNewInstructions(from: instBeforeInlining, to: instAfterInlining)
|
|
}
|
|
}
|
|
|
|
func loadFunction(function: Function, loadCalleesRecursively: Bool) -> Bool {
|
|
if function.isDefinition {
|
|
return true
|
|
}
|
|
_bridged.loadFunction(function.bridged, loadCalleesRecursively)
|
|
return function.isDefinition
|
|
}
|
|
|
|
private func notifyNewInstructions(from: Instruction, to: Instruction) {
|
|
var inst = from
|
|
while inst != to {
|
|
if !inst.isDeleted {
|
|
notifyInstructionChanged(inst)
|
|
}
|
|
if let next = inst.next {
|
|
inst = next
|
|
} else {
|
|
inst = inst.parentBlock.next!.instructions.first!
|
|
}
|
|
}
|
|
}
|
|
|
|
func getContextSubstitutionMap(for type: Type) -> SubstitutionMap {
|
|
SubstitutionMap(bridged: _bridged.getContextSubstitutionMap(type.bridged))
|
|
}
|
|
|
|
/// Notifies the pass manager that the optimization result of the current pass depends
|
|
/// on the body (i.e. SIL instructions) of another function than the currently optimized one.
|
|
func notifyDependency(onBodyOf otherFunction: Function) {
|
|
bridgedPassContext.notifyDependencyOnBodyOf(otherFunction.bridged)
|
|
}
|
|
}
|
|
|
|
extension Instruction {
|
|
var arraySemanticsCallKind: ArrayCallKind {
|
|
return BridgedPassContext.getArraySemanticsCallKind(self.bridged)
|
|
}
|
|
|
|
func canHoistArraySemanticsCall(to toInst: Instruction, _ context: FunctionPassContext) -> Bool {
|
|
return context.bridgedPassContext.canHoistArraySemanticsCall(self.bridged, toInst.bridged)
|
|
}
|
|
|
|
func hoistArraySemanticsCall(before toInst: Instruction, _ context: some MutatingContext) {
|
|
context.bridgedPassContext.hoistArraySemanticsCall(self.bridged, toInst.bridged) // Internally updates dom tree.
|
|
context.notifyInstructionsChanged()
|
|
}
|
|
}
|