|
| 1 | +//===--- AddressUtils.swift - Utilities for handling SIL addresses -------===// |
| 2 | +// |
| 3 | +// This source file is part of the Swift.org open source project |
| 4 | +// |
| 5 | +// Copyright (c) 2014 - 2023 Apple Inc. and the Swift project authors |
| 6 | +// Licensed under Apache License v2.0 with Runtime Library Exception |
| 7 | +// |
| 8 | +// See https://swift.org/LICENSE.txt for license information |
| 9 | +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors |
| 10 | +// |
| 11 | +//===----------------------------------------------------------------------===// |
| 12 | + |
| 13 | +import SIL |
| 14 | + |
| 15 | +/// Classify address uses. This can be used by def-use walkers to |
| 16 | +/// ensure complete handling of all legal SIL patterns. |
| 17 | +/// |
| 18 | +/// TODO: Integrate this with SIL verification to ensure completeness. |
| 19 | +/// |
| 20 | +/// TODO: Convert AddressDefUseWalker to conform to AddressUtils after |
| 21 | +/// checking that the additional instructions are handled correctly by |
| 22 | +/// escape analysis. |
| 23 | +/// |
| 24 | +/// TODO: Verify that pointerEscape is only called for live ranges in which |
| 25 | +/// `findPointerEscape()` returns true. |
| 26 | +protocol AddressUseVisitor { |
| 27 | + var context: Context { get } |
| 28 | + |
| 29 | + /// An address projection produces a single address result and does not |
| 30 | + /// escape its address operand in any other way. |
| 31 | + mutating func projectedAddressUse(of operand: Operand, into value: Value) |
| 32 | + -> WalkResult |
| 33 | + |
| 34 | + /// An access scope: begin_access, begin_apply, load_borrow. |
| 35 | + mutating func scopedAddressUse(of operand: Operand) -> WalkResult |
| 36 | + |
| 37 | + /// A address leaf use cannot propagate the address bits beyond the |
| 38 | + /// instruction. |
| 39 | + /// |
| 40 | + /// An apply or builtin propagates an address into the callee, but |
| 41 | + /// it is considered a leaf use as long as the argument does not escape. |
| 42 | + mutating func leafAddressUse(of operand: Operand) -> WalkResult |
| 43 | + |
| 44 | + /// A loaded address use propagates the value at the address. |
| 45 | + mutating func loadedAddressUse(of operand: Operand, into value: Value) |
| 46 | + -> WalkResult |
| 47 | + |
| 48 | + /// A loaded address use propagates the value at the address to the |
| 49 | + /// destination address operand. |
| 50 | + mutating func loadedAddressUse(of operand: Operand, into address: Operand) |
| 51 | + -> WalkResult |
| 52 | + |
| 53 | + /// A non-address owned `value` whose ownership depends on the in-memory |
| 54 | + /// value at `address`, such as `mark_dependence %value on %address`. |
| 55 | + mutating func dependentAddressUse(of operand: Operand, into value: Value) |
| 56 | + -> WalkResult |
| 57 | + |
| 58 | + /// A pointer escape may propagate the address beyond the current instruction. |
| 59 | + mutating func escapingAddressUse(of operand: Operand) -> WalkResult |
| 60 | + |
| 61 | + /// A unknown address use. This should never be called in valid SIL. |
| 62 | + mutating func unknownAddressUse(of operand: Operand) -> WalkResult |
| 63 | +} |
| 64 | + |
| 65 | +extension AddressUseVisitor { |
| 66 | + /// Classify an address-type operand, dispatching to one of the |
| 67 | + /// protocol methods above. |
| 68 | + mutating func classifyAddress(operand: Operand) -> WalkResult { |
| 69 | + switch operand.instruction { |
| 70 | + case is BeginAccessInst, is BeginApplyInst, is LoadBorrowInst, |
| 71 | + is StoreBorrowInst: |
| 72 | + return scopedAddressUse(of: operand) |
| 73 | + |
| 74 | + case let markDep as MarkDependenceInst: |
| 75 | + if markDep.valueOperand == operand { |
| 76 | + return projectedAddressUse(of: operand, into: markDep) |
| 77 | + } |
| 78 | + assert(markDep.baseOperand == operand) |
| 79 | + // If another address depends on the current address, |
| 80 | + // handle it like a projection. |
| 81 | + if markDep.type.isAddress { |
| 82 | + return projectedAddressUse(of: operand, into: markDep) |
| 83 | + } |
| 84 | + if LifetimeDependence(markDependence: markDep, context) != nil { |
| 85 | + // This is unreachable from InteriorUseVisitor because the |
| 86 | + // base address of a `mark_dependence [nonescaping]` must be a |
| 87 | + // `begin_access`, and interior liveness does not check uses of |
| 88 | + // the accessed address. |
| 89 | + return dependentAddressUse(of: operand, into: markDep) |
| 90 | + } |
| 91 | + // A potentially escaping value depends on this address. |
| 92 | + return escapingAddressUse(of: operand) |
| 93 | + |
| 94 | + case let pai as PartialApplyInst where pai.isOnStack: |
| 95 | + return dependentAddressUse(of: operand, into: pai) |
| 96 | + |
| 97 | + case let pai as PartialApplyInst where !pai.isOnStack: |
| 98 | + return escapingAddressUse(of: operand) |
| 99 | + |
| 100 | + case is AddressToPointerInst: |
| 101 | + return escapingAddressUse(of: operand) |
| 102 | + |
| 103 | + case is StructElementAddrInst, is TupleElementAddrInst, |
| 104 | + is IndexAddrInst, is TailAddrInst, is TuplePackElementAddrInst, |
| 105 | + is InitEnumDataAddrInst, is UncheckedTakeEnumDataAddrInst, |
| 106 | + is InitExistentialAddrInst, is OpenExistentialAddrInst, |
| 107 | + is ProjectBlockStorageInst, is UncheckedAddrCastInst, |
| 108 | + is UnconditionalCheckedCastAddrInst, |
| 109 | + is MarkUninitializedInst, is DropDeinitInst, |
| 110 | + is CopyableToMoveOnlyWrapperAddrInst, |
| 111 | + is MoveOnlyWrapperToCopyableAddrInst, |
| 112 | + is MarkUnresolvedNonCopyableValueInst: |
| 113 | + let svi = operand.instruction as! SingleValueInstruction |
| 114 | + return projectedAddressUse(of: operand, into: svi) |
| 115 | + |
| 116 | + case is ReturnInst, is ThrowInst, is YieldInst, is TryApplyInst, |
| 117 | + is SwitchEnumAddrInst, is CheckedCastAddrBranchInst, |
| 118 | + is SelectEnumAddrInst, is InjectEnumAddrInst, |
| 119 | + is StoreInst, is StoreUnownedInst, is StoreWeakInst, |
| 120 | + is AssignInst, is AssignByWrapperInst, is AssignOrInitInst, |
| 121 | + is TupleAddrConstructorInst, is InitBlockStorageHeaderInst, |
| 122 | + is RetainValueAddrInst, is ReleaseValueAddrInst, |
| 123 | + is DestroyAddrInst, is DeallocStackInst, |
| 124 | + is DeinitExistentialAddrInst, |
| 125 | + is EndApplyInst, is IsUniqueInst, is MarkFunctionEscapeInst, |
| 126 | + is PackElementSetInst: |
| 127 | + return leafAddressUse(of: operand) |
| 128 | + |
| 129 | + case is LoadInst, is LoadUnownedInst, is LoadWeakInst, |
| 130 | + is ValueMetatypeInst, is ExistentialMetatypeInst, |
| 131 | + is PackElementGetInst: |
| 132 | + let svi = operand.instruction as! SingleValueInstruction |
| 133 | + return loadedAddressUse(of: operand, into: svi) |
| 134 | + |
| 135 | + case let sdai as SourceDestAddrInstruction |
| 136 | + where sdai.sourceOperand == operand: |
| 137 | + return loadedAddressUse(of: operand, into: sdai.destinationOperand) |
| 138 | + |
| 139 | + case let sdai as SourceDestAddrInstruction |
| 140 | + where sdai.destinationOperand == operand: |
| 141 | + return leafAddressUse(of: operand) |
| 142 | + |
| 143 | + case let builtin as BuiltinInst: |
| 144 | + switch builtin.id { |
| 145 | + case .Copy where builtin.operands[1] == operand: // source |
| 146 | + return loadedAddressUse(of: operand, into: builtin.operands[0]) |
| 147 | + |
| 148 | + case .Copy where builtin.operands[0] == operand: // dest |
| 149 | + return leafAddressUse(of: operand) |
| 150 | + |
| 151 | + // Builtins that cannot load a nontrivial value. |
| 152 | + case .TSanInoutAccess, .ResumeThrowingContinuationReturning, |
| 153 | + .ResumeNonThrowingContinuationReturning, .GenericAdd, |
| 154 | + .GenericFAdd, .GenericAnd, .GenericAShr, .GenericLShr, .GenericOr, |
| 155 | + .GenericFDiv, .GenericMul, .GenericFMul, .GenericSDiv, |
| 156 | + .GenericExactSDiv, .GenericShl, .GenericSRem, .GenericSub, |
| 157 | + .GenericFSub, .GenericUDiv, .GenericExactUDiv, .GenericURem, |
| 158 | + .GenericFRem, .GenericXor, .TaskRunInline, .ZeroInitializer, |
| 159 | + .GetEnumTag, .InjectEnumTag: |
| 160 | + return leafAddressUse(of: operand) |
| 161 | + default: |
| 162 | + // TODO: SIL verification should check that this exhaustively |
| 163 | + // recognizes all builtin address uses. |
| 164 | + return .abortWalk |
| 165 | + } |
| 166 | + |
| 167 | + case is BranchInst, is CondBranchInst: |
| 168 | + fatalError("address phi is not allowed") |
| 169 | + |
| 170 | + default: |
| 171 | + if operand.instruction.isIncidentalUse { |
| 172 | + return leafAddressUse(of: operand) |
| 173 | + } |
| 174 | + // Unkown instruction. |
| 175 | + return unknownAddressUse(of: operand) |
| 176 | + } |
| 177 | + } |
| 178 | +} |
0 commit comments