Skip to content

Commit aeefc1c

Browse files
authored
Merge pull request #34452 from gottesmm/pr-f34d2cbf39d3441da59079c01e8647ff26b5c375
[SIL.rst] Add an Ownership SSA sub-section about forwarding uses.
2 parents 96c1e47 + a37e7b9 commit aeefc1c

File tree

1 file changed

+134
-3
lines changed

1 file changed

+134
-3
lines changed

docs/SIL.rst

Lines changed: 134 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1750,7 +1750,7 @@ Owned
17501750
Owned ownership models "move only" values. We require that each such value is
17511751
consumed exactly once along all program paths. The IR verifier will flag values
17521752
that are not consumed along a path as a leak and any double consumes as
1753-
use-after-frees. We model move operations via "forwarding uses" such as casts
1753+
use-after-frees. We model move operations via `forwarding uses`_ such as casts
17541754
and transforming terminators (e.x.: `switch_enum`_, `checked_cast_br`_) that
17551755
transform the input value, consuming it in the process, and producing a new
17561756
transformed owned value as a result.
@@ -1805,7 +1805,7 @@ derived from the ARC object. As an example, consider the following Swift/SIL::
18051805
}
18061806

18071807
Notice how our individual copy (``%1``) threads its way through the IR using
1808-
forwarding of ``@owned`` ownership. These forwarding operations partition the
1808+
`forwarding uses`_ of ``@owned`` ownership. These `forwarding uses`_ partition the
18091809
lifetime of the result of the copy_value into a set of disjoint individual owned
18101810
lifetimes (``%2``, ``%3``, ``%5``).
18111811

@@ -1847,7 +1847,7 @@ optimizations that they can never shrink the lifetime of ``%0`` by moving
18471847
`destroy_value`_ above ``%1``.
18481848

18491849
Values with guaranteed ownership follow a dataflow rule that states that
1850-
non-consuming "forwarding" uses of the guaranteed value are also guaranteed and
1850+
non-consuming `forwarding uses`_ of the guaranteed value are also guaranteed and
18511851
are recursively validated as being in the original values scope. This was a
18521852
choice we made to reduce idempotent scopes in the IR::
18531853

@@ -1912,6 +1912,137 @@ This is a form of ownership that is used to model two different use cases:
19121912
trivial pointer to a class. In that case, since we have no reason to assume
19131913
that the object will remain alive, we need to make a copy of the value.
19141914

