mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
A type (mostly classes) can be attributed with `@_semantics("arc.immortal")`.
ARC operations on values of such types are eliminated.
This is useful for the bridged SIL objects in the swift compiler sources.
116 lines
4.2 KiB
Swift
116 lines
4.2 KiB
Swift
//===--- SimplifyStrongRetainRelease.swift - strong_retain/release opt ----===//
|
|
//
|
|
// 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 SIL
|
|
|
|
extension StrongRetainInst : Simplifyable, SILCombineSimplifyable {
|
|
func simplify(_ context: SimplifyContext) {
|
|
if isNotReferenceCounted(value: instance) {
|
|
context.erase(instruction: self)
|
|
return
|
|
}
|
|
|
|
// Sometimes in the stdlib due to hand offs, we will see code like:
|
|
//
|
|
// strong_release %0
|
|
// strong_retain %0
|
|
//
|
|
// with the matching strong_retain to the strong_release in a predecessor
|
|
// basic block and the matching strong_release for the strong_retain in a
|
|
// successor basic block.
|
|
//
|
|
// Due to the matching pairs being in different basic blocks, the ARC
|
|
// Optimizer (which is currently local to one basic block does not handle
|
|
// it). But that does not mean that we cannot eliminate this pair with a
|
|
// peephole.
|
|
if let prev = previous {
|
|
if let release = prev as? StrongReleaseInst {
|
|
if release.instance == self.instance {
|
|
context.erase(instruction: self)
|
|
context.erase(instruction: release)
|
|
return
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
extension StrongReleaseInst : Simplifyable, SILCombineSimplifyable {
|
|
func simplify(_ context: SimplifyContext) {
|
|
let op = instance
|
|
if isNotReferenceCounted(value: op) {
|
|
context.erase(instruction: self)
|
|
return
|
|
}
|
|
|
|
// Release of a classbound existential converted from a class is just a
|
|
// release of the class, squish the conversion.
|
|
if let ier = op as? InitExistentialRefInst {
|
|
if ier.uses.isSingleUse {
|
|
setOperand(at: 0, to: ier.instance, context)
|
|
context.erase(instruction: ier)
|
|
return
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Returns true if \p value is something where reference counting instructions
|
|
/// don't have any effect.
|
|
private func isNotReferenceCounted(value: Value) -> Bool {
|
|
if value.type.isMarkedAsImmortal {
|
|
return true
|
|
}
|
|
switch value {
|
|
case let cfi as ConvertFunctionInst:
|
|
return isNotReferenceCounted(value: cfi.fromFunction)
|
|
case let uci as UpcastInst:
|
|
return isNotReferenceCounted(value: uci.fromInstance)
|
|
case let urc as UncheckedRefCastInst:
|
|
return isNotReferenceCounted(value: urc.fromInstance)
|
|
case let gvi as GlobalValueInst:
|
|
// Since Swift 5.1, statically allocated objects have "immortal" reference
|
|
// counts. Therefore we can safely eliminate unbalanced retains and
|
|
// releases, because they are no-ops on immortal objects.
|
|
// Note that the `simplifyGlobalValuePass` pass is deleting balanced
|
|
// retains/releases, which doesn't require a Swift 5.1 minimum deployment
|
|
// target.
|
|
return gvi.parentFunction.isSwift51RuntimeAvailable
|
|
case let rptr as RawPointerToRefInst:
|
|
// Like `global_value` but for the empty collection singletons from the
|
|
// stdlib, e.g. the empty Array singleton.
|
|
if rptr.parentFunction.isSwift51RuntimeAvailable {
|
|
// The pattern generated for empty collection singletons is:
|
|
// %0 = global_addr @_swiftEmptyArrayStorage
|
|
// %1 = address_to_pointer %0
|
|
// %2 = raw_pointer_to_ref %1
|
|
if let atp = rptr.pointer as? AddressToPointerInst {
|
|
return atp.address is GlobalAddrInst
|
|
}
|
|
}
|
|
return false
|
|
case // Thin functions are not reference counted.
|
|
is ThinToThickFunctionInst,
|
|
// The same for meta types.
|
|
is ObjCExistentialMetatypeToObjectInst,
|
|
is ObjCMetatypeToObjectInst,
|
|
// Retain and Release of tagged strings is a no-op.
|
|
// The builtin code pattern to find tagged strings is:
|
|
// builtin "stringObjectOr_Int64" (or to tag the string)
|
|
// value_to_bridge_object (cast the UInt to bridge object)
|
|
is ValueToBridgeObjectInst:
|
|
return true
|
|
default:
|
|
return false
|
|
}
|
|
}
|