Skip to content

Commit d57ee13

Browse files
committed
docs: add a SIL COW support section in SIL.rst
1 parent fb4168e commit d57ee13

File tree

1 file changed

+88
-0
lines changed

1 file changed

+88
-0
lines changed

docs/SIL.rst

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2107,6 +2107,94 @@ make the use of such types more convenient; it does not shift the
21072107
ultimate responsibility for assuring the safety of unsafe
21082108
language/library features away from the user.
21092109

2110+
Copy-on-Write Representation
2111+
----------------------------
2112+
2113+
Copy-on-Write (COW) data structures are implemented by a reference to an object
2114+
which is copied on mutation in case it's not uniquely referenced.
2115+
2116+
A COW mutation sequence in SIL typically looks like::
2117+
2118+
(%uniq, %buffer) = begin_cow_mutation %immutable_buffer : $BufferClass
2119+
cond_br %uniq, bb_uniq, bb_not_unique
2120+
bb_uniq:
2121+
br bb_mutate(%buffer : $BufferClass)
2122+
bb_not_unique:
2123+
%copied_buffer = apply %copy_buffer_function(%buffer) : ...
2124+
br bb_mutate(%copied_buffer : $BufferClass)
2125+
bb_mutate(%mutable_buffer : $BufferClass):
2126+
%field = ref_element_addr %mutable_buffer : $BufferClass, #BufferClass.Field
2127+
store %value to %field : $ValueType
2128+
%new_immutable_buffer = end_cow_mutation %buffer : $BufferClass
2129+
2130+
Loading from a COW data structure looks like::
2131+
2132+
%field1 = ref_element_addr [immutable] %immutable_buffer : $BufferClass, #BufferClass.Field
2133+
%value1 = load %field1 : $*FieldType
2134+
...
2135+
%field2 = ref_element_addr [immutable] %immutable_buffer : $BufferClass, #BufferClass.Field
2136+
%value2 = load %field2 : $*FieldType
2137+
2138+
The ``immutable`` attribute means that loading values from ``ref_element_addr``
2139+
and ``ref_tail_addr`` instructions, which have the *same* operand, are
2140+
equivalent.
2141+
In other words, it's guaranteed that a buffer's properties are not mutated
2142+
between two ``ref_element/tail_addr [immutable]`` as long as they have the
2143+
same buffer reference as operand.
2144+
This is even true if e.g. the buffer 'escapes' to an unknown function.
2145+
2146+
2147+
In the example above, ``%value2`` is equal to ``%value1`` because the operand
2148+
of both ``ref_element_addr`` instructions is the same ``%immutable_buffer``.
2149+
Conceptually, the content of a COW buffer object can be seen as part of
2150+
the same *static* (immutable) SSA value as the buffer reference.
2151+
2152+
The lifetime of a COW value is strictly separated into *mutable* and
2153+
*immutable* regions by ``begin_cow_mutation`` and
2154+
``end_cow_mutation`` instructions::
2155+
2156+
%b1 = alloc_ref $BufferClass
2157+
// The buffer %b1 is mutable
2158+
%b2 = end_cow_mutation %b1 : $BufferClass
2159+
// The buffer %b2 is immutable
2160+
(%u1, %b3) = begin_cow_mutation %b1 : $BufferClass
2161+
// The buffer %b3 is mutable
2162+
%b4 = end_cow_mutation %b3 : $BufferClass
2163+
// The buffer %b4 is immutable
2164+
...
2165+
2166+
Both, ``begin_cow_mutation`` and ``end_cow_mutation``, consume their operand
2167+
and return the new buffer as an *owned* value.
2168+
The ``begin_cow_mutation`` will compile down to a uniqueness check and
2169+
``end_cow_mutation`` will compile to a no-op.
2170+
2171+
Although the physical pointer value of the returned buffer reference is the
2172+
same as the operand, it's important to generate a *new* buffer reference in
2173+
SIL. It prevents the optimizer from moving buffer accesses from a *mutable* into
2174+
a *immutable* region and vice versa.
2175+
2176+
Because the buffer *content* is conceptually part of the
2177+
buffer *reference* SSA value, there must be a new buffer reference every time
2178+
the buffer content is mutated.
2179+
2180+
To illustrate this, let's look at an example, where a COW value is mutated in
2181+
a loop. As with a scalar SSA value, also mutating a COW buffer will enforce a
2182+
phi-argument in the loop header block (for simplicity the code for copying a
2183+
non-unique buffer is not shown)::
2184+
2185+
header_block(%b_phi : $BufferClass):
2186+
(%u, %b_mutate) = begin_cow_mutation %b_phi : $BufferClass
2187+
// Store something to %b_mutate
2188+
%b_immutable = end_cow_mutation %b_mutate : $BufferClass
2189+
cond_br %loop_cond, exit_block, backedge_block
2190+
backedge_block:
2191+
br header_block(b_immutable : $BufferClass)
2192+
exit_block:
2193+
2194+
Two adjacent ``begin_cow_mutation`` and ``end_cow_mutation`` instructions
2195+
don't need to be in the same function.
2196+
2197+
21102198
Instruction Set
21112199
---------------
21122200

0 commit comments

Comments
 (0)