Skip to content

Commit 6992276

Browse files
committed
Document SIL's dominance rules, which apparently we've never done.
1 parent 2710f20 commit 6992276

File tree

1 file changed

+204
-34
lines changed

1 file changed

+204
-34
lines changed

docs/SIL/SIL.md

Lines changed: 204 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -780,6 +780,178 @@ _lexical_ in order to specify this property for all contributing lifetimes.
780780
For details see [Variable Lifetimes](Ownership.md#variable-lifetimes) in the
781781
Ownership document.
782782

783+
# Dominance
784+
785+
## Value and instruction dominance
786+
787+
Whenever an instruction uses a [value](#values-and-operands) as an
788+
operand, the definition of the value must dominate the instruction.
789+
This is a common concept across all SSA-like representations. SIL
790+
uses a standard definition of dominance, modified slightly to account
791+
for SIL's use of basic block arguments rather than phi instructions:
792+
793+
- The value `undef` always dominates an instruction.
794+
795+
- An instruction result `R` dominates an instruction `I` if the
796+
instruction that defines `R` dominates `I`.
797+
798+
- An argument of a basic block `B` dominates an instruction `I` if all
799+
initial paths passing through `I` must also pass through the start
800+
of `B`.
801+
802+
An instruction `D` dominates another instruction `I` if they are
803+
different instructions and all initial paths passing through `I`
804+
must also pass through `D`.
805+
806+
See [below](#definition-of-a-path) for the formal definition of an
807+
initial path.
808+
809+
## Basic block dominance
810+
811+
A basic block `B1` dominates a basic block `B2` if they are different
812+
blocks and if all initial paths passing through the start of `B2` must
813+
also pass through through the start of `B1`.
814+
815+
This relationship between blocks can be thought of as creating a
816+
directed acyclic graph of basic blocks, called the *dominance tree*.
817+
The dominance tree is not directly represented in SIL; it is just
818+
an emergent property of the dominance requirement on SIL functions.
819+
820+
## Joint post-dominance
821+
822+
Certain instructions are required to have a *joint post-dominance*
823+
relationship with certain other instructions. Informally, this means
824+
that all terminating paths through the first instruction must
825+
eventually pass through one of the others. This is common for
826+
instructions that define a scope in the SIL function, such as
827+
`alloc_stack` and `begin_access`.
828+
829+
The dominating instruction is called the *scope instruction*,
830+
and the post-dominating instructions are called the *scope-ending
831+
instructions*. The specific joint post-dominance requirement
832+
defines the set of instructions that count as scope-ending
833+
instructions for the begin instruction.
834+
835+
For example, an `alloc_stack` instruction must be jointly
836+
post-dominated by the set of `dealloc_stack` instructions
837+
whose operand is the result of the `alloc_stack`. The
838+
`alloc_stack` is the scope instruction, and the `dealloc_stack`s
839+
are the scope-ending instructions.
840+
841+
The *scope* of a joint post-dominance relationship is the set
842+
of all points in the function following the scope instruction
843+
but prior to a scope-ending instruction. Making this precisely
844+
defined is part of the point of the joint post-dominance rules.
845+
A formal definition is given later.
846+
847+
In SIL, if an instruction acts as a scope instruction, it always
848+
has exactly one set of scope-ending instructions associated
849+
with it, and so it forms exactly one scope. People will therefore
850+
often talk about, e.g., the scope of an `alloc_stack`, meaning
851+
the scope between it and its `dealloc_stack`s. Furthermore,
852+
there are no instructions in SIL which act as scope-ending
853+
instructions for multiple scopes.
854+
855+
A scope instruction `I` is jointly post-dominated by its
856+
scope-ending instructions if:
857+
858+
- All initial paths that pass through a scope-ending instruction
859+
of `I` must also pass through `I`. (This is just the normal
860+
dominance rule, and it is typically already required by the
861+
definition of the joint post-dominance relationship. For example,
862+
a `dealloc_stack` must be dominated by its associated
863+
`alloc_stack` because it uses its result as an operand.)
864+
865+
- All initial paths that pass through `I` twice must also pass
866+
through a scope-ending instruction of `I` in between.
867+
868+
- All initial paths that pass through a scope-ending instruction
869+
of `I` twice must also pass through `I` in between.
870+
871+
- All terminating initial paths that pass through `I` must also
872+
pass through a scope-ending instruction of `I`.
873+
874+
In other words, all paths must strictly alternate between `I`
875+
and its scope-ending instructions, starting with `I` and (if
876+
the path exits) ending with a scope-ending instruction.
877+
878+
Note that a scope-ending instruction does not need to appear on
879+
a path following a scope instruction if the path doesn't exit
880+
the function. In fact, a function needn't include any scope-ending
881+
instructions for a particular scope instruction if all paths from
882+
that point are non-terminating, such as by ending in `unreachable`
883+
or containing an infinite loop.
884+
885+
The scope defined by a joint post-dominance relationship for
886+
a scope instruction `I` is the set of points in the function for
887+
which there exists an initial path that visits that point and
888+
which passes through `I` but which does not pass through a
889+
scope-ending instruction of `I` under that relationship. Note
890+
that the point before a scope-ending instruction is always within
891+
the scope.
892+
893+
## Definition of a path
894+
895+
A *point* in a SIL function is the moment before an instruction.
896+
Every basic block has an entry point, which is the point before
897+
its first instruction. The entry point of the entry block is also
898+
called the entry point of the function.
899+
900+
A path through a SIL function is a path (in the usual graph-theory
901+
sense) in the underlying directed graph of points, in which:
902+
903+
- every point in the SIL function is a vertex in the graph,
904+
905+
- each non-terminator instruction creates an edge from the point
906+
before it to the point after it, and
907+
908+
- each terminator instruction creates edges from the point before
909+
the terminator to the initial point of each its successor blocks.
910+
911+
A path is said to pass through an instruction if it includes
912+
any of the edges created by that instruction. A path is said to
913+
pass through the start of a basic block if it visits the entry
914+
point of that block.
915+
916+
An *initial path* is a path which begins at the entry point of the
917+
function. A *terminating path* is a path which ends at the point
918+
before an exiting instruction, such as `return` or `throw`.
919+
920+
Note that the dominance rules generally require only an initial path,
921+
not a terminating path. A path that simply stops in the middle of a
922+
block still counts for dominance. Among other things, this ensures that
923+
dominance holds in blocks that are part of an infinite loop.
924+
925+
Note also that paths consider successors without regard to the
926+
nature of the terminator. Paths that are provably impossible because
927+
of value relationships still count for dominance. For example,
928+
consider the following function:
929+
930+
```
931+
bb0(%cond : $Builtin.Int1):
932+
cond_br %cond, bb1, b22
933+
bb1:
934+
%value = integer_literal $Builtin.Int32, 0
935+
br bb3
936+
bb2:
937+
br bb3
938+
bb3:
939+
cond_br %cond, bb4, bb5
940+
bb4:
941+
%twice_value = builtin "add_Int32"(%value, %value) : $Builtin.Int32
942+
br bb6
943+
bb5:
944+
br bb6
945+
bb6:
946+
ret %cond
947+
```
948+
949+
Dynamically, it is impossible to reach the `builtin` instruction
950+
without passing through the definition of `%value`: to reach
951+
the `builtin`, `%cond` must be `true`, and so the first `cond_br`
952+
must have branched to `bb1`. This is not taken into consideration
953+
by dominance, and so this function is ill-formed.
954+
783955
# Debug Information
784956

785957
Each instruction may have a debug location and a SIL scope reference at
@@ -1364,48 +1536,39 @@ stack deallocation instructions. It can even be paired with no
13641536
instructions at all; by the rules below, this can only happen in
13651537
non-terminating functions.
13661538

1367-
- At any point in a SIL function, there is an ordered list of stack
1368-
allocation instructions called the *active allocations list*.
1539+
- All stack allocation instructions must be jointly post-dominated
1540+
by stack deallocation instructions paired with them.
13691541

1370-
- The active allocations list is defined to be empty at the initial
1371-
point of the entry block of the function.
1542+
- No path through the function that passes through a stack allocation
1543+
instruction `B`, having already passed a stack allocation
1544+
instruction `A`, may subsequently pass through a stack deallocation
1545+
instruction paired with `A` without first passing through a stack
1546+
deallocation instruction paired with `B`.
13721547

1373-
- The active allocations list is required to be the same at the
1374-
initial point of any successor block as it is at the final point of
1375-
any predecessor block. Note that this also requires all
1376-
predecessors/successors of a given block to have the same
1377-
final/initial active allocations lists.
1548+
These two rules statically enforce that all stack allocations are
1549+
properly nested. In simpler terms:
13781550

1379-
In other words, the set of active stack allocations must be the same
1380-
at a given place in the function no matter how it was reached.
1551+
- At every point in a SIL function, there is an ordered list of stack
1552+
allocation instructions called the *active allocations list*.
13811553

1382-
- The active allocations list for the point following a stack
1383-
allocation instruction is defined to be the result of adding that
1384-
instruction to the end of the active allocations list for the point
1385-
preceding the instruction.
1554+
- The active allocations list is empty at the start of the entry block
1555+
of the function, and it must be empty again whenever an instruction
1556+
that exits the function is reached, like `return` or `throw`.
13861557

1387-
- The active allocations list for the point following a stack
1388-
deallocation instruction is defined to be the result of removing the
1389-
instruction from the end of the active allocations list for the
1390-
point preceding the instruction. The active allocations list for the
1391-
preceding point is required to be non-empty, and the last
1392-
instruction in it must be paired with the deallocation instruction.
1558+
- Whenever a stack allocation instruction is reached, it is added to
1559+
the end of the list.
13931560

1394-
In other words, all stack allocations must be deallocated in
1395-
last-in, first-out order, aka stack order.
1561+
- Whenever a stack deallocation instruction is reached, its paired
1562+
stack allocation instruction must be at the end of the list, which it
1563+
is then removed from.
13961564

1397-
- The active allocations list for the point following any other
1398-
instruction is defined to be the same as the active allocations list
1399-
for the point preceding the instruction.
1565+
- The active allocations list always be the same on both sides of a
1566+
control flow edge. This implies both that all successors of a block
1567+
must start with the same list and that all predecessors of a block
1568+
must end with the same list.
14001569

1401-
- The active allocations list is required to be empty prior to
1402-
`return` or `throw` instructions.
1403-
1404-
In other words, all stack allocations must be deallocated prior to
1405-
exiting the function.
1406-
1407-
Note that these rules implicitly prevent an allocation instruction from
1408-
still being active when it is reached.
1570+
Note that these rules implicitly prevent stack allocations from leaking
1571+
or being double-freed.
14091572

14101573
The control-flow rule forbids certain patterns that would theoretically
14111574
be useful, such as conditionally performing an allocation around an
@@ -1414,6 +1577,13 @@ to use, however, as it is illegal to locally abstract over addresses,
14141577
and therefore a conditional allocation cannot be used in the
14151578
intermediate operation anyway.
14161579

1580+
There is currently an exception to the stack discipline rules which
1581+
allows the predecessors of a dead-end block (a block from which no
1582+
exit is reachable) to disagree about the state of the stack.
1583+
The current exception is unsound and permits manipulation of the
1584+
stack in ways that may not be valid in all predecessor states. We
1585+
are exploring ways to improve this situation.
1586+
14171587
# Structural type matching for pack indices
14181588

14191589
In order to catch type errors in applying pack indices, SIL requires the

0 commit comments

Comments
 (0)