1915+
Forwarding Uses
1916+
~~~~~~~~~~~~~~~
1917+
1918+
NOTE: In the following, we assumed that one read the section above, `Value Ownership Kind`_.
1919+
1920+
A subset of SIL instructions define the value ownership kind of their results in
1921+
terms of the value ownership kind of their operands. Such an instruction is
1922+
called a "forwarding instruction" and any use with such a user instruction a
1923+
"forwarding use". This inference generally occurs upon instruction construction
1924+
and as a result:
1925+
1926+
* When manipulating forwarding instructions programatically, one must manually
1927+
update their forwarded ownership since most of the time the ownership is
1928+
stored in the instruction itself. Don't worry though because the SIL verifier
1929+
will catch this error for you if you forget to do so!
1930+
1931+
* Textual SIL does not represent the ownership of forwarding instructions
1932+
explicitly. Instead, the instruction's ownership is inferred normally from the
1933+
parsed operand. Since the SILVerifier runs on Textual SIL after parsing, you
1934+
can feel confident that ownership constraints were inferred correctly.
1935+
1936+
Forwarding has slightly different ownership semantics depending on the value
1937+
ownership kind of the operand on construction and the result's type. We go
1938+
through each below:
1939+
1940+
* Given an ``@owned`` operand, the forwarding instruction is assumed to end the
1941+
lifetime of the operand and produce an ``@owned`` value if non-trivially typed
1942+
and ``@none`` if trivially typed. Example: This is used to represent the
1943+
semantics of casts::
1944+
1945+
sil @unsafelyCastToSubClass : $@convention(thin) (@owned Klass) -> @owned SubKlass {
1946+
bb0(%0 : @owned $Klass): // %0 is defined here.
1947+
1948+
// %0 is consumed here and can no longer be used after this point.
1949+
// %1 is defined here and after this point must be used to access the object
1950+
// passed in via %0.
1951+
%1 = unchecked_ref_cast %0 : $Klass to $SubKlass
1952+
1953+
// Then %1's lifetime ends here and we return the casted argument to our
1954+
// caller as an @owned result.
1955+
return %1 : $SubKlass
1956+
}
1957+
1958+
* Given a ``@guaranteed`` operand, the forwarding instruction is assumed to
1959+
produce ``@guaranteed`` non-trivially typed values and ``@none`` trivially
1960+
typed values. Given the non-trivial case, the instruction is assumed to begin
1961+
a new implicit borrow scope for the incoming value. Since the borrow scope is
1962+
implicit, we validate the uses of the result as if they were uses of the
1963+
operand (recursively). This of course means that one should never see
1964+
end_borrows on any guaranteed forwarded results, the end_borrow is always on
1965+
the instruction that "introduces" the borrowed value. An example of a
1966+
guaranteed forwarding instruction is ``struct_extract``::
1967+
1968+
// In this function, I have a pair of Klasses and I want to grab some state
1969+
// and then call the hand off function for someone else to continue
1970+
// processing the pair.
1971+
sil @accessLHSStateAndHandOff : $@convention(thin) (@owned KlassPair) -> @owned State {
1972+
bb0(%0 : @owned $KlassPair): // %0 is defined here.
1973+
1974+
// Begin the borrow scope for %0. We want to access %1's subfield in a
1975+
// read only way that doesn't involve destructuring and extra copies. So
1976+
// we construct a guaranteed scope here so we can safely use a
1977+
// struct_extract.
1978+
%1 = begin_borrow %0 : $KlassPair
1979+
1980+
// Now we perform our struct_extract operation. This operation
1981+
// structurally grabs a value out of a struct without safety relying on
1982+
// the guaranteed ownership of its operand to know that %1 is live at all
1983+
// use points of %2, its result.
1984+
%2 = struct_extract %1 : $KlassPair, #KlassPair.lhs
1985+
1986+
// Then grab the state from our left hand side klass and copy it so we
1987+
// can pass off our klass pair to handOff for continued processing.
1988+
%3 = ref_element_addr %2 : $Klass, #Klass.state
1989+
%4 = load [copy] %3 : $*State
1990+
1991+
// Now that we have finished accessing %1, we end the borrow scope for %1.
1992+
end_borrow %1 : $KlassPair
1993+
1994+
%handOff = function_ref @handOff : $@convention(thin) (@owned KlassPair) -> ()
1995+
apply %handOff(%0) : $@convention(thin) (@owned KlassPair) -> ()
1996+
1997+
return %4 : $State
1998+
}
1999+
2000+
* Given an ``@none`` operand, the result value must have ``@none`` ownership.
2001+
2002+
* Given an ``@unowned`` operand, the result value will have ``@unowned``
2003+
ownership. It will be validated just like any other ``@unowned`` value, namely
2004+
that it must be copied before use.
2005+
2006+
An additional wrinkle here is that even though the vast majority of forwarding
2007+
instructions forward all types of ownership, this is not true in general. To see
2008+
why this is necessary, lets compare/contrast `struct_extract`_ (which does not
2009+
forward ``@owned`` ownership) and `unchecked_enum_data`_ (which can forward
2010+
/all/ ownership kinds). The reason for this difference is that `struct_extract`_
2011+
inherently can only extract out a single field of a larger object implying that
2012+
the instruction could only represent consuming a sub-field of a value instead of
2013+
the entire value at once. This violates our constraint that owned values can
2014+
never be partially consumed: a value is either completely alive or completely
2015+
dead. In contrast, enums always represent their payloads as elements in a single
2016+
tuple value. This means that `unchecked_enum_data`_ when it extracts that
2017+
payload from an enum, can consume the entire enum+payload.
2018+
2019+
To handle cases where we want to use `struct_extract`_ in a consuming way, we
2020+
instead are able to use the `destructure_struct`_ instruction that consumes the
2021+
entire struct at once and gives one back the structs individual constituant
2022+
parts::
2023+
2024+
struct KlassPair {
2025+
var fieldOne: Klass
2026+
var fieldTwo: Klass
2027+
}
2028+
2029+
sil @getFirstPairElt : $@convention(thin) (@owned KlassPair) -> @owned Klass {
2030+
bb0(%0 : @owned $KlassPair):
2031+
// If we were to just do this directly and consume KlassPair to access
2032+
// fieldOne... what would happen to fieldTwo? Would it be consumed?
2033+
//
2034+
// %1 = struct_extract %0 : $KlassPair, #KlassPair.fieldOne
2035+
//
2036+
// Instead we need to destructure to ensure we consume the entire owned value at once.
2037+
(%1, %2) = destructure_struct $KlassPair
2038+
2039+
// We only want to return %1, so we need to cleanup %2.
2040+
destroy_value %2 : $Klass
2041+
2042+
// Then return %1 to our caller
2043+
return %1 : $Klass
2044+
}
2045+
19152046
Runtime Failure
19162047
---------------
19172048

0 commit comments

Comments
 (0)