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(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    /// Returns `(major, minor)`.
35    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::recyclable::Recyclable for PhysicalLayout {
43    fn recycle(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::recyclable::Recyclable for LogicalLayout {
71    fn recycle(self) -> Self {
72        Self {
73            capabilities: self.capabilities.recycle(),
74            extensions: self.extensions.recycle(),
75            ext_inst_imports: self.ext_inst_imports.recycle(),
76            memory_model: self.memory_model.recycle(),
77            entry_points: self.entry_points.recycle(),
78            execution_modes: self.execution_modes.recycle(),
79            debugs: self.debugs.recycle(),
80            annotations: self.annotations.recycle(),
81            declarations: self.declarations.recycle(),
82            function_declarations: self.function_declarations.recycle(),
83            function_definitions: self.function_definitions.recycle(),
84        }
85    }
86}
87
88impl Instruction {
89    pub(super) const fn new(op: Op) -> Self {
90        Instruction {
91            op,
92            wc: 1, // Always start at 1 for the first word (OP + WC),
93            type_id: None,
94            result_id: None,
95            operands: vec![],
96        }
97    }
98
99    #[allow(clippy::panic)]
100    pub(super) fn set_type(&mut self, id: Word) {
101        assert!(self.type_id.is_none(), "Type can only be set once");
102        self.type_id = Some(id);
103        self.wc += 1;
104    }
105
106    #[allow(clippy::panic)]
107    pub(super) fn set_result(&mut self, id: Word) {
108        assert!(self.result_id.is_none(), "Result can only be set once");
109        self.result_id = Some(id);
110        self.wc += 1;
111    }
112
113    pub(super) fn add_operand(&mut self, operand: Word) {
114        self.operands.push(operand);
115        self.wc += 1;
116    }
117
118    pub(super) fn add_operands(&mut self, operands: Vec<Word>) {
119        for operand in operands.into_iter() {
120            self.add_operand(operand)
121        }
122    }
123
124    pub(super) fn to_words(&self, sink: &mut impl Extend<Word>) {
125        sink.extend(Some((self.wc << 16) | self.op as u32));
126        sink.extend(self.type_id);
127        sink.extend(self.result_id);
128        sink.extend(self.operands.iter().cloned());
129    }
130}
131
132impl Instruction {
133    #[cfg(test)]
134    fn validate(&self, words: &[Word]) {
135        let mut inst_index = 0;
136        let (wc, op) = ((words[inst_index] >> 16) as u16, words[inst_index] as u16);
137        inst_index += 1;
138
139        assert_eq!(wc, words.len() as u16);
140        assert_eq!(op, self.op as u16);
141
142        if self.type_id.is_some() {
143            assert_eq!(words[inst_index], self.type_id.unwrap());
144            inst_index += 1;
145        }
146
147        if self.result_id.is_some() {
148            assert_eq!(words[inst_index], self.result_id.unwrap());
149            inst_index += 1;
150        }
151
152        for (op_index, i) in (inst_index..wc as usize).enumerate() {
153            assert_eq!(words[i], self.operands[op_index]);
154        }
155    }
156}
157
158#[test]
159fn test_physical_layout_in_words() {
160    let bound = 5;
161
162    // The least and most significant bytes of `version` must both be zero
163    // according to the SPIR-V spec.
164    let version = 0x0001_0200;
165
166    let mut output = vec![];
167    let mut layout = PhysicalLayout::new(1, 2);
168    layout.bound = bound;
169
170    layout.in_words(&mut output);
171
172    assert_eq!(&output, &[MAGIC_NUMBER, version, GENERATOR, bound, 0,]);
173}
174
175#[test]
176fn test_logical_layout_in_words() {
177    let mut output = vec![];
178    let mut layout = LogicalLayout::default();
179    let layout_vectors = 11;
180    let mut instructions = Vec::with_capacity(layout_vectors);
181
182    let vector_names = &[
183        "Capabilities",
184        "Extensions",
185        "External Instruction Imports",
186        "Memory Model",
187        "Entry Points",
188        "Execution Modes",
189        "Debugs",
190        "Annotations",
191        "Declarations",
192        "Function Declarations",
193        "Function Definitions",
194    ];
195
196    for (i, _) in vector_names.iter().enumerate().take(layout_vectors) {
197        let mut dummy_instruction = Instruction::new(Op::Constant);
198        dummy_instruction.set_type((i + 1) as u32);
199        dummy_instruction.set_result((i + 2) as u32);
200        dummy_instruction.add_operand((i + 3) as u32);
201        dummy_instruction.add_operands(super::helpers::string_to_words(
202            format!("This is the vector: {}", vector_names[i]).as_str(),
203        ));
204        instructions.push(dummy_instruction);
205    }
206
207    instructions[0].to_words(&mut layout.capabilities);
208    instructions[1].to_words(&mut layout.extensions);
209    instructions[2].to_words(&mut layout.ext_inst_imports);
210    instructions[3].to_words(&mut layout.memory_model);
211    instructions[4].to_words(&mut layout.entry_points);
212    instructions[5].to_words(&mut layout.execution_modes);
213    instructions[6].to_words(&mut layout.debugs);
214    instructions[7].to_words(&mut layout.annotations);
215    instructions[8].to_words(&mut layout.declarations);
216    instructions[9].to_words(&mut layout.function_declarations);
217    instructions[10].to_words(&mut layout.function_definitions);
218
219    layout.in_words(&mut output);
220
221    let mut index: usize = 0;
222    for instruction in instructions {
223        let wc = instruction.wc as usize;
224        instruction.validate(&output[index..index + wc]);
225        index += wc;
226    }
227}