|
19 | 19 | ///
|
20 | 20 | /// This pass never creates copies except to replace explicit value copies
|
21 | 21 | /// (copy_value, load [copy], store). For move-only values, this allows complete
|
22 |
| -/// diagnostics. And in general, it makes it impossible for SIL passes to |
| 22 | +/// diagnostics. And in general, this makes it impossible for SIL passes to |
23 | 23 | /// "accidentally" create copies.
|
24 | 24 | ///
|
25 | 25 | /// This pass inserts moves (copy_addr [take] [initialize]) of owned values to
|
26 | 26 | /// - compose aggregates
|
27 | 27 | /// - resolve phi interference
|
28 | 28 | ///
|
29 |
| -/// For guarantee values, this pass inserts neither copies nor moves. Opaque |
| 29 | +/// For guaranteed values, this pass inserts neither copies nor moves. Opaque |
30 | 30 | /// values are potentially unmovable when borrowed. This means that guaranteed
|
31 | 31 | /// address-only aggregates and phis are prohibited. This SIL invariant is
|
32 | 32 | /// enforced by SILVerifier::checkOwnershipForwardingInst() and
|
33 | 33 | /// SILVerifier::visitSILPhiArgument().
|
34 | 34 | ///
|
| 35 | +/// The simplest approach to address lowering is to map each opaque SILValue to |
| 36 | +/// a separate alloc_stack. This pass avoids doing that in the following cases: |
| 37 | +/// |
| 38 | +/// 1. Reused-storage: Some operations are guaranteed to reuse their operand's |
| 39 | +/// storage. This includes extracting an enum payload and opening an existential |
| 40 | +/// value. This is required avoid introducing new copies or moves. |
| 41 | +/// |
| 42 | +/// // %data's storage must reuse storage allocated for %enum |
| 43 | +/// %data = unchecked_enum_data %enum : $Optional<T>, #Optional.some!enumelt |
| 44 | +/// |
| 45 | +/// 2. Def-projection: Some operations are guaranteed to directly project out of |
| 46 | +/// their operand's storage. This is also required to avoid introducing new |
| 47 | +/// copies or moves. Unlike reused-storage, such projections are non-destructive |
| 48 | +/// and repeatable. |
| 49 | +/// |
| 50 | +/// // %field's storage is part of the storage allocated for %struct |
| 51 | +/// %field = struct_extract %struct, #field |
| 52 | +/// |
| 53 | +/// 3. Use-projection: Operations that compose aggregates may optionally allow |
| 54 | +/// their operands to project into the storage allocated for their result. This |
| 55 | +/// is only an optimization but is essential for reasonable code generation. |
| 56 | +/// |
| 57 | +/// // %field's storage may be part of the storage allocated for %struct |
| 58 | +/// %struct = struct(..., %field, ...) |
| 59 | +/// |
| 60 | +/// 4. Phi-projection: Phi's may optionally allow their (branch) operands to |
| 61 | +/// reuse the storage allocated for their result (block argument). This is only |
| 62 | +/// an optimization, but is important to avoid many useless moves: |
| 63 | +/// |
| 64 | +/// // %arg's storage may be part of the storage allocated for %phi |
| 65 | +/// br bb(%arg) |
| 66 | +/// bb(%phi : @owned $T) |
| 67 | +/// |
| 68 | +/// The algorithm proceeds as follows: |
| 69 | +/// |
35 | 70 | /// ## Step #1: Map opaque values
|
36 | 71 | ///
|
37 | 72 | /// Populate a map from each opaque SILValue to its ValueStorage in forward
|
38 | 73 | /// order (RPO). Each opaque value is mapped to an ordinal ID representing the
|
39 | 74 | /// storage. Storage locations can now be optimized by remapping the values.
|
40 | 75 | ///
|
| 76 | +/// Reused-storage operations are not mapped to ValueStorage. |
| 77 | +/// |
41 | 78 | /// ## Step #2: Allocate storage
|
42 | 79 | ///
|
43 | 80 | /// In reverse order (PO), allocate the parent storage object for each opaque
|
44 | 81 | /// value.
|
45 | 82 | ///
|
46 |
| -/// If the value is a subobject extraction (struct_extract, tuple_extract, |
47 |
| -/// open_existential_value, unchecked_enum_data), then mark the value's storage |
48 |
| -/// as a projection from the def's storage. |
| 83 | +/// Handle def-projection: If the value is a subobject extraction |
| 84 | +/// (struct_extract, tuple_extract, open_existential_value, |
| 85 | +/// unchecked_enum_data), then mark the value's storage as a projection from the |
| 86 | +/// def's storage. |
49 | 87 | ///
|
50 |
| -/// If the value's use composes a parent object from this value (struct, tuple, |
51 |
| -/// enum), and the use's storage dominates this value, then mark the value's |
52 |
| -/// storage as a projection into the use's storage. |
| 88 | +/// Handle use-projection: If the value's use composes a parent object from this |
| 89 | +/// value (struct, tuple, enum), and the use's storage dominates this value, |
| 90 | +/// then mark the value's storage as a projection into the use's storage. |
53 | 91 | ///
|
54 | 92 | /// ValueStorage projections can be chained. A non-projection ValueStorage is
|
55 | 93 | /// the root of a tree of projections.
|
|
59 | 97 | /// projections are not mapped to a `storageAddress` at this point. That happens
|
60 | 98 | /// during rewriting.
|
61 | 99 | ///
|
62 |
| -/// After allocating storage for all non-phi opaque values, phi storage is |
63 |
| -/// allocated. (Phi values are block arguments in which phi's arguments are |
64 |
| -/// branch operands). This is handled by a PhiStorageOptimizer that checks for |
65 |
| -/// interference among the phi operands and reuses storage allocated to other |
66 |
| -/// values. |
| 100 | +/// Handle phi-projection: After allocating storage for all non-phi opaque |
| 101 | +/// values, phi storage is allocated. (Phi values are block arguments in which |
| 102 | +/// phi's arguments are branch operands). This is handled by a |
| 103 | +/// PhiStorageOptimizer that checks for interference among the phi operands and |
| 104 | +/// reuses storage allocated to other values. |
67 | 105 | ///
|
68 | 106 | /// ## Step #3. Rewrite opaque values
|
69 | 107 | ///
|
|
0 commit comments