1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115
|
//===--- 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
}
}
|