Skip to content

Commit fd45bd0

Browse files
committed
[SIL-opaque] More file-level documentation
Explain high-level objectives and terminology with more precision.
1 parent d50c8f6 commit fd45bd0

File tree

1 file changed

+51
-13
lines changed

1 file changed

+51
-13
lines changed

lib/SILOptimizer/Mandatory/AddressLowering.cpp

Lines changed: 51 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -19,37 +19,75 @@
1919
///
2020
/// This pass never creates copies except to replace explicit value copies
2121
/// (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
2323
/// "accidentally" create copies.
2424
///
2525
/// This pass inserts moves (copy_addr [take] [initialize]) of owned values to
2626
/// - compose aggregates
2727
/// - resolve phi interference
2828
///
29-
/// For guarantee values, this pass inserts neither copies nor moves. Opaque
29+
/// For guaranteed values, this pass inserts neither copies nor moves. Opaque
3030
/// values are potentially unmovable when borrowed. This means that guaranteed
3131
/// address-only aggregates and phis are prohibited. This SIL invariant is
3232
/// enforced by SILVerifier::checkOwnershipForwardingInst() and
3333
/// SILVerifier::visitSILPhiArgument().
3434
///
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+
///
3570
/// ## Step #1: Map opaque values
3671
///
3772
/// Populate a map from each opaque SILValue to its ValueStorage in forward
3873
/// order (RPO). Each opaque value is mapped to an ordinal ID representing the
3974
/// storage. Storage locations can now be optimized by remapping the values.
4075
///
76+
/// Reused-storage operations are not mapped to ValueStorage.
77+
///
4178
/// ## Step #2: Allocate storage
4279
///
4380
/// In reverse order (PO), allocate the parent storage object for each opaque
4481
/// value.
4582
///
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.
4987
///
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.
5391
///
5492
/// ValueStorage projections can be chained. A non-projection ValueStorage is
5593
/// the root of a tree of projections.
@@ -59,11 +97,11 @@
5997
/// projections are not mapped to a `storageAddress` at this point. That happens
6098
/// during rewriting.
6199
///
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.
67105
///
68106
/// ## Step #3. Rewrite opaque values
69107
///

0 commit comments

Comments
 (0)