Skip to content

Commit 043acc8

Browse files
authored
Merge pull request #35233 from gottesmm/pr-4542def9fc3d07b8054aa5ab5322083dc97ebe57
[sil.rst] Add a discussion of "Dead End Blocks" and its implications upon OSSA.
2 parents 399d1c9 + a7422b7 commit 043acc8

File tree

1 file changed

+81
-5
lines changed

1 file changed

+81
-5
lines changed

docs/SIL.rst

Lines changed: 81 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1681,11 +1681,12 @@ that use a SIL value are required to be able to be semantically partitioned in
16811681
between "non-lifetime ending uses" that just require the value to be live and
16821682
"lifetime ending uses" that end the lifetime of the value and after which the
16831683
value can no longer be used. Since by definition operands that are lifetime
1684-
ending uses end their associated value's lifetime, we must have that the
1685-
lifetime ending use points jointly post-dominate all non-lifetime ending use
1686-
points and that a value must have exactly one lifetime ending use along all
1687-
reachable program paths, preventing leaks and use-after-frees. As an example,
1688-
consider the following SIL example with partitioned defs/uses annotated inline::
1684+
ending uses end their associated value's lifetime, we must have that, ignoring
1685+
program ending `Dead End Blocks`_, the lifetime ending use points jointly
1686+
post-dominate all non-lifetime ending use points and that a value must have
1687+
exactly one lifetime ending use along all reachable program paths, preventing
1688+
leaks and use-after-frees. As an example, consider the following SIL example
1689+
with partitioned defs/uses annotated inline::
16891690

16901691
sil @stash_and_cast : $@convention(thin) (@owned Klass) -> @owned SuperKlass {
16911692
bb0(%kls1 : @owned $Klass): // Definition of %kls1
@@ -2316,6 +2317,81 @@ The current list of interior pointer SIL instructions are:
23162317
(*) We still need to finish adding support for project_box, but all other
23172318
interior pointers are guarded already.
23182319

2320+
Dead End Blocks
2321+
~~~~~~~~~~~~~~~
2322+
2323+
In SIL, one can express that a program is semantically expected to exit at the
2324+
end of a block by terminating the block with an `unreachable`_. Such a block is
2325+
called a *program terminating block* and all blocks that are post-dominated by
2326+
blocks of the aforementioned kind are called *dead end blocks*. Intuitively, any
2327+
path through a dead end block is known to result in program termination, so
2328+
resources that normally would need to be released back to the system will
2329+
instead be returned to the system by process tear down.
2330+
2331+
Since we rely on the system at these points to perform resource cleanup, we are
2332+
able to loosen our lifetime requirements by allowing for values to not have
2333+
their lifetimes ended along paths that end in program terminating
2334+
blocks. Operationally, this implies that:
2335+
2336+
* All SIL values must have exactly one lifetime ending use on all paths that
2337+
terminate in a `return`_ or `throw`_. In contrast, a SIL value does not need to
2338+
have a lifetime ending use along paths that end in an `unreachable`_.
2339+
2340+
* `end_borrow`_ and `destroy_value`_ are redundent, albeit legal, in blocks
2341+
where all paths through the block end in an `unreachable`_.
2342+
2343+
Consider the following legal SIL where we leak ``%0`` in blocks prefixed with
2344+
``bbDeadEndBlock`` and consume it in ``bb2``::
2345+
2346+
sil @user : $@convention(thin) (@owned Klass) -> @owned Klass {
2347+
bb0(%0 : @owned $Klass):
2348+
cond_br ..., bb1, bb2
2349+
2350+
bb1:
2351+
// This is a dead end block since it is post-dominated by two dead end
2352+
// blocks. It is not a program terminating block though since the program
2353+
// does not end in this block.
2354+
cond_br ..., bbDeadEndBlock1, bbDeadEndBlock2
2355+
2356+
bbDeadEndBlock1:
2357+
// This is a dead end block and a program terminating block.
2358+
//
2359+
// We are exiting the program here causing the operating system to clean up
2360+
// all resources associated with our process, so there is no need for a
2361+
// destroy_value. That memory will be cleaned up anyways.
2362+
unreachable
2363+
2364+
bbDeadEndBlock2:
2365+
// This is a dead end block and a program terminating block.
2366+
//
2367+
// Even though we do not need to insert destroy_value along these paths, we
2368+
// can if we want to. It is just necessary and the optimizer can eliminate
2369+
// such a destroy_value if it wishes.
2370+
//
2371+
// NOTE: The author arbitrarily chose just to destroy %0: we could legally
2372+
// destroy either value (or both!).
2373+
destroy_value %0 : $Klass
2374+
unreachable
2375+
2376+
bb2:
2377+
cond_br ..., bb3, bb4
2378+
2379+
bb3:
2380+
// This block is live, so we need to ensure that %0 is consumed within the
2381+
// block. In this case, %0 is consumed by returning %0 to our caller.
2382+
return %0 : $Klass
2383+
2384+
bb4:
2385+
// This block is also live, but since we do not return %0, we must insert a
2386+
// destroy_value to cleanup %0.
2387+
//
2388+
// NOTE: The copy_value/destroy_value here is redundent and can be removed by
2389+
// the optimizer. The author left it in for illustrative purposes.
2390+
%1 = copy_value %0 : $Klass
2391+
destroy_value %0 : $Klass
2392+
return %1 : $Klass
2393+
}
2394+
23192395
Runtime Failure
23202396
---------------
23212397

0 commit comments

Comments
 (0)