use crate::{
arena::{Arena, Handle},
FastHashMap, FastHashSet,
};
use num_traits::cast::FromPrimitive;
use std::convert::TryInto;
pub const SUPPORTED_CAPABILITIES: &[spirv::Capability] = &[spirv::Capability::Shader];
pub const SUPPORTED_EXTENSIONS: &[&str] = &[];
pub const SUPPORTED_EXT_SETS: &[&str] = &["GLSL.std.450"];
#[derive(Debug)]
pub enum Error {
InvalidHeader,
InvalidWordCount,
UnknownInstruction(u16),
UnknownCapability(u32),
UnsupportedInstruction(ModuleState, spirv::Op),
UnsupportedCapability(spirv::Capability),
UnsupportedExtension(String),
UnsupportedExtSet(String),
UnsupportedType(Handle<crate::Type>),
UnsupportedExecutionModel(u32),
UnsupportedStorageClass(u32),
UnsupportedFunctionControl(u32),
UnsupportedDim(u32),
InvalidParameter(spirv::Op),
InvalidOperandCount(spirv::Op, u16),
InvalidOperand,
InvalidDecoration(spirv::Word),
InvalidId(spirv::Word),
InvalidTypeWidth(spirv::Word),
InvalidSign(spirv::Word),
InvalidInnerType(spirv::Word),
InvalidVectorSize(spirv::Word),
InvalidVariableClass(spirv::StorageClass),
InvalidAccessType(spirv::Word),
InvalidAccessIndex(Handle<crate::Expression>),
InvalidLoadType(spirv::Word),
InvalidStoreType(spirv::Word),
InvalidBinding(spirv::Word),
WrongFunctionResultType(spirv::Word),
WrongFunctionParameterType(spirv::Word),
BadString,
IncompleteData,
}
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))
}
}
}
#[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))
}
}
fn map_vector_size(word: spirv::Word) -> Result<crate::VectorSize, Error> {
match word {
2 => Ok(crate::VectorSize::Bi),
3 => Ok(crate::VectorSize::Tri),
4 => Ok(crate::VectorSize::Quad),
_ => Err(Error::InvalidVectorSize(word)),
}
}
fn map_storage_class(word: spirv::Word) -> Result<spirv::StorageClass, Error> {
spirv::StorageClass::from_u32(word).ok_or(Error::UnsupportedStorageClass(word))
}
type MemberIndex = u32;
#[derive(Debug, Default)]
struct Decoration {
name: Option<String>,
built_in: Option<spirv::BuiltIn>,
location: Option<spirv::Word>,
desc_set: Option<spirv::Word>,
desc_index: Option<spirv::Word>,
}
impl Decoration {
fn get_binding(&self) -> Option<crate::Binding> {
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(set),
desc_index: Some(binding),
..
} => Some(crate::Binding::Descriptor { set, binding }),
_ => None,
}
}
}
#[derive(Debug)]
struct LookupFunctionType {
parameter_type_ids: Vec<spirv::Word>,
return_type_id: spirv::Word,
}
#[derive(Debug)]
struct EntryPoint {
exec_model: spirv::ExecutionModel,
name: String,
function_id: spirv::Word,
variable_ids: Vec<spirv::Word>,
}
#[derive(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>,
}
pub struct Parser<I> {
data: I,
state: ModuleState,
temp_bytes: Vec<u8>,
future_decor: FastHashMap<spirv::Word, Decoration>,
future_member_decor: FastHashMap<(spirv::Word, MemberIndex), Decoration>,
lookup_member_type_id: FastHashMap<(spirv::Word, MemberIndex), spirv::Word>,
lookup_type: FastHashMap<spirv::Word, LookupType>,
lookup_void_type: FastHashSet<spirv::Word>,
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>>,
}
impl<I: Iterator<Item = u32>> Parser<I> {
pub fn new(data: I) -> Self {
Parser {
data,
state: ModuleState::Empty,
temp_bytes: Vec::new(),
future_decor: FastHashMap::default(),
future_member_decor: FastHashMap::default(),
lookup_member_type_id: FastHashMap::default(),
lookup_type: FastHashMap::default(),
lookup_void_type: 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(),
}
}
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_typed);
match dec_typed {
spirv::Decoration::BuiltIn => {
inst.expect(base_words + 2)?;
let raw = self.next()?;
let built_in = spirv::BuiltIn::from_u32(raw);
if built_in.is_none() {
log::warn!("Unknown built in {:?}", raw);
}
dec.built_in = built_in;
}
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()?);
}
other => {
log::warn!("Unknown decoration {:?}", other);
for _ in base_words + 1..inst.wc {
let _var = self.next()?;
}
}
}
Ok(())
}
fn next_block(
&mut self,
fun: &mut crate::Function,
type_arena: &Arena<crate::Type>,
const_arena: &Arena<crate::Constant>,
) -> Result<(), Error> {
loop {
use spirv::Op;
let inst = self.next_inst()?;
log::debug!("\t\t{:?} [{}]", inst.op, inst.wc);
match inst.op {
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)?;
let ptr_type = self.lookup_type.lookup(expr.type_id)?;
AccessExpression {
base_handle: expr.handle,
type_id: ptr_type.base_id.unwrap(),
}
};
for _ in 4..inst.wc {
let access_id = self.next()?;
log::trace!("\t\t\tlooking up 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 fun.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::InvalidAccessIndex(
index_expr.handle,
))
}
}
}
_ => return Err(Error::InvalidAccessIndex(index_expr.handle)),
};
AccessExpression {
base_handle: fun.expressions.append(
crate::Expression::AccessIndex {
base: acex.base_handle,
index,
},
),
type_id: *self
.lookup_member_type_id
.get(&(acex.type_id, index))
.ok_or(Error::InvalidAccessType(acex.type_id))?,
}
}
crate::TypeInner::Array { .. }
| crate::TypeInner::Vector { .. }
| crate::TypeInner::Matrix { .. } => AccessExpression {
base_handle: fun.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)),
};
}
self.lookup_expression.insert(
result_id,
LookupExpression {
handle: acex.base_handle,
type_id: result_type_id,
},
);
}
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(&(lexp.type_id, 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: fun.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: fun.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)?;
let base_type = self.lookup_type.lookup(base_expr.type_id)?;
if base_type.base_id != Some(result_type_id) {
return Err(Error::InvalidLoadType(result_type_id));
}
match type_arena[base_type.handle].inner {
crate::TypeInner::Pointer { .. } => (),
_ => return Err(Error::UnsupportedType(base_type.handle)),
}
let expr = crate::Expression::Load {
pointer: base_expr.handle,
};
self.lookup_expression.insert(
result_id,
LookupExpression {
handle: fun.expressions.append(expr),
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 base_type = self.lookup_type.lookup(base_expr.type_id)?;
match type_arena[base_type.handle].inner {
crate::TypeInner::Pointer { .. } => (),
_ => return Err(Error::UnsupportedType(base_type.handle)),
};
let value_expr = self.lookup_expression.lookup(value_id)?;
if base_type.base_id != Some(value_expr.type_id) {
return Err(Error::InvalidStoreType(value_expr.type_id));
}
fun.body.push(crate::Statement::Store {
pointer: base_expr.handle,
value: value_expr.handle,
})
}
Op::Return => {
inst.expect(1)?;
fun.body.push(crate::Statement::Return { value: None });
break;
}
Op::VectorTimesScalar => {
inst.expect(5)?;
let result_type_id = self.next()?;
let result_type_loookup = self.lookup_type.lookup(result_type_id)?;
let (res_size, res_width) = match type_arena[result_type_loookup.handle].inner {
crate::TypeInner::Vector {
size,
kind: crate::ScalarKind::Float,
width,
} => (size, width),
_ => return Err(Error::UnsupportedType(result_type_loookup.handle)),
};
let result_id = self.next()?;
let vector_id = self.next()?;
let scalar_id = self.next()?;
let vector_lexp = self.lookup_expression.lookup(vector_id)?;
let vector_type_lookup = self.lookup_type.lookup(vector_lexp.type_id)?;
match type_arena[vector_type_lookup.handle].inner {
crate::TypeInner::Vector {
size,
kind: crate::ScalarKind::Float,
width,
} if size == res_size && width == res_width => (),
_ => return Err(Error::UnsupportedType(vector_type_lookup.handle)),
};
let scalar_lexp = self.lookup_expression.lookup(scalar_id)?.clone();
let scalar_type_lookup = self.lookup_type.lookup(scalar_lexp.type_id)?;
match type_arena[scalar_type_lookup.handle].inner {
crate::TypeInner::Scalar {
kind: crate::ScalarKind::Float,
width,
} if width == res_width => (),
_ => return Err(Error::UnsupportedType(scalar_type_lookup.handle)),
};
let expr = crate::Expression::Binary {
op: crate::BinaryOperator::Multiply,
left: vector_lexp.handle,
right: scalar_lexp.handle,
};
self.lookup_expression.insert(
result_id,
LookupExpression {
handle: fun.expressions.append(expr),
type_id: result_type_id,
},
);
}
Op::MatrixTimesVector => {
inst.expect(5)?;
let result_type_id = self.next()?;
let result_type_loookup = self.lookup_type.lookup(result_type_id)?;
let (res_size, res_width) = match type_arena[result_type_loookup.handle].inner {
crate::TypeInner::Vector {
size,
kind: crate::ScalarKind::Float,
width,
} => (size, width),
_ => return Err(Error::UnsupportedType(result_type_loookup.handle)),
};
let result_id = self.next()?;
let matrix_id = self.next()?;
let vector_id = self.next()?;
let matrix_lexp = self.lookup_expression.lookup(matrix_id)?;
let matrix_type_lookup = self.lookup_type.lookup(matrix_lexp.type_id)?;
let columns = match type_arena[matrix_type_lookup.handle].inner {
crate::TypeInner::Matrix {
columns,
rows,
kind: crate::ScalarKind::Float,
width,
} if rows == res_size && width == res_width => columns,
_ => return Err(Error::UnsupportedType(matrix_type_lookup.handle)),
};
let vector_lexp = self.lookup_expression.lookup(vector_id)?.clone();
let vector_type_lookup = self.lookup_type.lookup(vector_lexp.type_id)?;
match type_arena[vector_type_lookup.handle].inner {
crate::TypeInner::Vector {
size,
kind: crate::ScalarKind::Float,
width,
} if size == columns && width == res_width => (),
_ => return Err(Error::UnsupportedType(vector_type_lookup.handle)),
};
let expr = crate::Expression::Binary {
op: crate::BinaryOperator::Multiply,
left: matrix_lexp.handle,
right: vector_lexp.handle,
};
self.lookup_expression.insert(
result_id,
LookupExpression {
handle: fun.expressions.append(expr),
type_id: result_type_id,
},
);
}
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)?;
self.lookup_sampled_image.insert(
result_id,
LookupSampledImage {
image: image_lexp.handle,
sampler: sampler_lexp.handle,
},
);
}
Op::ImageSampleImplicitLod => {
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)?;
let coord_lexp = self.lookup_expression.lookup(coordinate_id)?;
let coord_type_lookup = self.lookup_type.lookup(coord_lexp.type_id)?;
match type_arena[coord_type_lookup.handle].inner {
crate::TypeInner::Scalar {
kind: crate::ScalarKind::Float,
..
}
| crate::TypeInner::Vector {
kind: crate::ScalarKind::Float,
..
} => (),
_ => return Err(Error::UnsupportedType(coord_type_lookup.handle)),
}
let expr = crate::Expression::ImageSample {
image: si_lexp.image,
sampler: si_lexp.sampler,
coordinate: coord_lexp.handle,
};
self.lookup_expression.insert(
result_id,
LookupExpression {
handle: fun.expressions.append(expr),
type_id: result_type_id,
},
);
}
_ => return Err(Error::UnsupportedInstruction(self.state, inst.op)),
}
}
Ok(())
}
fn make_expression_storage(&mut self) -> Arena<crate::Expression> {
let mut expressions = Arena::new();
assert!(self.lookup_expression.is_empty());
for (&id, var) in self.lookup_variable.iter() {
self.lookup_expression.insert(
id,
LookupExpression {
type_id: var.type_id,
handle: expressions.append(crate::Expression::GlobalVariable(var.handle)),
},
);
}
for (&id, con) in self.lookup_constant.iter() {
self.lookup_expression.insert(
id,
LookupExpression {
type_id: con.type_id,
handle: expressions.append(crate::Expression::Constant(con.handle)),
},
);
}
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 = crate::Module::from_header({
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::Header {
version: (version_raw[2], version_raw[1], version_raw[0]),
generator,
}
});
let mut entry_points = Vec::new();
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, &mut entry_points),
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::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_variable(inst, &mut module),
Op::Function => self.parse_function(inst, &mut module),
_ => Err(Error::UnsupportedInstruction(self.state, inst.op)),
}?;
}
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();
}
module.entry_points.reserve(entry_points.len());
for raw in entry_points {
module.entry_points.push(crate::EntryPoint {
exec_model: raw.exec_model,
name: raw.name,
function: *self.lookup_function.lookup(raw.function_id)?,
});
}
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 = 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));
}
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,
entry_points: &mut Vec<EntryPoint>,
) -> 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 {
exec_model,
name,
function_id,
variable_ids: self.data.by_ref().take(left as usize).collect(),
};
entry_points.push(ep);
Ok(())
}
fn parse_execution_mode(&mut self, inst: Instruction) -> Result<(), Error> {
self.switch(ModuleState::ExecutionMode, inst.op)?;
inst.expect_at_least(3)?;
let _ep_id = self.next()?;
let _mode = self.next()?;
for _ in 3..inst.wc {
let _ = self.next()?;
}
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_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: width
.try_into()
.map_err(|_| Error::InvalidTypeWidth(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: width
.try_into()
.map_err(|_| Error::InvalidTypeWidth(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, kind, width } => crate::TypeInner::Matrix {
columns: map_vector_size(num_columns)?,
rows: size,
kind,
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 inner = crate::TypeInner::Pointer {
base: self.lookup_type.lookup(type_id)?.handle,
class: map_storage_class(storage)?,
};
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_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 = self.next()?;
let inner = crate::TypeInner::Array {
base: self.lookup_type.lookup(type_id)?.handle,
size: crate::ArraySize::Static(length),
};
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_runtime_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 inner = crate::TypeInner::Array {
base: self.lookup_type.lookup(type_id)?.handle,
size: crate::ArraySize::Dynamic,
};
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_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 mut members = Vec::with_capacity(inst.wc as usize - 2);
for i in 0..u32::from(inst.wc) - 2 {
let type_id = self.next()?;
let ty = self.lookup_type.lookup(type_id)?.handle;
self.lookup_member_type_id.insert((id, i), type_id);
let decor = self
.future_member_decor
.remove(&(id, i))
.unwrap_or_default();
let binding = decor.get_binding();
members.push(crate::StructMember {
name: decor.name,
binding,
ty,
});
}
let inner = crate::TypeInner::Struct { members };
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_image(
&mut self,
inst: Instruction,
module: &mut crate::Module,
) -> Result<(), Error> {
self.switch(ModuleState::Type, inst.op)?;
inst.expect_at_least(9)?;
let id = self.next()?;
let sample_type_id = self.next()?;
let dim = self.next()?;
let mut flags = crate::ImageFlags::empty();
let _is_depth = self.next()?;
if self.next()? != 0 {
flags |= crate::ImageFlags::ARRAYED;
}
if self.next()? != 0 {
flags |= crate::ImageFlags::MULTISAMPLED;
}
let is_sampled = self.next()?;
if is_sampled != 0 {
flags |= crate::ImageFlags::SAMPLED;
}
let _format = self.next()?;
if inst.wc > 9 {
inst.expect(10)?;
let access = self.next()?;
if access == 0 || access == 2 {
flags |= crate::ImageFlags::CAN_LOAD;
}
if access == 1 || access == 2 {
flags |= crate::ImageFlags::CAN_STORE;
}
};
let decor = self.future_decor.remove(&id).unwrap_or_default();
let inner = crate::TypeInner::Image {
base: self.lookup_type.lookup(sample_type_id)?.handle,
dim: spirv::Dim::from_u32(dim).ok_or(Error::UnsupportedDim(dim))?,
flags,
};
self.lookup_type.insert(
id,
LookupType {
handle: module.types.append(crate::Type {
name: decor.name,
inner,
}),
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();
let inner = crate::TypeInner::Sampler { comparison: false };
self.lookup_type.insert(
id,
LookupType {
handle: module.types.append(crate::Type {
name: decor.name,
inner,
}),
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[type_lookup.handle].inner {
crate::TypeInner::Scalar {
kind: crate::ScalarKind::Uint,
width,
} => {
let low = self.next()?;
let high = if width > 32 {
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(&32) {
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 {
32 => f64::from(f32::from_bits(low)),
64 => {
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,
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_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 = self.next()?;
if inst.wc != 4 {
inst.expect(5)?;
let _init = self.next()?;
}
let lookup_type = self.lookup_type.lookup(type_id)?;
let dec = self
.future_decor
.remove(&id)
.ok_or(Error::InvalidBinding(id))?;
let binding = match module.types[lookup_type.handle].inner {
crate::TypeInner::Pointer {
base,
class: spirv::StorageClass::Input,
}
| crate::TypeInner::Pointer {
base,
class: spirv::StorageClass::Output,
} => {
match module.types[base].inner {
crate::TypeInner::Struct { ref members } => {
for member in members {
if member.binding.is_none() {
log::warn!(
"Struct {:?} member {:?} doesn't have a binding",
base,
member
);
return Err(Error::InvalidBinding(id));
}
}
None
}
_ => Some(dec.get_binding().ok_or(Error::InvalidBinding(id))?),
}
}
_ => Some(dec.get_binding().ok_or(Error::InvalidBinding(id))?),
};
let var = crate::GlobalVariable {
name: dec.name,
class: map_storage_class(storage)?,
binding,
ty: lookup_type.handle,
};
self.lookup_variable.insert(
id,
LookupVariable {
handle: module.global_variables.append(var),
type_id,
},
);
Ok(())
}
fn parse_function(
&mut self,
inst: Instruction,
module: &mut crate::Module,
) -> Result<(), Error> {
self.switch(ModuleState::Function, inst.op)?;
inst.expect(5)?;
let result_type = self.next()?;
let fun_id = self.next()?;
let fun_control = self.next()?;
let fun_type = self.next()?;
let mut fun = {
let ft = self.lookup_function_type.lookup(fun_type)?;
if ft.return_type_id != result_type {
return Err(Error::WrongFunctionResultType(result_type));
}
crate::Function {
name: self.future_decor.remove(&fun_id).and_then(|dec| dec.name),
control: spirv::FunctionControl::from_bits(fun_control)
.ok_or(Error::UnsupportedFunctionControl(fun_control))?,
parameter_types: Vec::with_capacity(ft.parameter_type_ids.len()),
return_type: if self.lookup_void_type.contains(&result_type) {
None
} else {
Some(self.lookup_type.lookup(result_type)?.handle)
},
global_usage: Vec::new(),
local_variables: Arena::new(),
expressions: self.make_expression_storage(),
body: Vec::new(),
}
};
for i in 0..fun.parameter_types.capacity() {
match self.next_inst()? {
Instruction {
op: spirv::Op::FunctionParameter,
wc: 3,
} => {
let type_id = self.next()?;
let _id = self.next()?;
if type_id
!= self
.lookup_function_type
.lookup(fun_type)?
.parameter_type_ids[i]
{
return Err(Error::WrongFunctionParameterType(type_id));
}
let ty = self.lookup_type.lookup(type_id)?.handle;
fun.parameter_types.push(ty);
}
Instruction { op, .. } => return Err(Error::InvalidParameter(op)),
}
}
loop {
let fun_inst = self.next_inst()?;
log::debug!("\t\t{:?}", fun_inst.op);
match fun_inst.op {
spirv::Op::Label => {
fun_inst.expect(2)?;
let _id = self.next()?;
self.next_block(&mut fun, &module.types, &module.constants)?;
}
spirv::Op::FunctionEnd => {
fun_inst.expect(1)?;
break;
}
_ => return Err(Error::UnsupportedInstruction(self.state, fun_inst.op)),
}
}
fun.global_usage =
crate::GlobalUse::scan(&fun.expressions, &fun.body, &module.global_variables);
let handle = module.functions.append(fun);
self.lookup_function.insert(fun_id, handle);
self.lookup_expression.clear();
self.lookup_sampled_image.clear();
Ok(())
}
}
pub fn parse_u8_slice(data: &[u8]) -> 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).parse()
}
#[cfg(test)]
mod test {
#[test]
fn parse() {
let bin = vec![
0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x0e, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00,
];
let _ = super::parse_u8_slice(&bin).unwrap();
}
}