naga/back/spv/
layout.rs

1use alloc::{vec, vec::Vec};
2use core::iter;
3
4use spirv::{Op, Word, MAGIC_NUMBER};
5
6use super::{Instruction, LogicalLayout, PhysicalLayout};
7
8#[cfg(test)]
9use alloc::format;
10
11// https://github.com/KhronosGroup/SPIRV-Headers/pull/195
12const GENERATOR: Word = 28;
13
14impl PhysicalLayout {
15    pub(super) const fn new(version: Word) -> Self {
16        PhysicalLayout {
17            magic_number: MAGIC_NUMBER,
18            version,
19            generator: GENERATOR,
20            bound: 0,
21            instruction_schema: 0x0u32,
22        }
23    }
24
25    pub(super) fn in_words(&self, sink: &mut impl Extend<Word>) {
26        sink.extend(iter::once(self.magic_number));
27        sink.extend(iter::once(self.version));
28        sink.extend(iter::once(self.generator));
29        sink.extend(iter::once(self.bound));
30        sink.extend(iter::once(self.instruction_schema));
31    }
32}
33
34impl super::recyclable::Recyclable for PhysicalLayout {
35    fn recycle(self) -> Self {
36        PhysicalLayout {
37            magic_number: self.magic_number,
38            version: self.version,
39            generator: self.generator,
40            instruction_schema: self.instruction_schema,
41            bound: 0,
42        }
43    }
44}
45
46impl LogicalLayout {
47    pub(super) fn in_words(&self, sink: &mut impl Extend<Word>) {
48        sink.extend(self.capabilities.iter().cloned());
49        sink.extend(self.extensions.iter().cloned());
50        sink.extend(self.ext_inst_imports.iter().cloned());
51        sink.extend(self.memory_model.iter().cloned());
52        sink.extend(self.entry_points.iter().cloned());
53        sink.extend(self.execution_modes.iter().cloned());
54        sink.extend(self.debugs.iter().cloned());
55        sink.extend(self.annotations.iter().cloned());
56        sink.extend(self.declarations.iter().cloned());
57        sink.extend(self.function_declarations.iter().cloned());
58        sink.extend(self.function_definitions.iter().cloned());
59    }
60}
61
62impl super::recyclable::Recyclable for LogicalLayout {
63    fn recycle(self) -> Self {
64        Self {
65            capabilities: self.capabilities.recycle(),
66            extensions: self.extensions.recycle(),
67            ext_inst_imports: self.ext_inst_imports.recycle(),
68            memory_model: self.memory_model.recycle(),
69            entry_points: self.entry_points.recycle(),
70            execution_modes: self.execution_modes.recycle(),
71            debugs: self.debugs.recycle(),
72            annotations: self.annotations.recycle(),
73            declarations: self.declarations.recycle(),
74            function_declarations: self.function_declarations.recycle(),
75            function_definitions: self.function_definitions.recycle(),
76        }
77    }
78}
79
80impl Instruction {
81    pub(super) const fn new(op: Op) -> Self {
82        Instruction {
83            op,
84            wc: 1, // Always start at 1 for the first word (OP + WC),
85            type_id: None,
86            result_id: None,
87            operands: vec![],
88        }
89    }
90
91    #[allow(clippy::panic)]
92    pub(super) fn set_type(&mut self, id: Word) {
93        assert!(self.type_id.is_none(), "Type can only be set once");
94        self.type_id = Some(id);
95        self.wc += 1;
96    }
97
98    #[allow(clippy::panic)]
99    pub(super) fn set_result(&mut self, id: Word) {
100        assert!(self.result_id.is_none(), "Result can only be set once");
101        self.result_id = Some(id);
102        self.wc += 1;
103    }
104
105    pub(super) fn add_operand(&mut self, operand: Word) {
106        self.operands.push(operand);
107        self.wc += 1;
108    }
109
110    pub(super) fn add_operands(&mut self, operands: Vec<Word>) {
111        for operand in operands.into_iter() {
112            self.add_operand(operand)
113        }
114    }
115
116    pub(super) fn to_words(&self, sink: &mut impl Extend<Word>) {
117        sink.extend(Some((self.wc << 16) | self.op as u32));
118        sink.extend(self.type_id);
119        sink.extend(self.result_id);
120        sink.extend(self.operands.iter().cloned());
121    }
122}
123
124impl Instruction {
125    #[cfg(test)]
126    fn validate(&self, words: &[Word]) {
127        let mut inst_index = 0;
128        let (wc, op) = ((words[inst_index] >> 16) as u16, words[inst_index] as u16);
129        inst_index += 1;
130
131        assert_eq!(wc, words.len() as u16);
132        assert_eq!(op, self.op as u16);
133
134        if self.type_id.is_some() {
135            assert_eq!(words[inst_index], self.type_id.unwrap());
136            inst_index += 1;
137        }
138
139        if self.result_id.is_some() {
140            assert_eq!(words[inst_index], self.result_id.unwrap());
141            inst_index += 1;
142        }
143
144        for (op_index, i) in (inst_index..wc as usize).enumerate() {
145            assert_eq!(words[i], self.operands[op_index]);
146        }
147    }
148}
149
150#[test]
151fn test_physical_layout_in_words() {
152    let bound = 5;
153    let version = 0x10203;
154
155    let mut output = vec![];
156    let mut layout = PhysicalLayout::new(version);
157    layout.bound = bound;
158
159    layout.in_words(&mut output);
160
161    assert_eq!(&output, &[MAGIC_NUMBER, version, GENERATOR, bound, 0,]);
162}
163
164#[test]
165fn test_logical_layout_in_words() {
166    let mut output = vec![];
167    let mut layout = LogicalLayout::default();
168    let layout_vectors = 11;
169    let mut instructions = Vec::with_capacity(layout_vectors);
170
171    let vector_names = &[
172        "Capabilities",
173        "Extensions",
174        "External Instruction Imports",
175        "Memory Model",
176        "Entry Points",
177        "Execution Modes",
178        "Debugs",
179        "Annotations",
180        "Declarations",
181        "Function Declarations",
182        "Function Definitions",
183    ];
184
185    for (i, _) in vector_names.iter().enumerate().take(layout_vectors) {
186        let mut dummy_instruction = Instruction::new(Op::Constant);
187        dummy_instruction.set_type((i + 1) as u32);
188        dummy_instruction.set_result((i + 2) as u32);
189        dummy_instruction.add_operand((i + 3) as u32);
190        dummy_instruction.add_operands(super::helpers::string_to_words(
191            format!("This is the vector: {}", vector_names[i]).as_str(),
192        ));
193        instructions.push(dummy_instruction);
194    }
195
196    instructions[0].to_words(&mut layout.capabilities);
197    instructions[1].to_words(&mut layout.extensions);
198    instructions[2].to_words(&mut layout.ext_inst_imports);
199    instructions[3].to_words(&mut layout.memory_model);
200    instructions[4].to_words(&mut layout.entry_points);
201    instructions[5].to_words(&mut layout.execution_modes);
202    instructions[6].to_words(&mut layout.debugs);
203    instructions[7].to_words(&mut layout.annotations);
204    instructions[8].to_words(&mut layout.declarations);
205    instructions[9].to_words(&mut layout.function_declarations);
206    instructions[10].to_words(&mut layout.function_definitions);
207
208    layout.in_words(&mut output);
209
210    let mut index: usize = 0;
211    for instruction in instructions {
212        let wc = instruction.wc as usize;
213        instruction.validate(&output[index..index + wc]);
214        index += wc;
215    }
216}