rspirv/dr/build/
mod.rs

1#![allow(clippy::too_many_arguments)]
2
3use crate::dr;
4use crate::spirv;
5
6use super::Error;
7use std::result;
8
9type BuildResult<T> = result::Result<T, Error>;
10
11/// The data representation builder.
12///
13/// Constructs a [`Module`](struct.Module.html) by aggregating results from
14/// method calls for various instructions.
15///
16/// This builder is designed to be low level; its build methods' signatures
17/// basically reflects the layout of the corresponding SPIR-V instructions
18/// faithfully.
19///
20/// If a SPIR-V instruction generates a result id and the result id can be
21/// forward referenced, the build method will take an optional `result_id`
22/// parameter. Filling it with `Some(val)` will instruct the builder to use
23/// the given `val` as the result id. For other cases, an unused result id
24/// will be automatically assigned from the builder.
25///
26/// So for instructions forward referencing an id, to avoid id collision,
27/// you can either
28///
29/// * first append the target instruction generating that id and then append the
30///   forward referencing instruction; or
31/// * use the `id()` method to get an unused id from the builder, use it in the
32///   forward referencing instruction, and then later fill the optional
33///   `result_id` parameter of the target instruction with the same id.
34///
35/// Instructions belonging to the module (e.g., `OpDecorate`) can be appended
36/// at any time, no matter that a block is currently under construction
37/// or not. Intructions that can appear both in the module and block
38/// (e.g., `OpVariable`) will be inserted to the current block under
39/// construction first, if any.
40///
41/// # Errors
42///
43/// Methods in the builder implement little sanity check; only appending
44/// instructions that violates the module structure is guarded. So methods
45/// possibly returning errors are basically those related to function and
46/// block construction (e.g., `OpFunction` and `OpLabel`).
47///
48/// Errors returned are enumerants related to function structure from the
49/// [`Error`](enum.Error.html) enum.
50///
51/// # Examples
52///
53/// ```
54/// use rspirv::binary::Disassemble;
55///
56/// let mut b = rspirv::dr::Builder::new();
57/// b.set_version(1, 0);
58/// b.memory_model(spirv::AddressingModel::Logical, spirv::MemoryModel::Simple);
59/// let void = b.type_void();
60/// let voidf = b.type_function(void, vec![void]);
61/// b.begin_function(void,
62///                  None,
63///                  (spirv::FunctionControl::DONT_INLINE |
64///                   spirv::FunctionControl::CONST),
65///                  voidf)
66///  .unwrap();
67/// b.begin_block(None).unwrap();
68/// b.ret().unwrap();
69/// b.end_function().unwrap();
70///
71/// assert_eq!(b.module().disassemble(),
72///            "; SPIR-V\n\
73///             ; Version: 1.0\n\
74///             ; Generator: rspirv\n\
75///             ; Bound: 5\n\
76///             OpMemoryModel Logical Simple\n\
77///             %1 = OpTypeVoid\n\
78///             %2 = OpTypeFunction %1 %1\n\
79///             %3 = OpFunction  %1  DontInline|Const %2\n\
80///             %4 = OpLabel\n\
81///             OpReturn\n\
82///             OpFunctionEnd");
83/// ```
84#[derive(Default)]
85pub struct Builder {
86    module: dr::Module,
87    next_id: u32,
88    selected_function: Option<usize>,
89    selected_block: Option<usize>,
90}
91
92pub enum InsertPoint {
93    Begin,
94    End,
95    FromBegin(usize),
96    FromEnd(usize),
97}
98
99impl Builder {
100    /// Creates a new empty builder.
101    pub fn new() -> Builder {
102        Builder {
103            module: dr::Module::new(),
104            next_id: 1,
105            selected_function: None,
106            selected_block: None,
107        }
108    }
109
110    /// Create a new builder from an existing module
111    pub fn new_from_module(module: dr::Module) -> Builder {
112        let next_id = module
113            .header
114            .as_ref()
115            .map(|h| h.bound)
116            .expect("Expecting ModuleHeader with valid bound");
117
118        Builder {
119            module,
120            next_id,
121            selected_function: None,
122            selected_block: None,
123        }
124    }
125
126    pub fn insert_into_block(
127        &mut self,
128        insert_point: InsertPoint,
129        inst: dr::Instruction,
130    ) -> BuildResult<()> {
131        let (selected_function, selected_block) =
132            match (self.selected_function, self.selected_block) {
133                (Some(f), Some(b)) => (f, b),
134                _ => return Err(Error::DetachedInstruction(Some(inst))),
135            };
136
137        let block = &mut self.module.functions[selected_function].blocks[selected_block];
138
139        match insert_point {
140            InsertPoint::End => block.instructions.push(inst),
141            InsertPoint::Begin => block.instructions.insert(0, inst),
142            InsertPoint::FromEnd(offset) => {
143                let end = block.instructions.len();
144                block.instructions.insert(end - offset, inst)
145            }
146            InsertPoint::FromBegin(offset) => block.instructions.insert(offset, inst),
147        }
148
149        Ok(())
150    }
151
152    pub fn insert_types_global_values(&mut self, insert_point: InsertPoint, inst: dr::Instruction) {
153        match insert_point {
154            InsertPoint::End => self.module.types_global_values.push(inst),
155            InsertPoint::Begin => self.module.types_global_values.insert(0, inst),
156            InsertPoint::FromEnd(offset) => {
157                let end = self.module.types_global_values.len();
158                self.module.types_global_values.insert(end - offset, inst)
159            }
160            InsertPoint::FromBegin(offset) => self.module.types_global_values.insert(offset, inst),
161        }
162    }
163
164    pub fn pop_instruction(&mut self) -> BuildResult<dr::Instruction> {
165        let (selected_function, selected_block) =
166            match (self.selected_function, self.selected_block) {
167                (Some(f), Some(b)) => (f, b),
168                _ => return Err(Error::DetachedInstruction(None)),
169            };
170
171        let block = &mut self.module.functions[selected_function].blocks[selected_block];
172
173        block.instructions.pop().ok_or(Error::EmptyInstructionList)
174    }
175
176    /// Sets the SPIR-V version to the given major.minor version.
177    ///
178    /// If this method is not called, the generated SPIR-V will be set as the newest version
179    /// supported.
180    pub fn set_version(&mut self, major: u8, minor: u8) {
181        if self.module.header.is_none() {
182            // The bound will be fixed up when module() is called.
183            self.module.header = Some(dr::ModuleHeader::new(0));
184        }
185        self.module
186            .header
187            .as_mut()
188            .unwrap()
189            .set_version(major, minor);
190    }
191
192    /// Get the SPIR-V version as a (major, minor) tuple
193    pub fn version(&self) -> Option<(u8, u8)> {
194        self.module.header.as_ref().map(|h| h.version())
195    }
196
197    /// Returns the `Module` under construction.
198    pub fn module(self) -> dr::Module {
199        let mut module = self.module;
200
201        match &mut module.header {
202            Some(header) => header.bound = self.next_id,
203            None => module.header = Some(dr::ModuleHeader::new(self.next_id)),
204        }
205
206        module
207    }
208
209    /// Returns the `Module` under construction as a reference. Note that header.bound will be inaccurate.
210    pub fn module_ref(&self) -> &dr::Module {
211        &self.module
212    }
213
214    /// Returns the `Module` under construction as a mutable reference. Note that header.bound will be inaccurate.
215    pub fn module_mut(&mut self) -> &mut dr::Module {
216        &mut self.module
217    }
218
219    pub fn selected_function(&self) -> Option<usize> {
220        self.selected_function
221    }
222
223    pub fn selected_block(&self) -> Option<usize> {
224        self.selected_block
225    }
226
227    /// Returns the next unused id.
228    pub fn id(&mut self) -> spirv::Word {
229        let id = self.next_id;
230        self.next_id += 1;
231        id
232    }
233
234    /// Insert a OpType instruction, deduplicate it if needed and either return the existing ID
235    /// or a new unused ID if we can't find find the instruction already. Useful to uphold
236    /// the SPIR-V rule that non-aggregate types can't be duplicates.
237    pub fn dedup_insert_type(&mut self, inst: &dr::Instruction) -> Option<spirv::Word> {
238        for ty in &self.module.types_global_values {
239            if ty.is_type_identical(inst) {
240                if let Some(id) = ty.result_id {
241                    return Some(id);
242                }
243            }
244        }
245
246        None
247    }
248
249    /// Find all blocks that end in OpReturn
250    pub fn find_return_block_indices(&self) -> Vec<usize> {
251        let mut result = vec![];
252
253        if let Some(sel_fn) = self.selected_function {
254            let func = &self.module.functions[sel_fn];
255
256            for (idx, blk) in func.blocks.iter().enumerate() {
257                // OpReturn must be the last instruction in a block
258                let last_instr = blk.instructions.last().unwrap();
259
260                match last_instr.class.opcode {
261                    spirv::Op::Return | spirv::Op::ReturnValue => {
262                        result.push(idx);
263                    }
264                    _ => {}
265                }
266            }
267        }
268
269        result
270    }
271
272    /// Select a function to insert instructions into by name
273    pub fn select_function_by_name(&mut self, name: &str) -> BuildResult<()> {
274        for dbg in &self.module.debug_names {
275            if dbg.class.opcode == spirv::Op::Name {
276                if let dr::Operand::IdRef(target_id) = dbg.operands[0] {
277                    if let dr::Operand::LiteralString(found_name) = &dbg.operands[1] {
278                        if found_name == name {
279                            for (idx, func) in self.module.functions.iter().enumerate() {
280                                if func.def.as_ref().unwrap().result_id.unwrap() == target_id {
281                                    return self.select_function(Some(idx));
282                                }
283                            }
284                        }
285                    }
286                }
287            }
288        }
289
290        Err(Error::FunctionNotFound)
291    }
292
293    /// Select a function to insert instructions into by index (indexed into self.module.functions), or unselect if None
294    pub fn select_function(&mut self, idx: Option<usize>) -> BuildResult<()> {
295        match idx {
296            Some(idx) => {
297                if idx < self.module.functions.len() {
298                    self.selected_function = Some(idx);
299                    Ok(())
300                } else {
301                    Err(Error::FunctionNotFound)
302                }
303            }
304            None => {
305                // make sure to unselect block too
306                self.selected_block = None;
307                self.selected_function = None;
308                Ok(())
309            }
310        }
311    }
312
313    /// Select a basic block (by index) to insert instructions into, indexed off of self.modules.functions[self.selected_function].blocks[idx], or unselect if None
314    pub fn select_block(&mut self, idx: Option<usize>) -> BuildResult<()> {
315        match idx {
316            Some(idx) => {
317                let selected_function = match self.selected_function {
318                    Some(f) => f,
319                    None => return Err(Error::DetachedBlock),
320                };
321                if idx < self.module.functions[selected_function].blocks.len() {
322                    self.selected_block = Some(idx);
323                    Ok(())
324                } else {
325                    Err(Error::BlockNotFound)
326                }
327            }
328            None => {
329                self.selected_block = None;
330                Ok(())
331            }
332        }
333    }
334
335    /// Begins building of a new function.
336    ///
337    /// If `function_id` is `Some(val)`, then `val` will be used as the result
338    /// id of the function under construction; otherwise, an unused result id
339    /// will be automatically assigned.
340    pub fn begin_function(
341        &mut self,
342        return_type: spirv::Word,
343        function_id: Option<spirv::Word>,
344        control: spirv::FunctionControl,
345        function_type: spirv::Word,
346    ) -> BuildResult<spirv::Word> {
347        if self.selected_function.is_some() {
348            return Err(Error::NestedFunction);
349        }
350
351        let id = match function_id {
352            Some(v) => v,
353            None => self.id(),
354        };
355
356        let mut f = dr::Function::new();
357        f.def = Some(dr::Instruction::new(
358            spirv::Op::Function,
359            Some(return_type),
360            Some(id),
361            vec![
362                dr::Operand::FunctionControl(control),
363                dr::Operand::IdRef(function_type),
364            ],
365        ));
366        self.module.functions.push(f);
367        self.selected_function = Some(self.module.functions.len() - 1);
368        Ok(id)
369    }
370
371    /// Ends building of the current function.
372    pub fn end_function(&mut self) -> BuildResult<()> {
373        let selected_function = match self.selected_function {
374            Some(f) => f,
375            None => return Err(Error::MismatchedFunctionEnd),
376        };
377
378        self.module.functions[selected_function].end = Some(dr::Instruction::new(
379            spirv::Op::FunctionEnd,
380            None,
381            None,
382            vec![],
383        ));
384        self.selected_function = None;
385        Ok(())
386    }
387
388    /// Declares a formal parameter for the current function.
389    pub fn function_parameter(&mut self, result_type: spirv::Word) -> BuildResult<spirv::Word> {
390        let selected_function = match self.selected_function {
391            Some(f) => f,
392            None => return Err(Error::DetachedFunctionParameter),
393        };
394        let id = self.id();
395        let inst = dr::Instruction::new(
396            spirv::Op::FunctionParameter,
397            Some(result_type),
398            Some(id),
399            vec![],
400        );
401        self.module.functions[selected_function]
402            .parameters
403            .push(inst);
404        Ok(id)
405    }
406
407    /// Begins building of a new block.
408    ///
409    /// If `label_id` is `Some(val)`, then `val` will be used as the result
410    /// id for the `OpLabel` instruction begining this block; otherwise,
411    /// a unused result id will be automatically assigned.
412    pub fn begin_block(&mut self, label_id: Option<spirv::Word>) -> BuildResult<spirv::Word> {
413        let selected_function = match self.selected_function {
414            Some(f) => f,
415            None => return Err(Error::DetachedBlock),
416        };
417        if self.selected_block.is_some() {
418            return Err(Error::NestedBlock);
419        }
420
421        let id = match label_id {
422            Some(v) => v,
423            None => self.id(),
424        };
425
426        let mut bb = dr::Block::new();
427        bb.label = Some(dr::Instruction::new(
428            spirv::Op::Label,
429            None,
430            Some(id),
431            vec![],
432        ));
433
434        let blocks = &mut self.module.functions[selected_function].blocks;
435        blocks.push(bb);
436        self.selected_block = Some(blocks.len() - 1);
437        Ok(id)
438    }
439
440    /// Begins building of a new block.
441    ///
442    /// Counter to `begin_block` that always generates a new OpLabel at the beginning of a block - in some cases
443    /// this is undesirable (such as when constructing a branch).
444    pub fn begin_block_no_label(
445        &mut self,
446        label_id: Option<spirv::Word>,
447    ) -> BuildResult<spirv::Word> {
448        let selected_function = match self.selected_function {
449            Some(f) => f,
450            None => return Err(Error::DetachedBlock),
451        };
452        if self.selected_block.is_some() {
453            return Err(Error::NestedBlock);
454        }
455
456        let id = match label_id {
457            Some(v) => v,
458            None => self.id(),
459        };
460
461        let bb = dr::Block::new();
462        let blocks = &mut self.module.functions[selected_function].blocks;
463        blocks.push(bb);
464        self.selected_block = Some(blocks.len() - 1);
465        Ok(id)
466    }
467
468    fn end_block(&mut self, inst: dr::Instruction) -> BuildResult<()> {
469        self.insert_end_block(InsertPoint::End, inst)
470    }
471
472    fn insert_end_block(
473        &mut self,
474        insert_point: InsertPoint,
475        inst: dr::Instruction,
476    ) -> BuildResult<()> {
477        if self.selected_block.is_some() {
478            self.insert_into_block(insert_point, inst)?;
479            self.selected_block = None;
480            return Ok(());
481        }
482
483        Err(Error::MismatchedTerminator)
484    }
485
486    /// Appends an OpCapability instruction.
487    pub fn capability(&mut self, capability: spirv::Capability) {
488        let inst = dr::Instruction::new(
489            spirv::Op::Capability,
490            None,
491            None,
492            vec![dr::Operand::Capability(capability)],
493        );
494        self.module.capabilities.push(inst);
495    }
496
497    /// Appends an OpExtension instruction.
498    pub fn extension(&mut self, extension: impl Into<String>) {
499        let inst = dr::Instruction::new(
500            spirv::Op::Extension,
501            None,
502            None,
503            vec![dr::Operand::LiteralString(extension.into())],
504        );
505        self.module.extensions.push(inst);
506    }
507
508    /// Appends an OpExtInstImport instruction and returns the result id.
509    pub fn ext_inst_import(&mut self, extended_inst_set: impl Into<String>) -> spirv::Word {
510        let id = self.id();
511        let inst = dr::Instruction::new(
512            spirv::Op::ExtInstImport,
513            None,
514            Some(id),
515            vec![dr::Operand::LiteralString(extended_inst_set.into())],
516        );
517        self.module.ext_inst_imports.push(inst);
518        id
519    }
520
521    /// Appends an OpMemoryModel instruction.
522    pub fn memory_model(
523        &mut self,
524        addressing_model: spirv::AddressingModel,
525        memory_model: spirv::MemoryModel,
526    ) {
527        let inst = dr::Instruction::new(
528            spirv::Op::MemoryModel,
529            None,
530            None,
531            vec![
532                dr::Operand::AddressingModel(addressing_model),
533                dr::Operand::MemoryModel(memory_model),
534            ],
535        );
536        self.module.memory_model = Some(inst);
537    }
538
539    /// Appends an OpEntryPoint instruction.
540    pub fn entry_point(
541        &mut self,
542        execution_model: spirv::ExecutionModel,
543        entry_point: spirv::Word,
544        name: impl Into<String>,
545        interface: impl AsRef<[spirv::Word]>,
546    ) {
547        let mut operands = vec![
548            dr::Operand::ExecutionModel(execution_model),
549            dr::Operand::IdRef(entry_point),
550            dr::Operand::LiteralString(name.into()),
551        ];
552        for v in interface.as_ref() {
553            operands.push(dr::Operand::IdRef(*v));
554        }
555
556        let inst = dr::Instruction::new(spirv::Op::EntryPoint, None, None, operands);
557        self.module.entry_points.push(inst);
558    }
559
560    /// Appends an OpExecutionMode instruction.
561    pub fn execution_mode(
562        &mut self,
563        entry_point: spirv::Word,
564        execution_mode: spirv::ExecutionMode,
565        params: impl AsRef<[u32]>,
566    ) {
567        let mut operands = vec![
568            dr::Operand::IdRef(entry_point),
569            dr::Operand::ExecutionMode(execution_mode),
570        ];
571        for v in params.as_ref() {
572            operands.push(dr::Operand::LiteralBit32(*v));
573        }
574
575        let inst = dr::Instruction::new(spirv::Op::ExecutionMode, None, None, operands);
576        self.module.execution_modes.push(inst);
577    }
578
579    /// Appends an OpExecutionModeId instruction.
580    pub fn execution_mode_id(
581        &mut self,
582        entry_point: spirv::Word,
583        execution_mode: spirv::ExecutionMode,
584        params: impl AsRef<[u32]>,
585    ) {
586        let mut operands = vec![
587            dr::Operand::IdRef(entry_point),
588            dr::Operand::ExecutionMode(execution_mode),
589        ];
590        for v in params.as_ref() {
591            operands.push(dr::Operand::LiteralBit32(*v));
592        }
593
594        let inst = dr::Instruction::new(spirv::Op::ExecutionModeId, None, None, operands);
595        self.module.execution_modes.push(inst);
596    }
597
598    pub fn ext_inst(
599        &mut self,
600        result_type: spirv::Word,
601        result_id: Option<spirv::Word>,
602        extension_set: spirv::Word,
603        instruction: spirv::Word,
604        operands: impl IntoIterator<Item = dr::Operand>,
605    ) -> BuildResult<spirv::Word> {
606        let mut ops = vec![
607            dr::Operand::IdRef(extension_set),
608            dr::Operand::LiteralExtInstInteger(instruction),
609        ];
610        ops.extend(operands);
611        let _id = result_id.unwrap_or_else(|| self.id());
612        let inst = dr::Instruction::new(spirv::Op::ExtInst, Some(result_type), Some(_id), ops);
613        self.insert_into_block(InsertPoint::End, inst)?;
614        Ok(_id)
615    }
616
617    /// Appends an `OpLine` instruction.
618    ///
619    /// If a block is currently selected, the `OpLine` is inserted into that block. If no block is
620    /// currently selected, the `OpLine` is inserted into `types_global_values`.
621    pub fn line(&mut self, file: spirv::Word, line: u32, column: u32) {
622        let inst = dr::Instruction::new(
623            spirv::Op::Line,
624            None,
625            None,
626            vec![
627                dr::Operand::IdRef(file),
628                dr::Operand::LiteralBit32(line),
629                dr::Operand::LiteralBit32(column),
630            ],
631        );
632        if self.selected_block.is_some() {
633            self.insert_into_block(InsertPoint::End, inst)
634                .expect("Internal error: insert_into_block failed when selected_block was Some");
635        } else {
636            // types_global_values is the only valid section (other than functions) that
637            // OpLine/OpNoLine can be placed in, so put it there.
638            self.module.types_global_values.push(inst);
639        }
640    }
641
642    /// Appends an `OpNoLine` instruction.
643    ///
644    /// If a block is currently selected, the `OpNoLine` is inserted into that block. If no block
645    /// is currently selected, the `OpNoLine` is inserted into `types_global_values`.
646    pub fn no_line(&mut self) {
647        let inst = dr::Instruction::new(spirv::Op::NoLine, None, None, vec![]);
648        if self.selected_block.is_some() {
649            self.insert_into_block(InsertPoint::End, inst)
650                .expect("Internal error: insert_into_block failed when selected_block was Some");
651        } else {
652            // types_global_values is the only valid section (other than functions) that
653            // OpLine/OpNoLine can be placed in, so put it there.
654            self.module.types_global_values.push(inst);
655        }
656    }
657}
658
659include!("autogen_type.rs");
660include!("autogen_constant.rs");
661include!("autogen_annotation.rs");
662include!("autogen_terminator.rs");
663include!("autogen_debug.rs");
664
665impl Builder {
666    /// Appends an OpDecorationGroup instruction and returns the result id.
667    pub fn decoration_group(&mut self) -> spirv::Word {
668        let id = self.id();
669        self.module.annotations.push(dr::Instruction::new(
670            spirv::Op::DecorationGroup,
671            None,
672            Some(id),
673            vec![],
674        ));
675        id
676    }
677
678    pub fn string(&mut self, s: impl Into<String>) -> spirv::Word {
679        let id = self.id();
680        self.module.debug_string_source.push(dr::Instruction::new(
681            spirv::Op::String,
682            None,
683            Some(id),
684            vec![dr::Operand::LiteralString(s.into())],
685        ));
686        id
687    }
688}
689
690impl Builder {
691    /// Appends an OpTypeForwardPointer instruction.
692    pub fn type_forward_pointer(
693        &mut self,
694        pointer_type: spirv::Word,
695        storage_class: spirv::StorageClass,
696    ) {
697        self.module.types_global_values.push(dr::Instruction::new(
698            spirv::Op::TypeForwardPointer,
699            None,
700            None,
701            vec![
702                dr::Operand::IdRef(pointer_type),
703                dr::Operand::StorageClass(storage_class),
704            ],
705        ));
706    }
707
708    /// Appends an OpTypePointer instruction and returns the result id, or return the existing id if the instruction was already present.
709    pub fn type_pointer(
710        &mut self,
711        result_id: Option<spirv::Word>,
712        storage_class: spirv::StorageClass,
713        pointee_type: spirv::Word,
714    ) -> spirv::Word {
715        let mut inst = dr::Instruction::new(
716            spirv::Op::TypePointer,
717            None,
718            result_id,
719            vec![
720                dr::Operand::StorageClass(storage_class),
721                dr::Operand::IdRef(pointee_type),
722            ],
723        );
724        if let Some(result_id) = result_id {
725            // An explicit ID was provided, emit it no matter what.
726            self.module.types_global_values.push(inst);
727            result_id
728        } else if let Some(id) = self.dedup_insert_type(&inst) {
729            // No ID was provided, and the type has already been declared.
730            id
731        } else {
732            // No ID was provided, it didn't already exist, so generate a new ID and emit it.
733            let new_id = self.id();
734            inst.result_id = Some(new_id);
735            self.module.types_global_values.push(inst);
736            new_id
737        }
738    }
739
740    /// Appends an OpTypeOpaque instruction and returns the result id.
741    pub fn type_opaque(&mut self, type_name: impl Into<String>) -> spirv::Word {
742        let id = self.id();
743        self.module.types_global_values.push(dr::Instruction::new(
744            spirv::Op::TypeOpaque,
745            None,
746            Some(id),
747            vec![dr::Operand::LiteralString(type_name.into())],
748        ));
749        id
750    }
751
752    /// Appends an OpConstant instruction with the given 32-bit bit pattern `value`.
753    /// or the module if no block is under construction.
754    pub fn constant_bit32(&mut self, result_type: spirv::Word, value: u32) -> spirv::Word {
755        let id = self.id();
756        let inst = dr::Instruction::new(
757            spirv::Op::Constant,
758            Some(result_type),
759            Some(id),
760            vec![dr::Operand::LiteralBit32(value)],
761        );
762        self.module.types_global_values.push(inst);
763        id
764    }
765
766    /// Appends an OpConstant instruction with the given 64-bit bit pattern `value`.
767    pub fn constant_bit64(&mut self, result_type: spirv::Word, value: u64) -> spirv::Word {
768        let id = self.id();
769        let inst = dr::Instruction::new(
770            spirv::Op::Constant,
771            Some(result_type),
772            Some(id),
773            vec![dr::Operand::LiteralBit64(value)],
774        );
775        self.module.types_global_values.push(inst);
776        id
777    }
778
779    /// Appends an OpSpecConstant instruction with the given 32-bit bit pattern `value`.
780    /// or the module if no block is under construction.
781    pub fn spec_constant_bit32(&mut self, result_type: spirv::Word, value: u32) -> spirv::Word {
782        let id = self.id();
783        let inst = dr::Instruction::new(
784            spirv::Op::SpecConstant,
785            Some(result_type),
786            Some(id),
787            vec![dr::Operand::LiteralBit32(value)],
788        );
789        self.module.types_global_values.push(inst);
790        id
791    }
792
793    /// Appends an OpSpecConstant instruction with the given 64-bit bit pattern `value`.
794    pub fn spec_constant_bit64(&mut self, result_type: spirv::Word, value: u64) -> spirv::Word {
795        let id = self.id();
796        let inst = dr::Instruction::new(
797            spirv::Op::SpecConstant,
798            Some(result_type),
799            Some(id),
800            vec![dr::Operand::LiteralBit64(value)],
801        );
802        self.module.types_global_values.push(inst);
803        id
804    }
805
806    /// Appends an OpVariable instruction to either the current block
807    /// or the module if no block is under construction.
808    pub fn variable(
809        &mut self,
810        result_type: spirv::Word,
811        result_id: Option<spirv::Word>,
812        storage_class: spirv::StorageClass,
813        initializer: Option<spirv::Word>,
814    ) -> spirv::Word {
815        let id = match result_id {
816            Some(v) => v,
817            None => self.id(),
818        };
819        let mut operands = vec![dr::Operand::StorageClass(storage_class)];
820        if let Some(val) = initializer {
821            operands.push(dr::Operand::IdRef(val));
822        }
823        let inst = dr::Instruction::new(spirv::Op::Variable, Some(result_type), Some(id), operands);
824
825        match (self.selected_function, self.selected_block) {
826            (Some(selected_function), Some(selected_block)) => {
827                self.module.functions[selected_function].blocks[selected_block]
828                    .instructions
829                    .push(inst)
830            }
831            _ => self.module.types_global_values.push(inst),
832        }
833        id
834    }
835
836    /// Appends an OpUndef instruction to either the current block
837    /// or the module if no block is under construction.
838    pub fn undef(
839        &mut self,
840        result_type: spirv::Word,
841        result_id: Option<spirv::Word>,
842    ) -> spirv::Word {
843        let id = match result_id {
844            Some(v) => v,
845            None => self.id(),
846        };
847        let inst = dr::Instruction::new(spirv::Op::Undef, Some(result_type), Some(id), vec![]);
848
849        match (self.selected_function, self.selected_block) {
850            (Some(selected_function), Some(selected_block)) => {
851                self.module.functions[selected_function].blocks[selected_block]
852                    .instructions
853                    .push(inst)
854            }
855            _ => self.module.types_global_values.push(inst),
856        }
857        id
858    }
859}
860
861include!("autogen_norm_insts.rs");
862
863#[cfg(test)]
864mod tests {
865    use crate::dr;
866    use crate::spirv;
867
868    use super::Builder;
869    use std::f32;
870
871    use crate::binary::Disassemble;
872
873    fn has_only_one_global_inst(module: &dr::Module) -> bool {
874        if !module.functions.is_empty() {
875            return false;
876        }
877        (module.capabilities.len()
878            + module.extensions.len()
879            + module.ext_inst_imports.len()
880            + module.entry_points.len()
881            + module.types_global_values.len()
882            + module.execution_modes.len()
883            + module.debug_string_source.len()
884            + module.debug_names.len()
885            + module.debug_module_processed.len()
886            + module.annotations.len())
887            + (usize::from(module.memory_model.is_some()))
888            == 1
889    }
890
891    #[test]
892    fn test_spirv_version() {
893        let mut b = Builder::new();
894        b.set_version(1, 2);
895        let m = b.module();
896        let header = &m.header;
897        assert!(header.is_some());
898        assert_eq!((1, 2), header.as_ref().unwrap().version());
899    }
900
901    #[test]
902    fn test_memory_model() {
903        let mut b = Builder::new();
904        b.memory_model(spirv::AddressingModel::Logical, spirv::MemoryModel::Simple);
905        let m = b.module();
906        assert!(m.memory_model.is_some());
907        let inst = m.memory_model.as_ref().unwrap();
908        assert!(has_only_one_global_inst(&m));
909        assert_eq!("MemoryModel", inst.class.opname);
910        assert_eq!(2, inst.operands.len());
911        assert_eq!(
912            dr::Operand::from(spirv::AddressingModel::Logical),
913            inst.operands[0]
914        );
915        assert_eq!(
916            dr::Operand::from(spirv::MemoryModel::Simple),
917            inst.operands[1]
918        );
919    }
920
921    #[test]
922    fn test_decoration_no_additional_params() {
923        let mut b = Builder::new();
924        b.member_decorate(1, 0, spirv::Decoration::RelaxedPrecision, vec![]);
925        let m = b.module();
926        assert!(has_only_one_global_inst(&m));
927        let inst = m.annotations.last().unwrap();
928        assert_eq!("MemberDecorate", inst.class.opname);
929        assert_eq!(3, inst.operands.len());
930        assert_eq!(dr::Operand::IdRef(1), inst.operands[0]);
931        assert_eq!(dr::Operand::from(0u32), inst.operands[1]);
932        assert_eq!(
933            dr::Operand::from(spirv::Decoration::RelaxedPrecision),
934            inst.operands[2]
935        );
936    }
937
938    #[test]
939    fn test_decoration_with_additional_params() {
940        let mut b = Builder::new();
941        b.decorate(
942            1,
943            spirv::Decoration::LinkageAttributes,
944            vec![
945                dr::Operand::from("name"),
946                dr::Operand::from(spirv::LinkageType::Export),
947            ],
948        );
949        let m = b.module();
950        assert!(has_only_one_global_inst(&m));
951        let inst = m.annotations.last().unwrap();
952        assert_eq!("Decorate", inst.class.opname);
953        assert_eq!(4, inst.operands.len());
954        assert_eq!(dr::Operand::IdRef(1), inst.operands[0]);
955        assert_eq!(
956            dr::Operand::from(spirv::Decoration::LinkageAttributes),
957            inst.operands[1]
958        );
959        assert_eq!(dr::Operand::from("name"), inst.operands[2]);
960        assert_eq!(
961            dr::Operand::from(spirv::LinkageType::Export),
962            inst.operands[3]
963        );
964    }
965
966    #[test]
967    fn test_constant_bit32() {
968        let mut b = Builder::new();
969        let float = b.type_float(32);
970        // Normal numbers
971        b.constant_bit32(float, f32::consts::PI.to_bits());
972        b.constant_bit32(float, 2e-10f32.to_bits());
973        // Zero
974        b.constant_bit32(float, 0.0f32.to_bits());
975        // Inf
976        b.constant_bit32(float, f32::NEG_INFINITY.to_bits());
977        // Subnormal numbers
978        b.constant_bit32(float, (-1.0e-40f32).to_bits());
979        // NaN
980        b.constant_bit32(float, f32::NAN.to_bits());
981        let m = b.module();
982        assert_eq!(7, m.types_global_values.len());
983
984        let inst = &m.types_global_values[1];
985        assert_eq!(spirv::Op::Constant, inst.class.opcode);
986        assert_eq!(Some(1), inst.result_type);
987        assert_eq!(Some(2), inst.result_id);
988        assert_eq!(
989            dr::Operand::from(f32::consts::PI.to_bits()),
990            inst.operands[0]
991        );
992
993        let inst = &m.types_global_values[2];
994        assert_eq!(spirv::Op::Constant, inst.class.opcode);
995        assert_eq!(Some(1), inst.result_type);
996        assert_eq!(Some(3), inst.result_id);
997        assert_eq!(dr::Operand::from(2e-10f32.to_bits()), inst.operands[0]);
998
999        let inst = &m.types_global_values[3];
1000        assert_eq!(spirv::Op::Constant, inst.class.opcode);
1001        assert_eq!(Some(1), inst.result_type);
1002        assert_eq!(Some(4), inst.result_id);
1003        assert_eq!(dr::Operand::from(0.0f32.to_bits()), inst.operands[0]);
1004
1005        let inst = &m.types_global_values[4];
1006        assert_eq!(spirv::Op::Constant, inst.class.opcode);
1007        assert_eq!(Some(1), inst.result_type);
1008        assert_eq!(Some(5), inst.result_id);
1009        assert_eq!(
1010            dr::Operand::from(f32::NEG_INFINITY.to_bits()),
1011            inst.operands[0]
1012        );
1013
1014        let inst = &m.types_global_values[5];
1015        assert_eq!(spirv::Op::Constant, inst.class.opcode);
1016        assert_eq!(Some(1), inst.result_type);
1017        assert_eq!(Some(6), inst.result_id);
1018        assert_eq!(dr::Operand::from((-1.0e-40f32).to_bits()), inst.operands[0]);
1019
1020        let inst = &m.types_global_values[6];
1021        assert_eq!(spirv::Op::Constant, inst.class.opcode);
1022        assert_eq!(Some(1), inst.result_type);
1023        assert_eq!(Some(7), inst.result_id);
1024        // NaN != NaN
1025        match inst.operands[0] {
1026            dr::Operand::LiteralBit32(f) => assert!(f32::from_bits(f).is_nan()),
1027            _ => panic!(),
1028        }
1029    }
1030
1031    #[test]
1032    fn test_spec_constant_bit32() {
1033        let mut b = Builder::new();
1034        let float = b.type_float(32);
1035        // Normal numbers
1036        b.spec_constant_bit32(float, 10.0f32.to_bits());
1037        // Zero
1038        b.spec_constant_bit32(float, (-0.0f32).to_bits());
1039        // Inf
1040        b.spec_constant_bit32(float, f32::INFINITY.to_bits());
1041        // Subnormal numbers
1042        b.spec_constant_bit32(float, 1.0e-40f32.to_bits());
1043        // Nan
1044        b.spec_constant_bit32(float, f32::NAN.to_bits());
1045        let m = b.module();
1046        assert_eq!(6, m.types_global_values.len());
1047
1048        let inst = &m.types_global_values[1];
1049        assert_eq!(spirv::Op::SpecConstant, inst.class.opcode);
1050        assert_eq!(Some(1), inst.result_type);
1051        assert_eq!(Some(2), inst.result_id);
1052        assert_eq!(dr::Operand::from(10.0f32.to_bits()), inst.operands[0]);
1053
1054        let inst = &m.types_global_values[2];
1055        assert_eq!(spirv::Op::SpecConstant, inst.class.opcode);
1056        assert_eq!(Some(1), inst.result_type);
1057        assert_eq!(Some(3), inst.result_id);
1058        assert_eq!(dr::Operand::from((-0.0f32).to_bits()), inst.operands[0]);
1059
1060        let inst = &m.types_global_values[3];
1061        assert_eq!(spirv::Op::SpecConstant, inst.class.opcode);
1062        assert_eq!(Some(1), inst.result_type);
1063        assert_eq!(Some(4), inst.result_id);
1064        assert_eq!(dr::Operand::from(f32::INFINITY.to_bits()), inst.operands[0]);
1065
1066        let inst = &m.types_global_values[4];
1067        assert_eq!(spirv::Op::SpecConstant, inst.class.opcode);
1068        assert_eq!(Some(1), inst.result_type);
1069        assert_eq!(Some(5), inst.result_id);
1070        assert_eq!(dr::Operand::from(1.0e-40f32.to_bits()), inst.operands[0]);
1071
1072        let inst = &m.types_global_values[5];
1073        assert_eq!(spirv::Op::SpecConstant, inst.class.opcode);
1074        assert_eq!(Some(1), inst.result_type);
1075        assert_eq!(Some(6), inst.result_id);
1076        // NaN != NaN
1077        match inst.operands[0] {
1078            dr::Operand::LiteralBit32(f) => assert!(f32::from_bits(f).is_nan()),
1079            _ => panic!(),
1080        }
1081    }
1082
1083    #[test]
1084    fn test_forward_ref_pointer_type() {
1085        let mut b = Builder::new();
1086        let float = b.type_float(32); // 1
1087                                      // Let builder generate
1088        let p1 = b.type_pointer(None, spirv::StorageClass::Input, float); // 2
1089                                                                          // We supply
1090        let pointee = b.id(); // 3
1091        b.type_forward_pointer(pointee, spirv::StorageClass::Output);
1092        let p2 = b.type_pointer(Some(pointee), spirv::StorageClass::Output, float);
1093        let m = b.module();
1094        assert_eq!(1, float);
1095        assert_eq!(2, p1);
1096        assert_eq!(pointee, p2); // Return the same id
1097        assert_eq!(4, m.types_global_values.len());
1098
1099        let inst = &m.types_global_values[0];
1100        assert_eq!(spirv::Op::TypeFloat, inst.class.opcode);
1101        assert_eq!(None, inst.result_type);
1102        assert_eq!(Some(1), inst.result_id);
1103        assert_eq!(vec![dr::Operand::LiteralBit32(32)], inst.operands);
1104
1105        let inst = &m.types_global_values[1];
1106        assert_eq!(spirv::Op::TypePointer, inst.class.opcode);
1107        assert_eq!(None, inst.result_type);
1108        assert_eq!(Some(2), inst.result_id);
1109        assert_eq!(
1110            vec![
1111                dr::Operand::from(spirv::StorageClass::Input),
1112                dr::Operand::IdRef(1),
1113            ],
1114            inst.operands
1115        );
1116
1117        let inst = &m.types_global_values[2];
1118        assert_eq!(spirv::Op::TypeForwardPointer, inst.class.opcode);
1119        assert_eq!(None, inst.result_type);
1120        assert_eq!(None, inst.result_id);
1121        assert_eq!(
1122            vec![
1123                dr::Operand::IdRef(3),
1124                dr::Operand::from(spirv::StorageClass::Output),
1125            ],
1126            inst.operands
1127        );
1128
1129        let inst = &m.types_global_values[3];
1130        assert_eq!(spirv::Op::TypePointer, inst.class.opcode);
1131        assert_eq!(None, inst.result_type);
1132        assert_eq!(Some(3), inst.result_id);
1133        assert_eq!(
1134            vec![
1135                dr::Operand::from(spirv::StorageClass::Output),
1136                dr::Operand::IdRef(1),
1137            ],
1138            inst.operands
1139        );
1140    }
1141
1142    #[test]
1143    fn test_forward_ref_phi() {
1144        let mut b = Builder::new();
1145
1146        let float = b.type_float(32);
1147        assert_eq!(1, float);
1148        let f32ff32 = b.type_function(float, vec![float]);
1149        assert_eq!(2, f32ff32);
1150        let c0 = b.constant_bit32(float, 0.0f32.to_bits());
1151        assert_eq!(3, c0);
1152
1153        let fid = b
1154            .begin_function(float, None, spirv::FunctionControl::NONE, f32ff32)
1155            .unwrap();
1156        assert_eq!(4, fid);
1157
1158        let epid = b.begin_block(None).unwrap(); // Entry block id
1159        assert_eq!(5, epid);
1160        let target1 = b.id();
1161        assert_eq!(6, target1);
1162        assert!(b.branch(target1).is_ok());
1163
1164        let pbid = b.begin_block(Some(target1)).unwrap(); // Phi block id
1165        assert_eq!(target1, pbid);
1166        let target2 = b.id();
1167        assert_eq!(7, target2);
1168        let fr_add = b.id();
1169        assert_eq!(8, fr_add);
1170        // OpPhi can forward reference ids for both labels and results
1171        let phi = b
1172            .phi(
1173                float,
1174                None,
1175                // From above, from this, from below
1176                vec![(c0, epid), (fr_add, pbid), (c0, target2)],
1177            )
1178            .unwrap();
1179        assert_eq!(9, phi);
1180        let res_add = b.f_add(float, Some(fr_add), c0, c0).unwrap();
1181        assert_eq!(res_add, fr_add);
1182        assert!(b.branch(target2).is_ok());
1183
1184        let exid = b.begin_block(Some(target2)).unwrap(); // Exit block id
1185        assert_eq!(exid, target2);
1186        assert!(b.ret_value(c0).is_ok());
1187
1188        assert!(b.end_function().is_ok());
1189
1190        let m = b.module();
1191        assert_eq!(1, m.functions.len());
1192        assert_eq!(
1193            m.functions.first().unwrap().disassemble(),
1194            "%4 = OpFunction  %1  None %2\n\
1195                    %5 = OpLabel\n\
1196                    OpBranch %6\n\
1197                    %6 = OpLabel\n\
1198                    %9 = OpPhi  %1  %3 %5 %8 %6 %3 %7\n\
1199                    %8 = OpFAdd  %1  %3 %3\n\
1200                    OpBranch %7\n\
1201                    %7 = OpLabel\n\
1202                    OpReturnValue %3\n\
1203                    OpFunctionEnd"
1204        );
1205    }
1206
1207    #[test]
1208    fn test_build_variables() {
1209        let mut b = Builder::new();
1210
1211        let void = b.type_void();
1212        assert_eq!(1, void);
1213        let float = b.type_float(32);
1214        assert_eq!(2, float);
1215        let ifp = b.type_pointer(None, spirv::StorageClass::Input, float);
1216        assert_eq!(3, ifp);
1217        let ffp = b.type_pointer(None, spirv::StorageClass::Function, float);
1218        assert_eq!(4, ffp);
1219        let voidfvoid = b.type_function(void, vec![void]);
1220        assert_eq!(5, voidfvoid);
1221
1222        // Global variable
1223        let v1 = b.variable(ifp, None, spirv::StorageClass::Input, None);
1224        assert_eq!(6, v1);
1225
1226        let f = b
1227            .begin_function(void, None, spirv::FunctionControl::NONE, voidfvoid)
1228            .unwrap();
1229        assert_eq!(7, f);
1230        let bb = b.begin_block(None).unwrap();
1231        assert_eq!(8, bb);
1232        // Local variable
1233        let v2 = b.variable(ffp, None, spirv::StorageClass::Function, None);
1234        assert_eq!(9, v2);
1235        assert!(b.ret().is_ok());
1236        assert!(b.end_function().is_ok());
1237
1238        // Global variable again
1239        let v3 = b.variable(ifp, None, spirv::StorageClass::Input, None);
1240        assert_eq!(10, v3);
1241
1242        assert_eq!(
1243            b.module().disassemble(),
1244            "; SPIR-V\n; Version: 1.6\n; Generator: rspirv\n; Bound: 11\n\
1245                    %1 = OpTypeVoid\n\
1246                    %2 = OpTypeFloat 32\n\
1247                    %3 = OpTypePointer Input %2\n\
1248                    %4 = OpTypePointer Function %2\n\
1249                    %5 = OpTypeFunction %1 %1\n\
1250                    %6 = OpVariable  %3  Input\n\
1251                    %10 = OpVariable  %3  Input\n\
1252                    %7 = OpFunction  %1  None %5\n\
1253                    %8 = OpLabel\n\
1254                    %9 = OpVariable  %4  Function\n\
1255                    OpReturn\n\
1256                    OpFunctionEnd"
1257        );
1258    }
1259
1260    #[test]
1261    fn test_build_undefs() {
1262        let mut b = Builder::new();
1263
1264        let void = b.type_void();
1265        assert_eq!(1, void);
1266        let float = b.type_float(32);
1267        assert_eq!(2, float);
1268        let voidfvoid = b.type_function(void, vec![void]);
1269        assert_eq!(3, voidfvoid);
1270
1271        // Global undef
1272        let v1 = b.undef(float, None);
1273        assert_eq!(4, v1);
1274
1275        let f = b
1276            .begin_function(void, None, spirv::FunctionControl::NONE, voidfvoid)
1277            .unwrap();
1278        assert_eq!(5, f);
1279        let bb = b.begin_block(None).unwrap();
1280        assert_eq!(6, bb);
1281        // Local undef
1282        let v2 = b.undef(float, None);
1283        assert_eq!(7, v2);
1284        assert!(b.ret().is_ok());
1285        assert!(b.end_function().is_ok());
1286
1287        // Global undef again
1288        let v3 = b.undef(float, None);
1289        assert_eq!(8, v3);
1290
1291        assert_eq!(
1292            b.module().disassemble(),
1293            "; SPIR-V\n; Version: 1.6\n; Generator: rspirv\n; Bound: 9\n\
1294                    %1 = OpTypeVoid\n\
1295                    %2 = OpTypeFloat 32\n\
1296                    %3 = OpTypeFunction %1 %1\n\
1297                    %4 = OpUndef  %2\n\
1298                    %8 = OpUndef  %2\n\
1299                    %5 = OpFunction  %1  None %3\n\
1300                    %6 = OpLabel\n\
1301                    %7 = OpUndef  %2\n\
1302                    OpReturn\n\
1303                    OpFunctionEnd"
1304        );
1305    }
1306}