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
11const GENERATOR: Word = 28;
13
14impl PhysicalLayout {
15 pub(super) const fn new(major_version: u8, minor_version: u8) -> Self {
16 let version = ((major_version as u32) << 16) | ((minor_version as u32) << 8);
17 PhysicalLayout {
18 magic_number: MAGIC_NUMBER,
19 version,
20 generator: GENERATOR,
21 bound: 0,
22 instruction_schema: 0x0u32,
23 }
24 }
25
26 pub(super) fn in_words(&self, sink: &mut impl Extend<Word>) {
27 sink.extend(iter::once(self.magic_number));
28 sink.extend(iter::once(self.version));
29 sink.extend(iter::once(self.generator));
30 sink.extend(iter::once(self.bound));
31 sink.extend(iter::once(self.instruction_schema));
32 }
33
34 pub(super) const fn lang_version(&self) -> (u8, u8) {
36 let major = (self.version >> 16) as u8;
37 let minor = (self.version >> 8) as u8;
38 (major, minor)
39 }
40}
41
42impl super::reclaimable::Reclaimable for PhysicalLayout {
43 fn reclaim(self) -> Self {
44 PhysicalLayout {
45 magic_number: self.magic_number,
46 version: self.version,
47 generator: self.generator,
48 instruction_schema: self.instruction_schema,
49 bound: 0,
50 }
51 }
52}
53
54impl LogicalLayout {
55 pub(super) fn in_words(&self, sink: &mut impl Extend<Word>) {
56 sink.extend(self.capabilities.iter().cloned());
57 sink.extend(self.extensions.iter().cloned());
58 sink.extend(self.ext_inst_imports.iter().cloned());
59 sink.extend(self.memory_model.iter().cloned());
60 sink.extend(self.entry_points.iter().cloned());
61 sink.extend(self.execution_modes.iter().cloned());
62 sink.extend(self.debugs.iter().cloned());
63 sink.extend(self.annotations.iter().cloned());
64 sink.extend(self.declarations.iter().cloned());
65 sink.extend(self.function_declarations.iter().cloned());
66 sink.extend(self.function_definitions.iter().cloned());
67 }
68}
69
70impl super::reclaimable::Reclaimable for LogicalLayout {
71 fn reclaim(self) -> Self {
72 Self {
73 capabilities: self.capabilities.reclaim(),
74 extensions: self.extensions.reclaim(),
75 ext_inst_imports: self.ext_inst_imports.reclaim(),
76 memory_model: self.memory_model.reclaim(),
77 entry_points: self.entry_points.reclaim(),
78 execution_modes: self.execution_modes.reclaim(),
79 debugs: self.debugs.reclaim(),
80 annotations: self.annotations.reclaim(),
81 declarations: self.declarations.reclaim(),
82 function_declarations: self.function_declarations.reclaim(),
83 function_definitions: self.function_definitions.reclaim(),
84 }
85 }
86}
87
88impl Instruction {
89 pub(super) const fn new(op: Op) -> Self {
90 Instruction {
91 op,
92 type_id: None,
93 result_id: None,
94 operands: vec![],
95 }
96 }
97
98 pub(super) fn set_type(&mut self, id: Word) {
99 assert!(self.type_id.is_none(), "Type can only be set once");
100 self.type_id = Some(id);
101 }
102
103 pub(super) fn set_result(&mut self, id: Word) {
104 assert!(self.result_id.is_none(), "Result can only be set once");
105 self.result_id = Some(id);
106 }
107
108 pub(super) fn add_operand(&mut self, operand: Word) {
109 self.operands.push(operand);
110 }
111
112 pub(super) fn add_operands(&mut self, operands: Vec<Word>) {
113 for operand in operands.into_iter() {
114 self.add_operand(operand)
115 }
116 }
117
118 fn word_count(&self) -> usize {
119 const OPCODE_WORD_COUNT_FIELDS_SIZE: usize = 1;
120
121 OPCODE_WORD_COUNT_FIELDS_SIZE
122 + usize::from(self.type_id.is_some())
123 + usize::from(self.result_id.is_some())
124 + self.operands.len()
125 }
126
127 pub(super) fn to_words(&self, sink: &mut impl Extend<Word>) {
128 let word_count = self.word_count();
129 assert!(
130 word_count <= usize::from(u16::MAX),
131 "{:?} instruction is {word_count} words long, but SPIR-V instructions are limited to {} words",
132 self.op,
133 u16::MAX
134 );
135 sink.extend(Some(((word_count as u32) << 16) | self.op as u32));
136 sink.extend(self.type_id);
137 sink.extend(self.result_id);
138 sink.extend(self.operands.iter().cloned());
139 }
140}
141
142impl Instruction {
143 #[cfg(test)]
144 fn validate(&self, words: &[Word]) {
145 let mut inst_index = 0;
146 let (wc, op) = ((words[inst_index] >> 16) as u16, words[inst_index] as u16);
147 inst_index += 1;
148
149 assert_eq!(wc, words.len() as u16);
150 assert_eq!(op, self.op as u16);
151
152 if let Some(type_id) = self.type_id {
153 assert_eq!(words[inst_index], type_id);
154 inst_index += 1;
155 }
156
157 if let Some(result_id) = self.result_id {
158 assert_eq!(words[inst_index], result_id);
159 inst_index += 1;
160 }
161
162 for (op_index, i) in (inst_index..wc as usize).enumerate() {
163 assert_eq!(words[i], self.operands[op_index]);
164 }
165 }
166}
167
168#[test]
169fn test_physical_layout_in_words() {
170 let bound = 5;
171
172 let version = 0x0001_0200;
175
176 let mut output = vec![];
177 let mut layout = PhysicalLayout::new(1, 2);
178 layout.bound = bound;
179
180 layout.in_words(&mut output);
181
182 assert_eq!(&output, &[MAGIC_NUMBER, version, GENERATOR, bound, 0,]);
183}
184
185#[test]
186fn test_logical_layout_in_words() {
187 let mut output = vec![];
188 let mut layout = LogicalLayout::default();
189 let layout_vectors = 11;
190 let mut instructions = Vec::with_capacity(layout_vectors);
191
192 let vector_names = &[
193 "Capabilities",
194 "Extensions",
195 "External Instruction Imports",
196 "Memory Model",
197 "Entry Points",
198 "Execution Modes",
199 "Debugs",
200 "Annotations",
201 "Declarations",
202 "Function Declarations",
203 "Function Definitions",
204 ];
205
206 for (i, _) in vector_names.iter().enumerate().take(layout_vectors) {
207 let mut dummy_instruction = Instruction::new(Op::Constant);
208 dummy_instruction.set_type((i + 1) as u32);
209 dummy_instruction.set_result((i + 2) as u32);
210 dummy_instruction.add_operand((i + 3) as u32);
211 dummy_instruction.add_operands(super::helpers::string_to_words(
212 format!("This is the vector: {}", vector_names[i]).as_str(),
213 ));
214 instructions.push(dummy_instruction);
215 }
216
217 instructions[0].to_words(&mut layout.capabilities);
218 instructions[1].to_words(&mut layout.extensions);
219 instructions[2].to_words(&mut layout.ext_inst_imports);
220 instructions[3].to_words(&mut layout.memory_model);
221 instructions[4].to_words(&mut layout.entry_points);
222 instructions[5].to_words(&mut layout.execution_modes);
223 instructions[6].to_words(&mut layout.debugs);
224 instructions[7].to_words(&mut layout.annotations);
225 instructions[8].to_words(&mut layout.declarations);
226 instructions[9].to_words(&mut layout.function_declarations);
227 instructions[10].to_words(&mut layout.function_definitions);
228
229 layout.in_words(&mut output);
230
231 let mut index: usize = 0;
232 for instruction in instructions {
233 let wc = instruction.word_count();
234 instruction.validate(&output[index..index + wc]);
235 index += wc;
236 }
237}
238
239#[cfg(test)]
240fn entry_point_of_word_count(word_count: usize) -> Vec<Word> {
241 let mut instruction = Instruction::new(Op::EntryPoint);
242 instruction.add_operands(vec![1; word_count - 1]);
244
245 let mut output = vec![];
246 instruction.to_words(&mut output);
247 output
248}
249
250#[test]
252fn test_longest_encodable_instruction() {
253 let output = entry_point_of_word_count(u16::MAX as usize);
254
255 assert_eq!(output.len(), u16::MAX as usize);
256 assert_eq!(output[0] >> 16, u16::MAX as Word);
257}
258
259#[test]
261#[should_panic(
262 expected = "EntryPoint instruction is 65536 words long, but SPIR-V instructions are limited to 65535 words"
263)]
264fn test_instruction_word_count_overflow() {
265 entry_point_of_word_count(u16::MAX as usize + 1);
266}