Skip to content

Commit

Permalink
Rename ControlRegion to just Region.
Browse files Browse the repository at this point in the history
  • Loading branch information
eddyb committed Nov 5, 2024
1 parent d520591 commit abee1e0
Show file tree
Hide file tree
Showing 13 changed files with 294 additions and 320 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ With the initial focus being on [Rust-GPU]'s usecase, various (otherwise desirab
* "entity" system for e.g. definitions in a module, instructions in a function, etc.
* disallows iteration in favor of/forcing the use of efficient indexing
* structured control-flow "regions" inspired by RVSDG, stricter than SPIR-V
(see `ControlRegionDef`'s docs for more details)
(see `RegionDef`'s docs for more details)

</td><td>

Expand Down
189 changes: 90 additions & 99 deletions src/cfg.rs

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion src/cfgssa.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
//! be taken to preserve the correctness of such implicit dataflow across all
//! transformations, and it's overall far more fragile than the local dataflow
//! of e.g. phi nodes (or their alternative "block arguments"), or in SPIR-T's
//! case, `ControlRegion` inputs and `ControlNode` outputs (inspired by RVSDG,
//! case, `Region` inputs and `ControlNode` outputs (inspired by RVSDG,
//! which has even stricter isolation/locality in its regions).
use crate::{FxIndexMap, FxIndexSet};
Expand Down
2 changes: 1 addition & 1 deletion src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -962,7 +962,7 @@ macro_rules! entities {
entities! {
GlobalVar => chunk_size(0x1_0000) crate::GlobalVarDecl,
Func => chunk_size(0x1_0000) crate::FuncDecl,
ControlRegion => chunk_size(0x1000) crate::ControlRegionDef,
Region => chunk_size(0x1000) crate::RegionDef,
ControlNode => chunk_size(0x1000) EntityListNode<ControlNode, crate::ControlNodeDef>,
DataInst => chunk_size(0x1000) EntityListNode<DataInst, crate::DataInstDef>,
}
40 changes: 20 additions & 20 deletions src/func_at.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@
#![allow(clippy::should_implement_trait)]

use crate::{
Context, ControlNode, ControlNodeDef, ControlRegion, ControlRegionDef, DataInst, DataInstDef,
EntityDefs, EntityList, EntityListIter, FuncDefBody, Type, Value,
Context, ControlNode, ControlNodeDef, DataInst, DataInstDef, EntityDefs, EntityList,
EntityListIter, FuncDefBody, Region, RegionDef, Type, Value,
};

/// Immutable traversal (i.e. visiting) helper for intra-function entities.
Expand All @@ -22,7 +22,7 @@ use crate::{
/// (e.g. [`EntityList<ControlNode>`]).
#[derive(Copy, Clone)]
pub struct FuncAt<'a, P: Copy> {
pub control_regions: &'a EntityDefs<ControlRegion>,
pub regions: &'a EntityDefs<Region>,
pub control_nodes: &'a EntityDefs<ControlNode>,
pub data_insts: &'a EntityDefs<DataInst>,

Expand All @@ -33,17 +33,17 @@ impl<'a, P: Copy> FuncAt<'a, P> {
/// Reposition to `new_position`.
pub fn at<P2: Copy>(self, new_position: P2) -> FuncAt<'a, P2> {
FuncAt {
control_regions: self.control_regions,
regions: self.regions,
control_nodes: self.control_nodes,
data_insts: self.data_insts,
position: new_position,
}
}
}

impl<'a> FuncAt<'a, ControlRegion> {
pub fn def(self) -> &'a ControlRegionDef {
&self.control_regions[self.position]
impl<'a> FuncAt<'a, Region> {
pub fn def(self) -> &'a RegionDef {
&self.regions[self.position]
}

pub fn at_children(self) -> FuncAt<'a, EntityList<ControlNode>> {
Expand Down Expand Up @@ -110,7 +110,7 @@ impl FuncAt<'_, Value> {
pub fn type_of(self, cx: &Context) -> Type {
match self.position {
Value::Const(ct) => cx[ct].ty,
Value::ControlRegionInput { region, input_idx } => {
Value::RegionInput { region, input_idx } => {
self.at(region).def().inputs[input_idx as usize].ty
}
Value::ControlNodeOutput { control_node, output_idx } => {
Expand All @@ -126,7 +126,7 @@ impl FuncAt<'_, Value> {
/// The point/position type `P` should be an entity or a shallow entity wrapper
/// (e.g. [`EntityList<ControlNode>`]).
pub struct FuncAtMut<'a, P: Copy> {
pub control_regions: &'a mut EntityDefs<ControlRegion>,
pub regions: &'a mut EntityDefs<Region>,
pub control_nodes: &'a mut EntityDefs<ControlNode>,
pub data_insts: &'a mut EntityDefs<DataInst>,

Expand All @@ -137,7 +137,7 @@ impl<'a, P: Copy> FuncAtMut<'a, P> {
/// Emulate a "reborrow", which is automatic only for `&mut` types.
pub fn reborrow(&mut self) -> FuncAtMut<'_, P> {
FuncAtMut {
control_regions: self.control_regions,
regions: self.regions,
control_nodes: self.control_nodes,
data_insts: self.data_insts,
position: self.position,
Expand All @@ -147,7 +147,7 @@ impl<'a, P: Copy> FuncAtMut<'a, P> {
/// Reposition to `new_position`.
pub fn at<P2: Copy>(self, new_position: P2) -> FuncAtMut<'a, P2> {
FuncAtMut {
control_regions: self.control_regions,
regions: self.regions,
control_nodes: self.control_nodes,
data_insts: self.data_insts,
position: new_position,
Expand All @@ -158,14 +158,14 @@ impl<'a, P: Copy> FuncAtMut<'a, P> {
//
// FIXME(eddyb) maybe find a better name for this?
pub fn freeze(self) -> FuncAt<'a, P> {
let FuncAtMut { control_regions, control_nodes, data_insts, position } = self;
FuncAt { control_regions, control_nodes, data_insts, position }
let FuncAtMut { regions, control_nodes, data_insts, position } = self;
FuncAt { regions, control_nodes, data_insts, position }
}
}

impl<'a> FuncAtMut<'a, ControlRegion> {
pub fn def(self) -> &'a mut ControlRegionDef {
&mut self.control_regions[self.position]
impl<'a> FuncAtMut<'a, Region> {
pub fn def(self) -> &'a mut RegionDef {
&mut self.regions[self.position]
}

pub fn at_children(mut self) -> FuncAtMut<'a, EntityList<ControlNode>> {
Expand Down Expand Up @@ -224,7 +224,7 @@ impl FuncDefBody {
/// Start immutably traversing the function at `position`.
pub fn at<P: Copy>(&self, position: P) -> FuncAt<'_, P> {
FuncAt {
control_regions: &self.control_regions,
regions: &self.regions,
control_nodes: &self.control_nodes,
data_insts: &self.data_insts,
position,
Expand All @@ -234,20 +234,20 @@ impl FuncDefBody {
/// Start mutably traversing the function at `position`.
pub fn at_mut<P: Copy>(&mut self, position: P) -> FuncAtMut<'_, P> {
FuncAtMut {
control_regions: &mut self.control_regions,
regions: &mut self.regions,
control_nodes: &mut self.control_nodes,
data_insts: &mut self.data_insts,
position,
}
}

/// Shorthand for `func_def_body.at(func_def_body.body)`.
pub fn at_body(&self) -> FuncAt<'_, ControlRegion> {
pub fn at_body(&self) -> FuncAt<'_, Region> {
self.at(self.body)
}

/// Shorthand for `func_def_body.at_mut(func_def_body.body)`.
pub fn at_mut_body(&mut self) -> FuncAtMut<'_, ControlRegion> {
pub fn at_mut_body(&mut self) -> FuncAtMut<'_, Region> {
self.at_mut(self.body)
}
}
74 changes: 37 additions & 37 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@
// HACK(eddyb) using `(struct.Context.html)` to link `Context`, not `context::Context`.
//! * [`Context`](struct.Context.html): handles interning ([`Type`]s, [`Const`]s, etc.) and allocating entity handles
//! * [`Module`]: owns [`Func`]s and [`GlobalVar`]s (rooted by [`exports`](Module::exports))
//! * [`FuncDefBody`]: owns [`ControlRegion`]s and [DataInst]s (rooted by [`body`](FuncDefBody::body))
//! * [`FuncDefBody`]: owns [`Region`]s and [DataInst]s (rooted by [`body`](FuncDefBody::body))
//!
//! ##### Utilities and passes
//! * [`print`](mod@print): pretty-printer with (styled and hyperlinked) HTML output
Expand Down Expand Up @@ -479,7 +479,7 @@ pub enum TypeKind {
/// attached as `Attr`s on those `Value`s (see [`Attr::QPtr`]).
//
// FIXME(eddyb) a "refinement system" that's orthogonal from types, and kept
// separately in e.g. `ControlRegionInputDecl`, might be a better approach?
// separately in e.g. `RegionInputDecl`, might be a better approach?
QPtr,

SpvInst {
Expand Down Expand Up @@ -624,18 +624,18 @@ pub struct FuncParam {
// FIXME(eddyb) `FuncDefBody`/`func_def_body` are too long, find shorter names.
#[derive(Clone)]
pub struct FuncDefBody {
pub control_regions: EntityDefs<ControlRegion>,
pub regions: EntityDefs<Region>,
pub control_nodes: EntityDefs<ControlNode>,
pub data_insts: EntityDefs<DataInst>,

/// The [`ControlRegion`] representing the whole body of the function.
/// The [`Region`] representing the whole body of the function.
///
/// Function parameters are provided via `body.inputs`, i.e. they can be
/// only accessed with `Value::ControlRegionInputs { region: body, idx }`.
/// only accessed with `Value::RegionInputs { region: body, idx }`.
///
/// When `unstructured_cfg` is `None`, this includes the structured return
/// of the function, with `body.outputs` as the returned values.
pub body: ControlRegion,
pub body: Region,

/// The unstructured (part of the) control-flow graph of the function.
///
Expand All @@ -648,19 +648,19 @@ pub struct FuncDefBody {
pub unstructured_cfg: Option<cfg::ControlFlowGraph>,
}

/// Entity handle for a [`ControlRegionDef`](crate::ControlRegionDef)
/// Entity handle for a [`RegionDef`](crate::RegionDef)
/// (a control-flow region).
///
/// A [`ControlRegion`] ("control-flow region") is a linear chain of [`ControlNode`]s,
/// A [`Region`] ("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 and [`ControlNode`]s in a "mutual tree"
/// * i.e. each such [`ControlRegion`] can only appear in exactly one [`ControlNode`],
/// and each [`ControlNode`] can only appear in exactly one [`ControlRegion`]
/// * "structured": [`Region`]s and [`ControlNode`]s in a "mutual tree"
/// * i.e. each such [`Region`] can only appear in exactly one [`ControlNode`],
/// and each [`ControlNode`] can only appear in exactly one [`Region`]
/// * a region is either the function's body, or used as part of [`ControlNode`]
/// (e.g. the "then" case of an `if`-`else`), itself part of a larger region
/// * when inside a region, reaching any other part of the function (or any
Expand All @@ -670,16 +670,16 @@ pub struct FuncDefBody {
/// [`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)
/// * "unstructured": [`ControlRegion`]s which connect to other [`ControlRegion`]s
/// * "unstructured": [`Region`]s which connect to other [`Region`]s
/// using [`cfg::ControlInst`](crate::cfg::ControlInst)s (as described by a
/// [`cfg::ControlFlowGraph`](crate::cfg::ControlFlowGraph))
///
/// When a function's entire body can be described by a single [`ControlRegion`],
/// When a function's entire body can be described by a single [`Region`],
/// 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
/// by the same CFG edges that were connecting smaller [`Region`]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
///
Expand All @@ -705,7 +705,7 @@ pub struct FuncDefBody {
/// (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`](crate::ControlRegion) can be used:
/// * dominance is simpler, so values defined in a [`Region`](crate::Region) 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`](crate::ControlNode)
Expand All @@ -728,37 +728,37 @@ pub struct FuncDefBody {
/// passing values to their target regions
/// * all value uses across unstructured control-flow edges (i.e. not in the
/// same region containing the value definition) *require* explicit passing,
/// as unstructured control-flow [`ControlRegion`](crate::ControlRegion)s
/// as unstructured control-flow [`Region`](crate::Region)s
/// do *not* themselves get *any* implied dominance relations from the
/// shape of the control-flow graph (unlike most typical CFG+SSA IRs)
pub use context::ControlRegion;
pub use context::Region;

/// Definition for a [`ControlRegion`]: a control-flow region.
/// Definition for a [`Region`]: a control-flow region.
#[derive(Clone, Default)]
pub struct ControlRegionDef {
/// Inputs to this [`ControlRegion`]:
/// * accessed using [`Value::ControlRegionInput`]
pub struct RegionDef {
/// Inputs to this [`Region`]:
/// * accessed using [`Value::RegionInput`]
/// * values provided by the parent:
/// * when this is the function body: the function's parameters
pub inputs: SmallVec<[ControlRegionInputDecl; 2]>,
pub inputs: SmallVec<[RegionInputDecl; 2]>,

pub children: EntityList<ControlNode>,

/// Output values from this [`ControlRegion`], provided to the parent:
/// Output values from this [`Region`], provided to the parent:
/// * when this is the function body: these are the structured return values
/// * when this is a `Select` case: these are the values for the parent
/// [`ControlNode`]'s outputs (accessed using [`Value::ControlNodeOutput`])
/// * when this is a `Loop` body: these are the values to be used for the
/// next loop iteration's body `inputs`
/// * **not** accessible through [`Value::ControlNodeOutput`] on the `Loop`,
/// as it's both confusing regarding [`Value::ControlRegionInput`], and
/// as it's both confusing regarding [`Value::RegionInput`], and
/// also there's nothing stopping body-defined values from directly being
/// used outside the loop (once that changes, this aspect can be flipped)
pub outputs: SmallVec<[Value; 2]>,
}

#[derive(Copy, Clone)]
pub struct ControlRegionInputDecl {
pub struct RegionInputDecl {
pub attrs: AttrSet,

pub ty: Type,
Expand All @@ -767,20 +767,20 @@ pub struct ControlRegionInputDecl {
/// Entity handle for a [`ControlNodeDef`](crate::ControlNodeDef)
/// (a control-flow operator or leaf).
///
/// See [`ControlRegion`] docs for more on control-flow in SPIR-T.
/// See [`Region`] docs for more on control-flow in SPIR-T.
pub use context::ControlNode;

/// Definition for a [`ControlNode`]: a control-flow operator or leaf.
///
/// See [`ControlRegion`] docs for more on control-flow in SPIR-T.
/// See [`Region`] docs for more on control-flow in SPIR-T.
#[derive(Clone)]
pub struct ControlNodeDef {
pub kind: ControlNodeKind,

/// Outputs from this [`ControlNode`]:
/// * accessed using [`Value::ControlNodeOutput`]
/// * values provided by `region.outputs`, where `region` is the executed
/// child [`ControlRegion`]:
/// child [`Region`]:
/// * when this is a `Select`: the case that was chosen
pub outputs: SmallVec<[ControlNodeOutputDecl; 2]>,
}
Expand All @@ -796,20 +796,20 @@ pub struct ControlNodeOutputDecl {
pub enum ControlNodeKind {
/// Linear chain of [`DataInst`]s, executing in sequence.
///
/// This is only an optimization over keeping [`DataInst`]s in [`ControlRegion`]
/// This is only an optimization over keeping [`DataInst`]s in [`Region`]
/// linear chains directly, or even merging [`DataInst`] with [`ControlNode`].
Block {
// FIXME(eddyb) should empty blocks be allowed? should `DataInst`s be
// linked directly into the `ControlRegion` `children` list?
// linked directly into the `Region` `children` list?
insts: EntityList<DataInst>,
},

/// Choose one [`ControlRegion`] out of `cases` to execute, based on a single
/// Choose one [`Region`] out of `cases` to execute, based on a single
/// value input (`scrutinee`) interpreted according to [`SelectionKind`].
///
/// This corresponds to "gamma" (`γ`) nodes in (R)VSDG, though those are
/// sometimes limited only to a two-way selection on a boolean condition.
Select { kind: SelectionKind, scrutinee: Value, cases: SmallVec<[ControlRegion; 2]> },
Select { kind: SelectionKind, scrutinee: Value, cases: SmallVec<[Region; 2]> },

/// Execute `body` repeatedly, until `repeat_condition` evaluates to `false`.
///
Expand All @@ -825,7 +825,7 @@ pub enum ControlNodeKind {
Loop {
initial_inputs: SmallVec<[Value; 2]>,

body: ControlRegion,
body: Region,

// FIXME(eddyb) should this be kept in `body.outputs`? (that would not
// have any ambiguity as to whether it can see `body`-computed values)
Expand Down Expand Up @@ -912,19 +912,19 @@ pub enum DataInstKind {
pub enum Value {
Const(Const),

/// One of the inputs to a [`ControlRegion`]:
/// One of the inputs to a [`Region`]:
/// * declared by `region.inputs[input_idx]`
/// * value provided by the parent of the `region`:
/// * when `region` is the function body: `input_idx`th function parameter
ControlRegionInput {
region: ControlRegion,
RegionInput {
region: Region,
input_idx: u32,
},

/// One of the outputs produced by a [`ControlNode`]:
/// * declared by `control_node.outputs[output_idx]`
/// * value provided by `region.outputs[output_idx]`, where `region` is the
/// executed child [`ControlRegion`] (of `control_node`):
/// executed child [`Region`] (of `control_node`):
/// * when `control_node` is a `Select`: the case that was chosen
ControlNodeOutput {
control_node: ControlNode,
Expand Down
Loading

0 comments on commit abee1e0

Please sign in to comment.