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 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235
|
# Utilities for SIL optimizer passes
This document lists a set of SIL utilities to be used by the Swift implementation of the SIL optimizer.
Some of the utilities presented in this document are still in the design phase and are not implemented yet (see **Status**).
### Goals for utilities
We want to avoid a situation like we have in the C++ SIL optimizer, where a huge amount of (partly) redundant utilities exist. Many of those utilities have overlapping functionality, are difficult to discover and are poorly designed. We want to do better in the Swift SIL optimizer.
## Basic Data-Structures
#### `Stack`
An allocation-free, array like data structure. To be used instead of `Swift.Array` wherever random-access is not needed.
**Related C++ utilities:** `llvm::SmallVector`, `Stack`
**Status:** done
#### `BasicBlockSet`
An extremely efficient implementation of a set of basic blocks.
**Related C++ utilities:** `BasicBlockSet`
**Status:** done
#### `ValueSet`
An extremely efficient implementation of a set of values.
**Related C++ utilities:** `ValueSet`
**Status:** done
#### `InstructionSet`
An extremely efficient implementation of a set of instructions.
**Related C++ utilities:** `InstructionSet `
**Status:** done
#### `BasicBlockWorklist`
To be used for all kind of basic-block work-list algorithms.
**Uses:** `Stack`, `BasicBlockSet`
**Related C++ utilities:** `BasicBlockWorklist`
**Status:** done
## SIL data structures
#### `SmallProjectionPath`
Describes a path of projections.
**Related C++ utilities:** `AccessPath`, `ProjectionPath`
**Status:** done
### `ProjectedValue`
A projected value is defined by the original value and a projection path.
**Related C++ utilities:** `AccessPath`
**Status:** done
### `SingleInlineArray`
An array with the first element stored inline. Useful for analyses that produces results that are typically a 1-to-1 map, but rarely a 1-to-N map.
## Building SIL
#### `static Builder.insert(after:, insertFunc: (Builder) -> ())`
Useful to insert instructions after a (potential) terminator instruction.
**Related C++ utilities:** `SILBuilderWithScope::insertAfter()`
**Status:** done
## SSA Traversal Utilities
#### Walk Utils
This consists of four protocols, which can be implemented to walk up or down the SSA graph:
* `ValueDefUseWalker`
* `AddressDefUseWalker`
* `ValueUseDefWalker`
* `AddressUseDefWalker`
**Uses:** instruction classifications
**Related C++ utilities:** `AccessPath`, `RCIdentityAnalysis`, various def-use/use-def walkers in optimization passes.
**Status:** done
#### Escape Utilities
Escape analysis, which is used e.g. in stack promotion or alias analysis.
Escape analysis is usable through the following methods of `ProjectedValue` and `Value`:
* `isEscaping()`
* `isAddressEscaping()`
* `visit()`
* `visitAddress()`
**Uses:** Walk Utilities, `ProjectedValue`
**Related C++ utilities:** `EscapeAnalysis`, various def-use walkers in optimization passes.
**Status:** done
#### Access Utils
A set of utilities for analyzing memory accesses. It defines the following concepts:
* `AccessBase`: represents the base address of a memory access.
* `AccessPath`: a pair of an `AccessBase` and `SmallProjectionPath` with the path describing the specific address (in terms of projections) of the access.
* Access storage path (which is of type `ProjectedValue`): identifies the reference - or a value which contains a reference - an address originates from.
**Uses:** Walk utils
**Related C++ utilities:** `AccessPath` and other access utilities.
**Status:** done
### Address Utils
* `AddressUseVisitor`: classify address uses. This can be used by def-use walkers to ensure complete handling of all legal SIL patterns.
**Related Swift Utilities**
`AddressDefUseWalker`
**Related C++ Utilities**
`Projection::isAddressProjection`
`isAccessStorageCast`
`transitiveAddressWalker`
TODO: Refactor AddressDefUseWalker to implement AddressUseVisitor.
### Ownership Utils
#### BorrowUtils.swift has utilities for traversing borrow scopes:
* `BorrowingInstruction`: find borrow scopes during def-use walks
* `BeginBorrowValue`: find borrow scopes during use-def walks
* `gatherBorrowIntroducers`: use-def walk finds the current scopes
* `gatherEnclosingValues`: use-def walk finds the outer lifetimes that enclose the current scope
#### OwnershipUtils.swift has utilities for traversing OSSA lifetimes:
* `computeLinearLiveness`: compute an InstructionRange from the immediate lifetime ending uses.
* `computeInteriorLiveness`: complete def-use walk to compute an InstructionRange from all transitive use points that must be within an OSSA lifetime.
* `InteriorUseWalker`: def-use walker for all transitive use points that must be within an OSSA lifetime.
* `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.
`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.
#### ForwardingUtils.swift has utilities for traversing forward-extended lifetimes:
Forward-extended lifetimes may include multiple OSSA lifetimes joined by ForwardingInstructions. Querying certain information about OSSA lifetimes, such as whether it has a lexical lifetime or a pointer escape, requires finding the introducer of the forward-extended lifetime. Forwarding walkers traverse the SSA graph of ForwardingInstructions:
* `ForwardingUseDefWalker`: Find the introducer of a forward-extended lifetime
* `ForwardingDefUseWalker`: Find all OSSA lifetimes within a forward-extended lifetime.
#### LifetimeDependence
Model lifetime dependencies in SIL, as required be ~Escapable types.
## Control- and Dataflow
#### `DeadEndBlocks`
A utility for finding dead-end blocks.
Dead-end blocks are blocks from which there is no path to the function exit (`return`, `throw` or unwind).
These are blocks which end with an unreachable instruction and blocks from which all paths end in "unreachable" blocks.
**Uses:** `BasicBlockWorklist`
**Related C++ utilities:** `DeadEndBlocks`
**Status:** done
#### `BasicBlockRange`
Defines a range from a dominating "begin" block to one or more "end" blocks. To be used for all kind of backward block reachability analysis.
**Uses:** `Stack`, `BasicBlockSet`, `BasicBlockWorklist`
**Related C++ utilities:** `PrunedLiveBlocks`, `findJointPostDominatingSet()`
**Status:** done
#### `InstructionRange`
Like `BasicBlockRange`, but at the granularity of instructions.
**Uses:** `BasicBlockRange`
**Related C++ utilities:** `PrunedLiveness`, `ValueLifetimeAnalysis`
**Status:** done
## Utilities for Inter-procedural Analysis
### `FunctionUses`
Provides a list of instructions, which reference a function. This utility performs an analysis of all functions in the module and collects instructions which reference other functions. It can be used to do inter-procedural caller-analysis.
**Related C++ utilities:** `CallerAnalysis`
**Status:** done
## OSSA Utilities
#### `Value.makeAvailable()` and `Value.copy(at:)`
To be used where a value is copied in one block and used in another block.
**Uses:** `BasicBlockRange`
**Related C++ utilities:** `makeValueAvailable()`, `OwnershipLifetimeExtender`
**Status:** done
## Instruction classifications
We want to classify certain instructions into common groups so that passes can deal deal with the group instead of individual instruction opcodes. Optimization passes then don't need to be updated if new instructions are added to a group.
In Swift we can easily do this by introducing protocols to which instruction classes can conform to.
#### `ApplySite`
**Related C++ utilities:** `ApplySite`
**Status:** exists; to-do: complete member variables/functions
#### `CastInstruction`
Details need to be decided.
**Conforming instructions:** `UpcastInst`, `UncheckedRefCastInst`, etc.
**Members:** ownership behavior, e.g. forwarding, etc.
**Status:** to-do
#### `AddressProjectionInstruction`
Details need to be decided.
**Conforming instructions:** `StructElementAddrInst`, `TupleElementAddrInst`
**Members:** `var fieldIndex: Int`
**Related C++ utilities:** `skipAddrProjections`
**Status:** to-do
#### `AggregateInstruction`
Details need to be decided.
**Conforming instructions:** `StructInst`, `TupleInst`
**Status:** to-do
#### `ExtractInstruction`
Details need to be decided.
**Conforming instructions:** `StructExtractInst`, `TupleExtractInst`
**Status:** to-do
#### `BorrowScope`
Details need to be decided.
Maybe it's better to not do this as a protocol but just add an extension function `Value.introducesBorrowScope`. Reason: an `Argument` can be guaranteed or owned.
**Conforming instructions:** `BeginBorrowInst`, `Argument` with guaranteed ownership (however will do that), `LoadBorrowInst`
**Status:** to-do
|