Struct spirt::ControlRegion
source · pub struct ControlRegion(/* private fields */);
Expand description
Entity handle for a ControlRegionDef
(a control-flow region).
A ControlRegion
(“control-flow region”) is a linear chain of ControlNode
s,
describing a single-entry single-exit (SESE) control-flow “region” (subgraph)
in a function’s control-flow graph (CFG).
Control-flow
In SPIR-T, two forms of control-flow are used:
- “structured”:
ControlRegion
s andControlNode
s in a “mutual tree”- i.e. each such
ControlRegion
can only appear in exactly oneControlNode
, and eachControlNode
can only appear in exactly oneControlRegion
- a region is either the function’s body, or used as part of
ControlNode
(e.g. the “then” case of anif
-else
), itself part of a larger region - when inside a region, reaching any other part of the function (or any
other function on call stack) requires leaving through the region’s
single exit (also called “merge”) point, i.e. its execution is either:
- “convergent”: the region completes and continues into its parent
ControlNode
, or function (the latter being a “structured return”) - “divergent”: execution gets stuck in the region (an infinite loop),
or is aborted (e.g.
OpTerminateInvocation
from SPIR-V)
- “convergent”: the region completes and continues into its parent
- i.e. each such
- “unstructured”:
ControlRegion
s which connect to otherControlRegion
s usingcfg::ControlInst
s (as described by acfg::ControlFlowGraph
)
When a function’s entire body can be described by a single ControlRegion
,
that function is said to have (entirely) “structured control-flow”.
Mixing “structured” and “unstructured” control-flow is supported because:
- during structurization, it allows structured subgraphs to remain connected
by the same CFG edges that were connecting smaller
ControlRegion
s before - structurization doesn’t have to fail in the cases it doesn’t fully support yet, but can instead result in a “maximally structured” function
Other IRs may use different “structured control-flow” definitions, notably:
- SPIR-V uses a laxer definition, that corresponds more to the constraints
of the GLSL language, and is single-entry multiple-exit (SEME) with
“alternate exits” consisting of
break
s out ofswitch
es and loops, andreturn
s (making it non-trivial to inline one function into another) - RVSDG inspired SPIR-T’s design, but its regions are (acyclic) graphs, it makes no distinction between control-flow and “computational” nodes, and its execution order is determined by value/state dependencies alone (SPIR-T may get closer to it in the future, but the initial compromise was chosen to limit the effort of lowering/lifting from/to SPIR-V)
Data-flow interactions
SPIR-T Value
s follow “single static assignment” (SSA), just like SPIR-V:
- inside a function, any new value is produced (or “defined”) as an output
of
DataInst
/ControlNode
, and “uses” of that value areValue
s variants which refer to the definingDataInst
/ControlNode
directly (guaranteeing the “single” and “static” of “SSA”, by construction) - the definition of a value must “dominate” all of its uses (i.e. in all possible execution paths, the definition precedes all uses)
But unlike SPIR-V, SPIR-T’s structured control-flow has implications for SSA:
- dominance is simpler, so values defined in a
ControlRegion
can be used:- later in that region, including in the region’s
outputs
(which allows “exporting” values out to the rest of the function) - outside that region, but only if the parent
ControlNode
only has exactly one child region (i.e. a single-caseSelect
, or aLoop
)- this is an “emergent” property, stemming from the region having to
execute (at least once) before the parent
ControlNode
can complete, but is not is not ideal (especially for reasoning about loops) and should eventually be replaced with passing all such values through the regionoutputs
(or by inlining the region, in theSelect
case)
- this is an “emergent” property, stemming from the region having to
execute (at least once) before the parent
- later in that region, including in the region’s
- instead of φ (“phi”) nodes, SPIR-T uses region
outputs
to merge values coming from separate control-flow paths (i.e. the cases of aSelect
), and regioninputs
for passing values back along loop backedges (additionally, the body’sinputs
are used for function parameters)- like the “block arguments” alternative to SSA phi nodes (which some other SSA IRs use), this has the advantage of keeping the uses of the “source” values in their respective paths (where they’re dominated), instead of in the merge (where phi nodes require special-casing, as their “uses” of all the “source” values would normally be illegal)
- in unstructured control-flow, region
inputs
are additionally used for phi nodes, ascfg::ControlInst
s passing values to their target regions
Trait Implementations§
source§impl Clone for ControlRegion
impl Clone for ControlRegion
source§fn clone(&self) -> ControlRegion
fn clone(&self) -> ControlRegion
Returns a copy of the value. Read more
1.0.0 · source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
Performs copy-assignment from
source
. Read moresource§impl Hash for ControlRegion
impl Hash for ControlRegion
source§impl PartialEq<ControlRegion> for ControlRegion
impl PartialEq<ControlRegion> for ControlRegion
source§fn eq(&self, other: &ControlRegion) -> bool
fn eq(&self, other: &ControlRegion) -> bool
This method tests for
self
and other
values to be equal, and is used
by ==
.impl Copy for ControlRegion
impl Eq for ControlRegion
impl StructuralEq for ControlRegion
impl StructuralPartialEq for ControlRegion
Auto Trait Implementations§
impl RefUnwindSafe for ControlRegion
impl Send for ControlRegion
impl Sync for ControlRegion
impl Unpin for ControlRegion
impl UnwindSafe for ControlRegion
Blanket Implementations§
source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere T: ?Sized,
source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Mutably borrows from an owned value. Read more
source§impl<E, V> EntityOrientedMapKey<V> for Ewhere
E: Entity,
impl<E, V> EntityOrientedMapKey<V> for Ewhere E: Entity,
fn to_entity(key: E) -> E
§type DenseValueSlots = Option<V>
type DenseValueSlots = Option<V>
A type holding enough different
Option<V>
slots, for all possible
values of Self
, for a given Self::Entity
value contained inside.fn get_dense_value_slot(_: E, slot: &Option<V>) -> &Option<V>
fn get_dense_value_slot_mut(_: E, slot: &mut Option<V>) -> &mut Option<V>
source§impl<Q, K> Equivalent<K> for Qwhere
Q: Eq + ?Sized,
K: Borrow<Q> + ?Sized,
impl<Q, K> Equivalent<K> for Qwhere Q: Eq + ?Sized, K: Borrow<Q> + ?Sized,
source§fn equivalent(&self, key: &K) -> bool
fn equivalent(&self, key: &K) -> bool
Compare self to
key
and return true
if they are equal.source§impl<Q, K> Equivalent<K> for Qwhere
Q: Eq + ?Sized,
K: Borrow<Q> + ?Sized,
impl<Q, K> Equivalent<K> for Qwhere Q: Eq + ?Sized, K: Borrow<Q> + ?Sized,
source§fn equivalent(&self, key: &K) -> bool
fn equivalent(&self, key: &K) -> bool
Compare self to
key
and return true
if they are equal.