summaryrefslogtreecommitdiffstats
path: root/third_party/rust/naga/src/back/spv
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/rust/naga/src/back/spv')
-rw-r--r--third_party/rust/naga/src/back/spv/helpers.rs20
-rw-r--r--third_party/rust/naga/src/back/spv/instructions.rs708
-rw-r--r--third_party/rust/naga/src/back/spv/layout.rs91
-rw-r--r--third_party/rust/naga/src/back/spv/layout_tests.rs166
-rw-r--r--third_party/rust/naga/src/back/spv/mod.rs52
-rw-r--r--third_party/rust/naga/src/back/spv/test_framework.rs27
-rw-r--r--third_party/rust/naga/src/back/spv/writer.rs1776
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)
+ }
+}