1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
use spirv_tools_sys::{assembler, shared};

pub struct CompiledAssembler {
    inner: *mut shared::ToolContext,
}

use super::Assembler;

impl Assembler for CompiledAssembler {
    fn with_env(target_env: crate::TargetEnv) -> Self {
        Self {
            inner: unsafe { shared::context_create(target_env) },
        }
    }

    fn assemble(
        &self,
        text: &str,
        options: super::AssemblerOptions,
    ) -> Result<crate::binary::Binary, crate::error::Error> {
        unsafe {
            let mut binary = std::ptr::null_mut();
            let mut diagnostic = std::ptr::null_mut();

            let res = assembler::assemble(
                self.inner,
                text.as_ptr().cast(),
                text.len(),
                options.into(),
                &mut binary,
                &mut diagnostic,
            );

            // Always wrap diagnostic, it's fine if it's null
            let diagnostic = crate::error::Diagnostic::from_diag(diagnostic).ok();

            match res {
                shared::SpirvResult::Success => {
                    if binary.is_null() {
                        return Err(crate::error::Error {
                            inner: shared::SpirvResult::InternalError,
                            diagnostic: Some("spirv assemble indicated success but did not return a valid binary".to_owned().into()),
                        });
                    }

                    let bin = crate::binary::external::ExternalBinary::new(binary);
                    Ok(crate::binary::Binary::External(bin))
                }
                other => Err(crate::error::Error {
                    inner: other,
                    diagnostic,
                }),
            }
        }
    }

    fn disassemble(
        &self,
        binary: impl AsRef<[u32]>,
        options: super::DisassembleOptions,
    ) -> Result<Option<String>, crate::error::Error> {
        unsafe {
            let mut text = std::ptr::null_mut();
            let mut diagnostic = std::ptr::null_mut();

            let binary = binary.as_ref();

            let res = assembler::disassemble(
                self.inner,
                binary.as_ptr().cast(),
                binary.len(),
                options.into(),
                &mut text,
                &mut diagnostic,
            );

            // Always wrap diagnostic, it's fine if it's null
            let diagnostic = crate::error::Diagnostic::from_diag(diagnostic).ok();

            match res {
                shared::SpirvResult::Success => {
                    if text.is_null() {
                        return Ok(None);
                    }

                    // Sanity check the text first
                    let disassemble_res = std::str::from_utf8(std::slice::from_raw_parts(
                        (*text).data.cast::<u8>(),
                        (*text).length,
                    ))
                    .map(|disasm| Some(disasm.to_owned()))
                    .map_err(|e| crate::error::Error {
                        inner: shared::SpirvResult::InvalidText,
                        diagnostic: Some(
                            format!("spirv disassemble returned non-utf8 text: {}", e).into(),
                        ),
                    });

                    assembler::text_destroy(text);

                    disassemble_res
                }
                other => Err(crate::error::Error {
                    inner: other,
                    diagnostic,
                }),
            }
        }
    }
}

impl Default for CompiledAssembler {
    fn default() -> Self {
        Self::with_env(crate::TargetEnv::default())
    }
}

impl Drop for CompiledAssembler {
    fn drop(&mut self) {
        unsafe {
            shared::context_destroy(self.inner);
        }
    }
}