From eb712b1f2f4d28f99d67f224af500b7bc4da1efa Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Wed, 31 Oct 2018 01:21:47 -0700 Subject: [PATCH] new SPIR-V dump format passes initial test --- spirv-parser-generator/src/generate.rs | 435 ++++++++++++++++++++++--- spirv-parser/src/lib.rs | 104 +++++- 2 files changed, 488 insertions(+), 51 deletions(-) diff --git a/spirv-parser-generator/src/generate.rs b/spirv-parser-generator/src/generate.rs index bd23484..43c59cb 100644 --- a/spirv-parser-generator/src/generate.rs +++ b/spirv-parser-generator/src/generate.rs @@ -211,6 +211,10 @@ pub(crate) fn generate( -> Result<(Self, &'a [u32])>; } + trait SPIRVDisplay { + fn spirv_display(&self, f: &mut fmt::Formatter) -> fmt::Result; + } + impl SPIRVParse for Option { fn spirv_parse<'a>( words: &'a [u32], @@ -225,6 +229,15 @@ pub(crate) fn generate( } } + impl SPIRVDisplay for Option { + fn spirv_display(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Some(v) => v.spirv_display(f), + None => Ok(()), + } + } + } + impl SPIRVParse for Vec { fn spirv_parse<'a>( mut words: &'a [u32], @@ -240,6 +253,15 @@ pub(crate) fn generate( } } + impl SPIRVDisplay for Vec { + fn spirv_display(&self, f: &mut fmt::Formatter) -> fmt::Result { + for i in self { + i.spirv_display(f)?; + } + Ok(()) + } + } + impl SPIRVParse for (A, B) { fn spirv_parse<'a>( words: &'a [u32], @@ -251,6 +273,13 @@ pub(crate) fn generate( } } + impl SPIRVDisplay for (A, B) { + fn spirv_display(&self, f: &mut fmt::Formatter) -> fmt::Result { + self.0.spirv_display(f)?; + self.1.spirv_display(f) + } + } + const BYTES_PER_WORD: usize = 4; struct ByteIterator<'a> { @@ -319,6 +348,12 @@ pub(crate) fn generate( } } + impl SPIRVDisplay for String { + fn spirv_display(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, " {:?}", self) + } + } + impl SPIRVParse for u32 { fn spirv_parse<'a>( words: &'a [u32], @@ -331,6 +366,12 @@ pub(crate) fn generate( } } + impl SPIRVDisplay for u32 { + fn spirv_display(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, " {}", self) + } + } + impl SPIRVParse for u64 { fn spirv_parse<'a>( words: &'a [u32], @@ -346,6 +387,12 @@ pub(crate) fn generate( } } + impl SPIRVDisplay for u64 { + fn spirv_display(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, " {}", self) + } + } + impl SPIRVParse for IdRef { fn spirv_parse<'a>( words: &'a [u32], @@ -359,6 +406,12 @@ pub(crate) fn generate( } } } + + impl SPIRVDisplay for IdRef { + fn spirv_display(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, " {}", self) + } + } ) )?; writeln!( @@ -379,23 +432,33 @@ pub(crate) fn generate( let mut enumerant_member_names = Vec::new(); let mut enumerant_items = Vec::new(); let mut enumerant_parse_operations = Vec::new(); + let mut enumerant_display_operations = Vec::new(); + let mut none_name = "None"; for enumerant in enumerants { if enumerant.value.0 == 0 { + none_name = enumerant.enumerant.as_ref(); continue; } let member_name = new_id(&enumerant.enumerant, SnakeCase); + let member_name = &member_name; enumerant_member_names.push(member_name.clone()); let type_name = new_combined_id(&[kind.as_ref(), &enumerant.enumerant], CamelCase); - let enumerant_parse_operation = if enumerant.parameters.is_empty() { + let enumerant_parse_operation; + if enumerant.parameters.is_empty() { enumerant_items.push(quote!{ #[derive(Clone, Debug, Default)] pub struct #type_name; }); - quote!{(Some(#type_name), words)} + enumerant_parse_operation = quote!{(Some(#type_name), words)}; + enumerant_display_operations.push(quote!{ + if let Some(#type_name) = &self.#member_name { + unimplemented!(); + } + }); } else { let mut enumerant_member_declarations = Vec::new(); - let mut enumerant_member_parse_initializers = Vec::new(); + let mut enumerant_member_names = Vec::new(); let mut parse_enumerant_members = Vec::new(); for (index, parameter) in enumerant.parameters.iter().enumerate() { let name = new_id(format!("parameter_{}", index), SnakeCase); @@ -403,7 +466,7 @@ pub(crate) fn generate( enumerant_member_declarations.push(quote!{ pub #kind, }); - enumerant_member_parse_initializers.push(quote!{ + enumerant_member_names.push(quote!{ #name, }); parse_enumerant_members.push(quote!{ @@ -414,10 +477,16 @@ pub(crate) fn generate( #[derive(Clone, Debug, Default)] pub struct #type_name(#(#enumerant_member_declarations)*); }); - quote!{ + let enumerant_member_names = &enumerant_member_names; + enumerant_parse_operation = quote!{ #(#parse_enumerant_members)* - (Some(#type_name(#(#enumerant_member_parse_initializers)*)), words) - } + (Some(#type_name(#(#enumerant_member_names)*)), words) + }; + enumerant_display_operations.push(quote!{ + if let Some(#type_name(#(#enumerant_member_names)*)) = &self.#member_name { + unimplemented!(); + } + }); }; enumerant_members.push(quote!{ pub #member_name: Option<#type_name> @@ -468,49 +537,85 @@ pub(crate) fn generate( } } )?; + writeln!( + &mut out, + "{}", + quote!{ + impl SPIRVDisplay for #kind_id { + fn spirv_display(&self, f: &mut fmt::Formatter) -> fmt::Result { + let mut is_none = true; + #(#enumerant_display_operations)* + if is_none { + write!(f, " {}", #none_name) + } else { + Ok(()) + } + } + } + } + )?; } ast::OperandKind::ValueEnum { kind, enumerants } => { let kind_id = new_id(&kind, CamelCase); let mut generated_enumerants = Vec::new(); let mut enumerant_parse_cases = Vec::new(); + let mut enumerant_display_cases = Vec::new(); for enumerant in enumerants { let name = new_enumerant_id(&kind, &enumerant.enumerant); let enumerant_value = enumerant.value; + let display_name = &enumerant.enumerant; if enumerant.parameters.is_empty() { generated_enumerants.push(quote!{#name}); enumerant_parse_cases.push(quote!{ #enumerant_value => Ok((#kind_id::#name, words)), }); + enumerant_display_cases.push(quote!{ + #kind_id::#name => write!(f, " {}", #display_name), + }); } else { let mut enumerant_member_declarations = Vec::new(); - let mut enumerant_member_parse_initializers = Vec::new(); + let mut enumerant_member_names = Vec::new(); let mut parse_enumerant_members = Vec::new(); + let mut display_enumerant_members = Vec::new(); for parameter in enumerant.parameters.iter() { let name = new_id(parameter.name.as_ref().unwrap(), SnakeCase); let kind = new_id(¶meter.kind, CamelCase); enumerant_member_declarations.push(quote!{ #name: #kind, }); - enumerant_member_parse_initializers.push(quote!{ + enumerant_member_names.push(quote!{ #name, }); parse_enumerant_members.push(quote!{ let (#name, words) = #kind::spirv_parse(words, parse_state)?; }); + display_enumerant_members.push(quote!{ + #name.spirv_display(f)?; + }); } generated_enumerants.push(quote!{ #name { #(#enumerant_member_declarations)* } }); + let enumerant_member_names = &enumerant_member_names; enumerant_parse_cases.push(quote!{ #enumerant_value => { #(#parse_enumerant_members)* Ok((#kind_id::#name { - #(#enumerant_member_parse_initializers)* + #(#enumerant_member_names)* }, words)) }, }); + enumerant_display_cases.push(quote!{ + #kind_id::#name { + #(#enumerant_member_names)* + } => { + write!(f, " {}", #display_name)?; + #(#display_enumerant_members)* + Ok(()) + } + }); } } writeln!( @@ -541,6 +646,19 @@ pub(crate) fn generate( } } )?; + writeln!( + &mut out, + "{}", + quote!{ + impl SPIRVDisplay for #kind_id { + fn spirv_display(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + #(#enumerant_display_cases)* + } + } + } + } + )?; } ast::OperandKind::Id { kind, .. } => { let base = if *kind == ast::Kind::IdRef { @@ -584,6 +702,17 @@ pub(crate) fn generate( } } )?; + writeln!( + &mut out, + "{}", + quote!{ + impl SPIRVDisplay for #kind_id { + fn spirv_display(&self, f: &mut fmt::Formatter) -> fmt::Result { + self.0.spirv_display(f) + } + } + } + )?; } } ast::OperandKind::Literal { kind, .. } => { @@ -621,9 +750,12 @@ pub(crate) fn generate( let mut instruction_enumerants = Vec::new(); let mut spec_constant_op_instruction_enumerants = Vec::new(); let mut instruction_parse_cases = Vec::new(); + let mut instruction_display_cases = Vec::new(); let mut instruction_spec_constant_parse_cases = Vec::new(); + let mut instruction_spec_constant_display_cases = Vec::new(); let mut instruction_extension_enumerants = Vec::new(); let mut instruction_extension_parse_cases = Vec::new(); + let mut instruction_extension_display_cases = Vec::new(); for parsed_extension_instruction_set in &parsed_extension_instruction_sets { let extension_instruction_set = &parsed_extension_instruction_set.enumerant_name; for instruction in &parsed_extension_instruction_set.ast.instructions { @@ -670,6 +802,7 @@ pub(crate) fn generate( }); operand_names.push(name); } + let operand_names = &operand_names; let body = quote!{ #(#parse_operations)* if words.is_empty() { @@ -686,16 +819,28 @@ pub(crate) fn generate( let instruction_extension_parse_case = quote!{ (ExtensionInstructionSet::#extension_instruction_set, #opcode) => { #body - }, + } }; instruction_extension_parse_cases.push(instruction_extension_parse_case); + let instruction_extension_display_case = quote!{ + Instruction::#instruction_enumerant_name { + id_result_type, + id_result, + set, + #(#operand_names,)* + } => unimplemented!(), + }; + instruction_extension_display_cases.push(instruction_extension_display_case); } } let instruction_extension_parse_cases = &instruction_extension_parse_cases; for instruction in core_instructions.iter() { let opcode = instruction.opcode; let opname = new_id(remove_initial_op(instruction.opname.as_ref()), CamelCase); - let instruction_parse_case = match &instruction.opname { + let display_opname = instruction.opname.as_ref(); + let instruction_parse_case; + let instruction_display_case; + match &instruction.opname { ast::InstructionName::OpExtInstImport => { let body = quote!{ parse_state.define_id( @@ -708,11 +853,16 @@ pub(crate) fn generate( Err(Error::InstructionTooLong) } }; - quote!{#opcode => { + instruction_parse_case = quote!{#opcode => { let (id_result, words) = IdResult::spirv_parse(words, parse_state)?; let (name, words) = LiteralString::spirv_parse(words, parse_state)?; #body - }} + }}; + instruction_display_case = quote!{ + Instruction::ExtInstImport { id_result, name } => { + writeln!(f, "{}{} {:?}", InstructionIndentAndResult(Some(*id_result)), #display_opname, name) + } + }; } ast::InstructionName::OpExtInst => { let body = quote!{ @@ -742,7 +892,7 @@ pub(crate) fn generate( (extension_instruction_set, instruction) => Err(Error::UnknownExtensionOpcode(extension_instruction_set, instruction)), } }; - quote!{ + instruction_parse_case = quote!{ #opcode => { let (id_result_type, words) = IdResultType::spirv_parse(words, parse_state)?; let (id_result, words) = IdResult::spirv_parse(words, parse_state)?; @@ -751,7 +901,23 @@ pub(crate) fn generate( let (instruction, words) = LiteralExtInstInteger::spirv_parse(words, parse_state)?; #body } - } + }; + instruction_display_case = quote!{ + Instruction::ExtInst { + id_result_type, + id_result, + set, + instruction, + operands, + } => { + write!(f, "{}{}", InstructionIndentAndResult(Some(*id_result)), #display_opname)?; + id_result_type.spirv_display(f)?; + set.spirv_display(f)?; + instruction.spirv_display(f)?; + operands.spirv_display(f)?; + writeln!(f) + } + }; } ast::InstructionName::OpTypeInt => { let body = quote!{ @@ -772,16 +938,33 @@ pub(crate) fn generate( Err(Error::InstructionTooLong) } }; - quote!{ + instruction_parse_case = quote!{ #opcode => { let (id_result, words) = IdResult::spirv_parse(words, parse_state)?; let (width, words) = LiteralInteger32::spirv_parse(words, parse_state)?; #body } - } + }; + instruction_display_case = quote!{ + Instruction::TypeInt { + id_result, + width, + signedness, + } => { + write!( + f, + "{}{}", + InstructionIndentAndResult(Some(*id_result)), + "OpTypeInt" + )?; + width.spirv_display(f)?; + signedness.spirv_display(f)?; + writeln!(f) + } + }; } ast::InstructionName::OpTypeFloat => { - quote!{ + instruction_parse_case = quote!{ #opcode => { let (id_result, words) = IdResult::spirv_parse(words, parse_state)?; let (width, words) = LiteralInteger32::spirv_parse(words, parse_state)?; @@ -800,7 +983,19 @@ pub(crate) fn generate( Err(Error::InstructionTooLong) } } - } + }; + instruction_display_case = quote!{ + Instruction::TypeFloat { id_result, width } => { + write!( + f, + "{}{}", + InstructionIndentAndResult(Some(*id_result)), + "OpTypeFloat" + )?; + width.spirv_display(f)?; + writeln!(f) + } + }; } ast::InstructionName::OpSwitch32 => { let body32 = quote!{ @@ -831,7 +1026,7 @@ pub(crate) fn generate( } } }; - quote!{ + instruction_parse_case = quote!{ #opcode => { let (selector, words) = IdRef::spirv_parse(words, parse_state)?; let (default, words) = IdRef::spirv_parse(words, parse_state)?; @@ -841,9 +1036,46 @@ pub(crate) fn generate( _ => Err(Error::SwitchSelectorIsInvalid(selector)), } } - } + }; + instruction_display_case = quote!{ + Instruction::Switch32 { + selector, + default, + target, + } => { + write!( + f, + "{}{}", + InstructionIndentAndResult(None), + "OpSwitch" + )?; + selector.spirv_display(f)?; + default.spirv_display(f)?; + target.spirv_display(f)?; + writeln!(f) + } + Instruction::Switch64 { + selector, + default, + target, + } => { + write!( + f, + "{}{}", + InstructionIndentAndResult(None), + "OpSwitch" + )?; + selector.spirv_display(f)?; + default.spirv_display(f)?; + target.spirv_display(f)?; + writeln!(f) + } + }; + } + ast::InstructionName::OpSwitch64 => { + instruction_parse_case = quote!{}; + instruction_display_case = quote!{}; } - ast::InstructionName::OpSwitch64 => quote!{}, ast::InstructionName::OpConstant32 => { let body32 = quote!{ IdStateType(BitWidth::Width32OrLess) => { @@ -873,7 +1105,7 @@ pub(crate) fn generate( } } }; - quote!{ + instruction_parse_case = quote!{ #opcode => { let (id_result_type, words) = IdResultType::spirv_parse(words, parse_state)?; let (id_result, words) = IdResult::spirv_parse(words, parse_state)?; @@ -883,9 +1115,42 @@ pub(crate) fn generate( #body64 } } - } + }; + instruction_display_case = quote!{ + Instruction::Constant32 { + id_result_type, + id_result, + value, + } => { + write!( + f, + "{}{}", + InstructionIndentAndResult(Some(*id_result)), + "OpConstant" + )?; + id_result_type.spirv_display(f)?; + writeln!(f, " {:#010X}", value) + } + Instruction::Constant64 { + id_result_type, + id_result, + value, + } => { + write!( + f, + "{}{}", + InstructionIndentAndResult(Some(*id_result)), + "OpConstant" + )?; + id_result_type.spirv_display(f)?; + writeln!(f, " {:#018X}", value) + } + }; + } + ast::InstructionName::OpConstant64 => { + instruction_parse_case = quote!{}; + instruction_display_case = quote!{}; } - ast::InstructionName::OpConstant64 => quote!{}, ast::InstructionName::OpSpecConstant32 => { let body32 = quote!{ IdStateType(BitWidth::Width32OrLess) => { @@ -915,7 +1180,7 @@ pub(crate) fn generate( } } }; - quote!{ + instruction_parse_case = quote!{ #opcode => { let (id_result_type, words) = IdResultType::spirv_parse(words, parse_state)?; let (id_result, words) = IdResult::spirv_parse(words, parse_state)?; @@ -925,22 +1190,34 @@ pub(crate) fn generate( #body64 } } - } + }; + instruction_display_case = quote!{ + Instruction::SpecConstant32 { .. } => unimplemented!(), + Instruction::SpecConstant64 { .. } => unimplemented!(), + }; + } + ast::InstructionName::OpSpecConstant64 => { + instruction_parse_case = quote!{}; + instruction_display_case = quote!{}; } - ast::InstructionName::OpSpecConstant64 => quote!{}, ast::InstructionName::OpSpecConstantOp => { - quote!{#opcode => { + instruction_parse_case = quote!{#opcode => { let (operation, words) = OpSpecConstantOp::spirv_parse(words, parse_state)?; if words.is_empty() { Ok(Instruction::#opname { operation }) } else { Err(Error::InstructionTooLong) } - }} + }}; + instruction_display_case = quote!{ + Instruction::#opname { operation } => fmt::Display::fmt(operation, f), + }; } _ => { let mut parse_operations = Vec::new(); + let mut display_operations = Vec::new(); let mut operand_names = Vec::new(); + let mut result_name = None; for operand in &instruction.operands { let kind = new_id(&operand.kind, CamelCase); let name = new_id(operand.name.as_ref().unwrap(), SnakeCase); @@ -952,7 +1229,15 @@ pub(crate) fn generate( parse_operations.push(quote!{ let (#name, words) = #kind::spirv_parse(words, parse_state)?; }); - operand_names.push(name); + operand_names.push(name.clone()); + if operand.kind == ast::Kind::IdResult { + assert_eq!(result_name, None); + result_name = Some(name); + } else { + display_operations.push(quote!{ + #name.spirv_display(f)?; + }); + } } if let Some([operand1, operand2]) = instruction.operands.get(..2) { if operand1.kind == ast::Kind::IdResultType @@ -965,7 +1250,8 @@ pub(crate) fn generate( }); } } - quote!{#opcode => { + let operand_names = &operand_names; + instruction_parse_case = quote!{#opcode => { #(#parse_operations)* if words.is_empty() { Ok(Instruction::#opname { @@ -974,10 +1260,22 @@ pub(crate) fn generate( } else { Err(Error::InstructionTooLong) } - }} + }}; + let result_value = match result_name { + None => quote!{None}, + Some(result_name) => quote!{Some(*#result_name)}, + }; + instruction_display_case = quote!{ + Instruction::#opname { #(#operand_names,)* } => { + write!(f, "{}{}", InstructionIndentAndResult(#result_value), #display_opname)?; + #(#display_operations)* + writeln!(f) + } + }; } - }; + } instruction_parse_cases.push(instruction_parse_case); + instruction_display_cases.push(instruction_display_case); let instruction_enumerant = if instruction.opname == ast::InstructionName::OpSpecConstantOp { quote!{ @@ -1039,6 +1337,7 @@ pub(crate) fn generate( "spec constant op is missing id_result_type and id_result" ); } + let operand_names = &operand_names; instruction_spec_constant_parse_cases.push(quote!{#opcode => { #(#parse_operations)* if words.is_empty() { @@ -1049,6 +1348,11 @@ pub(crate) fn generate( Err(Error::InstructionTooLong) } }}); + instruction_spec_constant_display_cases.push(quote!{ + OpSpecConstantOp::#opname { + #(#operand_names,)* + } => unimplemented!(), + }); } instruction_enumerants.push(instruction_enumerant); } @@ -1062,6 +1366,19 @@ pub(crate) fn generate( } } )?; + writeln!( + &mut out, + "{}", + quote!{ + impl fmt::Display for OpSpecConstantOp { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + #(#instruction_spec_constant_display_cases)* + } + } + } + } + )?; writeln!( &mut out, "{}", @@ -1105,7 +1422,7 @@ pub(crate) fn generate( impl fmt::Display for IdRef { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "#{}", self.0) + write!(f, "%{}", self.0) } } @@ -1315,6 +1632,20 @@ pub(crate) fn generate( } } )?; + writeln!( + &mut out, + "{}", + quote!{ + impl fmt::Display for Instruction { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + #(#instruction_display_cases)* + #(#instruction_extension_display_cases)* + } + } + } + } + )?; let body = quote!{ let (id_result_type, words) = IdResultType::spirv_parse(words, parse_state)?; let (id_result, words) = IdResult::spirv_parse(words, parse_state)?; @@ -1350,6 +1681,32 @@ pub(crate) fn generate( .map(|v| v.spirv_instruction_set_name) .collect(); let spirv_instruction_set_names = &spirv_instruction_set_names; + for parsed_extension_instruction_set in parsed_extension_instruction_sets.iter() { + let version_name = new_combined_id( + &[ + parsed_extension_instruction_set.spirv_instruction_set_name, + "version", + ], + UppercaseSnakeCase, + ); + let version = parsed_extension_instruction_set.ast.version; + let revision_name = new_combined_id( + &[ + parsed_extension_instruction_set.spirv_instruction_set_name, + "revision", + ], + UppercaseSnakeCase, + ); + let revision = parsed_extension_instruction_set.ast.revision; + writeln!( + &mut out, + "{}", + quote!{ + pub const #version_name: u32 = #version; + pub const #revision_name: u32 = #revision; + } + )?; + } writeln!( &mut out, "{}", @@ -1368,7 +1725,7 @@ pub(crate) fn generate( impl<'a> From> for ExtensionInstructionSet { fn from(s: Cow<'a, str>) -> ExtensionInstructionSet { match s.as_ref() { - #(#spirv_instruction_set_names=>return ExtensionInstructionSet::#extension_instruction_set_enumerants,)* + #(#spirv_instruction_set_names => return ExtensionInstructionSet::#extension_instruction_set_enumerants,)* _ => {} } ExtensionInstructionSet::Other(s.into_owned()) @@ -1384,8 +1741,8 @@ pub(crate) fn generate( type Target = str; fn deref(&self) -> &str { match self { - #(ExtensionInstructionSet::#extension_instruction_set_enumerants=>#spirv_instruction_set_names,)* - ExtensionInstructionSet::Other(s)=>&**s, + #(ExtensionInstructionSet::#extension_instruction_set_enumerants => #spirv_instruction_set_names,)* + ExtensionInstructionSet::Other(s) => &**s, } } } diff --git a/spirv-parser/src/lib.rs b/spirv-parser/src/lib.rs index 0a291d2..198ee4d 100644 --- a/spirv-parser/src/lib.rs +++ b/spirv-parser/src/lib.rs @@ -9,21 +9,20 @@ include!(concat!(env!("OUT_DIR"), "/generated_parser.rs")); #[cfg(test)] mod tests { use super::*; + use std::io::Write; use std::mem; use std::slice; - #[test] - fn parse_test() { - const BYTES: &[u8] = include_bytes!("../test_inputs/test.spv"); - assert_eq!(BYTES.len() % mem::size_of::(), 0); + fn parse_and_dump(bytes: &[u8]) -> Result { + assert_eq!(bytes.len() % mem::size_of::(), 0); let mut words: Vec = Vec::new(); - words.resize(BYTES.len() / mem::size_of::(), 0); + words.resize(bytes.len() / mem::size_of::(), 0); unsafe { - let bytes = slice::from_raw_parts_mut( + slice::from_raw_parts_mut( words.as_mut_ptr() as *mut u8, words.len() * mem::size_of::(), - ); - bytes.copy_from_slice(BYTES); + ) + .copy_from_slice(bytes); } assert!(!words.is_empty()); if words[0].swap_bytes() == MAGIC_NUMBER { @@ -31,10 +30,91 @@ mod tests { *word = word.swap_bytes(); } } - let parser = Parser::start(&words).unwrap(); - println!("{}", parser.header()); - for instruction in parser.map(Result::unwrap) { - println!("{:?}", instruction); + let parser = Parser::start(&words)?; + let mut out = Vec::::new(); + println!("Dumped output:"); + print!("{}", parser.header()); + write!(&mut out, "{}", parser.header()).unwrap(); + for instruction in parser { + let instruction = instruction?; + print!("{}", instruction); + write!(&mut out, "{}", instruction).unwrap(); + } + println!(); + Ok(String::from_utf8(out).unwrap()) + } + + #[test] + fn parse_test() { + let output = parse_and_dump(include_bytes!("../test_inputs/test.spv")).unwrap(); + let expected = r#"; SPIR-V +; Version: 1.0 +; Generator: 0x80001 +; Bound: 44 +; Schema: 0 + OpCapability Shader + OpCapability Int64 + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %4 "main" %10 %15 + OpMemberDecorate %8 0 BuiltIn Position + OpDecorate %8 Block + OpDecorate %15 Location 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeVector %6 4 + %8 = OpTypeStruct %7 + %9 = OpTypePointer Output %8 + %10 = OpVariable %9 Output + %11 = OpTypeInt 32 1 + %12 = OpConstant %11 0x00000000 + %13 = OpTypeVector %6 3 + %14 = OpTypePointer Input %13 + %15 = OpVariable %14 Input + %17 = OpConstant %6 0x3F800000 + %22 = OpTypePointer Output %7 + %24 = OpTypePointer Function %11 + %26 = OpTypeInt 32 0 + %27 = OpConstant %26 0x00000002 + %28 = OpTypePointer Input %6 + %31 = OpTypeInt 64 0 + %40 = OpConstant %6 0x00000000 + %41 = OpConstant %26 0x00000000 + %42 = OpTypePointer Output %6 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %25 = OpVariable %24 Function + %16 = OpLoad %13 %15 + %18 = OpCompositeExtract %6 %16 0 + %19 = OpCompositeExtract %6 %16 1 + %20 = OpCompositeExtract %6 %16 2 + %21 = OpCompositeConstruct %7 %18 %19 %20 %17 + %23 = OpAccessChain %22 %10 %12 + OpStore %23 %21 + %29 = OpAccessChain %28 %15 %27 + %30 = OpLoad %6 %29 + %32 = OpConvertFToU %31 %30 + %33 = OpUConvert %26 %32 + %34 = OpBitcast %11 %33 + OpStore %25 %34 + %35 = OpLoad %11 %25 + OpSelectionMerge %38 None + OpSwitch %32 %37 1 %36 2 %36 8 %36 + %37 = OpLabel + %43 = OpAccessChain %42 %10 %12 %41 + OpStore %43 %40 + OpBranch %38 + %36 = OpLabel + OpBranch %38 + %38 = OpLabel + OpReturn + OpFunctionEnd +"#; + println!("Line-by-line:"); + for (a, b) in output.lines().zip(expected.lines()) { + println!("{}\n{}", a, b); } + assert!(output == expected); } } -- 2.30.2