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
125
126
127
128
129
130
131
132
133
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() as *const _,
                text.len(),
                options.into(),
                &mut binary,
                &mut diagnostic,
            );

            // Always wrap diagnostic, it's fine if it's null
            use std::convert::TryFrom;
            let diagnostic = crate::error::Diagnostic::try_from(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<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() as *const _,
                binary.len(),
                options.into(),
                &mut text,
                &mut diagnostic,
            );

            // Always wrap diagnostic, it's fine if it's null
            use std::convert::TryFrom;
            let diagnostic = crate::error::Diagnostic::try_from(diagnostic).ok();

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

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