Skip to content

Commit 3bf7e71

Browse files
committed
Add AddressUtils.swift
1 parent d9a8a0e commit 3bf7e71

File tree

4 files changed

+194
-5
lines changed

4 files changed

+194
-5
lines changed
Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
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+
}

SwiftCompilerSources/Sources/Optimizer/Utilities/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
# See http://swift.org/CONTRIBUTORS.txt for Swift project authors
88

99
swift_compiler_sources(Optimizer
10+
AddressUtils.swift
1011
BorrowUtils.swift
1112
DiagnosticEngine.swift
1213
Devirtualization.swift

SwiftCompilerSources/Sources/Optimizer/Utilities/OwnershipLiveness.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,6 @@ func computeInteriorLiveness(for definingValue: Value,
118118
return range
119119
}
120120

121-
122121
/// Visit all uses of an interior address that keep the parent object alive.
123122
///
124123
/// See C++ TransitiveAddressWalker.

docs/SIL-Utilities.md

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,20 @@ A set of utilities for analyzing memory accesses. It defines the following conce
104104
**Related C++ utilities:** `AccessPath` and other access utilities.
105105
**Status:** done
106106

107+
### Address Utils
108+
109+
* `AddressUseVisitor`: classify address uses. This can be used by def-use walkers to ensure complete handling of all legal SIL patterns.
110+
111+
**Related Swift Utilities**
112+
`AddressDefUseWalker`
113+
114+
**Related C++ Utilities**
115+
`Projection::isAddressProjection`
116+
`isAccessStorageCast`
117+
`transitiveAddressWalker`
118+
119+
TODO: Refactor AddressDefUseWalker to implement AddressUseVisitor.
120+
107121
### Ownership Utils
108122

109123
#### BorrowUtils.swift has utilities for traversing borrow scopes:
@@ -118,12 +132,9 @@ A set of utilities for analyzing memory accesses. It defines the following conce
118132
* `computeLinearLiveness`: compute an InstructionRange from the immediate lifetime ending uses.
119133
* `computeInteriorLiveness`: complete def-use walk to compute an InstructionRange from all transitive use points that must be within an OSSA lifetime.
120134
* `InteriorUseWalker`: def-use walker for all transitive use points that must be within an OSSA lifetime.
121-
* `AddressLifetimeDefUseWalker`: def-use address walker to categorize all legal address uses by ownership effect.
122135
* `OwnershipUseVistor`: categorize all uses of an owned or guaranteed use by ownership effect. Use this within a recursive def-use walker to decide how to follow each use.
123136

124-
`AddressLifetimeDefUseWalker` currently differs from `AddressDefUseWalker`. It visits all uses regardless of whether they are projections, has callbacks for handling inner scopes, and automatically handles the lifetime effect of inner scopes and dependent values.
125-
126-
TODO: Define address projections in a single place rather than in both `AddressDefUseWalker` and `AddressUseVisitor`. This can be done with a simple `AddressProjection` protocol.
137+
`InteriorUseWalker`, like `AddressDefUseWalker`, walks def-use address projections. The difference is that it visits and classifies all uses regardless of whether they are projections, it has callbacks for handling inner scopes, and it automatically handles the lifetime effect of inner scopes and dependent values.
127138

128139
#### ForwardingUtils.swift has utilities for traversing forward-extended lifetimes:
129140

0 commit comments

Comments
 (0)