mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
The main changes are: *) Rewrite everything in swift. So far, parts of memory-behavior analysis were already implemented in swift. Now everything is done in swift and lives in `AliasAnalysis.swift`. This is a big code simplification. *) Support many more instructions in the memory-behavior analysis - especially OSSA instructions, like `begin_borrow`, `end_borrow`, `store_borrow`, `load_borrow`. The computation of end_borrow effects is now much more precise. Also, partial_apply is now handled more precisely. *) Simplify and reduce type-based alias analysis (TBAA). The complexity of the old TBAA comes from old days where the language and SIL didn't have strict aliasing and exclusivity rules (e.g. for inout arguments). Now TBAA is only needed for code using unsafe pointers. The new TBAA handles this - and not more. Note that TBAA for classes is already done in `AccessBase.isDistinct`. *) Handle aliasing in `begin_access [modify]` scopes. We already supported truly immutable scopes like `begin_access [read]` or `ref_element_addr [immutable]`. For `begin_access [modify]` we know that there are no other reads or writes to the access-address within the scope. *) Don't cache memory-behavior results. It turned out that the hit-miss rate was pretty bad (~ 1:7). The overhead of the cache lookup took as long as recomputing the memory behavior.
166 lines
5.4 KiB
Swift
166 lines
5.4 KiB
Swift
//===--- CalleeAnalysis.swift - the callee analysis -----------------------===//
|
|
//
|
|
// 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 https://swift.org/LICENSE.txt for license information
|
|
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
import OptimizerBridging
|
|
import SIL
|
|
|
|
public struct CalleeAnalysis {
|
|
let bridged: BridgedCalleeAnalysis
|
|
|
|
static func register() {
|
|
BridgedCalleeAnalysis.registerAnalysis(
|
|
// isDeinitBarrierFn:
|
|
{ (inst : BridgedInstruction, bca: BridgedCalleeAnalysis) -> Bool in
|
|
return inst.instruction.isDeinitBarrier(bca.analysis)
|
|
},
|
|
// getMemBehaviorFn
|
|
{ (bridgedApply: BridgedInstruction, observeRetains: Bool, bca: BridgedCalleeAnalysis) -> BridgedMemoryBehavior in
|
|
let apply = bridgedApply.instruction as! FullApplySite
|
|
let e = bca.analysis.getSideEffects(ofApply: apply)
|
|
return e.getMemBehavior(observeRetains: observeRetains)
|
|
}
|
|
)
|
|
}
|
|
|
|
public func getCallees(callee: Value) -> FunctionArray? {
|
|
let bridgedFuncs = bridged.getCallees(callee.bridged)
|
|
if bridgedFuncs.isIncomplete() {
|
|
return nil
|
|
}
|
|
return FunctionArray(bridged: bridgedFuncs)
|
|
}
|
|
|
|
public func getIncompleteCallees(callee: Value) -> FunctionArray {
|
|
return FunctionArray(bridged: bridged.getCallees(callee.bridged))
|
|
}
|
|
|
|
public func getDestructor(ofExactType type: Type) -> Function? {
|
|
let destructors = FunctionArray(bridged: bridged.getDestructors(type.bridged, /*isExactType*/ true))
|
|
if destructors.count == 1 {
|
|
return destructors[0]
|
|
}
|
|
return nil
|
|
}
|
|
|
|
public func getDestructors(of type: Type) -> FunctionArray? {
|
|
let bridgedDtors = bridged.getDestructors(type.bridged, /*isExactType*/ false)
|
|
if bridgedDtors.isIncomplete() {
|
|
return nil
|
|
}
|
|
return FunctionArray(bridged: bridgedDtors)
|
|
}
|
|
|
|
/// Returns the global (i.e. not argument specific) side effects of an apply.
|
|
public func getSideEffects(ofApply apply: FullApplySite) -> SideEffects.GlobalEffects {
|
|
return getSideEffects(ofCallee: apply.callee)
|
|
}
|
|
|
|
public func getSideEffects(ofCallee callee: Value) -> SideEffects.GlobalEffects {
|
|
guard let callees = getCallees(callee: callee) else {
|
|
return .worstEffects
|
|
}
|
|
|
|
var result = SideEffects.GlobalEffects()
|
|
for callee in callees {
|
|
let calleeEffects = callee.getSideEffects()
|
|
result.merge(with: calleeEffects)
|
|
}
|
|
return result
|
|
}
|
|
|
|
/// Returns the argument specific side effects of an apply.
|
|
public func getSideEffects(of apply: FullApplySite, operand: Operand, path: SmallProjectionPath) -> SideEffects.GlobalEffects {
|
|
var result = SideEffects.GlobalEffects()
|
|
guard let calleeArgIdx = apply.calleeArgumentIndex(of: operand) else {
|
|
return result
|
|
}
|
|
let convention = apply.convention(of: operand)!
|
|
let argument = operand.value.at(path)
|
|
|
|
guard let callees = getCallees(callee: apply.callee) else {
|
|
return .worstEffects.restrictedTo(argument: argument, withConvention: convention)
|
|
}
|
|
|
|
for callee in callees {
|
|
let calleeEffects = callee.getSideEffects(forArgument: argument,
|
|
atIndex: calleeArgIdx,
|
|
withConvention: convention)
|
|
result.merge(with: calleeEffects)
|
|
}
|
|
return result.restrictedTo(argument: argument, withConvention: convention)
|
|
}
|
|
}
|
|
|
|
extension Value {
|
|
fileprivate func isBarrier(_ analysis: CalleeAnalysis) -> Bool {
|
|
guard let callees = analysis.getCallees(callee: self) else {
|
|
return true
|
|
}
|
|
return callees.contains { $0.isDeinitBarrier }
|
|
}
|
|
}
|
|
|
|
extension FullApplySite {
|
|
fileprivate func isBarrier(_ analysis: CalleeAnalysis) -> Bool {
|
|
return callee.isBarrier(analysis)
|
|
}
|
|
}
|
|
|
|
extension EndApplyInst {
|
|
fileprivate func isBarrier(_ analysis: CalleeAnalysis) -> Bool {
|
|
return (operand.value.definingInstruction as! FullApplySite).isBarrier(analysis)
|
|
}
|
|
}
|
|
|
|
extension AbortApplyInst {
|
|
fileprivate func isBarrier(_ analysis: CalleeAnalysis) -> Bool {
|
|
return (operand.value.definingInstruction as! FullApplySite).isBarrier(analysis)
|
|
}
|
|
}
|
|
|
|
extension Instruction {
|
|
/// Whether lifetime ends of lexical values may safely be hoisted over this
|
|
/// instruction.
|
|
///
|
|
/// Deinitialization barriers constrain variable lifetimes. Lexical
|
|
/// end_borrow, destroy_value, and destroy_addr cannot be hoisted above them.
|
|
public final func isDeinitBarrier(_ analysis: CalleeAnalysis) -> Bool {
|
|
if let site = self as? FullApplySite {
|
|
return site.isBarrier(analysis)
|
|
}
|
|
if let eai = self as? EndApplyInst {
|
|
return eai.isBarrier(analysis)
|
|
}
|
|
if let aai = self as? AbortApplyInst {
|
|
return aai.isBarrier(analysis)
|
|
}
|
|
return mayAccessPointer || mayLoadWeakOrUnowned || maySynchronize
|
|
}
|
|
}
|
|
|
|
public struct FunctionArray : RandomAccessCollection, FormattedLikeArray {
|
|
fileprivate let bridged: BridgedCalleeAnalysis.CalleeList
|
|
|
|
public var startIndex: Int { 0 }
|
|
public var endIndex: Int { bridged.getCount() }
|
|
|
|
public subscript(_ index: Int) -> Function {
|
|
return bridged.getCallee(index).function
|
|
}
|
|
}
|
|
// Bridging utilities
|
|
|
|
extension BridgedCalleeAnalysis {
|
|
public var analysis: CalleeAnalysis { .init(bridged: self) }
|
|
}
|
|
|