rspirv/dr/
constructs.rs

1use crate::grammar;
2use crate::spirv;
3
4use crate::spirv::Word;
5use crate::utils::version;
6use std::{convert, fmt};
7
8/// Data representation of a SPIR-V module.
9///
10/// Most of the fields are just vectors of `Instruction`s, but some fields
11/// store values decomposed from `Instruction`s for better investigation.
12///
13/// The order of its fields basically reveal the requirements in the
14/// [Logical Layout of a Module](https://goo.gl/2kVnfX) of the SPIR-V
15/// of the SPIR-V specification.
16#[derive(Clone, Debug, Default)]
17pub struct Module {
18    /// The module header.
19    pub header: Option<ModuleHeader>,
20    /// All OpCapability instructions.
21    pub capabilities: Vec<Instruction>,
22    /// All OpExtension instructions.
23    pub extensions: Vec<Instruction>,
24    /// All OpExtInstImport instructions.
25    pub ext_inst_imports: Vec<Instruction>,
26    /// The OpMemoryModel instruction.
27    ///
28    /// Although it is required by the specification to appear exactly once
29    /// per module, we keep it optional here to allow flexibility.
30    pub memory_model: Option<Instruction>,
31    /// All entry point declarations, using OpEntryPoint.
32    pub entry_points: Vec<Instruction>,
33    /// All execution mode declarations, using OpExecutionMode.
34    pub execution_modes: Vec<Instruction>,
35    /// Debug subsection: All OpString, OpSourceExtension, OpSource, and OpSourceContinued.
36    pub debug_string_source: Vec<Instruction>,
37    /// Debug subsection: All OpName and all OpMemberName.
38    pub debug_names: Vec<Instruction>,
39    /// Debug subsection: All OpModuleProcessed instructions.
40    pub debug_module_processed: Vec<Instruction>,
41    /// All annotation instructions.
42    pub annotations: Vec<Instruction>,
43    /// All types, constants, and global variables.
44    ///
45    /// As per the specification, they have to be bundled together
46    /// because they can depend on one another.
47    pub types_global_values: Vec<Instruction>,
48    /// All functions.
49    pub functions: Vec<Function>,
50}
51
52/// Data representation of a SPIR-V module header.
53#[derive(Clone, Debug, PartialEq, Eq)]
54pub struct ModuleHeader {
55    pub magic_number: Word,
56    pub version: Word,
57    pub generator: Word,
58    pub bound: Word,
59    pub reserved_word: Word,
60}
61
62/// Data representation of a SPIR-V function.
63#[derive(Clone, Debug, Default)]
64pub struct Function {
65    /// First (defining) instruction in this function.
66    pub def: Option<Instruction>,
67    /// Last (ending) instruction in this function.
68    pub end: Option<Instruction>,
69    /// Function parameters.
70    pub parameters: Vec<Instruction>,
71    /// Blocks in this function.
72    pub blocks: Vec<Block>,
73}
74
75/// Data representation of a SPIR-V block.
76#[derive(Clone, Debug, Default)]
77pub struct Block {
78    /// The label starting this block.
79    pub label: Option<Instruction>,
80    /// Instructions in this block.
81    pub instructions: Vec<Instruction>,
82}
83
84/// Data representation of a SPIR-V instruction.
85#[derive(Clone, Debug, PartialEq, Eq, Hash)]
86pub struct Instruction {
87    /// The class (grammar specification) of this instruction.
88    pub class: &'static grammar::Instruction<'static>,
89    /// Result type id.
90    pub result_type: Option<Word>,
91    /// Result id.
92    pub result_id: Option<Word>,
93    /// Operands.
94    pub operands: Vec<Operand>,
95}
96
97impl Instruction {
98    /// Compare two instructions by opcode and operands; this is the equality identity for `OpType` instructions
99    pub fn is_type_identical(&self, other: &Instruction) -> bool {
100        self.class.opcode == other.class.opcode && self.operands == other.operands
101    }
102}
103
104include!("autogen_operand.rs");
105
106impl Module {
107    /// Creates a new empty `Module` instance.
108    pub fn new() -> Self {
109        Module {
110            header: None,
111            capabilities: vec![],
112            extensions: vec![],
113            ext_inst_imports: vec![],
114            memory_model: None,
115            entry_points: vec![],
116            execution_modes: vec![],
117            debug_string_source: vec![],
118            debug_names: vec![],
119            debug_module_processed: vec![],
120            annotations: vec![],
121            types_global_values: vec![],
122            functions: vec![],
123        }
124    }
125
126    /// Returns an iterator over all global instructions.
127    pub fn global_inst_iter(&self) -> impl Iterator<Item = &Instruction> {
128        self.capabilities
129            .iter()
130            .chain(&self.extensions)
131            .chain(&self.ext_inst_imports)
132            .chain(&self.memory_model)
133            .chain(&self.entry_points)
134            .chain(&self.execution_modes)
135            .chain(&self.debug_string_source)
136            .chain(&self.debug_names)
137            .chain(&self.debug_module_processed)
138            .chain(&self.annotations)
139            .chain(&self.types_global_values)
140    }
141
142    /// Returns a mut iterator over all global instructions.
143    pub fn global_inst_iter_mut(&mut self) -> impl Iterator<Item = &mut Instruction> {
144        self.capabilities
145            .iter_mut()
146            .chain(&mut self.extensions)
147            .chain(&mut self.ext_inst_imports)
148            .chain(&mut self.memory_model)
149            .chain(&mut self.entry_points)
150            .chain(&mut self.execution_modes)
151            .chain(&mut self.debug_string_source)
152            .chain(&mut self.debug_names)
153            .chain(&mut self.debug_module_processed)
154            .chain(&mut self.annotations)
155            .chain(&mut self.types_global_values)
156    }
157
158    /// Returns a iterator over all instructions.
159    pub fn all_inst_iter(&self) -> impl Iterator<Item = &Instruction> {
160        self.capabilities
161            .iter()
162            .chain(&self.extensions)
163            .chain(&self.ext_inst_imports)
164            .chain(&self.memory_model)
165            .chain(&self.entry_points)
166            .chain(&self.execution_modes)
167            .chain(&self.debug_string_source)
168            .chain(&self.debug_names)
169            .chain(&self.debug_module_processed)
170            .chain(&self.annotations)
171            .chain(&self.types_global_values)
172            .chain(self.functions.iter().flat_map(|f| f.all_inst_iter()))
173    }
174
175    /// Returns a mut iterator over all instructions.
176    pub fn all_inst_iter_mut(&mut self) -> impl Iterator<Item = &mut Instruction> {
177        self.capabilities
178            .iter_mut()
179            .chain(&mut self.extensions)
180            .chain(&mut self.ext_inst_imports)
181            .chain(&mut self.memory_model)
182            .chain(&mut self.entry_points)
183            .chain(&mut self.execution_modes)
184            .chain(&mut self.debug_string_source)
185            .chain(&mut self.debug_names)
186            .chain(&mut self.debug_module_processed)
187            .chain(&mut self.annotations)
188            .chain(&mut self.types_global_values)
189            .chain(
190                self.functions
191                    .iter_mut()
192                    .flat_map(|f| f.all_inst_iter_mut()),
193            )
194    }
195}
196
197impl ModuleHeader {
198    /// Creates a new `ModuleHeader` instance.
199    pub fn new(bound: Word) -> ModuleHeader {
200        ModuleHeader {
201            magic_number: spirv::MAGIC_NUMBER,
202            version: version::create_word_from_version(spirv::MAJOR_VERSION, spirv::MINOR_VERSION),
203            generator: 0x000f_0000, // TODO: lower 16-bit: tool version number
204            bound,
205            reserved_word: 0,
206        }
207    }
208
209    /// Sets the SPIR-V version to the given major.minor version.
210    pub fn set_version(&mut self, major: u8, minor: u8) {
211        self.version = version::create_word_from_version(major, minor);
212    }
213
214    /// Returns the major and minor version numbers as a tuple.
215    pub fn version(&self) -> (u8, u8) {
216        version::create_version_from_word(self.version)
217    }
218
219    /// Returns the generator's name and version as a tuple.
220    pub fn generator(&self) -> (&str, u16) {
221        let tool = (self.generator & 0xffff_0000) >> 16;
222        let version = (self.generator & 0xffff) as u16;
223        let tool: &str = match tool {
224            0 => "The Khronos Group",
225            1 => "LunarG",
226            2 => "Valve",
227            3 => "Codeplay",
228            4 => "NVIDIA",
229            5 => "ARM",
230            6 => "LLVM/SPIR-V Translator",
231            7 => "SPIR-V Tools Assembler",
232            8 => "Glslang",
233            9 => "Qualcomm",
234            10 => "AMD",
235            11 => "Intel",
236            12 => "Imagination",
237            13 => "Shaderc",
238            14 => "spiregg",
239            15 => "rspirv",
240            _ => "Unknown",
241        };
242        (tool, version)
243    }
244}
245
246impl Function {
247    /// Creates a new empty `Function` instance.
248    pub fn new() -> Self {
249        Function {
250            def: None,
251            end: None,
252            parameters: vec![],
253            blocks: vec![],
254        }
255    }
256
257    pub fn def_id(&self) -> Option<Word> {
258        self.def.as_ref().and_then(|inst| inst.result_id)
259    }
260
261    pub fn all_inst_iter(&self) -> impl Iterator<Item = &Instruction> {
262        self.def
263            .iter()
264            .chain(self.parameters.iter())
265            .chain(
266                self.blocks
267                    .iter()
268                    .flat_map(|b| b.label.iter().chain(b.instructions.iter())),
269            )
270            .chain(self.end.iter())
271    }
272
273    pub fn all_inst_iter_mut(&mut self) -> impl Iterator<Item = &mut Instruction> {
274        self.def
275            .iter_mut()
276            .chain(self.parameters.iter_mut())
277            .chain(
278                self.blocks
279                    .iter_mut()
280                    .flat_map(|b| b.label.iter_mut().chain(b.instructions.iter_mut())),
281            )
282            .chain(self.end.iter_mut())
283    }
284}
285
286impl Block {
287    /// Creates a new empty `Block` instance.
288    pub fn new() -> Self {
289        Block {
290            label: None,
291            instructions: vec![],
292        }
293    }
294
295    pub fn label_id(&self) -> Option<Word> {
296        self.label.as_ref().and_then(|inst| inst.result_id)
297    }
298}
299
300impl Instruction {
301    /// Creates a new `Instruction` instance.
302    pub fn new(
303        opcode: spirv::Op,
304        result_type: Option<Word>,
305        result_id: Option<Word>,
306        operands: Vec<Operand>,
307    ) -> Self {
308        Instruction {
309            class: grammar::CoreInstructionTable::get(opcode),
310            result_type,
311            result_id,
312            operands,
313        }
314    }
315}
316
317// Sadly cannot use impl<T: Into<String>> here.
318impl<'a> convert::From<&'a str> for Operand {
319    fn from(val: &'a str) -> Self {
320        Operand::LiteralString(val.to_owned())
321    }
322}
323
324#[cfg(test)]
325mod tests {
326    use crate::dr;
327    use crate::spirv;
328
329    #[test]
330    fn test_convert_from_string() {
331        assert_eq!(
332            dr::Operand::LiteralString("wow".to_string()),
333            dr::Operand::from("wow")
334        );
335        assert_eq!(
336            dr::Operand::LiteralString("wow".to_string()),
337            dr::Operand::from("wow".to_string())
338        );
339    }
340
341    #[test]
342    fn test_convert_from_numbers() {
343        assert_eq!(dr::Operand::LiteralBit32(16u32), dr::Operand::from(16u32));
344        assert_eq!(
345            dr::Operand::LiteralBit64(128934u64),
346            dr::Operand::from(128934u64)
347        );
348        assert_eq!(
349            dr::Operand::LiteralBit32(std::f32::consts::PI.to_bits()),
350            dr::Operand::from(std::f32::consts::PI.to_bits())
351        );
352        assert_eq!(
353            dr::Operand::LiteralBit64(10.4235f64.to_bits()),
354            dr::Operand::from(10.4235f64.to_bits())
355        );
356    }
357
358    #[test]
359    fn test_convert_from_bit_enums() {
360        assert_eq!(
361            dr::Operand::LoopControl(spirv::LoopControl::DONT_UNROLL | spirv::LoopControl::UNROLL),
362            dr::Operand::from(spirv::LoopControl::DONT_UNROLL | spirv::LoopControl::UNROLL)
363        );
364        assert_eq!(
365            dr::Operand::MemoryAccess(spirv::MemoryAccess::NONE),
366            dr::Operand::from(spirv::MemoryAccess::NONE)
367        );
368    }
369
370    #[test]
371    fn test_convert_from_value_enums() {
372        assert_eq!(
373            dr::Operand::BuiltIn(spirv::BuiltIn::Position),
374            dr::Operand::from(spirv::BuiltIn::Position)
375        );
376        assert_eq!(
377            dr::Operand::Capability(spirv::Capability::Pipes),
378            dr::Operand::from(spirv::Capability::Pipes)
379        );
380    }
381
382    #[test]
383    fn test_convert_from_op() {
384        assert_eq!(
385            dr::Operand::LiteralSpecConstantOpInteger(spirv::Op::IAdd),
386            dr::Operand::from(spirv::Op::IAdd)
387        );
388    }
389
390    #[test]
391    fn test_operand_display() {
392        assert_eq!(
393            format!(
394                "{}",
395                dr::Operand::FunctionControl(
396                    spirv::FunctionControl::INLINE | spirv::FunctionControl::CONST
397                )
398            ),
399            "FunctionControl(INLINE | CONST)",
400        );
401        assert_eq!(format!("{}", dr::Operand::IdRef(3)), "%3");
402        assert_eq!(format!("{}", dr::Operand::LiteralBit32(3)), "3");
403    }
404}