diff options
Diffstat (limited to 'third_party/rust/naga/src/back/spv')
-rw-r--r-- | third_party/rust/naga/src/back/spv/helpers.rs | 20 | ||||
-rw-r--r-- | third_party/rust/naga/src/back/spv/instructions.rs | 708 | ||||
-rw-r--r-- | third_party/rust/naga/src/back/spv/layout.rs | 91 | ||||
-rw-r--r-- | third_party/rust/naga/src/back/spv/layout_tests.rs | 166 | ||||
-rw-r--r-- | third_party/rust/naga/src/back/spv/mod.rs | 52 | ||||
-rw-r--r-- | third_party/rust/naga/src/back/spv/test_framework.rs | 27 | ||||
-rw-r--r-- | third_party/rust/naga/src/back/spv/writer.rs | 1776 |
7 files changed, 2840 insertions, 0 deletions
diff --git a/third_party/rust/naga/src/back/spv/helpers.rs b/third_party/rust/naga/src/back/spv/helpers.rs new file mode 100644 index 0000000000..5facbe8b69 --- /dev/null +++ b/third_party/rust/naga/src/back/spv/helpers.rs @@ -0,0 +1,20 @@ +use spirv::Word; + +pub(crate) fn bytes_to_words(bytes: &[u8]) -> Vec<Word> { + bytes + .chunks(4) + .map(|chars| chars.iter().rev().fold(0u32, |u, c| (u << 8) | *c as u32)) + .collect() +} + +pub(crate) fn string_to_words(input: &str) -> Vec<Word> { + let bytes = input.as_bytes(); + let mut words = bytes_to_words(bytes); + + if bytes.len() % 4 == 0 { + // nul-termination + words.push(0x0u32); + } + + words +} diff --git a/third_party/rust/naga/src/back/spv/instructions.rs b/third_party/rust/naga/src/back/spv/instructions.rs new file mode 100644 index 0000000000..ab8e56844a --- /dev/null +++ b/third_party/rust/naga/src/back/spv/instructions.rs @@ -0,0 +1,708 @@ +use crate::back::spv::{helpers, Instruction}; +use spirv::{Op, Word}; + +pub(super) enum Signedness { + Unsigned = 0, + Signed = 1, +} + +// +// Debug Instructions +// + +pub(super) fn instruction_source( + source_language: spirv::SourceLanguage, + version: u32, +) -> Instruction { + let mut instruction = Instruction::new(Op::Source); + instruction.add_operand(source_language as u32); + instruction.add_operands(helpers::bytes_to_words(&version.to_le_bytes())); + instruction +} + +pub(super) fn instruction_name(target_id: Word, name: &str) -> Instruction { + let mut instruction = Instruction::new(Op::Name); + instruction.add_operand(target_id); + instruction.add_operands(helpers::string_to_words(name)); + instruction +} + +// +// Annotation Instructions +// + +pub(super) fn instruction_decorate( + target_id: Word, + decoration: spirv::Decoration, + operands: &[Word], +) -> Instruction { + let mut instruction = Instruction::new(Op::Decorate); + instruction.add_operand(target_id); + instruction.add_operand(decoration as u32); + + for operand in operands { + instruction.add_operand(*operand) + } + + instruction +} + +// +// Extension Instructions +// + +pub(super) fn instruction_ext_inst_import(id: Word, name: &str) -> Instruction { + let mut instruction = Instruction::new(Op::ExtInstImport); + instruction.set_result(id); + instruction.add_operands(helpers::string_to_words(name)); + instruction +} + +// +// Mode-Setting Instructions +// + +pub(super) fn instruction_memory_model( + addressing_model: spirv::AddressingModel, + memory_model: spirv::MemoryModel, +) -> Instruction { + let mut instruction = Instruction::new(Op::MemoryModel); + instruction.add_operand(addressing_model as u32); + instruction.add_operand(memory_model as u32); + instruction +} + +pub(super) fn instruction_entry_point( + execution_model: spirv::ExecutionModel, + entry_point_id: Word, + name: &str, + interface_ids: &[Word], +) -> Instruction { + let mut instruction = Instruction::new(Op::EntryPoint); + instruction.add_operand(execution_model as u32); + instruction.add_operand(entry_point_id); + instruction.add_operands(helpers::string_to_words(name)); + + for interface_id in interface_ids { + instruction.add_operand(*interface_id); + } + + instruction +} + +pub(super) fn instruction_execution_mode( + entry_point_id: Word, + execution_mode: spirv::ExecutionMode, +) -> Instruction { + let mut instruction = Instruction::new(Op::ExecutionMode); + instruction.add_operand(entry_point_id); + instruction.add_operand(execution_mode as u32); + instruction +} + +pub(super) fn instruction_capability(capability: spirv::Capability) -> Instruction { + let mut instruction = Instruction::new(Op::Capability); + instruction.add_operand(capability as u32); + instruction +} + +// +// Type-Declaration Instructions +// + +pub(super) fn instruction_type_void(id: Word) -> Instruction { + let mut instruction = Instruction::new(Op::TypeVoid); + instruction.set_result(id); + instruction +} + +pub(super) fn instruction_type_bool(id: Word) -> Instruction { + let mut instruction = Instruction::new(Op::TypeBool); + instruction.set_result(id); + instruction +} + +pub(super) fn instruction_type_int(id: Word, width: Word, signedness: Signedness) -> Instruction { + let mut instruction = Instruction::new(Op::TypeInt); + instruction.set_result(id); + instruction.add_operand(width); + instruction.add_operand(signedness as u32); + instruction +} + +pub(super) fn instruction_type_float(id: Word, width: Word) -> Instruction { + let mut instruction = Instruction::new(Op::TypeFloat); + instruction.set_result(id); + instruction.add_operand(width); + instruction +} + +pub(super) fn instruction_type_vector( + id: Word, + component_type_id: Word, + component_count: crate::VectorSize, +) -> Instruction { + let mut instruction = Instruction::new(Op::TypeVector); + instruction.set_result(id); + instruction.add_operand(component_type_id); + instruction.add_operand(component_count as u32); + instruction +} + +pub(super) fn instruction_type_matrix( + id: Word, + column_type_id: Word, + column_count: crate::VectorSize, +) -> Instruction { + let mut instruction = Instruction::new(Op::TypeMatrix); + instruction.set_result(id); + instruction.add_operand(column_type_id); + instruction.add_operand(column_count as u32); + instruction +} + +pub(super) fn instruction_type_image( + id: Word, + sampled_type_id: Word, + dim: spirv::Dim, + arrayed: bool, + image_class: crate::ImageClass, +) -> Instruction { + let mut instruction = Instruction::new(Op::TypeImage); + instruction.set_result(id); + instruction.add_operand(sampled_type_id); + instruction.add_operand(dim as u32); + + instruction.add_operand(match image_class { + crate::ImageClass::Depth => 1, + _ => 0, + }); + instruction.add_operand(arrayed as u32); + instruction.add_operand(match image_class { + crate::ImageClass::Sampled { multi: true, .. } => 1, + _ => 0, + }); + instruction.add_operand(match image_class { + crate::ImageClass::Sampled { .. } => 1, + _ => 0, + }); + + let format = match image_class { + crate::ImageClass::Storage(format) => match format { + crate::StorageFormat::R8Unorm => spirv::ImageFormat::R8, + crate::StorageFormat::R8Snorm => spirv::ImageFormat::R8Snorm, + crate::StorageFormat::R8Uint => spirv::ImageFormat::R8ui, + crate::StorageFormat::R8Sint => spirv::ImageFormat::R8i, + crate::StorageFormat::R16Uint => spirv::ImageFormat::R16ui, + crate::StorageFormat::R16Sint => spirv::ImageFormat::R16i, + crate::StorageFormat::R16Float => spirv::ImageFormat::R16f, + crate::StorageFormat::Rg8Unorm => spirv::ImageFormat::Rg8, + crate::StorageFormat::Rg8Snorm => spirv::ImageFormat::Rg8Snorm, + crate::StorageFormat::Rg8Uint => spirv::ImageFormat::Rg8ui, + crate::StorageFormat::Rg8Sint => spirv::ImageFormat::Rg8i, + crate::StorageFormat::R32Uint => spirv::ImageFormat::R32ui, + crate::StorageFormat::R32Sint => spirv::ImageFormat::R32i, + crate::StorageFormat::R32Float => spirv::ImageFormat::R32f, + crate::StorageFormat::Rg16Uint => spirv::ImageFormat::Rg16ui, + crate::StorageFormat::Rg16Sint => spirv::ImageFormat::Rg16i, + crate::StorageFormat::Rg16Float => spirv::ImageFormat::Rg16f, + crate::StorageFormat::Rgba8Unorm => spirv::ImageFormat::Rgba8, + crate::StorageFormat::Rgba8Snorm => spirv::ImageFormat::Rgba8Snorm, + crate::StorageFormat::Rgba8Uint => spirv::ImageFormat::Rgba8ui, + crate::StorageFormat::Rgba8Sint => spirv::ImageFormat::Rgba8i, + crate::StorageFormat::Rgb10a2Unorm => spirv::ImageFormat::Rgb10a2ui, + crate::StorageFormat::Rg11b10Float => spirv::ImageFormat::R11fG11fB10f, + crate::StorageFormat::Rg32Uint => spirv::ImageFormat::Rg32ui, + crate::StorageFormat::Rg32Sint => spirv::ImageFormat::Rg32i, + crate::StorageFormat::Rg32Float => spirv::ImageFormat::Rg32f, + crate::StorageFormat::Rgba16Uint => spirv::ImageFormat::Rgba16ui, + crate::StorageFormat::Rgba16Sint => spirv::ImageFormat::Rgba16i, + crate::StorageFormat::Rgba16Float => spirv::ImageFormat::Rgba16f, + crate::StorageFormat::Rgba32Uint => spirv::ImageFormat::Rgba32ui, + crate::StorageFormat::Rgba32Sint => spirv::ImageFormat::Rgba32i, + crate::StorageFormat::Rgba32Float => spirv::ImageFormat::Rgba32f, + }, + _ => spirv::ImageFormat::Unknown, + }; + + instruction.add_operand(format as u32); + instruction +} + +pub(super) fn instruction_type_sampler(id: Word) -> Instruction { + let mut instruction = Instruction::new(Op::TypeSampler); + instruction.set_result(id); + instruction +} + +pub(super) fn instruction_type_sampled_image(id: Word, image_type_id: Word) -> Instruction { + let mut instruction = Instruction::new(Op::TypeSampledImage); + instruction.set_result(id); + instruction.add_operand(image_type_id); + instruction +} + +pub(super) fn instruction_type_array( + id: Word, + element_type_id: Word, + length_id: Word, +) -> Instruction { + let mut instruction = Instruction::new(Op::TypeArray); + instruction.set_result(id); + instruction.add_operand(element_type_id); + instruction.add_operand(length_id); + instruction +} + +pub(super) fn instruction_type_runtime_array(id: Word, element_type_id: Word) -> Instruction { + let mut instruction = Instruction::new(Op::TypeRuntimeArray); + instruction.set_result(id); + instruction.add_operand(element_type_id); + instruction +} + +pub(super) fn instruction_type_struct(id: Word, member_ids: &[Word]) -> Instruction { + let mut instruction = Instruction::new(Op::TypeStruct); + instruction.set_result(id); + + for member_id in member_ids { + instruction.add_operand(*member_id) + } + + instruction +} + +pub(super) fn instruction_type_pointer( + id: Word, + storage_class: spirv::StorageClass, + type_id: Word, +) -> Instruction { + let mut instruction = Instruction::new(Op::TypePointer); + instruction.set_result(id); + instruction.add_operand(storage_class as u32); + instruction.add_operand(type_id); + instruction +} + +pub(super) fn instruction_type_function( + id: Word, + return_type_id: Word, + parameter_ids: &[Word], +) -> Instruction { + let mut instruction = Instruction::new(Op::TypeFunction); + instruction.set_result(id); + instruction.add_operand(return_type_id); + + for parameter_id in parameter_ids { + instruction.add_operand(*parameter_id); + } + + instruction +} + +// +// Constant-Creation Instructions +// + +pub(super) fn instruction_constant_true(result_type_id: Word, id: Word) -> Instruction { + let mut instruction = Instruction::new(Op::ConstantTrue); + instruction.set_type(result_type_id); + instruction.set_result(id); + instruction +} + +pub(super) fn instruction_constant_false(result_type_id: Word, id: Word) -> Instruction { + let mut instruction = Instruction::new(Op::ConstantFalse); + instruction.set_type(result_type_id); + instruction.set_result(id); + instruction +} + +pub(super) fn instruction_constant(result_type_id: Word, id: Word, values: &[Word]) -> Instruction { + let mut instruction = Instruction::new(Op::Constant); + instruction.set_type(result_type_id); + instruction.set_result(id); + + for value in values { + instruction.add_operand(*value); + } + + instruction +} + +pub(super) fn instruction_constant_composite( + result_type_id: Word, + id: Word, + constituent_ids: &[Word], +) -> Instruction { + let mut instruction = Instruction::new(Op::ConstantComposite); + instruction.set_type(result_type_id); + instruction.set_result(id); + + for constituent_id in constituent_ids { + instruction.add_operand(*constituent_id); + } + + instruction +} + +// +// Memory Instructions +// + +pub(super) fn instruction_variable( + result_type_id: Word, + id: Word, + storage_class: spirv::StorageClass, + initializer_id: Option<Word>, +) -> Instruction { + let mut instruction = Instruction::new(Op::Variable); + instruction.set_type(result_type_id); + instruction.set_result(id); + instruction.add_operand(storage_class as u32); + + if let Some(initializer_id) = initializer_id { + instruction.add_operand(initializer_id); + } + + instruction +} + +pub(super) fn instruction_load( + result_type_id: Word, + id: Word, + pointer_type_id: Word, + memory_access: Option<spirv::MemoryAccess>, +) -> Instruction { + let mut instruction = Instruction::new(Op::Load); + instruction.set_type(result_type_id); + instruction.set_result(id); + instruction.add_operand(pointer_type_id); + + if let Some(memory_access) = memory_access { + instruction.add_operand(memory_access.bits()); + } + + instruction +} + +pub(super) fn instruction_store( + pointer_type_id: Word, + object_id: Word, + memory_access: Option<spirv::MemoryAccess>, +) -> Instruction { + let mut instruction = Instruction::new(Op::Store); + instruction.add_operand(pointer_type_id); + instruction.add_operand(object_id); + + if let Some(memory_access) = memory_access { + instruction.add_operand(memory_access.bits()); + } + + instruction +} + +pub(super) fn instruction_access_chain( + result_type_id: Word, + id: Word, + base_id: Word, + index_ids: &[Word], +) -> Instruction { + let mut instruction = Instruction::new(Op::AccessChain); + instruction.set_type(result_type_id); + instruction.set_result(id); + instruction.add_operand(base_id); + + for index_id in index_ids { + instruction.add_operand(*index_id); + } + + instruction +} + +// +// Function Instructions +// + +pub(super) fn instruction_function( + return_type_id: Word, + id: Word, + function_control: spirv::FunctionControl, + function_type_id: Word, +) -> Instruction { + let mut instruction = Instruction::new(Op::Function); + instruction.set_type(return_type_id); + instruction.set_result(id); + instruction.add_operand(function_control.bits()); + instruction.add_operand(function_type_id); + instruction +} + +pub(super) fn instruction_function_parameter(result_type_id: Word, id: Word) -> Instruction { + let mut instruction = Instruction::new(Op::FunctionParameter); + instruction.set_type(result_type_id); + instruction.set_result(id); + instruction +} + +pub(super) fn instruction_function_end() -> Instruction { + Instruction::new(Op::FunctionEnd) +} + +pub(super) fn instruction_function_call( + result_type_id: Word, + id: Word, + function_id: Word, + argument_ids: &[Word], +) -> Instruction { + let mut instruction = Instruction::new(Op::FunctionCall); + instruction.set_type(result_type_id); + instruction.set_result(id); + instruction.add_operand(function_id); + + for argument_id in argument_ids { + instruction.add_operand(*argument_id); + } + + instruction +} + +// +// Image Instructions +// +pub(super) fn instruction_sampled_image( + result_type_id: Word, + id: Word, + image: Word, + sampler: Word, +) -> Instruction { + let mut instruction = Instruction::new(Op::SampledImage); + instruction.set_type(result_type_id); + instruction.set_result(id); + instruction.add_operand(image); + instruction.add_operand(sampler); + instruction +} + +pub(super) fn instruction_image_sample_implicit_lod( + result_type_id: Word, + id: Word, + sampled_image: Word, + coordinates: Word, +) -> Instruction { + let mut instruction = Instruction::new(Op::ImageSampleImplicitLod); + instruction.set_type(result_type_id); + instruction.set_result(id); + instruction.add_operand(sampled_image); + instruction.add_operand(coordinates); + instruction +} + +// +// Conversion Instructions +// +pub(super) fn instruction_unary( + op: Op, + result_type_id: Word, + id: Word, + value: Word, +) -> Instruction { + let mut instruction = Instruction::new(op); + instruction.set_type(result_type_id); + instruction.set_result(id); + instruction.add_operand(value); + instruction +} + +// +// Composite Instructions +// + +pub(super) fn instruction_composite_construct( + result_type_id: Word, + id: Word, + constituent_ids: &[Word], +) -> Instruction { + let mut instruction = Instruction::new(Op::CompositeConstruct); + instruction.set_type(result_type_id); + instruction.set_result(id); + + for constituent_id in constituent_ids { + instruction.add_operand(*constituent_id); + } + + instruction +} + +// +// Arithmetic Instructions +// +fn instruction_binary( + op: Op, + result_type_id: Word, + id: Word, + operand_1: Word, + operand_2: Word, +) -> Instruction { + let mut instruction = Instruction::new(op); + instruction.set_type(result_type_id); + instruction.set_result(id); + instruction.add_operand(operand_1); + instruction.add_operand(operand_2); + instruction +} + +pub(super) fn instruction_i_sub( + result_type_id: Word, + id: Word, + operand_1: Word, + operand_2: Word, +) -> Instruction { + instruction_binary(Op::ISub, result_type_id, id, operand_1, operand_2) +} + +pub(super) fn instruction_f_sub( + result_type_id: Word, + id: Word, + operand_1: Word, + operand_2: Word, +) -> Instruction { + instruction_binary(Op::FSub, result_type_id, id, operand_1, operand_2) +} + +pub(super) fn instruction_i_mul( + result_type_id: Word, + id: Word, + operand_1: Word, + operand_2: Word, +) -> Instruction { + instruction_binary(Op::IMul, result_type_id, id, operand_1, operand_2) +} + +pub(super) fn instruction_f_mul( + result_type_id: Word, + id: Word, + operand_1: Word, + operand_2: Word, +) -> Instruction { + instruction_binary(Op::FMul, result_type_id, id, operand_1, operand_2) +} + +pub(super) fn instruction_vector_times_scalar( + result_type_id: Word, + id: Word, + vector_type_id: Word, + scalar_type_id: Word, +) -> Instruction { + let mut instruction = Instruction::new(Op::VectorTimesScalar); + instruction.set_type(result_type_id); + instruction.set_result(id); + instruction.add_operand(vector_type_id); + instruction.add_operand(scalar_type_id); + instruction +} + +pub(super) fn instruction_matrix_times_scalar( + result_type_id: Word, + id: Word, + matrix_id: Word, + scalar_id: Word, +) -> Instruction { + let mut instruction = Instruction::new(Op::MatrixTimesScalar); + instruction.set_type(result_type_id); + instruction.set_result(id); + instruction.add_operand(matrix_id); + instruction.add_operand(scalar_id); + instruction +} + +pub(super) fn instruction_vector_times_matrix( + result_type_id: Word, + id: Word, + vector_id: Word, + matrix_id: Word, +) -> Instruction { + let mut instruction = Instruction::new(Op::VectorTimesMatrix); + instruction.set_type(result_type_id); + instruction.set_result(id); + instruction.add_operand(vector_id); + instruction.add_operand(matrix_id); + instruction +} + +pub(super) fn instruction_matrix_times_vector( + result_type_id: Word, + id: Word, + matrix_id: Word, + vector_id: Word, +) -> Instruction { + let mut instruction = Instruction::new(Op::MatrixTimesVector); + instruction.set_type(result_type_id); + instruction.set_result(id); + instruction.add_operand(matrix_id); + instruction.add_operand(vector_id); + instruction +} + +pub(super) fn instruction_matrix_times_matrix( + result_type_id: Word, + id: Word, + left_matrix: Word, + right_matrix: Word, +) -> Instruction { + let mut instruction = Instruction::new(Op::MatrixTimesMatrix); + instruction.set_type(result_type_id); + instruction.set_result(id); + instruction.add_operand(left_matrix); + instruction.add_operand(right_matrix); + instruction +} + +// +// Bit Instructions +// + +pub(super) fn instruction_bitwise_and( + result_type_id: Word, + id: Word, + operand_1: Word, + operand_2: Word, +) -> Instruction { + instruction_binary(Op::BitwiseAnd, result_type_id, id, operand_1, operand_2) +} + +// +// Relational and Logical Instructions +// + +// +// Derivative Instructions +// + +// +// Control-Flow Instructions +// + +pub(super) fn instruction_label(id: Word) -> Instruction { + let mut instruction = Instruction::new(Op::Label); + instruction.set_result(id); + instruction +} + +pub(super) fn instruction_return() -> Instruction { + Instruction::new(Op::Return) +} + +pub(super) fn instruction_return_value(value_id: Word) -> Instruction { + let mut instruction = Instruction::new(Op::ReturnValue); + instruction.add_operand(value_id); + instruction +} + +// +// Atomic Instructions +// + +// +// Primitive Instructions +// diff --git a/third_party/rust/naga/src/back/spv/layout.rs b/third_party/rust/naga/src/back/spv/layout.rs new file mode 100644 index 0000000000..006e785317 --- /dev/null +++ b/third_party/rust/naga/src/back/spv/layout.rs @@ -0,0 +1,91 @@ +use crate::back::spv::{Instruction, LogicalLayout, PhysicalLayout}; +use spirv::*; +use std::iter; + +impl PhysicalLayout { + pub(super) fn new(header: &crate::Header) -> Self { + let version: Word = ((header.version.0 as u32) << 16) + | ((header.version.1 as u32) << 8) + | header.version.2 as u32; + + PhysicalLayout { + magic_number: MAGIC_NUMBER, + version, + generator: header.generator, + bound: 0, + instruction_schema: 0x0u32, + } + } + + pub(super) fn in_words(&self, sink: &mut impl Extend<Word>) { + sink.extend(iter::once(self.magic_number)); + sink.extend(iter::once(self.version)); + sink.extend(iter::once(self.generator)); + sink.extend(iter::once(self.bound)); + sink.extend(iter::once(self.instruction_schema)); + } + + pub(super) fn supports_storage_buffers(&self) -> bool { + self.version >= 0x10300 + } +} + +impl LogicalLayout { + pub(super) fn in_words(&self, sink: &mut impl Extend<Word>) { + sink.extend(self.capabilities.iter().cloned()); + sink.extend(self.extensions.iter().cloned()); + sink.extend(self.ext_inst_imports.iter().cloned()); + sink.extend(self.memory_model.iter().cloned()); + sink.extend(self.entry_points.iter().cloned()); + sink.extend(self.execution_modes.iter().cloned()); + sink.extend(self.debugs.iter().cloned()); + sink.extend(self.annotations.iter().cloned()); + sink.extend(self.declarations.iter().cloned()); + sink.extend(self.function_declarations.iter().cloned()); + sink.extend(self.function_definitions.iter().cloned()); + } +} + +impl Instruction { + pub(super) fn new(op: Op) -> Self { + Instruction { + op, + wc: 1, // Always start at 1 for the first word (OP + WC), + type_id: None, + result_id: None, + operands: vec![], + } + } + + #[allow(clippy::panic)] + pub(super) fn set_type(&mut self, id: Word) { + assert!(self.type_id.is_none(), "Type can only be set once"); + self.type_id = Some(id); + self.wc += 1; + } + + #[allow(clippy::panic)] + pub(super) fn set_result(&mut self, id: Word) { + assert!(self.result_id.is_none(), "Result can only be set once"); + self.result_id = Some(id); + self.wc += 1; + } + + pub(super) fn add_operand(&mut self, operand: Word) { + self.operands.push(operand); + self.wc += 1; + } + + pub(super) fn add_operands(&mut self, operands: Vec<Word>) { + for operand in operands.into_iter() { + self.add_operand(operand) + } + } + + pub(super) fn to_words(&self, sink: &mut impl Extend<Word>) { + sink.extend(Some((self.wc << 16 | self.op as u32) as u32)); + sink.extend(self.type_id); + sink.extend(self.result_id); + sink.extend(self.operands.iter().cloned()); + } +} diff --git a/third_party/rust/naga/src/back/spv/layout_tests.rs b/third_party/rust/naga/src/back/spv/layout_tests.rs new file mode 100644 index 0000000000..37024b238f --- /dev/null +++ b/third_party/rust/naga/src/back/spv/layout_tests.rs @@ -0,0 +1,166 @@ +use crate::back::spv::test_framework::*; +use crate::back::spv::{helpers, Instruction, LogicalLayout, PhysicalLayout}; +use crate::Header; +use spirv::*; + +#[test] +fn test_physical_layout_in_words() { + let header = Header { + generator: 0, + version: (1, 2, 3), + }; + let bound = 5; + + let mut output = vec![]; + let mut layout = PhysicalLayout::new(&header); + layout.bound = bound; + + layout.in_words(&mut output); + + assert_eq!(output[0], spirv::MAGIC_NUMBER); + assert_eq!( + output[1], + to_word(&[header.version.0, header.version.1, header.version.2, 1]) + ); + assert_eq!(output[2], 0); + assert_eq!(output[3], bound); + assert_eq!(output[4], 0); +} + +#[test] +fn test_logical_layout_in_words() { + let mut output = vec![]; + let mut layout = LogicalLayout::default(); + let layout_vectors = 11; + let mut instructions = Vec::with_capacity(layout_vectors); + + let vector_names = &[ + "Capabilities", + "Extensions", + "External Instruction Imports", + "Memory Model", + "Entry Points", + "Execution Modes", + "Debugs", + "Annotations", + "Declarations", + "Function Declarations", + "Function Definitions", + ]; + + for i in 0..layout_vectors { + let mut dummy_instruction = Instruction::new(Op::Constant); + dummy_instruction.set_type((i + 1) as u32); + dummy_instruction.set_result((i + 2) as u32); + dummy_instruction.add_operand((i + 3) as u32); + dummy_instruction.add_operands(helpers::string_to_words( + format!("This is the vector: {}", vector_names[i]).as_str(), + )); + instructions.push(dummy_instruction); + } + + instructions[0].to_words(&mut layout.capabilities); + instructions[1].to_words(&mut layout.extensions); + instructions[2].to_words(&mut layout.ext_inst_imports); + instructions[3].to_words(&mut layout.memory_model); + instructions[4].to_words(&mut layout.entry_points); + instructions[5].to_words(&mut layout.execution_modes); + instructions[6].to_words(&mut layout.debugs); + instructions[7].to_words(&mut layout.annotations); + instructions[8].to_words(&mut layout.declarations); + instructions[9].to_words(&mut layout.function_declarations); + instructions[10].to_words(&mut layout.function_definitions); + + layout.in_words(&mut output); + + let mut index: usize = 0; + for instruction in instructions { + let wc = instruction.wc as usize; + let instruction_output = &output[index..index + wc]; + validate_instruction(instruction_output, &instruction); + index += wc; + } +} + +#[test] +fn test_instruction_set_type() { + let ty = 1; + let mut instruction = Instruction::new(Op::Constant); + assert_eq!(instruction.wc, 1); + + instruction.set_type(ty); + assert_eq!(instruction.type_id.unwrap(), ty); + assert_eq!(instruction.wc, 2); +} + +#[test] +#[should_panic] +fn test_instruction_set_type_twice() { + let ty = 1; + let mut instruction = Instruction::new(Op::Constant); + instruction.set_type(ty); + instruction.set_type(ty); +} + +#[test] +fn test_instruction_set_result() { + let result = 1; + let mut instruction = Instruction::new(Op::Constant); + assert_eq!(instruction.wc, 1); + + instruction.set_result(result); + assert_eq!(instruction.result_id.unwrap(), result); + assert_eq!(instruction.wc, 2); +} + +#[test] +#[should_panic] +fn test_instruction_set_result_twice() { + let result = 1; + let mut instruction = Instruction::new(Op::Constant); + instruction.set_result(result); + instruction.set_result(result); +} + +#[test] +fn test_instruction_add_operand() { + let operand = 1; + let mut instruction = Instruction::new(Op::Constant); + assert_eq!(instruction.operands.len(), 0); + assert_eq!(instruction.wc, 1); + + instruction.add_operand(operand); + assert_eq!(instruction.operands.len(), 1); + assert_eq!(instruction.wc, 2); +} + +#[test] +fn test_instruction_add_operands() { + let operands = vec![1, 2, 3]; + let mut instruction = Instruction::new(Op::Constant); + assert_eq!(instruction.operands.len(), 0); + assert_eq!(instruction.wc, 1); + + instruction.add_operands(operands); + assert_eq!(instruction.operands.len(), 3); + assert_eq!(instruction.wc, 4); +} + +#[test] +fn test_instruction_to_words() { + let ty = 1; + let result = 2; + let operand = 3; + let mut instruction = Instruction::new(Op::Constant); + instruction.set_type(ty); + instruction.set_result(result); + instruction.add_operand(operand); + + let mut output = vec![]; + instruction.to_words(&mut output); + validate_instruction(output.as_slice(), &instruction); +} + +fn to_word(bytes: &[u8]) -> Word { + ((bytes[0] as u32) << 16) | ((bytes[1] as u32) << 8) | bytes[2] as u32 +} diff --git a/third_party/rust/naga/src/back/spv/mod.rs b/third_party/rust/naga/src/back/spv/mod.rs new file mode 100644 index 0000000000..15f1598357 --- /dev/null +++ b/third_party/rust/naga/src/back/spv/mod.rs @@ -0,0 +1,52 @@ +mod helpers; +mod instructions; +mod layout; +mod writer; + +#[cfg(test)] +mod test_framework; + +#[cfg(test)] +mod layout_tests; + +pub use writer::Writer; + +use spirv::*; + +bitflags::bitflags! { + pub struct WriterFlags: u32 { + const NONE = 0x0; + const DEBUG = 0x1; + } +} + +struct PhysicalLayout { + magic_number: Word, + version: Word, + generator: Word, + bound: Word, + instruction_schema: Word, +} + +#[derive(Default)] +struct LogicalLayout { + capabilities: Vec<Word>, + extensions: Vec<Word>, + ext_inst_imports: Vec<Word>, + memory_model: Vec<Word>, + entry_points: Vec<Word>, + execution_modes: Vec<Word>, + debugs: Vec<Word>, + annotations: Vec<Word>, + declarations: Vec<Word>, + function_declarations: Vec<Word>, + function_definitions: Vec<Word>, +} + +pub(self) struct Instruction { + op: Op, + wc: u32, + type_id: Option<Word>, + result_id: Option<Word>, + operands: Vec<Word>, +} diff --git a/third_party/rust/naga/src/back/spv/test_framework.rs b/third_party/rust/naga/src/back/spv/test_framework.rs new file mode 100644 index 0000000000..be2fa74fe1 --- /dev/null +++ b/third_party/rust/naga/src/back/spv/test_framework.rs @@ -0,0 +1,27 @@ +pub(super) fn validate_instruction( + words: &[spirv::Word], + instruction: &crate::back::spv::Instruction, +) { + let mut inst_index = 0; + let (wc, op) = ((words[inst_index] >> 16) as u16, words[inst_index] as u16); + inst_index += 1; + + assert_eq!(wc, words.len() as u16); + assert_eq!(op, instruction.op as u16); + + if instruction.type_id.is_some() { + assert_eq!(words[inst_index], instruction.type_id.unwrap()); + inst_index += 1; + } + + if instruction.result_id.is_some() { + assert_eq!(words[inst_index], instruction.result_id.unwrap()); + inst_index += 1; + } + + let mut op_index = 0; + for i in inst_index..wc as usize { + assert_eq!(words[i as usize], instruction.operands[op_index]); + op_index += 1; + } +} diff --git a/third_party/rust/naga/src/back/spv/writer.rs b/third_party/rust/naga/src/back/spv/writer.rs new file mode 100644 index 0000000000..f1b43f5289 --- /dev/null +++ b/third_party/rust/naga/src/back/spv/writer.rs @@ -0,0 +1,1776 @@ +/*! Standard Portable Intermediate Representation (SPIR-V) backend !*/ +use super::{Instruction, LogicalLayout, PhysicalLayout, WriterFlags}; +use spirv::Word; +use std::{collections::hash_map::Entry, ops}; +use thiserror::Error; + +const BITS_PER_BYTE: crate::Bytes = 8; + +#[derive(Clone, Debug, Error)] +pub enum Error { + #[error("can't find local variable: {0:?}")] + UnknownLocalVariable(crate::LocalVariable), + #[error("bad image class for op: {0:?}")] + BadImageClass(crate::ImageClass), + #[error("not an image")] + NotImage, + #[error("empty value")] + FeatureNotImplemented(), +} + +struct Block { + label: Option<Instruction>, + body: Vec<Instruction>, + termination: Option<Instruction>, +} + +impl Block { + pub fn new() -> Self { + Block { + label: None, + body: vec![], + termination: None, + } + } +} + +struct LocalVariable { + id: Word, + name: Option<String>, + instruction: Instruction, +} + +struct Function { + signature: Option<Instruction>, + parameters: Vec<Instruction>, + variables: Vec<LocalVariable>, + blocks: Vec<Block>, +} + +impl Function { + pub fn new() -> Self { + Function { + signature: None, + parameters: vec![], + variables: vec![], + blocks: vec![], + } + } + + fn to_words(&self, sink: &mut impl Extend<Word>) { + self.signature.as_ref().unwrap().to_words(sink); + for instruction in self.parameters.iter() { + instruction.to_words(sink); + } + for (index, block) in self.blocks.iter().enumerate() { + block.label.as_ref().unwrap().to_words(sink); + if index == 0 { + for local_var in self.variables.iter() { + local_var.instruction.to_words(sink); + } + } + for instruction in block.body.iter() { + instruction.to_words(sink); + } + block.termination.as_ref().unwrap().to_words(sink); + } + } +} + +#[derive(Debug, PartialEq, Hash, Eq, Copy, Clone)] +enum LocalType { + Void, + Scalar { + kind: crate::ScalarKind, + width: crate::Bytes, + }, + Vector { + size: crate::VectorSize, + kind: crate::ScalarKind, + width: crate::Bytes, + }, + Pointer { + base: crate::Handle<crate::Type>, + class: crate::StorageClass, + }, + SampledImage { + image_type: crate::Handle<crate::Type>, + }, +} + +#[derive(Debug, PartialEq, Hash, Eq, Copy, Clone)] +enum LookupType { + Handle(crate::Handle<crate::Type>), + Local(LocalType), +} + +fn map_dim(dim: crate::ImageDimension) -> spirv::Dim { + match dim { + crate::ImageDimension::D1 => spirv::Dim::Dim1D, + crate::ImageDimension::D2 => spirv::Dim::Dim2D, + crate::ImageDimension::D3 => spirv::Dim::Dim2D, + crate::ImageDimension::Cube => spirv::Dim::DimCube, + } +} + +#[derive(Debug, PartialEq, Clone, Hash, Eq)] +struct LookupFunctionType { + parameter_type_ids: Vec<Word>, + return_type_id: Word, +} + +enum MaybeOwned<'a, T> { + Owned(T), + Borrowed(&'a T), +} + +impl<'a, T> ops::Deref for MaybeOwned<'a, T> { + type Target = T; + fn deref(&self) -> &T { + match *self { + MaybeOwned::Owned(ref value) => value, + MaybeOwned::Borrowed(reference) => reference, + } + } +} + +enum Dimension { + Scalar, + Vector, + Matrix, +} + +fn get_dimension(ty_inner: &crate::TypeInner) -> Dimension { + match *ty_inner { + crate::TypeInner::Scalar { .. } => Dimension::Scalar, + crate::TypeInner::Vector { .. } => Dimension::Vector, + crate::TypeInner::Matrix { .. } => Dimension::Matrix, + _ => unreachable!(), + } +} + +pub struct Writer { + physical_layout: PhysicalLayout, + logical_layout: LogicalLayout, + id_count: u32, + capabilities: crate::FastHashSet<spirv::Capability>, + debugs: Vec<Instruction>, + annotations: Vec<Instruction>, + writer_flags: WriterFlags, + void_type: Option<u32>, + lookup_type: crate::FastHashMap<LookupType, Word>, + lookup_function: crate::FastHashMap<crate::Handle<crate::Function>, Word>, + lookup_function_type: crate::FastHashMap<LookupFunctionType, Word>, + lookup_constant: crate::FastHashMap<crate::Handle<crate::Constant>, Word>, + lookup_global_variable: crate::FastHashMap<crate::Handle<crate::GlobalVariable>, Word>, +} + +// type alias, for success return of write_expression +type WriteExpressionOutput = (Word, LookupType); + +impl Writer { + pub fn new(header: &crate::Header, writer_flags: WriterFlags) -> Self { + Writer { + physical_layout: PhysicalLayout::new(header), + logical_layout: LogicalLayout::default(), + id_count: 0, + capabilities: crate::FastHashSet::default(), + debugs: vec![], + annotations: vec![], + writer_flags, + void_type: None, + lookup_type: crate::FastHashMap::default(), + lookup_function: crate::FastHashMap::default(), + lookup_function_type: crate::FastHashMap::default(), + lookup_constant: crate::FastHashMap::default(), + lookup_global_variable: crate::FastHashMap::default(), + } + } + + fn generate_id(&mut self) -> Word { + self.id_count += 1; + self.id_count + } + + fn try_add_capabilities(&mut self, capabilities: &[spirv::Capability]) { + for capability in capabilities.iter() { + self.capabilities.insert(*capability); + } + } + + fn get_type_id(&mut self, arena: &crate::Arena<crate::Type>, lookup_ty: LookupType) -> Word { + if let Entry::Occupied(e) = self.lookup_type.entry(lookup_ty) { + *e.get() + } else { + match lookup_ty { + LookupType::Handle(handle) => match arena[handle].inner { + crate::TypeInner::Scalar { kind, width } => self + .get_type_id(arena, LookupType::Local(LocalType::Scalar { kind, width })), + _ => self.write_type_declaration_arena(arena, handle), + }, + LookupType::Local(local_ty) => self.write_type_declaration_local(arena, local_ty), + } + } + } + + fn get_constant_id( + &mut self, + handle: crate::Handle<crate::Constant>, + ir_module: &crate::Module, + ) -> Word { + match self.lookup_constant.entry(handle) { + Entry::Occupied(e) => *e.get(), + _ => { + let (instruction, id) = self.write_constant_type(handle, ir_module); + instruction.to_words(&mut self.logical_layout.declarations); + id + } + } + } + + fn get_global_variable_id( + &mut self, + ir_module: &crate::Module, + handle: crate::Handle<crate::GlobalVariable>, + ) -> Word { + match self.lookup_global_variable.entry(handle) { + Entry::Occupied(e) => *e.get(), + _ => { + let (instruction, id) = self.write_global_variable(ir_module, handle); + instruction.to_words(&mut self.logical_layout.declarations); + id + } + } + } + + fn get_function_return_type( + &mut self, + ty: Option<crate::Handle<crate::Type>>, + arena: &crate::Arena<crate::Type>, + ) -> Word { + match ty { + Some(handle) => self.get_type_id(arena, LookupType::Handle(handle)), + None => match self.void_type { + Some(id) => id, + None => { + let id = self.generate_id(); + self.void_type = Some(id); + super::instructions::instruction_type_void(id) + .to_words(&mut self.logical_layout.declarations); + id + } + }, + } + } + + fn get_pointer_id( + &mut self, + arena: &crate::Arena<crate::Type>, + handle: crate::Handle<crate::Type>, + class: crate::StorageClass, + ) -> Word { + let ty = &arena[handle]; + let ty_id = self.get_type_id(arena, LookupType::Handle(handle)); + match ty.inner { + crate::TypeInner::Pointer { .. } => ty_id, + _ => { + match self + .lookup_type + .entry(LookupType::Local(LocalType::Pointer { + base: handle, + class, + })) { + Entry::Occupied(e) => *e.get(), + _ => { + let id = + self.create_pointer(ty_id, self.parse_to_spirv_storage_class(class)); + self.lookup_type.insert( + LookupType::Local(LocalType::Pointer { + base: handle, + class, + }), + id, + ); + id + } + } + } + } + } + + fn create_pointer(&mut self, ty_id: Word, class: spirv::StorageClass) -> Word { + let id = self.generate_id(); + let instruction = super::instructions::instruction_type_pointer(id, class, ty_id); + instruction.to_words(&mut self.logical_layout.declarations); + id + } + + fn create_constant(&mut self, type_id: Word, value: &[Word]) -> Word { + let id = self.generate_id(); + let instruction = super::instructions::instruction_constant(type_id, id, value); + instruction.to_words(&mut self.logical_layout.declarations); + id + } + + fn write_function( + &mut self, + ir_function: &crate::Function, + ir_module: &crate::Module, + ) -> spirv::Word { + let mut function = Function::new(); + + for (_, variable) in ir_function.local_variables.iter() { + let id = self.generate_id(); + + let init_word = variable + .init + .map(|constant| self.get_constant_id(constant, ir_module)); + + let pointer_id = + self.get_pointer_id(&ir_module.types, variable.ty, crate::StorageClass::Function); + function.variables.push(LocalVariable { + id, + name: variable.name.clone(), + instruction: super::instructions::instruction_variable( + pointer_id, + id, + spirv::StorageClass::Function, + init_word, + ), + }); + } + + let return_type_id = + self.get_function_return_type(ir_function.return_type, &ir_module.types); + let mut parameter_type_ids = Vec::with_capacity(ir_function.arguments.len()); + + let mut function_parameter_pointer_ids = vec![]; + + for argument in ir_function.arguments.iter() { + let id = self.generate_id(); + let pointer_id = + self.get_pointer_id(&ir_module.types, argument.ty, crate::StorageClass::Function); + + function_parameter_pointer_ids.push(pointer_id); + parameter_type_ids + .push(self.get_type_id(&ir_module.types, LookupType::Handle(argument.ty))); + function + .parameters + .push(super::instructions::instruction_function_parameter( + pointer_id, id, + )); + } + + let lookup_function_type = LookupFunctionType { + return_type_id, + parameter_type_ids, + }; + + let function_id = self.generate_id(); + let function_type = + self.get_function_type(lookup_function_type, function_parameter_pointer_ids); + function.signature = Some(super::instructions::instruction_function( + return_type_id, + function_id, + spirv::FunctionControl::empty(), + function_type, + )); + + self.write_block(&ir_function.body, ir_module, ir_function, &mut function); + + function.to_words(&mut self.logical_layout.function_definitions); + super::instructions::instruction_function_end() + .to_words(&mut self.logical_layout.function_definitions); + + function_id + } + + // TODO Move to instructions module + fn write_entry_point( + &mut self, + entry_point: &crate::EntryPoint, + stage: crate::ShaderStage, + name: &str, + ir_module: &crate::Module, + ) -> Instruction { + let function_id = self.write_function(&entry_point.function, ir_module); + + let exec_model = match stage { + crate::ShaderStage::Vertex => spirv::ExecutionModel::Vertex, + crate::ShaderStage::Fragment { .. } => spirv::ExecutionModel::Fragment, + crate::ShaderStage::Compute { .. } => spirv::ExecutionModel::GLCompute, + }; + + let mut interface_ids = vec![]; + for ((handle, _), &usage) in ir_module + .global_variables + .iter() + .filter(|&(_, var)| { + var.class == crate::StorageClass::Input || var.class == crate::StorageClass::Output + }) + .zip(&entry_point.function.global_usage) + { + if usage.contains(crate::GlobalUse::STORE) || usage.contains(crate::GlobalUse::LOAD) { + let id = self.get_global_variable_id(ir_module, handle); + interface_ids.push(id); + } + } + + self.try_add_capabilities(exec_model.required_capabilities()); + match stage { + crate::ShaderStage::Vertex => {} + crate::ShaderStage::Fragment => { + let execution_mode = spirv::ExecutionMode::OriginUpperLeft; + self.try_add_capabilities(execution_mode.required_capabilities()); + super::instructions::instruction_execution_mode(function_id, execution_mode) + .to_words(&mut self.logical_layout.execution_modes); + } + crate::ShaderStage::Compute => {} + } + + if self.writer_flags.contains(WriterFlags::DEBUG) { + self.debugs + .push(super::instructions::instruction_name(function_id, name)); + } + + super::instructions::instruction_entry_point( + exec_model, + function_id, + name, + interface_ids.as_slice(), + ) + } + + fn write_scalar(&self, id: Word, kind: crate::ScalarKind, width: crate::Bytes) -> Instruction { + let bits = (width * BITS_PER_BYTE) as u32; + match kind { + crate::ScalarKind::Sint => super::instructions::instruction_type_int( + id, + bits, + super::instructions::Signedness::Signed, + ), + crate::ScalarKind::Uint => super::instructions::instruction_type_int( + id, + bits, + super::instructions::Signedness::Unsigned, + ), + crate::ScalarKind::Float => super::instructions::instruction_type_float(id, bits), + crate::ScalarKind::Bool => super::instructions::instruction_type_bool(id), + } + } + + fn parse_to_spirv_storage_class(&self, class: crate::StorageClass) -> spirv::StorageClass { + match class { + crate::StorageClass::Handle => spirv::StorageClass::UniformConstant, + crate::StorageClass::Function => spirv::StorageClass::Function, + crate::StorageClass::Input => spirv::StorageClass::Input, + crate::StorageClass::Output => spirv::StorageClass::Output, + crate::StorageClass::Private => spirv::StorageClass::Private, + crate::StorageClass::Storage if self.physical_layout.supports_storage_buffers() => { + spirv::StorageClass::StorageBuffer + } + crate::StorageClass::Storage | crate::StorageClass::Uniform => { + spirv::StorageClass::Uniform + } + crate::StorageClass::WorkGroup => spirv::StorageClass::Workgroup, + crate::StorageClass::PushConstant => spirv::StorageClass::PushConstant, + } + } + + fn write_type_declaration_local( + &mut self, + arena: &crate::Arena<crate::Type>, + local_ty: LocalType, + ) -> Word { + let id = self.generate_id(); + let instruction = match local_ty { + LocalType::Void => unreachable!(), + LocalType::Scalar { kind, width } => self.write_scalar(id, kind, width), + LocalType::Vector { size, kind, width } => { + let scalar_id = + self.get_type_id(arena, LookupType::Local(LocalType::Scalar { kind, width })); + super::instructions::instruction_type_vector(id, scalar_id, size) + } + LocalType::Pointer { .. } => unimplemented!(), + LocalType::SampledImage { image_type } => { + let image_type_id = self.get_type_id(arena, LookupType::Handle(image_type)); + super::instructions::instruction_type_sampled_image(id, image_type_id) + } + }; + + self.lookup_type.insert(LookupType::Local(local_ty), id); + instruction.to_words(&mut self.logical_layout.declarations); + id + } + + fn write_type_declaration_arena( + &mut self, + arena: &crate::Arena<crate::Type>, + handle: crate::Handle<crate::Type>, + ) -> Word { + let ty = &arena[handle]; + let id = self.generate_id(); + + let instruction = match ty.inner { + crate::TypeInner::Scalar { kind, width } => { + self.lookup_type + .insert(LookupType::Local(LocalType::Scalar { kind, width }), id); + self.write_scalar(id, kind, width) + } + crate::TypeInner::Vector { size, kind, width } => { + let scalar_id = + self.get_type_id(arena, LookupType::Local(LocalType::Scalar { kind, width })); + self.lookup_type.insert( + LookupType::Local(LocalType::Vector { size, kind, width }), + id, + ); + super::instructions::instruction_type_vector(id, scalar_id, size) + } + crate::TypeInner::Matrix { + columns, + rows: _, + width, + } => { + let vector_id = self.get_type_id( + arena, + LookupType::Local(LocalType::Vector { + size: columns, + kind: crate::ScalarKind::Float, + width, + }), + ); + super::instructions::instruction_type_matrix(id, vector_id, columns) + } + crate::TypeInner::Image { + dim, + arrayed, + class, + } => { + let width = 4; + let local_type = match class { + crate::ImageClass::Sampled { kind, multi: _ } => { + LocalType::Scalar { kind, width } + } + crate::ImageClass::Depth => LocalType::Scalar { + kind: crate::ScalarKind::Float, + width, + }, + crate::ImageClass::Storage(format) => LocalType::Scalar { + kind: format.into(), + width, + }, + }; + let type_id = self.get_type_id(arena, LookupType::Local(local_type)); + let dim = map_dim(dim); + self.try_add_capabilities(dim.required_capabilities()); + super::instructions::instruction_type_image(id, type_id, dim, arrayed, class) + } + crate::TypeInner::Sampler { comparison: _ } => { + super::instructions::instruction_type_sampler(id) + } + crate::TypeInner::Array { base, size, stride } => { + if let Some(array_stride) = stride { + self.annotations + .push(super::instructions::instruction_decorate( + id, + spirv::Decoration::ArrayStride, + &[array_stride.get()], + )); + } + + let type_id = self.get_type_id(arena, LookupType::Handle(base)); + match size { + crate::ArraySize::Constant(const_handle) => { + let length_id = self.lookup_constant[&const_handle]; + super::instructions::instruction_type_array(id, type_id, length_id) + } + crate::ArraySize::Dynamic => { + super::instructions::instruction_type_runtime_array(id, type_id) + } + } + } + crate::TypeInner::Struct { ref members } => { + let mut member_ids = Vec::with_capacity(members.len()); + for member in members { + let member_id = self.get_type_id(arena, LookupType::Handle(member.ty)); + member_ids.push(member_id); + } + super::instructions::instruction_type_struct(id, member_ids.as_slice()) + } + crate::TypeInner::Pointer { base, class } => { + let type_id = self.get_type_id(arena, LookupType::Handle(base)); + self.lookup_type + .insert(LookupType::Local(LocalType::Pointer { base, class }), id); + super::instructions::instruction_type_pointer( + id, + self.parse_to_spirv_storage_class(class), + type_id, + ) + } + }; + + self.lookup_type.insert(LookupType::Handle(handle), id); + instruction.to_words(&mut self.logical_layout.declarations); + id + } + + fn write_constant_type( + &mut self, + handle: crate::Handle<crate::Constant>, + ir_module: &crate::Module, + ) -> (Instruction, Word) { + let id = self.generate_id(); + self.lookup_constant.insert(handle, id); + let constant = &ir_module.constants[handle]; + let arena = &ir_module.types; + + match constant.inner { + crate::ConstantInner::Sint(val) => { + let ty = &ir_module.types[constant.ty]; + let type_id = self.get_type_id(arena, LookupType::Handle(constant.ty)); + + let instruction = match ty.inner { + crate::TypeInner::Scalar { kind: _, width } => match width { + 4 => super::instructions::instruction_constant(type_id, id, &[val as u32]), + 8 => { + let (low, high) = ((val >> 32) as u32, val as u32); + super::instructions::instruction_constant(type_id, id, &[low, high]) + } + _ => unreachable!(), + }, + _ => unreachable!(), + }; + (instruction, id) + } + crate::ConstantInner::Uint(val) => { + let ty = &ir_module.types[constant.ty]; + let type_id = self.get_type_id(arena, LookupType::Handle(constant.ty)); + + let instruction = match ty.inner { + crate::TypeInner::Scalar { kind: _, width } => match width { + 4 => super::instructions::instruction_constant(type_id, id, &[val as u32]), + 8 => { + let (low, high) = ((val >> 32) as u32, val as u32); + super::instructions::instruction_constant(type_id, id, &[low, high]) + } + _ => unreachable!(), + }, + _ => unreachable!(), + }; + + (instruction, id) + } + crate::ConstantInner::Float(val) => { + let ty = &ir_module.types[constant.ty]; + let type_id = self.get_type_id(arena, LookupType::Handle(constant.ty)); + + let instruction = match ty.inner { + crate::TypeInner::Scalar { kind: _, width } => match width { + 4 => super::instructions::instruction_constant( + type_id, + id, + &[(val as f32).to_bits()], + ), + 8 => { + let bits = f64::to_bits(val); + let (low, high) = ((bits >> 32) as u32, bits as u32); + super::instructions::instruction_constant(type_id, id, &[low, high]) + } + _ => unreachable!(), + }, + _ => unreachable!(), + }; + (instruction, id) + } + crate::ConstantInner::Bool(val) => { + let type_id = self.get_type_id(arena, LookupType::Handle(constant.ty)); + + let instruction = if val { + super::instructions::instruction_constant_true(type_id, id) + } else { + super::instructions::instruction_constant_false(type_id, id) + }; + + (instruction, id) + } + crate::ConstantInner::Composite(ref constituents) => { + let mut constituent_ids = Vec::with_capacity(constituents.len()); + for constituent in constituents.iter() { + let constituent_id = self.get_constant_id(*constituent, &ir_module); + constituent_ids.push(constituent_id); + } + + let type_id = self.get_type_id(arena, LookupType::Handle(constant.ty)); + let instruction = super::instructions::instruction_constant_composite( + type_id, + id, + constituent_ids.as_slice(), + ); + (instruction, id) + } + } + } + + fn write_global_variable( + &mut self, + ir_module: &crate::Module, + handle: crate::Handle<crate::GlobalVariable>, + ) -> (Instruction, Word) { + let global_variable = &ir_module.global_variables[handle]; + let id = self.generate_id(); + + let class = self.parse_to_spirv_storage_class(global_variable.class); + self.try_add_capabilities(class.required_capabilities()); + + let init_word = global_variable + .init + .map(|constant| self.get_constant_id(constant, ir_module)); + let pointer_id = + self.get_pointer_id(&ir_module.types, global_variable.ty, global_variable.class); + let instruction = + super::instructions::instruction_variable(pointer_id, id, class, init_word); + + if self.writer_flags.contains(WriterFlags::DEBUG) { + if let Some(ref name) = global_variable.name { + self.debugs + .push(super::instructions::instruction_name(id, name.as_str())); + } + } + + if let Some(interpolation) = global_variable.interpolation { + let decoration = match interpolation { + crate::Interpolation::Linear => Some(spirv::Decoration::NoPerspective), + crate::Interpolation::Flat => Some(spirv::Decoration::Flat), + crate::Interpolation::Patch => Some(spirv::Decoration::Patch), + crate::Interpolation::Centroid => Some(spirv::Decoration::Centroid), + crate::Interpolation::Sample => Some(spirv::Decoration::Sample), + crate::Interpolation::Perspective => None, + }; + if let Some(decoration) = decoration { + self.annotations + .push(super::instructions::instruction_decorate( + id, + decoration, + &[], + )); + } + } + + match *global_variable.binding.as_ref().unwrap() { + crate::Binding::Location(location) => { + self.annotations + .push(super::instructions::instruction_decorate( + id, + spirv::Decoration::Location, + &[location], + )); + } + crate::Binding::Resource { group, binding } => { + self.annotations + .push(super::instructions::instruction_decorate( + id, + spirv::Decoration::DescriptorSet, + &[group], + )); + self.annotations + .push(super::instructions::instruction_decorate( + id, + spirv::Decoration::Binding, + &[binding], + )); + } + crate::Binding::BuiltIn(built_in) => { + let built_in = match built_in { + crate::BuiltIn::BaseInstance => spirv::BuiltIn::BaseInstance, + crate::BuiltIn::BaseVertex => spirv::BuiltIn::BaseVertex, + crate::BuiltIn::ClipDistance => spirv::BuiltIn::ClipDistance, + crate::BuiltIn::InstanceIndex => spirv::BuiltIn::InstanceIndex, + crate::BuiltIn::Position => spirv::BuiltIn::Position, + crate::BuiltIn::VertexIndex => spirv::BuiltIn::VertexIndex, + crate::BuiltIn::PointSize => spirv::BuiltIn::PointSize, + crate::BuiltIn::FragCoord => spirv::BuiltIn::FragCoord, + crate::BuiltIn::FrontFacing => spirv::BuiltIn::FrontFacing, + crate::BuiltIn::SampleIndex => spirv::BuiltIn::SampleId, + crate::BuiltIn::FragDepth => spirv::BuiltIn::FragDepth, + crate::BuiltIn::GlobalInvocationId => spirv::BuiltIn::GlobalInvocationId, + crate::BuiltIn::LocalInvocationId => spirv::BuiltIn::LocalInvocationId, + crate::BuiltIn::LocalInvocationIndex => spirv::BuiltIn::LocalInvocationIndex, + crate::BuiltIn::WorkGroupId => spirv::BuiltIn::WorkgroupId, + }; + + self.annotations + .push(super::instructions::instruction_decorate( + id, + spirv::Decoration::BuiltIn, + &[built_in as u32], + )); + } + } + + // TODO Initializer is optional and not (yet) included in the IR + + self.lookup_global_variable.insert(handle, id); + (instruction, id) + } + + fn get_function_type( + &mut self, + lookup_function_type: LookupFunctionType, + parameter_pointer_ids: Vec<Word>, + ) -> Word { + match self + .lookup_function_type + .entry(lookup_function_type.clone()) + { + Entry::Occupied(e) => *e.get(), + _ => { + let id = self.generate_id(); + let instruction = super::instructions::instruction_type_function( + id, + lookup_function_type.return_type_id, + parameter_pointer_ids.as_slice(), + ); + instruction.to_words(&mut self.logical_layout.declarations); + self.lookup_function_type.insert(lookup_function_type, id); + id + } + } + } + + fn write_composite_construct( + &mut self, + base_type_id: Word, + constituent_ids: &[Word], + block: &mut Block, + ) -> Word { + let id = self.generate_id(); + block + .body + .push(super::instructions::instruction_composite_construct( + base_type_id, + id, + constituent_ids, + )); + id + } + + fn get_type_inner<'a>( + &self, + ty_arena: &'a crate::Arena<crate::Type>, + lookup_ty: LookupType, + ) -> MaybeOwned<'a, crate::TypeInner> { + match lookup_ty { + LookupType::Handle(handle) => MaybeOwned::Borrowed(&ty_arena[handle].inner), + LookupType::Local(local_ty) => match local_ty { + LocalType::Scalar { kind, width } => { + MaybeOwned::Owned(crate::TypeInner::Scalar { kind, width }) + } + LocalType::Vector { size, kind, width } => { + MaybeOwned::Owned(crate::TypeInner::Vector { size, kind, width }) + } + LocalType::Pointer { base, class } => { + MaybeOwned::Owned(crate::TypeInner::Pointer { base, class }) + } + _ => unreachable!(), + }, + } + } + + fn write_expression<'a>( + &mut self, + ir_module: &'a crate::Module, + ir_function: &crate::Function, + expression: &crate::Expression, + block: &mut Block, + function: &mut Function, + ) -> Result<WriteExpressionOutput, Error> { + match *expression { + crate::Expression::Access { base, index } => { + let id = self.generate_id(); + + let (base_id, base_lookup_ty) = self.write_expression( + ir_module, + ir_function, + &ir_function.expressions[base], + block, + function, + )?; + let (index_id, _) = self.write_expression( + ir_module, + ir_function, + &ir_function.expressions[index], + block, + function, + )?; + + let base_ty_inner = self.get_type_inner(&ir_module.types, base_lookup_ty); + + let (pointer_id, type_id, lookup_ty) = match *base_ty_inner { + crate::TypeInner::Vector { kind, width, .. } => { + let scalar_id = self.get_type_id( + &ir_module.types, + LookupType::Local(LocalType::Scalar { kind, width }), + ); + ( + self.create_pointer(scalar_id, spirv::StorageClass::Function), + scalar_id, + LookupType::Local(LocalType::Scalar { kind, width }), + ) + } + _ => unimplemented!(), + }; + + block + .body + .push(super::instructions::instruction_access_chain( + pointer_id, + id, + base_id, + &[index_id], + )); + + let load_id = self.generate_id(); + block.body.push(super::instructions::instruction_load( + type_id, load_id, id, None, + )); + + Ok((load_id, lookup_ty)) + } + crate::Expression::AccessIndex { base, index } => { + let id = self.generate_id(); + let (base_id, base_lookup_ty) = self + .write_expression( + ir_module, + ir_function, + &ir_function.expressions[base], + block, + function, + ) + .unwrap(); + + let base_ty_inner = self.get_type_inner(&ir_module.types, base_lookup_ty); + + let (pointer_id, type_id, lookup_ty) = match *base_ty_inner { + crate::TypeInner::Vector { kind, width, .. } => { + let scalar_id = self.get_type_id( + &ir_module.types, + LookupType::Local(LocalType::Scalar { kind, width }), + ); + ( + self.create_pointer(scalar_id, spirv::StorageClass::Function), + scalar_id, + LookupType::Local(LocalType::Scalar { kind, width }), + ) + } + crate::TypeInner::Struct { ref members } => { + let member = &members[index as usize]; + let type_id = + self.get_type_id(&ir_module.types, LookupType::Handle(member.ty)); + ( + self.create_pointer(type_id, spirv::StorageClass::Uniform), + type_id, + LookupType::Handle(member.ty), + ) + } + _ => unimplemented!(), + }; + + let const_ty_id = self.get_type_id( + &ir_module.types, + LookupType::Local(LocalType::Scalar { + kind: crate::ScalarKind::Sint, + width: 4, + }), + ); + let const_id = self.create_constant(const_ty_id, &[index]); + + block + .body + .push(super::instructions::instruction_access_chain( + pointer_id, + id, + base_id, + &[const_id], + )); + + let load_id = self.generate_id(); + block.body.push(super::instructions::instruction_load( + type_id, load_id, id, None, + )); + + Ok((load_id, lookup_ty)) + } + crate::Expression::GlobalVariable(handle) => { + let var = &ir_module.global_variables[handle]; + let id = self.get_global_variable_id(&ir_module, handle); + + Ok((id, LookupType::Handle(var.ty))) + } + crate::Expression::Constant(handle) => { + let var = &ir_module.constants[handle]; + let id = self.get_constant_id(handle, ir_module); + Ok((id, LookupType::Handle(var.ty))) + } + crate::Expression::Compose { ty, ref components } => { + let base_type_id = self.get_type_id(&ir_module.types, LookupType::Handle(ty)); + + let mut constituent_ids = Vec::with_capacity(components.len()); + for component in components { + let expression = &ir_function.expressions[*component]; + let (component_id, component_local_ty) = self.write_expression( + ir_module, + &ir_function, + expression, + block, + function, + )?; + + let component_id = match expression { + crate::Expression::LocalVariable(_) + | crate::Expression::GlobalVariable(_) => { + let load_id = self.generate_id(); + block.body.push(super::instructions::instruction_load( + self.get_type_id(&ir_module.types, component_local_ty), + load_id, + component_id, + None, + )); + load_id + } + _ => component_id, + }; + + constituent_ids.push(component_id); + } + let constituent_ids_slice = constituent_ids.as_slice(); + + let id = match ir_module.types[ty].inner { + crate::TypeInner::Vector { .. } => { + self.write_composite_construct(base_type_id, constituent_ids_slice, block) + } + crate::TypeInner::Matrix { + rows, + columns, + width, + } => { + let vector_type_id = self.get_type_id( + &ir_module.types, + LookupType::Local(LocalType::Vector { + width, + kind: crate::ScalarKind::Float, + size: columns, + }), + ); + + let capacity = match rows { + crate::VectorSize::Bi => 2, + crate::VectorSize::Tri => 3, + crate::VectorSize::Quad => 4, + }; + + let mut vector_ids = Vec::with_capacity(capacity); + + for _ in 0..capacity { + let vector_id = self.write_composite_construct( + vector_type_id, + constituent_ids_slice, + block, + ); + vector_ids.push(vector_id); + } + + self.write_composite_construct(base_type_id, vector_ids.as_slice(), block) + } + _ => unreachable!(), + }; + + Ok((id, LookupType::Handle(ty))) + } + crate::Expression::Binary { op, left, right } => { + let id = self.generate_id(); + let left_expression = &ir_function.expressions[left]; + let right_expression = &ir_function.expressions[right]; + let (left_id, left_lookup_ty) = self.write_expression( + ir_module, + ir_function, + left_expression, + block, + function, + )?; + let (right_id, right_lookup_ty) = self.write_expression( + ir_module, + ir_function, + right_expression, + block, + function, + )?; + + let left_lookup_ty = left_lookup_ty; + let right_lookup_ty = right_lookup_ty; + + let left_ty_inner = self.get_type_inner(&ir_module.types, left_lookup_ty); + let right_ty_inner = self.get_type_inner(&ir_module.types, right_lookup_ty); + + let left_result_type_id = self.get_type_id(&ir_module.types, left_lookup_ty); + + let right_result_type_id = self.get_type_id(&ir_module.types, right_lookup_ty); + + let left_id = match *left_expression { + crate::Expression::LocalVariable(_) | crate::Expression::GlobalVariable(_) => { + let load_id = self.generate_id(); + block.body.push(super::instructions::instruction_load( + left_result_type_id, + load_id, + left_id, + None, + )); + load_id + } + _ => left_id, + }; + + let right_id = match *right_expression { + crate::Expression::LocalVariable(..) + | crate::Expression::GlobalVariable(..) => { + let load_id = self.generate_id(); + block.body.push(super::instructions::instruction_load( + right_result_type_id, + load_id, + right_id, + None, + )); + load_id + } + _ => right_id, + }; + + let left_dimension = get_dimension(&left_ty_inner); + let right_dimension = get_dimension(&right_ty_inner); + + let (instruction, lookup_ty) = match op { + crate::BinaryOperator::Multiply => match (left_dimension, right_dimension) { + (Dimension::Vector, Dimension::Scalar { .. }) => ( + super::instructions::instruction_vector_times_scalar( + left_result_type_id, + id, + left_id, + right_id, + ), + left_lookup_ty, + ), + (Dimension::Vector, Dimension::Matrix) => ( + super::instructions::instruction_vector_times_matrix( + left_result_type_id, + id, + left_id, + right_id, + ), + left_lookup_ty, + ), + (Dimension::Matrix, Dimension::Scalar { .. }) => ( + super::instructions::instruction_matrix_times_scalar( + left_result_type_id, + id, + left_id, + right_id, + ), + left_lookup_ty, + ), + (Dimension::Matrix, Dimension::Vector) => ( + super::instructions::instruction_matrix_times_vector( + right_result_type_id, + id, + left_id, + right_id, + ), + right_lookup_ty, + ), + (Dimension::Matrix, Dimension::Matrix) => ( + super::instructions::instruction_matrix_times_matrix( + left_result_type_id, + id, + left_id, + right_id, + ), + left_lookup_ty, + ), + (Dimension::Vector, Dimension::Vector) + | (Dimension::Scalar, Dimension::Scalar) + if left_ty_inner.scalar_kind() == Some(crate::ScalarKind::Float) => + { + ( + super::instructions::instruction_f_mul( + left_result_type_id, + id, + left_id, + right_id, + ), + left_lookup_ty, + ) + } + (Dimension::Vector, Dimension::Vector) + | (Dimension::Scalar, Dimension::Scalar) => ( + super::instructions::instruction_i_mul( + left_result_type_id, + id, + left_id, + right_id, + ), + left_lookup_ty, + ), + _ => unreachable!(), + }, + crate::BinaryOperator::Subtract => match *left_ty_inner { + crate::TypeInner::Scalar { kind, .. } => match kind { + crate::ScalarKind::Sint | crate::ScalarKind::Uint => ( + super::instructions::instruction_i_sub( + left_result_type_id, + id, + left_id, + right_id, + ), + left_lookup_ty, + ), + crate::ScalarKind::Float => ( + super::instructions::instruction_f_sub( + left_result_type_id, + id, + left_id, + right_id, + ), + left_lookup_ty, + ), + _ => unreachable!(), + }, + _ => unreachable!(), + }, + crate::BinaryOperator::And => ( + super::instructions::instruction_bitwise_and( + left_result_type_id, + id, + left_id, + right_id, + ), + left_lookup_ty, + ), + _ => unimplemented!("{:?}", op), + }; + + block.body.push(instruction); + Ok((id, lookup_ty)) + } + crate::Expression::LocalVariable(variable) => { + let var = &ir_function.local_variables[variable]; + function + .variables + .iter() + .find(|&v| v.name.as_ref().unwrap() == var.name.as_ref().unwrap()) + .map(|local_var| (local_var.id, LookupType::Handle(var.ty))) + .ok_or_else(|| Error::UnknownLocalVariable(var.clone())) + } + crate::Expression::FunctionArgument(index) => { + let handle = ir_function.arguments[index as usize].ty; + let type_id = self.get_type_id(&ir_module.types, LookupType::Handle(handle)); + let load_id = self.generate_id(); + + block.body.push(super::instructions::instruction_load( + type_id, + load_id, + function.parameters[index as usize].result_id.unwrap(), + None, + )); + Ok((load_id, LookupType::Handle(handle))) + } + crate::Expression::Call { + ref origin, + ref arguments, + } => match *origin { + crate::FunctionOrigin::Local(local_function) => { + let origin_function = &ir_module.functions[local_function]; + let id = self.generate_id(); + let mut argument_ids = vec![]; + + for argument in arguments { + let expression = &ir_function.expressions[*argument]; + let (id, lookup_ty) = self.write_expression( + ir_module, + ir_function, + expression, + block, + function, + )?; + + // Create variable - OpVariable + // Store value to variable - OpStore + // Use id of variable + + let handle = match lookup_ty { + LookupType::Handle(handle) => handle, + LookupType::Local(_) => unreachable!(), + }; + + let pointer_id = self.get_pointer_id( + &ir_module.types, + handle, + crate::StorageClass::Function, + ); + + let variable_id = self.generate_id(); + function.variables.push(LocalVariable { + id: variable_id, + name: None, + instruction: super::instructions::instruction_variable( + pointer_id, + variable_id, + spirv::StorageClass::Function, + None, + ), + }); + block.body.push(super::instructions::instruction_store( + variable_id, + id, + None, + )); + argument_ids.push(variable_id); + } + + let return_type_id = self + .get_function_return_type(origin_function.return_type, &ir_module.types); + + block + .body + .push(super::instructions::instruction_function_call( + return_type_id, + id, + *self.lookup_function.get(&local_function).unwrap(), + argument_ids.as_slice(), + )); + + let result_type = match origin_function.return_type { + Some(ty_handle) => LookupType::Handle(ty_handle), + None => LookupType::Local(LocalType::Void), + }; + Ok((id, result_type)) + } + _ => unimplemented!("{:?}", origin), + }, + crate::Expression::As { + expr, + kind, + convert, + } => { + if !convert { + return Err(Error::FeatureNotImplemented()); + } + + let (expr_id, expr_type) = self.write_expression( + ir_module, + ir_function, + &ir_function.expressions[expr], + block, + function, + )?; + + let expr_type_inner = self.get_type_inner(&ir_module.types, expr_type); + + let (expr_kind, local_type) = match *expr_type_inner { + crate::TypeInner::Scalar { + kind: expr_kind, + width, + } => (expr_kind, LocalType::Scalar { kind, width }), + crate::TypeInner::Vector { + size, + kind: expr_kind, + width, + } => (expr_kind, LocalType::Vector { size, kind, width }), + _ => unreachable!(), + }; + + let lookup_type = LookupType::Local(local_type); + let op = match (expr_kind, kind) { + _ if !convert => spirv::Op::Bitcast, + (crate::ScalarKind::Float, crate::ScalarKind::Uint) => spirv::Op::ConvertFToU, + (crate::ScalarKind::Float, crate::ScalarKind::Sint) => spirv::Op::ConvertFToS, + (crate::ScalarKind::Sint, crate::ScalarKind::Float) => spirv::Op::ConvertSToF, + (crate::ScalarKind::Uint, crate::ScalarKind::Float) => spirv::Op::ConvertUToF, + // We assume it's either an identity cast, or int-uint. + // In both cases no SPIR-V instructions need to be generated. + _ => { + let id = match ir_function.expressions[expr] { + crate::Expression::LocalVariable(_) + | crate::Expression::GlobalVariable(_) => { + let load_id = self.generate_id(); + let kind_type_id = self.get_type_id(&ir_module.types, expr_type); + block.body.push(super::instructions::instruction_load( + kind_type_id, + load_id, + expr_id, + None, + )); + load_id + } + _ => expr_id, + }; + return Ok((id, lookup_type)); + } + }; + + let id = self.generate_id(); + let kind_type_id = self.get_type_id(&ir_module.types, lookup_type); + let instruction = + super::instructions::instruction_unary(op, kind_type_id, id, expr_id); + block.body.push(instruction); + + Ok((id, lookup_type)) + } + crate::Expression::ImageSample { + image, + sampler, + coordinate, + level: _, + depth_ref: _, + } => { + // image + let image_expression = &ir_function.expressions[image]; + let (image_id, image_lookup_ty) = self.write_expression( + ir_module, + ir_function, + image_expression, + block, + function, + )?; + + let image_result_type_id = self.get_type_id(&ir_module.types, image_lookup_ty); + let image_id = match *image_expression { + crate::Expression::LocalVariable(_) | crate::Expression::GlobalVariable(_) => { + let load_id = self.generate_id(); + block.body.push(super::instructions::instruction_load( + image_result_type_id, + load_id, + image_id, + None, + )); + load_id + } + _ => image_id, + }; + + let image_ty = match image_lookup_ty { + LookupType::Handle(handle) => handle, + LookupType::Local(_) => unreachable!(), + }; + + // OpTypeSampledImage + let sampled_image_type_id = self.get_type_id( + &ir_module.types, + LookupType::Local(LocalType::SampledImage { + image_type: image_ty, + }), + ); + + // sampler + let sampler_expression = &ir_function.expressions[sampler]; + let (sampler_id, sampler_lookup_ty) = self.write_expression( + ir_module, + ir_function, + sampler_expression, + block, + function, + )?; + + let sampler_result_type_id = self.get_type_id(&ir_module.types, sampler_lookup_ty); + let sampler_id = match *sampler_expression { + crate::Expression::LocalVariable(_) | crate::Expression::GlobalVariable(_) => { + let load_id = self.generate_id(); + block.body.push(super::instructions::instruction_load( + sampler_result_type_id, + load_id, + sampler_id, + None, + )); + load_id + } + _ => sampler_id, + }; + + // coordinate + let coordinate_expression = &ir_function.expressions[coordinate]; + let (coordinate_id, coordinate_lookup_ty) = self.write_expression( + ir_module, + ir_function, + coordinate_expression, + block, + function, + )?; + + let coordinate_result_type_id = + self.get_type_id(&ir_module.types, coordinate_lookup_ty); + let coordinate_id = match *coordinate_expression { + crate::Expression::LocalVariable(_) | crate::Expression::GlobalVariable(_) => { + let load_id = self.generate_id(); + block.body.push(super::instructions::instruction_load( + coordinate_result_type_id, + load_id, + coordinate_id, + None, + )); + load_id + } + _ => coordinate_id, + }; + + // component kind + let image_type = &ir_module.types[image_ty]; + let image_sample_result_type = + if let crate::TypeInner::Image { class, .. } = image_type.inner { + let width = 4; + LookupType::Local(match class { + crate::ImageClass::Sampled { kind, multi: _ } => LocalType::Vector { + kind, + width, + size: crate::VectorSize::Quad, + }, + crate::ImageClass::Depth => LocalType::Scalar { + kind: crate::ScalarKind::Float, + width, + }, + _ => return Err(Error::BadImageClass(class)), + }) + } else { + return Err(Error::NotImage); + }; + + let sampled_image_id = self.generate_id(); + block + .body + .push(super::instructions::instruction_sampled_image( + sampled_image_type_id, + sampled_image_id, + image_id, + sampler_id, + )); + let id = self.generate_id(); + let image_sample_result_type_id = + self.get_type_id(&ir_module.types, image_sample_result_type); + block + .body + .push(super::instructions::instruction_image_sample_implicit_lod( + image_sample_result_type_id, + id, + sampled_image_id, + coordinate_id, + )); + Ok((id, image_sample_result_type)) + } + _ => unimplemented!("{:?}", expression), + } + } + + fn write_block( + &mut self, + statements: &[crate::Statement], + ir_module: &crate::Module, + ir_function: &crate::Function, + function: &mut Function, + ) -> spirv::Word { + let mut block = Block::new(); + let id = self.generate_id(); + block.label = Some(super::instructions::instruction_label(id)); + + for statement in statements { + match *statement { + crate::Statement::Block(ref ir_block) => { + if !ir_block.is_empty() { + //TODO: link the block with `OpBranch` + self.write_block(ir_block, ir_module, ir_function, function); + } + } + crate::Statement::Return { value } => { + block.termination = Some(match ir_function.return_type { + Some(_) => { + let expression = &ir_function.expressions[value.unwrap()]; + let (id, lookup_ty) = self + .write_expression( + ir_module, + ir_function, + expression, + &mut block, + function, + ) + .unwrap(); + + let id = match *expression { + crate::Expression::LocalVariable(_) + | crate::Expression::GlobalVariable(_) => { + let load_id = self.generate_id(); + let value_ty_id = self.get_type_id(&ir_module.types, lookup_ty); + block.body.push(super::instructions::instruction_load( + value_ty_id, + load_id, + id, + None, + )); + load_id + } + + _ => id, + }; + super::instructions::instruction_return_value(id) + } + None => super::instructions::instruction_return(), + }); + } + crate::Statement::Store { pointer, value } => { + let pointer_expression = &ir_function.expressions[pointer]; + let value_expression = &ir_function.expressions[value]; + let (pointer_id, _) = self + .write_expression( + ir_module, + ir_function, + pointer_expression, + &mut block, + function, + ) + .unwrap(); + let (value_id, value_lookup_ty) = self + .write_expression( + ir_module, + ir_function, + value_expression, + &mut block, + function, + ) + .unwrap(); + + let value_id = match value_expression { + crate::Expression::LocalVariable(_) + | crate::Expression::GlobalVariable(_) => { + let load_id = self.generate_id(); + let value_ty_id = self.get_type_id(&ir_module.types, value_lookup_ty); + block.body.push(super::instructions::instruction_load( + value_ty_id, + load_id, + value_id, + None, + )); + load_id + } + _ => value_id, + }; + + block.body.push(super::instructions::instruction_store( + pointer_id, value_id, None, + )); + } + _ => unimplemented!("{:?}", statement), + } + } + + function.blocks.push(block); + id + } + + fn write_physical_layout(&mut self) { + self.physical_layout.bound = self.id_count + 1; + } + + fn write_logical_layout(&mut self, ir_module: &crate::Module) { + let id = self.generate_id(); + super::instructions::instruction_ext_inst_import(id, "GLSL.std.450") + .to_words(&mut self.logical_layout.ext_inst_imports); + + if self.writer_flags.contains(WriterFlags::DEBUG) { + self.debugs.push(super::instructions::instruction_source( + spirv::SourceLanguage::GLSL, + 450, + )); + } + + for (handle, ir_function) in ir_module.functions.iter() { + let id = self.write_function(ir_function, ir_module); + self.lookup_function.insert(handle, id); + } + + for (&(stage, ref name), ir_ep) in ir_module.entry_points.iter() { + let entry_point_instruction = self.write_entry_point(ir_ep, stage, name, ir_module); + entry_point_instruction.to_words(&mut self.logical_layout.entry_points); + } + + for capability in self.capabilities.iter() { + super::instructions::instruction_capability(*capability) + .to_words(&mut self.logical_layout.capabilities); + } + + let addressing_model = spirv::AddressingModel::Logical; + let memory_model = spirv::MemoryModel::GLSL450; + self.try_add_capabilities(addressing_model.required_capabilities()); + self.try_add_capabilities(memory_model.required_capabilities()); + + super::instructions::instruction_memory_model(addressing_model, memory_model) + .to_words(&mut self.logical_layout.memory_model); + + if self.writer_flags.contains(WriterFlags::DEBUG) { + for debug in self.debugs.iter() { + debug.to_words(&mut self.logical_layout.debugs); + } + } + + for annotation in self.annotations.iter() { + annotation.to_words(&mut self.logical_layout.annotations); + } + } + + pub fn write(&mut self, ir_module: &crate::Module) -> Vec<Word> { + let mut words: Vec<Word> = vec![]; + + self.write_logical_layout(ir_module); + self.write_physical_layout(); + + self.physical_layout.in_words(&mut words); + self.logical_layout.in_words(&mut words); + words + } +} + +#[cfg(test)] +mod tests { + use crate::back::spv::{Writer, WriterFlags}; + use crate::Header; + + #[test] + fn test_writer_generate_id() { + let mut writer = create_writer(); + + assert_eq!(writer.id_count, 0); + writer.generate_id(); + assert_eq!(writer.id_count, 1); + } + + #[test] + fn test_try_add_capabilities() { + let mut writer = create_writer(); + + assert_eq!(writer.capabilities.len(), 0); + writer.try_add_capabilities(&[spirv::Capability::Shader]); + assert_eq!(writer.capabilities.len(), 1); + + writer.try_add_capabilities(&[spirv::Capability::Shader]); + assert_eq!(writer.capabilities.len(), 1); + } + + #[test] + fn test_write_physical_layout() { + let mut writer = create_writer(); + assert_eq!(writer.physical_layout.bound, 0); + writer.write_physical_layout(); + assert_eq!(writer.physical_layout.bound, 1); + } + + fn create_writer() -> Writer { + let header = Header { + generator: 0, + version: (1, 0, 0), + }; + Writer::new(&header, WriterFlags::NONE) + } +} |