diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
commit | 2aa4a82499d4becd2284cdb482213d541b8804dd (patch) | |
tree | b80bf8bf13c3766139fbacc530efd0dd9d54394c /third_party/rust/naga/src/front/spv/mod.rs | |
parent | Initial commit. (diff) | |
download | firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.tar.xz firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.zip |
Adding upstream version 86.0.1.upstream/86.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/naga/src/front/spv/mod.rs')
-rw-r--r-- | third_party/rust/naga/src/front/spv/mod.rs | 2416 |
1 files changed, 2416 insertions, 0 deletions
diff --git a/third_party/rust/naga/src/front/spv/mod.rs b/third_party/rust/naga/src/front/spv/mod.rs new file mode 100644 index 0000000000..f13c3c7577 --- /dev/null +++ b/third_party/rust/naga/src/front/spv/mod.rs @@ -0,0 +1,2416 @@ +/*! SPIR-V frontend + +## ID lookups + +Our IR links to everything with `Handle`, while SPIR-V uses IDs. +In order to keep track of the associations, the parser has many lookup tables. +There map `spv::Word` into a specific IR handle, plus potentially a bit of +extra info, such as the related SPIR-V type ID. +TODO: would be nice to find ways that avoid looking up as much + +!*/ +#![allow(dead_code)] + +mod convert; +mod error; +mod flow; +mod function; +#[cfg(all(test, feature = "serialize"))] +mod rosetta; + +use convert::*; +use error::Error; +use flow::*; +use function::*; + +use crate::{ + arena::{Arena, Handle}, + FastHashMap, FastHashSet, +}; + +use num_traits::cast::FromPrimitive; +use std::{convert::TryInto, num::NonZeroU32, path::PathBuf}; + +pub const SUPPORTED_CAPABILITIES: &[spirv::Capability] = &[ + spirv::Capability::Shader, + spirv::Capability::CullDistance, + spirv::Capability::StorageImageExtendedFormats, +]; +pub const SUPPORTED_EXTENSIONS: &[&str] = &[]; +pub const SUPPORTED_EXT_SETS: &[&str] = &["GLSL.std.450"]; + +#[derive(Copy, Clone)] +pub struct Instruction { + op: spirv::Op, + wc: u16, +} + +impl Instruction { + fn expect(self, count: u16) -> Result<(), Error> { + if self.wc == count { + Ok(()) + } else { + Err(Error::InvalidOperandCount(self.op, self.wc)) + } + } + + fn expect_at_least(self, count: u16) -> Result<(), Error> { + if self.wc >= count { + Ok(()) + } else { + Err(Error::InvalidOperandCount(self.op, self.wc)) + } + } +} +/// OpPhi instruction. +#[derive(Clone, Default, Debug)] +struct PhiInstruction { + /// SPIR-V's ID. + id: u32, + + /// Tuples of (variable, parent). + variables: Vec<(u32, u32)>, +} +#[derive(Clone, Copy, Debug, PartialEq, PartialOrd)] +pub enum ModuleState { + Empty, + Capability, + Extension, + ExtInstImport, + MemoryModel, + EntryPoint, + ExecutionMode, + Source, + Name, + ModuleProcessed, + Annotation, + Type, + Function, +} + +trait LookupHelper { + type Target; + fn lookup(&self, key: spirv::Word) -> Result<&Self::Target, Error>; +} + +impl<T> LookupHelper for FastHashMap<spirv::Word, T> { + type Target = T; + fn lookup(&self, key: spirv::Word) -> Result<&T, Error> { + self.get(&key).ok_or(Error::InvalidId(key)) + } +} + +//TODO: this method may need to be gone, depending on whether +// WGSL allows treating images and samplers as expressions and pass them around. +fn reach_global_type( + mut expr_handle: Handle<crate::Expression>, + expressions: &Arena<crate::Expression>, + globals: &Arena<crate::GlobalVariable>, +) -> Option<Handle<crate::Type>> { + loop { + expr_handle = match expressions[expr_handle] { + crate::Expression::Load { pointer } => pointer, + crate::Expression::GlobalVariable(var) => return Some(globals[var].ty), + _ => return None, + }; + } +} + +fn check_sample_coordinates( + ty: &crate::Type, + expect_kind: crate::ScalarKind, + dim: crate::ImageDimension, + is_array: bool, +) -> bool { + let base_count = match dim { + crate::ImageDimension::D1 => 1, + crate::ImageDimension::D2 => 2, + crate::ImageDimension::D3 | crate::ImageDimension::Cube => 3, + }; + let extra_count = if is_array { 1 } else { 0 }; + let count = base_count + extra_count; + match ty.inner { + crate::TypeInner::Scalar { kind, width: _ } => count == 1 && kind == expect_kind, + crate::TypeInner::Vector { + size, + kind, + width: _, + } => size as u8 == count && kind == expect_kind, + _ => false, + } +} + +type MemberIndex = u32; + +#[derive(Debug, Default)] +struct Block { + buffer: bool, +} + +bitflags::bitflags! { + #[derive(Default)] + struct DecorationFlags: u32 { + const NON_READABLE = 0x1; + const NON_WRITABLE = 0x2; + } +} + +#[derive(Debug, Default)] +struct Decoration { + name: Option<String>, + built_in: Option<crate::BuiltIn>, + location: Option<spirv::Word>, + desc_set: Option<spirv::Word>, + desc_index: Option<spirv::Word>, + block: Option<Block>, + offset: Option<spirv::Word>, + array_stride: Option<NonZeroU32>, + interpolation: Option<crate::Interpolation>, + flags: DecorationFlags, +} + +impl Decoration { + fn debug_name(&self) -> &str { + match self.name { + Some(ref name) => name.as_str(), + None => "?", + } + } + + fn get_binding(&self) -> Option<crate::Binding> { + //TODO: validate this better + match *self { + Decoration { + built_in: Some(built_in), + location: None, + desc_set: None, + desc_index: None, + .. + } => Some(crate::Binding::BuiltIn(built_in)), + Decoration { + built_in: None, + location: Some(loc), + desc_set: None, + desc_index: None, + .. + } => Some(crate::Binding::Location(loc)), + Decoration { + built_in: None, + location: None, + desc_set: Some(group), + desc_index: Some(binding), + .. + } => Some(crate::Binding::Resource { group, binding }), + _ => None, + } + } + + fn get_origin(&self) -> Result<crate::MemberOrigin, Error> { + match *self { + Decoration { + location: Some(_), .. + } + | Decoration { + desc_set: Some(_), .. + } + | Decoration { + desc_index: Some(_), + .. + } => Err(Error::MissingDecoration(spirv::Decoration::Offset)), + Decoration { + built_in: Some(built_in), + offset: None, + .. + } => Ok(crate::MemberOrigin::BuiltIn(built_in)), + Decoration { + built_in: None, + offset: Some(offset), + .. + } => Ok(crate::MemberOrigin::Offset(offset)), + _ => Ok(crate::MemberOrigin::Empty), + } + } +} + +bitflags::bitflags! { + /// Flags describing sampling method. + pub struct SamplingFlags: u32 { + /// Regular sampling. + const REGULAR = 0x1; + /// Comparison sampling. + const COMPARISON = 0x2; + } +} + +#[derive(Debug)] +struct LookupFunctionType { + parameter_type_ids: Vec<spirv::Word>, + return_type_id: spirv::Word, +} + +#[derive(Debug)] +struct EntryPoint { + stage: crate::ShaderStage, + name: String, + early_depth_test: Option<crate::EarlyDepthTest>, + workgroup_size: [u32; 3], + function_id: spirv::Word, + variable_ids: Vec<spirv::Word>, +} + +#[derive(Clone, Debug)] +struct LookupType { + handle: Handle<crate::Type>, + base_id: Option<spirv::Word>, +} + +#[derive(Debug)] +struct LookupConstant { + handle: Handle<crate::Constant>, + type_id: spirv::Word, +} + +#[derive(Debug)] +struct LookupVariable { + handle: Handle<crate::GlobalVariable>, + type_id: spirv::Word, +} + +#[derive(Clone, Debug)] +struct LookupExpression { + handle: Handle<crate::Expression>, + type_id: spirv::Word, +} + +#[derive(Clone, Debug)] +struct LookupSampledImage { + image: Handle<crate::Expression>, + sampler: Handle<crate::Expression>, +} +#[derive(Clone, Debug)] +enum DeferredSource { + EntryPoint(crate::ShaderStage, String), + Function(Handle<crate::Function>), +} +struct DeferredFunctionCall { + source: DeferredSource, + expr_handle: Handle<crate::Expression>, + dst_id: spirv::Word, +} + +#[derive(Clone, Debug)] +pub struct Assignment { + to: Handle<crate::Expression>, + value: Handle<crate::Expression>, +} + +#[derive(Clone, Debug, Default)] +pub struct Options { + pub flow_graph_dump_prefix: Option<PathBuf>, +} + +pub struct Parser<I> { + data: I, + state: ModuleState, + temp_bytes: Vec<u8>, + ext_glsl_id: Option<spirv::Word>, + future_decor: FastHashMap<spirv::Word, Decoration>, + future_member_decor: FastHashMap<(spirv::Word, MemberIndex), Decoration>, + lookup_member_type_id: FastHashMap<(Handle<crate::Type>, MemberIndex), spirv::Word>, + handle_sampling: FastHashMap<Handle<crate::Type>, SamplingFlags>, + lookup_type: FastHashMap<spirv::Word, LookupType>, + lookup_void_type: FastHashSet<spirv::Word>, + lookup_storage_buffer_types: FastHashSet<Handle<crate::Type>>, + // Lookup for samplers and sampled images, storing flags on how they are used. + lookup_constant: FastHashMap<spirv::Word, LookupConstant>, + lookup_variable: FastHashMap<spirv::Word, LookupVariable>, + lookup_expression: FastHashMap<spirv::Word, LookupExpression>, + lookup_sampled_image: FastHashMap<spirv::Word, LookupSampledImage>, + lookup_function_type: FastHashMap<spirv::Word, LookupFunctionType>, + lookup_function: FastHashMap<spirv::Word, Handle<crate::Function>>, + lookup_entry_point: FastHashMap<spirv::Word, EntryPoint>, + deferred_function_calls: Vec<DeferredFunctionCall>, + options: Options, +} + +impl<I: Iterator<Item = u32>> Parser<I> { + pub fn new(data: I, options: &Options) -> Self { + Parser { + data, + state: ModuleState::Empty, + temp_bytes: Vec::new(), + ext_glsl_id: None, + future_decor: FastHashMap::default(), + future_member_decor: FastHashMap::default(), + handle_sampling: FastHashMap::default(), + lookup_member_type_id: FastHashMap::default(), + lookup_type: FastHashMap::default(), + lookup_void_type: FastHashSet::default(), + lookup_storage_buffer_types: FastHashSet::default(), + lookup_constant: FastHashMap::default(), + lookup_variable: FastHashMap::default(), + lookup_expression: FastHashMap::default(), + lookup_sampled_image: FastHashMap::default(), + lookup_function_type: FastHashMap::default(), + lookup_function: FastHashMap::default(), + lookup_entry_point: FastHashMap::default(), + deferred_function_calls: Vec::new(), + options: options.clone(), + } + } + + fn next(&mut self) -> Result<u32, Error> { + self.data.next().ok_or(Error::IncompleteData) + } + + fn next_inst(&mut self) -> Result<Instruction, Error> { + let word = self.next()?; + let (wc, opcode) = ((word >> 16) as u16, (word & 0xffff) as u16); + if wc == 0 { + return Err(Error::InvalidWordCount); + } + let op = spirv::Op::from_u16(opcode).ok_or(Error::UnknownInstruction(opcode))?; + + Ok(Instruction { op, wc }) + } + + fn next_string(&mut self, mut count: u16) -> Result<(String, u16), Error> { + self.temp_bytes.clear(); + loop { + if count == 0 { + return Err(Error::BadString); + } + count -= 1; + let chars = self.next()?.to_le_bytes(); + let pos = chars.iter().position(|&c| c == 0).unwrap_or(4); + self.temp_bytes.extend_from_slice(&chars[..pos]); + if pos < 4 { + break; + } + } + std::str::from_utf8(&self.temp_bytes) + .map(|s| (s.to_owned(), count)) + .map_err(|_| Error::BadString) + } + + fn next_decoration( + &mut self, + inst: Instruction, + base_words: u16, + dec: &mut Decoration, + ) -> Result<(), Error> { + let raw = self.next()?; + let dec_typed = spirv::Decoration::from_u32(raw).ok_or(Error::InvalidDecoration(raw))?; + log::trace!("\t\t{}: {:?}", dec.debug_name(), dec_typed); + match dec_typed { + spirv::Decoration::BuiltIn => { + inst.expect(base_words + 2)?; + let raw = self.next()?; + match map_builtin(raw) { + Ok(built_in) => dec.built_in = Some(built_in), + Err(_e) => log::warn!("Unsupported builtin {}", raw), + }; + } + spirv::Decoration::Location => { + inst.expect(base_words + 2)?; + dec.location = Some(self.next()?); + } + spirv::Decoration::DescriptorSet => { + inst.expect(base_words + 2)?; + dec.desc_set = Some(self.next()?); + } + spirv::Decoration::Binding => { + inst.expect(base_words + 2)?; + dec.desc_index = Some(self.next()?); + } + spirv::Decoration::Block => { + dec.block = Some(Block { buffer: false }); + } + spirv::Decoration::BufferBlock => { + dec.block = Some(Block { buffer: true }); + } + spirv::Decoration::Offset => { + inst.expect(base_words + 2)?; + dec.offset = Some(self.next()?); + } + spirv::Decoration::ArrayStride => { + inst.expect(base_words + 2)?; + dec.array_stride = NonZeroU32::new(self.next()?); + } + spirv::Decoration::NoPerspective => { + dec.interpolation = Some(crate::Interpolation::Linear); + } + spirv::Decoration::Flat => { + dec.interpolation = Some(crate::Interpolation::Flat); + } + spirv::Decoration::Patch => { + dec.interpolation = Some(crate::Interpolation::Patch); + } + spirv::Decoration::Centroid => { + dec.interpolation = Some(crate::Interpolation::Centroid); + } + spirv::Decoration::Sample => { + dec.interpolation = Some(crate::Interpolation::Sample); + } + spirv::Decoration::NonReadable => { + dec.flags |= DecorationFlags::NON_READABLE; + } + spirv::Decoration::NonWritable => { + dec.flags |= DecorationFlags::NON_WRITABLE; + } + other => { + log::warn!("Unknown decoration {:?}", other); + for _ in base_words + 1..inst.wc { + let _var = self.next()?; + } + } + } + Ok(()) + } + + fn parse_expr_unary_op( + &mut self, + expressions: &mut Arena<crate::Expression>, + op: crate::UnaryOperator, + ) -> Result<(), Error> { + let result_type_id = self.next()?; + let result_id = self.next()?; + let p_id = self.next()?; + + let p_lexp = self.lookup_expression.lookup(p_id)?; + + let expr = crate::Expression::Unary { + op, + expr: p_lexp.handle, + }; + self.lookup_expression.insert( + result_id, + LookupExpression { + handle: expressions.append(expr), + type_id: result_type_id, + }, + ); + Ok(()) + } + + fn parse_expr_binary_op( + &mut self, + expressions: &mut Arena<crate::Expression>, + op: crate::BinaryOperator, + ) -> Result<(), Error> { + let result_type_id = self.next()?; + let result_id = self.next()?; + let p1_id = self.next()?; + let p2_id = self.next()?; + + let p1_lexp = self.lookup_expression.lookup(p1_id)?; + let p2_lexp = self.lookup_expression.lookup(p2_id)?; + + let expr = crate::Expression::Binary { + op, + left: p1_lexp.handle, + right: p2_lexp.handle, + }; + self.lookup_expression.insert( + result_id, + LookupExpression { + handle: expressions.append(expr), + type_id: result_type_id, + }, + ); + Ok(()) + } + + #[allow(clippy::too_many_arguments)] + fn next_block( + &mut self, + block_id: spirv::Word, + expressions: &mut Arena<crate::Expression>, + local_arena: &mut Arena<crate::LocalVariable>, + type_arena: &Arena<crate::Type>, + const_arena: &Arena<crate::Constant>, + global_arena: &Arena<crate::GlobalVariable>, + local_function_calls: &mut FastHashMap<Handle<crate::Expression>, spirv::Word>, + ) -> Result<ControlFlowNode, Error> { + let mut assignments = Vec::new(); + let mut phis = Vec::new(); + let mut merge = None; + let terminator = loop { + use spirv::Op; + let inst = self.next_inst()?; + log::debug!("\t\t{:?} [{}]", inst.op, inst.wc); + + match inst.op { + Op::Variable => { + inst.expect_at_least(4)?; + let result_type_id = self.next()?; + let result_id = self.next()?; + let storage = self.next()?; + match spirv::StorageClass::from_u32(storage) { + Some(spirv::StorageClass::Function) => (), + Some(class) => return Err(Error::InvalidVariableClass(class)), + None => return Err(Error::UnsupportedStorageClass(storage)), + } + let init = if inst.wc > 4 { + inst.expect(5)?; + let init_id = self.next()?; + let lconst = self.lookup_constant.lookup(init_id)?; + Some(lconst.handle) + } else { + None + }; + let name = self + .future_decor + .remove(&result_id) + .and_then(|decor| decor.name); + if let Some(ref name) = name { + log::debug!("\t\t\tid={} name={}", result_id, name); + } + let var_handle = local_arena.append(crate::LocalVariable { + name, + ty: self.lookup_type.lookup(result_type_id)?.handle, + init, + }); + self.lookup_expression.insert( + result_id, + LookupExpression { + handle: expressions + .append(crate::Expression::LocalVariable(var_handle)), + type_id: result_type_id, + }, + ); + } + Op::Phi => { + inst.expect_at_least(3)?; + + let result_type_id = self.next()?; + let result_id = self.next()?; + + let name = format!("phi_{}", result_id); + let var_handle = local_arena.append(crate::LocalVariable { + name: Some(name), + ty: self.lookup_type.lookup(result_type_id)?.handle, + init: None, + }); + self.lookup_expression.insert( + result_id, + LookupExpression { + handle: expressions + .append(crate::Expression::LocalVariable(var_handle)), + type_id: result_type_id, + }, + ); + + let mut phi = PhiInstruction::default(); + phi.id = result_id; + for _ in 0..(inst.wc - 3) / 2 { + phi.variables.push((self.next()?, self.next()?)); + } + + phis.push(phi); + } + Op::AccessChain => { + struct AccessExpression { + base_handle: Handle<crate::Expression>, + type_id: spirv::Word, + } + inst.expect_at_least(4)?; + let result_type_id = self.next()?; + let result_id = self.next()?; + let base_id = self.next()?; + log::trace!("\t\t\tlooking up expr {:?}", base_id); + let mut acex = { + let expr = self.lookup_expression.lookup(base_id)?; + AccessExpression { + base_handle: expr.handle, + type_id: expr.type_id, + } + }; + for _ in 4..inst.wc { + let access_id = self.next()?; + log::trace!("\t\t\tlooking up index expr {:?}", access_id); + let index_expr = self.lookup_expression.lookup(access_id)?.clone(); + let index_type_handle = self.lookup_type.lookup(index_expr.type_id)?.handle; + match type_arena[index_type_handle].inner { + crate::TypeInner::Scalar { + kind: crate::ScalarKind::Uint, + .. + } + | crate::TypeInner::Scalar { + kind: crate::ScalarKind::Sint, + .. + } => (), + _ => return Err(Error::UnsupportedType(index_type_handle)), + } + log::trace!("\t\t\tlooking up type {:?}", acex.type_id); + let type_lookup = self.lookup_type.lookup(acex.type_id)?; + acex = match type_arena[type_lookup.handle].inner { + crate::TypeInner::Struct { .. } => { + let index = match expressions[index_expr.handle] { + crate::Expression::Constant(const_handle) => { + match const_arena[const_handle].inner { + crate::ConstantInner::Uint(v) => v as u32, + crate::ConstantInner::Sint(v) => v as u32, + _ => { + return Err(Error::InvalidAccess(index_expr.handle)) + } + } + } + _ => return Err(Error::InvalidAccess(index_expr.handle)), + }; + AccessExpression { + base_handle: expressions.append( + crate::Expression::AccessIndex { + base: acex.base_handle, + index, + }, + ), + type_id: *self + .lookup_member_type_id + .get(&(type_lookup.handle, index)) + .ok_or(Error::InvalidAccessType(acex.type_id))?, + } + } + crate::TypeInner::Array { .. } + | crate::TypeInner::Vector { .. } + | crate::TypeInner::Matrix { .. } => AccessExpression { + base_handle: expressions.append(crate::Expression::Access { + base: acex.base_handle, + index: index_expr.handle, + }), + type_id: type_lookup + .base_id + .ok_or(Error::InvalidAccessType(acex.type_id))?, + }, + _ => return Err(Error::UnsupportedType(type_lookup.handle)), + }; + } + + let lookup_expression = LookupExpression { + handle: acex.base_handle, + type_id: result_type_id, + }; + self.lookup_expression.insert(result_id, lookup_expression); + } + Op::CompositeExtract => { + inst.expect_at_least(4)?; + let result_type_id = self.next()?; + let result_id = self.next()?; + let base_id = self.next()?; + log::trace!("\t\t\tlooking up expr {:?}", base_id); + let mut lexp = { + let expr = self.lookup_expression.lookup(base_id)?; + LookupExpression { + handle: expr.handle, + type_id: expr.type_id, + } + }; + for _ in 4..inst.wc { + let index = self.next()?; + log::trace!("\t\t\tlooking up type {:?}", lexp.type_id); + let type_lookup = self.lookup_type.lookup(lexp.type_id)?; + let type_id = match type_arena[type_lookup.handle].inner { + crate::TypeInner::Struct { .. } => *self + .lookup_member_type_id + .get(&(type_lookup.handle, index)) + .ok_or(Error::InvalidAccessType(lexp.type_id))?, + crate::TypeInner::Array { .. } + | crate::TypeInner::Vector { .. } + | crate::TypeInner::Matrix { .. } => type_lookup + .base_id + .ok_or(Error::InvalidAccessType(lexp.type_id))?, + _ => return Err(Error::UnsupportedType(type_lookup.handle)), + }; + lexp = LookupExpression { + handle: expressions.append(crate::Expression::AccessIndex { + base: lexp.handle, + index, + }), + type_id, + }; + } + + self.lookup_expression.insert( + result_id, + LookupExpression { + handle: lexp.handle, + type_id: result_type_id, + }, + ); + } + Op::CompositeConstruct => { + inst.expect_at_least(3)?; + let result_type_id = self.next()?; + let id = self.next()?; + let mut components = Vec::with_capacity(inst.wc as usize - 2); + for _ in 3..inst.wc { + let comp_id = self.next()?; + log::trace!("\t\t\tlooking up expr {:?}", comp_id); + let lexp = self.lookup_expression.lookup(comp_id)?; + components.push(lexp.handle); + } + let expr = crate::Expression::Compose { + ty: self.lookup_type.lookup(result_type_id)?.handle, + components, + }; + self.lookup_expression.insert( + id, + LookupExpression { + handle: expressions.append(expr), + type_id: result_type_id, + }, + ); + } + Op::Load => { + inst.expect_at_least(4)?; + let result_type_id = self.next()?; + let result_id = self.next()?; + let pointer_id = self.next()?; + if inst.wc != 4 { + inst.expect(5)?; + let _memory_access = self.next()?; + } + let base_expr = self.lookup_expression.lookup(pointer_id)?.clone(); + self.lookup_expression.insert( + result_id, + LookupExpression { + handle: base_expr.handle, // pass-through pointers + type_id: result_type_id, + }, + ); + } + Op::Store => { + inst.expect_at_least(3)?; + let pointer_id = self.next()?; + let value_id = self.next()?; + if inst.wc != 3 { + inst.expect(4)?; + let _memory_access = self.next()?; + } + let base_expr = self.lookup_expression.lookup(pointer_id)?; + let value_expr = self.lookup_expression.lookup(value_id)?; + assignments.push(Assignment { + to: base_expr.handle, + value: value_expr.handle, + }); + } + // Arithmetic Instructions +, -, *, /, % + Op::SNegate | Op::FNegate => { + inst.expect(4)?; + self.parse_expr_unary_op(expressions, crate::UnaryOperator::Negate)?; + } + Op::IAdd | Op::FAdd => { + inst.expect(5)?; + self.parse_expr_binary_op(expressions, crate::BinaryOperator::Add)?; + } + Op::ISub | Op::FSub => { + inst.expect(5)?; + self.parse_expr_binary_op(expressions, crate::BinaryOperator::Subtract)?; + } + Op::IMul | Op::FMul => { + inst.expect(5)?; + self.parse_expr_binary_op(expressions, crate::BinaryOperator::Multiply)?; + } + Op::SDiv | Op::UDiv | Op::FDiv => { + inst.expect(5)?; + self.parse_expr_binary_op(expressions, crate::BinaryOperator::Divide)?; + } + Op::UMod | Op::FMod | Op::SRem | Op::FRem => { + inst.expect(5)?; + self.parse_expr_binary_op(expressions, crate::BinaryOperator::Modulo)?; + } + Op::VectorTimesScalar + | Op::VectorTimesMatrix + | Op::MatrixTimesScalar + | Op::MatrixTimesVector + | Op::MatrixTimesMatrix => { + inst.expect(5)?; + self.parse_expr_binary_op(expressions, crate::BinaryOperator::Multiply)?; + } + Op::Transpose => { + inst.expect(4)?; + let result_type_id = self.next()?; + let result_id = self.next()?; + let matrix_id = self.next()?; + let matrix_lexp = self.lookup_expression.lookup(matrix_id)?; + let expr = crate::Expression::Transpose(matrix_lexp.handle); + self.lookup_expression.insert( + result_id, + LookupExpression { + handle: expressions.append(expr), + type_id: result_type_id, + }, + ); + } + Op::Dot => { + inst.expect(5)?; + let result_type_id = self.next()?; + let result_id = self.next()?; + let left_id = self.next()?; + let right_id = self.next()?; + let left_lexp = self.lookup_expression.lookup(left_id)?; + let right_lexp = self.lookup_expression.lookup(right_id)?; + let expr = crate::Expression::DotProduct(left_lexp.handle, right_lexp.handle); + self.lookup_expression.insert( + result_id, + LookupExpression { + handle: expressions.append(expr), + type_id: result_type_id, + }, + ); + } + // Bitwise instructions + Op::Not => { + inst.expect(4)?; + self.parse_expr_unary_op(expressions, crate::UnaryOperator::Not)?; + } + Op::BitwiseOr => { + inst.expect(5)?; + self.parse_expr_binary_op(expressions, crate::BinaryOperator::InclusiveOr)?; + } + Op::BitwiseXor => { + inst.expect(5)?; + self.parse_expr_binary_op(expressions, crate::BinaryOperator::ExclusiveOr)?; + } + Op::BitwiseAnd => { + inst.expect(5)?; + self.parse_expr_binary_op(expressions, crate::BinaryOperator::And)?; + } + Op::ShiftRightLogical => { + inst.expect(5)?; + //TODO: convert input and result to usigned + self.parse_expr_binary_op(expressions, crate::BinaryOperator::ShiftRight)?; + } + Op::ShiftRightArithmetic => { + inst.expect(5)?; + //TODO: convert input and result to signed + self.parse_expr_binary_op(expressions, crate::BinaryOperator::ShiftRight)?; + } + Op::ShiftLeftLogical => { + inst.expect(5)?; + self.parse_expr_binary_op(expressions, crate::BinaryOperator::ShiftLeft)?; + } + // Sampling + Op::SampledImage => { + inst.expect(5)?; + let _result_type_id = self.next()?; + let result_id = self.next()?; + let image_id = self.next()?; + let sampler_id = self.next()?; + let image_lexp = self.lookup_expression.lookup(image_id)?; + let sampler_lexp = self.lookup_expression.lookup(sampler_id)?; + //TODO: compare the result type + self.lookup_sampled_image.insert( + result_id, + LookupSampledImage { + image: image_lexp.handle, + sampler: sampler_lexp.handle, + }, + ); + } + Op::ImageSampleImplicitLod | Op::ImageSampleExplicitLod => { + inst.expect_at_least(5)?; + let result_type_id = self.next()?; + let result_id = self.next()?; + let sampled_image_id = self.next()?; + let coordinate_id = self.next()?; + let si_lexp = self.lookup_sampled_image.lookup(sampled_image_id)?.clone(); + let coord_lexp = self.lookup_expression.lookup(coordinate_id)?.clone(); + let coord_type_handle = self.lookup_type.lookup(coord_lexp.type_id)?.handle; + + let sampler_type_handle = + reach_global_type(si_lexp.sampler, &expressions, global_arena) + .ok_or(Error::InvalidSamplerExpression(si_lexp.sampler))?; + let image_type_handle = + reach_global_type(si_lexp.image, &expressions, global_arena) + .ok_or(Error::InvalidImageExpression(si_lexp.image))?; + log::debug!( + "\t\t\tImage {:?} with sampler {:?}", + image_type_handle, + sampler_type_handle + ); + *self.handle_sampling.get_mut(&sampler_type_handle).unwrap() |= + SamplingFlags::REGULAR; + *self.handle_sampling.get_mut(&image_type_handle).unwrap() |= + SamplingFlags::REGULAR; + match type_arena[sampler_type_handle].inner { + crate::TypeInner::Sampler { comparison: false } => (), + _ => return Err(Error::InvalidSampleSampler(sampler_type_handle)), + }; + match type_arena[image_type_handle].inner { + //TODO: compare the result type + crate::TypeInner::Image { + dim, + arrayed, + class: + crate::ImageClass::Sampled { + kind: _, + multi: false, + }, + } => { + if !check_sample_coordinates( + &type_arena[coord_type_handle], + crate::ScalarKind::Float, + dim, + arrayed, + ) { + return Err(Error::InvalidSampleCoordinates(coord_type_handle)); + } + } + _ => return Err(Error::InvalidSampleImage(image_type_handle)), + }; + + let mut level = crate::SampleLevel::Auto; + let mut base_wc = 5; + if base_wc < inst.wc { + let image_ops = self.next()?; + base_wc += 1; + let mask = spirv::ImageOperands::from_bits_truncate(image_ops); + if mask.contains(spirv::ImageOperands::BIAS) { + let bias_expr = self.next()?; + let bias_handle = self.lookup_expression.lookup(bias_expr)?.handle; + level = crate::SampleLevel::Bias(bias_handle); + base_wc += 1; + } + if mask.contains(spirv::ImageOperands::LOD) { + let lod_expr = self.next()?; + let lod_handle = self.lookup_expression.lookup(lod_expr)?.handle; + level = crate::SampleLevel::Exact(lod_handle); + base_wc += 1; + } + for _ in base_wc..inst.wc { + self.next()?; + } + } + + let expr = crate::Expression::ImageSample { + image: si_lexp.image, + sampler: si_lexp.sampler, + coordinate: coord_lexp.handle, + level, + depth_ref: None, + }; + self.lookup_expression.insert( + result_id, + LookupExpression { + handle: expressions.append(expr), + type_id: result_type_id, + }, + ); + } + Op::ImageSampleDrefImplicitLod => { + inst.expect_at_least(6)?; + let result_type_id = self.next()?; + let result_id = self.next()?; + let sampled_image_id = self.next()?; + let coordinate_id = self.next()?; + let dref_id = self.next()?; + + let si_lexp = self.lookup_sampled_image.lookup(sampled_image_id)?; + let coord_lexp = self.lookup_expression.lookup(coordinate_id)?; + let coord_type_handle = self.lookup_type.lookup(coord_lexp.type_id)?.handle; + let sampler_type_handle = + reach_global_type(si_lexp.sampler, &expressions, global_arena) + .ok_or(Error::InvalidSamplerExpression(si_lexp.sampler))?; + let image_type_handle = + reach_global_type(si_lexp.image, &expressions, global_arena) + .ok_or(Error::InvalidImageExpression(si_lexp.image))?; + *self.handle_sampling.get_mut(&sampler_type_handle).unwrap() |= + SamplingFlags::COMPARISON; + *self.handle_sampling.get_mut(&image_type_handle).unwrap() |= + SamplingFlags::COMPARISON; + match type_arena[sampler_type_handle].inner { + crate::TypeInner::Sampler { comparison: true } => (), + _ => return Err(Error::InvalidSampleSampler(sampler_type_handle)), + }; + match type_arena[image_type_handle].inner { + //TODO: compare the result type + crate::TypeInner::Image { + dim, + arrayed, + class: crate::ImageClass::Depth, + } => { + if !check_sample_coordinates( + &type_arena[coord_type_handle], + crate::ScalarKind::Float, + dim, + arrayed, + ) { + return Err(Error::InvalidSampleCoordinates(coord_type_handle)); + } + } + _ => return Err(Error::InvalidSampleImage(image_type_handle)), + }; + + let dref_lexp = self.lookup_expression.lookup(dref_id)?; + let dref_type_handle = self.lookup_type.lookup(dref_lexp.type_id)?.handle; + match type_arena[dref_type_handle].inner { + crate::TypeInner::Scalar { + kind: crate::ScalarKind::Float, + width: _, + } => (), + _ => return Err(Error::InvalidDepthReference(dref_type_handle)), + } + + let expr = crate::Expression::ImageSample { + image: si_lexp.image, + sampler: si_lexp.sampler, + coordinate: coord_lexp.handle, + level: crate::SampleLevel::Auto, + depth_ref: Some(dref_lexp.handle), + }; + self.lookup_expression.insert( + result_id, + LookupExpression { + handle: expressions.append(expr), + type_id: result_type_id, + }, + ); + } + Op::Select => { + inst.expect(6)?; + let result_type_id = self.next()?; + let result_id = self.next()?; + let condition = self.next()?; + let o1_id = self.next()?; + let o2_id = self.next()?; + + let cond_lexp = self.lookup_expression.lookup(condition)?; + let o1_lexp = self.lookup_expression.lookup(o1_id)?; + let o2_lexp = self.lookup_expression.lookup(o2_id)?; + + let expr = crate::Expression::Select { + condition: cond_lexp.handle, + accept: o1_lexp.handle, + reject: o2_lexp.handle, + }; + self.lookup_expression.insert( + result_id, + LookupExpression { + handle: expressions.append(expr), + type_id: result_type_id, + }, + ); + } + Op::VectorShuffle => { + inst.expect_at_least(5)?; + let result_type_id = self.next()?; + let result_id = self.next()?; + let v1_id = self.next()?; + let v2_id = self.next()?; + + let v1_lexp = self.lookup_expression.lookup(v1_id)?; + let v1_lty = self.lookup_type.lookup(v1_lexp.type_id)?; + let n1 = match type_arena[v1_lty.handle].inner { + crate::TypeInner::Vector { size, .. } => size as u8, + _ => return Err(Error::InvalidInnerType(v1_lexp.type_id)), + }; + let v1_handle = v1_lexp.handle; + let v2_lexp = self.lookup_expression.lookup(v2_id)?; + let v2_lty = self.lookup_type.lookup(v2_lexp.type_id)?; + let n2 = match type_arena[v2_lty.handle].inner { + crate::TypeInner::Vector { size, .. } => size as u8, + _ => return Err(Error::InvalidInnerType(v2_lexp.type_id)), + }; + let v2_handle = v2_lexp.handle; + + let mut components = Vec::with_capacity(inst.wc as usize - 5); + for _ in 0..components.capacity() { + let index = self.next()?; + let expr = if index < n1 as u32 { + crate::Expression::AccessIndex { + base: v1_handle, + index, + } + } else if index < n1 as u32 + n2 as u32 { + crate::Expression::AccessIndex { + base: v2_handle, + index: index - n1 as u32, + } + } else { + return Err(Error::InvalidAccessIndex(index)); + }; + components.push(expressions.append(expr)); + } + let expr = crate::Expression::Compose { + ty: self.lookup_type.lookup(result_type_id)?.handle, + components, + }; + self.lookup_expression.insert( + result_id, + LookupExpression { + handle: expressions.append(expr), + type_id: result_type_id, + }, + ); + } + Op::Bitcast + | Op::ConvertSToF + | Op::ConvertUToF + | Op::ConvertFToU + | Op::ConvertFToS => { + inst.expect_at_least(4)?; + let result_type_id = self.next()?; + let result_id = self.next()?; + let value_id = self.next()?; + + let value_lexp = self.lookup_expression.lookup(value_id)?; + let ty_lookup = self.lookup_type.lookup(result_type_id)?; + let kind = type_arena[ty_lookup.handle] + .inner + .scalar_kind() + .ok_or(Error::InvalidAsType(ty_lookup.handle))?; + + let expr = crate::Expression::As { + expr: value_lexp.handle, + kind, + convert: inst.op != Op::Bitcast, + }; + self.lookup_expression.insert( + result_id, + LookupExpression { + handle: expressions.append(expr), + type_id: result_type_id, + }, + ); + } + Op::FunctionCall => { + inst.expect_at_least(4)?; + let result_type_id = self.next()?; + let result_id = self.next()?; + let func_id = self.next()?; + + let mut arguments = Vec::with_capacity(inst.wc as usize - 4); + for _ in 0..arguments.capacity() { + let arg_id = self.next()?; + arguments.push(self.lookup_expression.lookup(arg_id)?.handle); + } + let expr = crate::Expression::Call { + // will be replaced by `Local()` after all the functions are parsed + origin: crate::FunctionOrigin::External(String::new()), + arguments, + }; + let expr_handle = expressions.append(expr); + local_function_calls.insert(expr_handle, func_id); + self.lookup_expression.insert( + result_id, + LookupExpression { + handle: expr_handle, + type_id: result_type_id, + }, + ); + } + Op::ExtInst => { + let base_wc = 5; + inst.expect_at_least(base_wc)?; + let result_type_id = self.next()?; + let result_id = self.next()?; + let set_id = self.next()?; + if Some(set_id) != self.ext_glsl_id { + return Err(Error::UnsupportedExtInstSet(set_id)); + } + let inst_id = self.next()?; + let name = match spirv::GLOp::from_u32(inst_id) { + Some(spirv::GLOp::FAbs) | Some(spirv::GLOp::SAbs) => { + inst.expect(base_wc + 1)?; + "abs" + } + Some(spirv::GLOp::FSign) | Some(spirv::GLOp::SSign) => { + inst.expect(base_wc + 1)?; + "sign" + } + Some(spirv::GLOp::Floor) => { + inst.expect(base_wc + 1)?; + "floor" + } + Some(spirv::GLOp::Ceil) => { + inst.expect(base_wc + 1)?; + "ceil" + } + Some(spirv::GLOp::Fract) => { + inst.expect(base_wc + 1)?; + "fract" + } + Some(spirv::GLOp::Sin) => { + inst.expect(base_wc + 1)?; + "sin" + } + Some(spirv::GLOp::Cos) => { + inst.expect(base_wc + 1)?; + "cos" + } + Some(spirv::GLOp::Tan) => { + inst.expect(base_wc + 1)?; + "tan" + } + Some(spirv::GLOp::Atan2) => { + inst.expect(base_wc + 2)?; + "atan2" + } + Some(spirv::GLOp::Pow) => { + inst.expect(base_wc + 2)?; + "pow" + } + Some(spirv::GLOp::MatrixInverse) => { + inst.expect(base_wc + 1)?; + "inverse" + } + Some(spirv::GLOp::FMix) => { + inst.expect(base_wc + 3)?; + "mix" + } + Some(spirv::GLOp::Step) => { + inst.expect(base_wc + 2)?; + "step" + } + Some(spirv::GLOp::SmoothStep) => { + inst.expect(base_wc + 3)?; + "smoothstep" + } + Some(spirv::GLOp::FMin) => { + inst.expect(base_wc + 2)?; + "min" + } + Some(spirv::GLOp::FMax) => { + inst.expect(base_wc + 2)?; + "max" + } + Some(spirv::GLOp::FClamp) => { + inst.expect(base_wc + 3)?; + "clamp" + } + Some(spirv::GLOp::Length) => { + inst.expect(base_wc + 1)?; + "length" + } + Some(spirv::GLOp::Distance) => { + inst.expect(base_wc + 2)?; + "distance" + } + Some(spirv::GLOp::Cross) => { + inst.expect(base_wc + 2)?; + "cross" + } + Some(spirv::GLOp::Normalize) => { + inst.expect(base_wc + 1)?; + "normalize" + } + Some(spirv::GLOp::Reflect) => { + inst.expect(base_wc + 2)?; + "reflect" + } + _ => return Err(Error::UnsupportedExtInst(inst_id)), + }; + + let mut arguments = Vec::with_capacity((inst.wc - base_wc) as usize); + for _ in 0..arguments.capacity() { + let arg_id = self.next()?; + arguments.push(self.lookup_expression.lookup(arg_id)?.handle); + } + let expr = crate::Expression::Call { + origin: crate::FunctionOrigin::External(name.to_string()), + arguments, + }; + self.lookup_expression.insert( + result_id, + LookupExpression { + handle: expressions.append(expr), + type_id: result_type_id, + }, + ); + } + // Relational and Logical Instructions + Op::LogicalNot => { + inst.expect(4)?; + self.parse_expr_unary_op(expressions, crate::UnaryOperator::Not)?; + } + op if inst.op >= Op::IEqual && inst.op <= Op::FUnordGreaterThanEqual => { + inst.expect(5)?; + self.parse_expr_binary_op(expressions, map_binary_operator(op)?)?; + } + Op::Kill => { + inst.expect(1)?; + break Terminator::Kill; + } + Op::Unreachable => { + inst.expect(1)?; + break Terminator::Unreachable; + } + Op::Return => { + inst.expect(1)?; + break Terminator::Return { value: None }; + } + Op::ReturnValue => { + inst.expect(2)?; + let value_id = self.next()?; + let value_lexp = self.lookup_expression.lookup(value_id)?; + break Terminator::Return { + value: Some(value_lexp.handle), + }; + } + Op::Branch => { + inst.expect(2)?; + let target_id = self.next()?; + break Terminator::Branch { target_id }; + } + Op::BranchConditional => { + inst.expect_at_least(4)?; + + let condition_id = self.next()?; + let condition = self.lookup_expression.lookup(condition_id)?.handle; + + let true_id = self.next()?; + let false_id = self.next()?; + + break Terminator::BranchConditional { + condition, + true_id, + false_id, + }; + } + Op::Switch => { + inst.expect_at_least(3)?; + + let selector = self.next()?; + let selector = self.lookup_expression[&selector].handle; + let default = self.next()?; + + let mut targets = Vec::new(); + for _ in 0..(inst.wc - 3) / 2 { + let literal = self.next()?; + let target = self.next()?; + targets.push((literal as i32, target)); + } + + break Terminator::Switch { + selector, + default, + targets, + }; + } + Op::SelectionMerge => { + inst.expect(3)?; + let merge_block_id = self.next()?; + // TODO: Selection Control Mask + let _selection_control = self.next()?; + let continue_block_id = None; + merge = Some(MergeInstruction { + merge_block_id, + continue_block_id, + }); + } + Op::LoopMerge => { + inst.expect_at_least(4)?; + let merge_block_id = self.next()?; + let continue_block_id = Some(self.next()?); + + // TODO: Loop Control Parameters + for _ in 0..inst.wc - 3 { + self.next()?; + } + + merge = Some(MergeInstruction { + merge_block_id, + continue_block_id, + }); + } + _ => return Err(Error::UnsupportedInstruction(self.state, inst.op)), + } + }; + + let mut block = Vec::new(); + for assignment in assignments.iter() { + block.push(crate::Statement::Store { + pointer: assignment.to, + value: assignment.value, + }); + } + + Ok(ControlFlowNode { + id: block_id, + ty: None, + phis, + block, + terminator, + merge, + }) + } + + fn make_expression_storage(&mut self) -> Arena<crate::Expression> { + let mut expressions = Arena::new(); + #[allow(clippy::panic)] + { + assert!(self.lookup_expression.is_empty()); + } + // register global variables + for (&id, var) in self.lookup_variable.iter() { + let handle = expressions.append(crate::Expression::GlobalVariable(var.handle)); + self.lookup_expression.insert( + id, + LookupExpression { + type_id: var.type_id, + handle, + }, + ); + } + // register constants + for (&id, con) in self.lookup_constant.iter() { + let handle = expressions.append(crate::Expression::Constant(con.handle)); + self.lookup_expression.insert( + id, + LookupExpression { + type_id: con.type_id, + handle, + }, + ); + } + // done + expressions + } + + fn switch(&mut self, state: ModuleState, op: spirv::Op) -> Result<(), Error> { + if state < self.state { + Err(Error::UnsupportedInstruction(self.state, op)) + } else { + self.state = state; + Ok(()) + } + } + + pub fn parse(mut self) -> Result<crate::Module, Error> { + let mut module = { + if self.next()? != spirv::MAGIC_NUMBER { + return Err(Error::InvalidHeader); + } + let _version_raw = self.next()?.to_le_bytes(); + let _generator = self.next()?; + let _bound = self.next()?; + let _schema = self.next()?; + crate::Module::generate_empty() + }; + + while let Ok(inst) = self.next_inst() { + use spirv::Op; + log::debug!("\t{:?} [{}]", inst.op, inst.wc); + match inst.op { + Op::Capability => self.parse_capability(inst), + Op::Extension => self.parse_extension(inst), + Op::ExtInstImport => self.parse_ext_inst_import(inst), + Op::MemoryModel => self.parse_memory_model(inst), + Op::EntryPoint => self.parse_entry_point(inst), + Op::ExecutionMode => self.parse_execution_mode(inst), + Op::Source => self.parse_source(inst), + Op::SourceExtension => self.parse_source_extension(inst), + Op::Name => self.parse_name(inst), + Op::MemberName => self.parse_member_name(inst), + Op::Decorate => self.parse_decorate(inst), + Op::MemberDecorate => self.parse_member_decorate(inst), + Op::TypeVoid => self.parse_type_void(inst), + Op::TypeBool => self.parse_type_bool(inst, &mut module), + Op::TypeInt => self.parse_type_int(inst, &mut module), + Op::TypeFloat => self.parse_type_float(inst, &mut module), + Op::TypeVector => self.parse_type_vector(inst, &mut module), + Op::TypeMatrix => self.parse_type_matrix(inst, &mut module), + Op::TypeFunction => self.parse_type_function(inst), + Op::TypePointer => self.parse_type_pointer(inst, &mut module), + Op::TypeArray => self.parse_type_array(inst, &mut module), + Op::TypeRuntimeArray => self.parse_type_runtime_array(inst, &mut module), + Op::TypeStruct => self.parse_type_struct(inst, &mut module), + Op::TypeImage => self.parse_type_image(inst, &mut module), + Op::TypeSampledImage => self.parse_type_sampled_image(inst), + Op::TypeSampler => self.parse_type_sampler(inst, &mut module), + Op::Constant | Op::SpecConstant => self.parse_constant(inst, &mut module), + Op::ConstantComposite => self.parse_composite_constant(inst, &mut module), + Op::Variable => self.parse_global_variable(inst, &mut module), + Op::Function => self.parse_function(inst, &mut module), + _ => Err(Error::UnsupportedInstruction(self.state, inst.op)), //TODO + }?; + } + + // Check all the images and samplers to have consistent comparison property. + for (handle, flags) in self.handle_sampling.drain() { + if !flags.contains(SamplingFlags::COMPARISON) { + continue; + } + if flags == SamplingFlags::all() { + return Err(Error::InconsistentComparisonSampling(handle)); + } + let ty = module.types.get_mut(handle); + match ty.inner { + crate::TypeInner::Sampler { ref mut comparison } => { + #[allow(clippy::panic)] + { + assert!(!*comparison) + }; + *comparison = true; + } + _ => { + return Err(Error::UnexpectedComparisonType(handle)); + } + } + } + + for dfc in self.deferred_function_calls.drain(..) { + let dst_handle = *self.lookup_function.lookup(dfc.dst_id)?; + let fun = match dfc.source { + DeferredSource::Function(fun_handle) => module.functions.get_mut(fun_handle), + DeferredSource::EntryPoint(stage, name) => { + &mut module + .entry_points + .get_mut(&(stage, name)) + .unwrap() + .function + } + }; + match *fun.expressions.get_mut(dfc.expr_handle) { + crate::Expression::Call { + ref mut origin, + arguments: _, + } => *origin = crate::FunctionOrigin::Local(dst_handle), + _ => unreachable!(), + } + } + + if !self.future_decor.is_empty() { + log::warn!("Unused item decorations: {:?}", self.future_decor); + self.future_decor.clear(); + } + if !self.future_member_decor.is_empty() { + log::warn!("Unused member decorations: {:?}", self.future_member_decor); + self.future_member_decor.clear(); + } + + Ok(module) + } + + fn parse_capability(&mut self, inst: Instruction) -> Result<(), Error> { + self.switch(ModuleState::Capability, inst.op)?; + inst.expect(2)?; + let capability = self.next()?; + let cap = + spirv::Capability::from_u32(capability).ok_or(Error::UnknownCapability(capability))?; + if !SUPPORTED_CAPABILITIES.contains(&cap) { + return Err(Error::UnsupportedCapability(cap)); + } + Ok(()) + } + + fn parse_extension(&mut self, inst: Instruction) -> Result<(), Error> { + self.switch(ModuleState::Extension, inst.op)?; + inst.expect_at_least(2)?; + let (name, left) = self.next_string(inst.wc - 1)?; + if left != 0 { + return Err(Error::InvalidOperand); + } + if !SUPPORTED_EXTENSIONS.contains(&name.as_str()) { + return Err(Error::UnsupportedExtension(name)); + } + Ok(()) + } + + fn parse_ext_inst_import(&mut self, inst: Instruction) -> Result<(), Error> { + self.switch(ModuleState::Extension, inst.op)?; + inst.expect_at_least(3)?; + let result_id = self.next()?; + let (name, left) = self.next_string(inst.wc - 2)?; + if left != 0 { + return Err(Error::InvalidOperand); + } + if !SUPPORTED_EXT_SETS.contains(&name.as_str()) { + return Err(Error::UnsupportedExtSet(name)); + } + self.ext_glsl_id = Some(result_id); + Ok(()) + } + + fn parse_memory_model(&mut self, inst: Instruction) -> Result<(), Error> { + self.switch(ModuleState::MemoryModel, inst.op)?; + inst.expect(3)?; + let _addressing_model = self.next()?; + let _memory_model = self.next()?; + Ok(()) + } + + fn parse_entry_point(&mut self, inst: Instruction) -> Result<(), Error> { + self.switch(ModuleState::EntryPoint, inst.op)?; + inst.expect_at_least(4)?; + let exec_model = self.next()?; + let exec_model = spirv::ExecutionModel::from_u32(exec_model) + .ok_or(Error::UnsupportedExecutionModel(exec_model))?; + let function_id = self.next()?; + let (name, left) = self.next_string(inst.wc - 3)?; + let ep = EntryPoint { + stage: match exec_model { + spirv::ExecutionModel::Vertex => crate::ShaderStage::Vertex, + spirv::ExecutionModel::Fragment => crate::ShaderStage::Fragment, + spirv::ExecutionModel::GLCompute => crate::ShaderStage::Compute, + _ => return Err(Error::UnsupportedExecutionModel(exec_model as u32)), + }, + name, + early_depth_test: None, + workgroup_size: [0; 3], + function_id, + variable_ids: self.data.by_ref().take(left as usize).collect(), + }; + self.lookup_entry_point.insert(function_id, ep); + Ok(()) + } + + fn parse_execution_mode(&mut self, inst: Instruction) -> Result<(), Error> { + use spirv::ExecutionMode; + + self.switch(ModuleState::ExecutionMode, inst.op)?; + inst.expect_at_least(3)?; + + let ep_id = self.next()?; + let mode_id = self.next()?; + let args: Vec<spirv::Word> = self.data.by_ref().take(inst.wc as usize - 3).collect(); + + let ep = self + .lookup_entry_point + .get_mut(&ep_id) + .ok_or(Error::InvalidId(ep_id))?; + let mode = spirv::ExecutionMode::from_u32(mode_id) + .ok_or(Error::UnsupportedExecutionMode(mode_id))?; + + match mode { + ExecutionMode::EarlyFragmentTests => { + if ep.early_depth_test.is_none() { + ep.early_depth_test = Some(crate::EarlyDepthTest { conservative: None }); + } + } + ExecutionMode::DepthUnchanged => { + ep.early_depth_test = Some(crate::EarlyDepthTest { + conservative: Some(crate::ConservativeDepth::Unchanged), + }); + } + ExecutionMode::DepthGreater => { + ep.early_depth_test = Some(crate::EarlyDepthTest { + conservative: Some(crate::ConservativeDepth::GreaterEqual), + }); + } + ExecutionMode::DepthLess => { + ep.early_depth_test = Some(crate::EarlyDepthTest { + conservative: Some(crate::ConservativeDepth::LessEqual), + }); + } + ExecutionMode::DepthReplacing => { + // Ignored because it can be deduced from the IR. + } + ExecutionMode::OriginUpperLeft => { + // Ignored because the other option (OriginLowerLeft) is not valid in Vulkan mode. + } + ExecutionMode::LocalSize => { + ep.workgroup_size = [args[0], args[1], args[2]]; + } + _ => { + return Err(Error::UnsupportedExecutionMode(mode_id)); + } + } + + Ok(()) + } + + fn parse_source(&mut self, inst: Instruction) -> Result<(), Error> { + self.switch(ModuleState::Source, inst.op)?; + for _ in 1..inst.wc { + let _ = self.next()?; + } + Ok(()) + } + + fn parse_source_extension(&mut self, inst: Instruction) -> Result<(), Error> { + self.switch(ModuleState::Source, inst.op)?; + inst.expect_at_least(2)?; + let (_name, _) = self.next_string(inst.wc - 1)?; + Ok(()) + } + + fn parse_name(&mut self, inst: Instruction) -> Result<(), Error> { + self.switch(ModuleState::Name, inst.op)?; + inst.expect_at_least(3)?; + let id = self.next()?; + let (name, left) = self.next_string(inst.wc - 2)?; + if left != 0 { + return Err(Error::InvalidOperand); + } + self.future_decor.entry(id).or_default().name = Some(name); + Ok(()) + } + + fn parse_member_name(&mut self, inst: Instruction) -> Result<(), Error> { + self.switch(ModuleState::Name, inst.op)?; + inst.expect_at_least(4)?; + let id = self.next()?; + let member = self.next()?; + let (name, left) = self.next_string(inst.wc - 3)?; + if left != 0 { + return Err(Error::InvalidOperand); + } + + self.future_member_decor + .entry((id, member)) + .or_default() + .name = Some(name); + Ok(()) + } + + fn parse_decorate(&mut self, inst: Instruction) -> Result<(), Error> { + self.switch(ModuleState::Annotation, inst.op)?; + inst.expect_at_least(3)?; + let id = self.next()?; + let mut dec = self.future_decor.remove(&id).unwrap_or_default(); + self.next_decoration(inst, 2, &mut dec)?; + self.future_decor.insert(id, dec); + Ok(()) + } + + fn parse_member_decorate(&mut self, inst: Instruction) -> Result<(), Error> { + self.switch(ModuleState::Annotation, inst.op)?; + inst.expect_at_least(4)?; + let id = self.next()?; + let member = self.next()?; + + let mut dec = self + .future_member_decor + .remove(&(id, member)) + .unwrap_or_default(); + self.next_decoration(inst, 3, &mut dec)?; + self.future_member_decor.insert((id, member), dec); + Ok(()) + } + + fn parse_type_void(&mut self, inst: Instruction) -> Result<(), Error> { + self.switch(ModuleState::Type, inst.op)?; + inst.expect(2)?; + let id = self.next()?; + self.lookup_void_type.insert(id); + Ok(()) + } + + fn parse_type_bool( + &mut self, + inst: Instruction, + module: &mut crate::Module, + ) -> Result<(), Error> { + self.switch(ModuleState::Type, inst.op)?; + inst.expect(2)?; + let id = self.next()?; + let inner = crate::TypeInner::Scalar { + kind: crate::ScalarKind::Bool, + width: 1, + }; + self.lookup_type.insert( + id, + LookupType { + handle: module.types.append(crate::Type { + name: self.future_decor.remove(&id).and_then(|dec| dec.name), + inner, + }), + base_id: None, + }, + ); + Ok(()) + } + + fn parse_type_int( + &mut self, + inst: Instruction, + module: &mut crate::Module, + ) -> Result<(), Error> { + self.switch(ModuleState::Type, inst.op)?; + inst.expect(4)?; + let id = self.next()?; + let width = self.next()?; + let sign = self.next()?; + let inner = crate::TypeInner::Scalar { + kind: match sign { + 0 => crate::ScalarKind::Uint, + 1 => crate::ScalarKind::Sint, + _ => return Err(Error::InvalidSign(sign)), + }, + width: map_width(width)?, + }; + self.lookup_type.insert( + id, + LookupType { + handle: module.types.append(crate::Type { + name: self.future_decor.remove(&id).and_then(|dec| dec.name), + inner, + }), + base_id: None, + }, + ); + Ok(()) + } + + fn parse_type_float( + &mut self, + inst: Instruction, + module: &mut crate::Module, + ) -> Result<(), Error> { + self.switch(ModuleState::Type, inst.op)?; + inst.expect(3)?; + let id = self.next()?; + let width = self.next()?; + let inner = crate::TypeInner::Scalar { + kind: crate::ScalarKind::Float, + width: map_width(width)?, + }; + self.lookup_type.insert( + id, + LookupType { + handle: module.types.append(crate::Type { + name: self.future_decor.remove(&id).and_then(|dec| dec.name), + inner, + }), + base_id: None, + }, + ); + Ok(()) + } + + fn parse_type_vector( + &mut self, + inst: Instruction, + module: &mut crate::Module, + ) -> Result<(), Error> { + self.switch(ModuleState::Type, inst.op)?; + inst.expect(4)?; + let id = self.next()?; + let type_id = self.next()?; + let type_lookup = self.lookup_type.lookup(type_id)?; + let (kind, width) = match module.types[type_lookup.handle].inner { + crate::TypeInner::Scalar { kind, width } => (kind, width), + _ => return Err(Error::InvalidInnerType(type_id)), + }; + let component_count = self.next()?; + let inner = crate::TypeInner::Vector { + size: map_vector_size(component_count)?, + kind, + width, + }; + self.lookup_type.insert( + id, + LookupType { + handle: module.types.append(crate::Type { + name: self.future_decor.remove(&id).and_then(|dec| dec.name), + inner, + }), + base_id: Some(type_id), + }, + ); + Ok(()) + } + + fn parse_type_matrix( + &mut self, + inst: Instruction, + module: &mut crate::Module, + ) -> Result<(), Error> { + self.switch(ModuleState::Type, inst.op)?; + inst.expect(4)?; + let id = self.next()?; + let vector_type_id = self.next()?; + let num_columns = self.next()?; + let vector_type_lookup = self.lookup_type.lookup(vector_type_id)?; + let inner = match module.types[vector_type_lookup.handle].inner { + crate::TypeInner::Vector { size, width, .. } => crate::TypeInner::Matrix { + columns: map_vector_size(num_columns)?, + rows: size, + width, + }, + _ => return Err(Error::InvalidInnerType(vector_type_id)), + }; + self.lookup_type.insert( + id, + LookupType { + handle: module.types.append(crate::Type { + name: self.future_decor.remove(&id).and_then(|dec| dec.name), + inner, + }), + base_id: Some(vector_type_id), + }, + ); + Ok(()) + } + + fn parse_type_function(&mut self, inst: Instruction) -> Result<(), Error> { + self.switch(ModuleState::Type, inst.op)?; + inst.expect_at_least(3)?; + let id = self.next()?; + let return_type_id = self.next()?; + let parameter_type_ids = self.data.by_ref().take(inst.wc as usize - 3).collect(); + self.lookup_function_type.insert( + id, + LookupFunctionType { + parameter_type_ids, + return_type_id, + }, + ); + Ok(()) + } + + fn parse_type_pointer( + &mut self, + inst: Instruction, + _module: &mut crate::Module, + ) -> Result<(), Error> { + self.switch(ModuleState::Type, inst.op)?; + inst.expect(4)?; + let id = self.next()?; + let _storage = self.next()?; + let type_id = self.next()?; + let type_lookup = self.lookup_type.lookup(type_id)?.clone(); + self.lookup_type.insert(id, type_lookup); // don't register pointers in the IR + Ok(()) + } + + fn parse_type_array( + &mut self, + inst: Instruction, + module: &mut crate::Module, + ) -> Result<(), Error> { + self.switch(ModuleState::Type, inst.op)?; + inst.expect(4)?; + let id = self.next()?; + let type_id = self.next()?; + let length_id = self.next()?; + let length_const = self.lookup_constant.lookup(length_id)?; + + let decor = self.future_decor.remove(&id); + let inner = crate::TypeInner::Array { + base: self.lookup_type.lookup(type_id)?.handle, + size: crate::ArraySize::Constant(length_const.handle), + stride: decor.as_ref().and_then(|dec| dec.array_stride), + }; + self.lookup_type.insert( + id, + LookupType { + handle: module.types.append(crate::Type { + name: decor.and_then(|dec| dec.name), + inner, + }), + base_id: Some(type_id), + }, + ); + Ok(()) + } + + fn parse_type_runtime_array( + &mut self, + inst: Instruction, + module: &mut crate::Module, + ) -> Result<(), Error> { + self.switch(ModuleState::Type, inst.op)?; + inst.expect(3)?; + let id = self.next()?; + let type_id = self.next()?; + + let decor = self.future_decor.remove(&id); + let inner = crate::TypeInner::Array { + base: self.lookup_type.lookup(type_id)?.handle, + size: crate::ArraySize::Dynamic, + stride: decor.as_ref().and_then(|dec| dec.array_stride), + }; + self.lookup_type.insert( + id, + LookupType { + handle: module.types.append(crate::Type { + name: decor.and_then(|dec| dec.name), + inner, + }), + base_id: Some(type_id), + }, + ); + Ok(()) + } + + fn parse_type_struct( + &mut self, + inst: Instruction, + module: &mut crate::Module, + ) -> Result<(), Error> { + self.switch(ModuleState::Type, inst.op)?; + inst.expect_at_least(2)?; + let id = self.next()?; + let parent_decor = self.future_decor.remove(&id); + let is_buffer_block = parent_decor + .as_ref() + .map_or(false, |decor| match decor.block { + Some(Block { buffer }) => buffer, + _ => false, + }); + + let mut members = Vec::with_capacity(inst.wc as usize - 2); + let mut member_type_ids = Vec::with_capacity(members.capacity()); + for i in 0..u32::from(inst.wc) - 2 { + let type_id = self.next()?; + member_type_ids.push(type_id); + let ty = self.lookup_type.lookup(type_id)?.handle; + let decor = self + .future_member_decor + .remove(&(id, i)) + .unwrap_or_default(); + let origin = decor.get_origin()?; + members.push(crate::StructMember { + name: decor.name, + origin, + ty, + }); + } + let inner = crate::TypeInner::Struct { members }; + let ty_handle = module.types.append(crate::Type { + name: parent_decor.and_then(|dec| dec.name), + inner, + }); + + if is_buffer_block { + self.lookup_storage_buffer_types.insert(ty_handle); + } + for (i, type_id) in member_type_ids.into_iter().enumerate() { + self.lookup_member_type_id + .insert((ty_handle, i as u32), type_id); + } + self.lookup_type.insert( + id, + LookupType { + handle: ty_handle, + base_id: None, + }, + ); + Ok(()) + } + + fn parse_type_image( + &mut self, + inst: Instruction, + module: &mut crate::Module, + ) -> Result<(), Error> { + self.switch(ModuleState::Type, inst.op)?; + inst.expect(9)?; + + let id = self.next()?; + let sample_type_id = self.next()?; + let dim = self.next()?; + let _is_depth = self.next()?; + let is_array = self.next()? != 0; + let is_msaa = self.next()? != 0; + let _is_sampled = self.next()?; + let format = self.next()?; + + let base_handle = self.lookup_type.lookup(sample_type_id)?.handle; + let kind = module.types[base_handle] + .inner + .scalar_kind() + .ok_or(Error::InvalidImageBaseType(base_handle))?; + + let class = if format != 0 { + crate::ImageClass::Storage(map_image_format(format)?) + } else { + crate::ImageClass::Sampled { + kind, + multi: is_msaa, + } + }; + + let decor = self.future_decor.remove(&id).unwrap_or_default(); + + let inner = crate::TypeInner::Image { + class, + dim: map_image_dim(dim)?, + arrayed: is_array, + }; + let handle = module.types.append(crate::Type { + name: decor.name, + inner, + }); + log::debug!("\t\ttracking {:?} for sampling properties", handle); + self.handle_sampling.insert(handle, SamplingFlags::empty()); + self.lookup_type.insert( + id, + LookupType { + handle, + base_id: Some(sample_type_id), + }, + ); + Ok(()) + } + + fn parse_type_sampled_image(&mut self, inst: Instruction) -> Result<(), Error> { + self.switch(ModuleState::Type, inst.op)?; + inst.expect(3)?; + let id = self.next()?; + let image_id = self.next()?; + self.lookup_type.insert( + id, + LookupType { + handle: self.lookup_type.lookup(image_id)?.handle, + base_id: Some(image_id), + }, + ); + Ok(()) + } + + fn parse_type_sampler( + &mut self, + inst: Instruction, + module: &mut crate::Module, + ) -> Result<(), Error> { + self.switch(ModuleState::Type, inst.op)?; + inst.expect(2)?; + let id = self.next()?; + let decor = self.future_decor.remove(&id).unwrap_or_default(); + // The comparison bit is temporary, will be overwritten based on the + // accumulated sampling flags at the end. + let inner = crate::TypeInner::Sampler { comparison: false }; + let handle = module.types.append(crate::Type { + name: decor.name, + inner, + }); + log::debug!("\t\ttracking {:?} for sampling properties", handle); + self.handle_sampling.insert(handle, SamplingFlags::empty()); + self.lookup_type.insert( + id, + LookupType { + handle, + base_id: None, + }, + ); + Ok(()) + } + + fn parse_constant( + &mut self, + inst: Instruction, + module: &mut crate::Module, + ) -> Result<(), Error> { + self.switch(ModuleState::Type, inst.op)?; + inst.expect_at_least(3)?; + let type_id = self.next()?; + let id = self.next()?; + let type_lookup = self.lookup_type.lookup(type_id)?; + let ty = type_lookup.handle; + let inner = match module.types[ty].inner { + crate::TypeInner::Scalar { + kind: crate::ScalarKind::Uint, + width, + } => { + let low = self.next()?; + let high = if width > 4 { + inst.expect(4)?; + self.next()? + } else { + 0 + }; + crate::ConstantInner::Uint((u64::from(high) << 32) | u64::from(low)) + } + crate::TypeInner::Scalar { + kind: crate::ScalarKind::Sint, + width, + } => { + use std::cmp::Ordering; + let low = self.next()?; + let high = match width.cmp(&4) { + Ordering::Less => return Err(Error::InvalidTypeWidth(u32::from(width))), + Ordering::Greater => { + inst.expect(4)?; + self.next()? + } + Ordering::Equal => 0, + }; + crate::ConstantInner::Sint(((u64::from(high) << 32) | u64::from(low)) as i64) + } + crate::TypeInner::Scalar { + kind: crate::ScalarKind::Float, + width, + } => { + let low = self.next()?; + let extended = match width { + 4 => f64::from(f32::from_bits(low)), + 8 => { + inst.expect(4)?; + let high = self.next()?; + f64::from_bits((u64::from(high) << 32) | u64::from(low)) + } + _ => return Err(Error::InvalidTypeWidth(u32::from(width))), + }; + crate::ConstantInner::Float(extended) + } + _ => return Err(Error::UnsupportedType(type_lookup.handle)), + }; + self.lookup_constant.insert( + id, + LookupConstant { + handle: module.constants.append(crate::Constant { + name: self.future_decor.remove(&id).and_then(|dec| dec.name), + specialization: None, //TODO + inner, + ty, + }), + type_id, + }, + ); + Ok(()) + } + + fn parse_composite_constant( + &mut self, + inst: Instruction, + module: &mut crate::Module, + ) -> Result<(), Error> { + self.switch(ModuleState::Type, inst.op)?; + inst.expect_at_least(3)?; + let type_id = self.next()?; + let type_lookup = self.lookup_type.lookup(type_id)?; + let ty = type_lookup.handle; + + let id = self.next()?; + + let constituents_count = inst.wc - 3; + let mut constituents = Vec::with_capacity(constituents_count as usize); + for _ in 0..constituents_count { + let constituent_id = self.next()?; + let constant = self.lookup_constant.lookup(constituent_id)?; + constituents.push(constant.handle); + } + + self.lookup_constant.insert( + id, + LookupConstant { + handle: module.constants.append(crate::Constant { + name: self.future_decor.remove(&id).and_then(|dec| dec.name), + specialization: None, + inner: crate::ConstantInner::Composite(constituents), + ty, + }), + type_id, + }, + ); + + Ok(()) + } + + fn parse_global_variable( + &mut self, + inst: Instruction, + module: &mut crate::Module, + ) -> Result<(), Error> { + self.switch(ModuleState::Type, inst.op)?; + inst.expect_at_least(4)?; + let type_id = self.next()?; + let id = self.next()?; + let storage_class = self.next()?; + let init = if inst.wc > 4 { + inst.expect(5)?; + let init_id = self.next()?; + let lconst = self.lookup_constant.lookup(init_id)?; + Some(lconst.handle) + } else { + None + }; + let lookup_type = self.lookup_type.lookup(type_id)?; + let dec = self + .future_decor + .remove(&id) + .ok_or(Error::InvalidBinding(id))?; + + let class = { + use spirv::StorageClass as Sc; + match Sc::from_u32(storage_class) { + Some(Sc::Function) => crate::StorageClass::Function, + Some(Sc::Input) => crate::StorageClass::Input, + Some(Sc::Output) => crate::StorageClass::Output, + Some(Sc::Private) => crate::StorageClass::Private, + Some(Sc::UniformConstant) => crate::StorageClass::Handle, + Some(Sc::StorageBuffer) => crate::StorageClass::Storage, + Some(Sc::Uniform) => { + if self + .lookup_storage_buffer_types + .contains(&lookup_type.handle) + { + crate::StorageClass::Storage + } else { + crate::StorageClass::Uniform + } + } + Some(Sc::Workgroup) => crate::StorageClass::WorkGroup, + Some(Sc::PushConstant) => crate::StorageClass::PushConstant, + _ => return Err(Error::UnsupportedStorageClass(storage_class)), + } + }; + + let binding = match (class, &module.types[lookup_type.handle].inner) { + (crate::StorageClass::Input, &crate::TypeInner::Struct { .. }) + | (crate::StorageClass::Output, &crate::TypeInner::Struct { .. }) => None, + _ => Some(dec.get_binding().ok_or(Error::InvalidBinding(id))?), + }; + let is_storage = match module.types[lookup_type.handle].inner { + crate::TypeInner::Struct { .. } => class == crate::StorageClass::Storage, + crate::TypeInner::Image { + class: crate::ImageClass::Storage(_), + .. + } => true, + _ => false, + }; + + let storage_access = if is_storage { + let mut access = crate::StorageAccess::all(); + if dec.flags.contains(DecorationFlags::NON_READABLE) { + access ^= crate::StorageAccess::LOAD; + } + if dec.flags.contains(DecorationFlags::NON_WRITABLE) { + access ^= crate::StorageAccess::STORE; + } + access + } else { + crate::StorageAccess::empty() + }; + + let var = crate::GlobalVariable { + name: dec.name, + class, + binding, + ty: lookup_type.handle, + init, + interpolation: dec.interpolation, + storage_access, + }; + self.lookup_variable.insert( + id, + LookupVariable { + handle: module.global_variables.append(var), + type_id, + }, + ); + Ok(()) + } +} + +pub fn parse_u8_slice(data: &[u8], options: &Options) -> Result<crate::Module, Error> { + if data.len() % 4 != 0 { + return Err(Error::IncompleteData); + } + + let words = data + .chunks(4) + .map(|c| u32::from_le_bytes(c.try_into().unwrap())); + Parser::new(words, options).parse() +} + +#[cfg(test)] +mod test { + #[test] + fn parse() { + let bin = vec![ + // Magic number. Version number: 1.0. + 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, + // Generator number: 0. Bound: 0. + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Reserved word: 0. + 0x00, 0x00, 0x00, 0x00, // OpMemoryModel. Logical. + 0x0e, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, // GLSL450. + 0x01, 0x00, 0x00, 0x00, + ]; + let _ = super::parse_u8_slice(&bin, &Default::default()).unwrap(); + } +} |