#![cfg_attr(
docsrs,
// NOTE(eddyb) this requires updating `repository` before every release to
// end in `/tree/` followed by the tag name, in order to be useful.
doc = concat!(
"[`", env!("CARGO_PKG_NAME"), " ", env!("CARGO_PKG_VERSION"), "`'s `README`]",
"(", env!("CARGO_PKG_REPOSITORY"), "#readme)* "
)
)]
#![cfg_attr(
git_main_docs,
doc = concat!(
"[`", env!("CARGO_PKG_NAME"), " @ ", env!("GIT_MAIN_DESCRIBE"), "`'s `README`]",
"(https://github.com/EmbarkStudios/spirt/tree/", env!("GIT_MAIN_COMMIT"), "#readme)* "
)
)]
#![cfg_attr(
any(docsrs, git_main_docs),
doc = "<sup> *(click through for the full version)*</sup>"
)]
#![cfg_attr(
not(any(docsrs, git_main_docs)),
doc = concat!("`", env!("CARGO_PKG_NAME"), "`'s `README`* ")
)]
#![deny(unsafe_code)]
#![warn(
clippy::all,
clippy::await_holding_lock,
clippy::char_lit_as_u8,
clippy::checked_conversions,
clippy::dbg_macro,
clippy::debug_assert_with_mut_call,
clippy::doc_markdown,
clippy::empty_enum,
clippy::enum_glob_use,
clippy::exit,
clippy::expl_impl_clone_on_copy,
clippy::explicit_deref_methods,
clippy::explicit_into_iter_loop,
clippy::fallible_impl_from,
clippy::filter_map_next,
clippy::flat_map_option,
clippy::float_cmp_const,
clippy::fn_params_excessive_bools,
clippy::from_iter_instead_of_collect,
clippy::if_let_mutex,
clippy::implicit_clone,
clippy::imprecise_flops,
clippy::inefficient_to_string,
clippy::invalid_upcast_comparisons,
clippy::large_digit_groups,
clippy::large_stack_arrays,
clippy::large_types_passed_by_value,
clippy::let_unit_value,
clippy::linkedlist,
clippy::lossy_float_literal,
clippy::macro_use_imports,
clippy::manual_ok_or,
clippy::map_err_ignore,
clippy::map_flatten,
clippy::map_unwrap_or,
clippy::match_on_vec_items,
clippy::match_same_arms,
clippy::match_wild_err_arm,
clippy::match_wildcard_for_single_variants,
clippy::mem_forget,
clippy::mismatched_target_os,
clippy::missing_enforced_import_renames,
clippy::mut_mut,
clippy::mutex_integer,
clippy::needless_borrow,
clippy::needless_continue,
clippy::needless_for_each,
clippy::option_option,
clippy::path_buf_push_overwrite,
clippy::ptr_as_ptr,
clippy::rc_mutex,
clippy::ref_option_ref,
clippy::rest_pat_in_fully_bound_structs,
clippy::same_functions_in_if_condition,
clippy::semicolon_if_nothing_returned,
clippy::single_match_else,
clippy::string_add_assign,
clippy::string_add,
clippy::string_lit_as_bytes,
clippy::string_to_string,
clippy::todo,
clippy::trait_duplication_in_bounds,
clippy::unimplemented,
clippy::unnested_or_patterns,
clippy::unused_self,
clippy::useless_transmute,
clippy::verbose_file_reads,
clippy::zero_sized_map_values,
future_incompatible,
nonstandard_style,
rust_2018_idioms
)]
#![allow(
// NOTE(eddyb) ignored for readability (`match` used when `if let` is too long).
clippy::single_match_else,
// NOTE(eddyb) ignored because it's misguided to suggest `let mut s = ...;`
// and `s.push_str(...);` when `+` is equivalent and does not require `let`.
clippy::string_add,
)]
#![forbid(unsafe_code)]
pub mod cfg;
mod context;
pub mod func_at;
pub mod print;
pub mod transform;
pub mod visit;
pub mod passes {
pub mod legalize;
pub mod link;
}
pub mod spv;
use smallvec::SmallVec;
use std::collections::BTreeSet;
#[doc(hidden)]
type FxIndexMap<K, V> =
indexmap::IndexMap<K, V, std::hash::BuildHasherDefault<rustc_hash::FxHasher>>;
#[doc(hidden)]
type FxIndexSet<V> = indexmap::IndexSet<V, std::hash::BuildHasherDefault<rustc_hash::FxHasher>>;
pub use context::{
Context, EntityDefs, EntityList, EntityListIter, EntityOrientedDenseMap, EntityOrientedMapKey,
};
pub use context::InternedStr;
#[doc(hidden)]
mod sealed {
use super::*;
use std::rc::Rc;
#[derive(Clone)]
pub struct Module {
cx: Rc<Context>,
pub dialect: ModuleDialect,
pub debug_info: ModuleDebugInfo,
pub global_vars: EntityDefs<GlobalVar>,
pub funcs: EntityDefs<Func>,
pub exports: FxIndexMap<ExportKey, Exportee>,
}
impl Module {
pub fn new(cx: Rc<Context>, dialect: ModuleDialect, debug_info: ModuleDebugInfo) -> Self {
Self {
cx,
dialect,
debug_info,
global_vars: Default::default(),
funcs: Default::default(),
exports: Default::default(),
}
}
pub fn cx(&self) -> Rc<Context> {
self.cx.clone()
}
pub fn cx_ref(&self) -> &Rc<Context> {
&self.cx
}
}
}
pub use sealed::Module;
#[derive(Clone)]
pub enum ModuleDialect {
Spv(spv::Dialect),
}
#[derive(Clone)]
pub enum ModuleDebugInfo {
Spv(spv::ModuleDebugInfo),
}
#[derive(Clone, PartialEq, Eq, Hash)]
pub enum ExportKey {
LinkName(InternedStr),
SpvEntryPoint {
imms: SmallVec<[spv::Imm; 2]>,
interface_global_vars: SmallVec<[GlobalVar; 4]>,
},
}
#[derive(Copy, Clone)]
pub enum Exportee {
GlobalVar(GlobalVar),
Func(Func),
}
pub use context::AttrSet;
#[derive(Default, PartialEq, Eq, Hash)]
pub struct AttrSetDef {
pub attrs: BTreeSet<Attr>,
}
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum Attr {
SpvAnnotation(spv::Inst),
SpvDebugLine {
file_path: OrdAssertEq<InternedStr>,
line: u32,
col: u32,
},
SpvBitflagsOperand(spv::Imm),
}
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
pub struct OrdAssertEq<T>(pub T);
impl<T: Eq> PartialOrd for OrdAssertEq<T> {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl<T: Eq> Ord for OrdAssertEq<T> {
#[track_caller]
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
assert!(
self == other,
"OrdAssertEq<{}>::cmp called with unequal values",
std::any::type_name::<T>(),
);
std::cmp::Ordering::Equal
}
}
pub use context::Type;
#[derive(PartialEq, Eq, Hash)]
pub struct TypeDef {
pub attrs: AttrSet,
pub ctor: TypeCtor,
pub ctor_args: SmallVec<[TypeCtorArg; 2]>,
}
#[derive(Clone, PartialEq, Eq, Hash)]
pub enum TypeCtor {
SpvInst(spv::Inst),
SpvStringLiteralForExtInst,
}
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
pub enum TypeCtorArg {
Type(Type),
Const(Const),
}
pub use context::Const;
#[derive(PartialEq, Eq, Hash)]
pub struct ConstDef {
pub attrs: AttrSet,
pub ty: Type,
pub ctor: ConstCtor,
pub ctor_args: SmallVec<[Const; 2]>,
}
#[derive(Clone, PartialEq, Eq, Hash)]
pub enum ConstCtor {
PtrToGlobalVar(GlobalVar),
SpvInst(spv::Inst),
SpvStringLiteralForExtInst(InternedStr),
}
#[derive(Clone)]
pub enum DeclDef<D> {
Imported(Import),
Present(D),
}
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
pub enum Import {
LinkName(InternedStr),
}
pub use context::GlobalVar;
#[derive(Clone)]
pub struct GlobalVarDecl {
pub attrs: AttrSet,
pub type_of_ptr_to: Type,
pub addr_space: AddrSpace,
pub def: DeclDef<GlobalVarDefBody>,
}
#[derive(Copy, Clone)]
pub enum AddrSpace {
SpvStorageClass(u32),
}
#[derive(Clone)]
pub struct GlobalVarDefBody {
pub initializer: Option<Const>,
}
pub use context::Func;
#[derive(Clone)]
pub struct FuncDecl {
pub attrs: AttrSet,
pub ret_type: Type,
pub params: SmallVec<[FuncParam; 2]>,
pub def: DeclDef<FuncDefBody>,
}
#[derive(Copy, Clone)]
pub struct FuncParam {
pub attrs: AttrSet,
pub ty: Type,
}
#[derive(Clone)]
pub struct FuncDefBody {
pub control_regions: EntityDefs<ControlRegion>,
pub control_nodes: EntityDefs<ControlNode>,
pub data_insts: EntityDefs<DataInst>,
pub body: ControlRegion,
pub unstructured_cfg: Option<cfg::ControlFlowGraph>,
}
pub use context::ControlRegion;
#[derive(Clone)]
pub struct ControlRegionDef {
pub inputs: SmallVec<[ControlRegionInputDecl; 2]>,
pub children: EntityList<ControlNode>,
pub outputs: SmallVec<[Value; 2]>,
}
#[derive(Copy, Clone)]
pub struct ControlRegionInputDecl {
pub attrs: AttrSet,
pub ty: Type,
}
pub use context::ControlNode;
#[derive(Clone)]
pub struct ControlNodeDef {
pub kind: ControlNodeKind,
pub outputs: SmallVec<[ControlNodeOutputDecl; 2]>,
}
#[derive(Copy, Clone)]
pub struct ControlNodeOutputDecl {
pub attrs: AttrSet,
pub ty: Type,
}
#[derive(Clone)]
pub enum ControlNodeKind {
Block {
insts: EntityList<DataInst>,
},
Select {
kind: SelectionKind,
scrutinee: Value,
cases: SmallVec<[ControlRegion; 2]>,
},
Loop {
initial_inputs: SmallVec<[Value; 2]>,
body: ControlRegion,
repeat_condition: Value,
},
}
#[derive(Clone)]
pub enum SelectionKind {
BoolCond,
SpvInst(spv::Inst),
}
pub use context::DataInst;
#[derive(Clone)]
pub struct DataInstDef {
pub attrs: AttrSet,
pub kind: DataInstKind,
pub output_type: Option<Type>,
pub inputs: SmallVec<[Value; 2]>,
}
#[derive(Clone, PartialEq, Eq)]
pub enum DataInstKind {
FuncCall(Func),
SpvInst(spv::Inst),
SpvExtInst { ext_set: InternedStr, inst: u32 },
}
#[derive(Copy, Clone, PartialEq, Eq)]
pub enum Value {
Const(Const),
ControlRegionInput {
region: ControlRegion,
input_idx: u32,
},
ControlNodeOutput {
control_node: ControlNode,
output_idx: u32,
},
DataInstOutput(DataInst),
}