File: SimplifyStrongRetainRelease.swift

package info (click to toggle)
swiftlang 6.0.3-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 2,519,992 kB
  • sloc: cpp: 9,107,863; ansic: 2,040,022; asm: 1,135,751; python: 296,500; objc: 82,456; f90: 60,502; lisp: 34,951; pascal: 19,946; sh: 18,133; perl: 7,482; ml: 4,937; javascript: 4,117; makefile: 3,840; awk: 3,535; xml: 914; fortran: 619; cs: 573; ruby: 573
file content (115 lines) | stat: -rw-r--r-- 4,337 bytes parent folder | download
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
  }
}