#![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/rust-gpu/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::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,
// FIXME(eddyb) rework doc comments to conform to linted expectations.
clippy::too_long_first_doc_paragraph,
)]
#![forbid(unsafe_code)]
pub mod cfg;
pub mod cfgssa;
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 qptr;
}
pub mod qptr;
pub mod spv;
use smallvec::SmallVec;
use std::borrow::Cow;
use std::collections::BTreeSet;
use std::rc::Rc;
#[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>,
}
impl AttrSetDef {
pub fn push_diag(&mut self, diag: Diag) {
let mut attr = if let Some(Attr::Diagnostics(_)) = self.attrs.last() {
self.attrs.pop_last().unwrap()
} else {
Attr::Diagnostics(OrdAssertEq(vec![]))
};
match &mut attr {
Attr::Diagnostics(OrdAssertEq(diags)) => diags.push(diag),
_ => unreachable!(),
}
self.attrs.insert(attr);
}
pub fn append_diag(&self, diag: Diag) -> Self {
let mut new_attrs = Self { attrs: self.attrs.clone() };
new_attrs.push_diag(diag);
new_attrs
}
}
impl AttrSet {
pub fn append_diag(self, cx: &Context, diag: Diag) -> Self {
cx.intern(cx[self].append_diag(diag))
}
pub fn push_diag(&mut self, cx: &Context, diag: Diag) {
*self = self.append_diag(cx, diag);
}
}
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, derive_more::From)]
pub enum Attr {
#[from]
QPtr(qptr::QPtrAttr),
SpvAnnotation(spv::Inst),
SpvDebugLine {
file_path: OrdAssertEq<InternedStr>,
line: u32,
col: u32,
},
SpvBitflagsOperand(spv::Imm),
Diagnostics(OrdAssertEq<Vec<Diag>>),
}
#[derive(Clone, PartialEq, Eq, Hash)]
pub struct Diag {
pub level: DiagLevel,
pub message: Vec<DiagMsgPart>,
}
impl Diag {
pub fn new(level: DiagLevel, message: impl IntoIterator<Item = DiagMsgPart>) -> Self {
Self { level, message: message.into_iter().collect() }
}
#[track_caller]
pub fn bug(message: impl IntoIterator<Item = DiagMsgPart>) -> Self {
Self::new(DiagLevel::Bug(std::panic::Location::caller()), message)
}
pub fn err(message: impl IntoIterator<Item = DiagMsgPart>) -> Self {
Self::new(DiagLevel::Error, message)
}
pub fn warn(message: impl IntoIterator<Item = DiagMsgPart>) -> Self {
Self::new(DiagLevel::Warning, message)
}
}
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
pub enum DiagLevel {
Bug(&'static std::panic::Location<'static>),
Error,
Warning,
}
#[derive(Clone, PartialEq, Eq, Hash, derive_more::From)]
#[from]
pub enum DiagMsgPart {
#[from(forward)]
Plain(Cow<'static, str>),
Attrs(AttrSet),
Type(Type),
Const(Const),
QPtrUsage(qptr::QPtrUsage),
}
#[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 kind: TypeKind,
}
#[derive(Clone, PartialEq, Eq, Hash)]
pub enum TypeKind {
QPtr,
SpvInst {
spv_inst: spv::Inst,
type_and_const_inputs: SmallVec<[TypeOrConst; 2]>,
},
SpvStringLiteralForExtInst,
}
impl context::InternInCx<Type> for TypeKind {
fn intern_in_cx(self, cx: &Context) -> Type {
cx.intern(TypeDef { attrs: Default::default(), kind: self })
}
}
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
pub enum TypeOrConst {
Type(Type),
Const(Const),
}
pub use context::Const;
#[derive(PartialEq, Eq, Hash)]
pub struct ConstDef {
pub attrs: AttrSet,
pub ty: Type,
pub kind: ConstKind,
}
#[derive(Clone, PartialEq, Eq, Hash)]
pub enum ConstKind {
PtrToGlobalVar(GlobalVar),
SpvInst {
spv_inst_and_const_inputs: Rc<(spv::Inst, SmallVec<[Const; 4]>)>,
},
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 shape: Option<qptr::shapes::GlobalVarShape>,
pub addr_space: AddrSpace,
pub def: DeclDef<GlobalVarDefBody>,
}
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
pub enum AddrSpace {
Handles,
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, Default)]
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,
},
ExitInvocation {
kind: cfg::ExitInvocationKind,
inputs: SmallVec<[Value; 2]>,
},
}
#[derive(Clone)]
pub enum SelectionKind {
BoolCond,
SpvInst(spv::Inst),
}
pub use context::DataInst;
#[derive(Clone)]
pub struct DataInstDef {
pub attrs: AttrSet,
pub form: DataInstForm,
pub inputs: SmallVec<[Value; 2]>,
}
pub use context::DataInstForm;
#[derive(Clone, PartialEq, Eq, Hash)]
pub struct DataInstFormDef {
pub kind: DataInstKind,
pub output_type: Option<Type>,
}
#[derive(Clone, PartialEq, Eq, Hash, derive_more::From)]
pub enum DataInstKind {
FuncCall(Func),
#[from]
QPtr(qptr::QPtrOp),
SpvInst(spv::Inst),
SpvExtInst {
ext_set: InternedStr,
inst: u32,
},
}
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
pub enum Value {
Const(Const),
ControlRegionInput {
region: ControlRegion,
input_idx: u32,
},
ControlNodeOutput {
control_node: ControlNode,
output_idx: u32,
},
DataInstOutput(DataInst),
}