Skip to main content

nox_spirv/
module.rs

1use core::{
2    fmt::{Display, self},
3    error::Error,
4    slice,
5};
6
7use crate::{
8    stream::*,
9    op,
10    core::*,
11};
12
13/// Indicates that the SPIR-V code passed to a [`module`][1] is invalid.
14///
15/// [1]: Module
16#[derive(Debug)]
17pub enum ParseError {
18    EndOfStream,
19    InvalidType {
20         expected: &'static str,
21         found: op::Code,
22    },
23    UnknownOpCode(op::Code),
24    WrongOpCode {
25        expected: &'static str,
26        found: op::Code,
27    },
28    InvalidIdResult(op::IdRef),
29    ExpectedTypeDeclaration(op::IdRef),
30    ExpectedContextResultType,
31    InvalidIntLiteralWidth(u32),
32    InvalidFloatLiteralWidth(u32),
33    UnknownVariant {
34        kind: &'static str,
35        value: u32,
36    },
37    InvalidPairWordCount(u32),
38}
39
40#[derive(Clone)]
41pub struct ResultStorage<'a> {
42    data: Vec<Option<InstructionStream<'a>>>,
43}
44
45impl Default for ResultStorage<'_> {
46
47    #[inline]
48    fn default() -> Self {
49        Self {
50            data: vec![],
51        }
52    }
53}
54
55impl<'a> ResultStorage<'a>
56{
57
58    #[inline]
59    pub fn insert(&mut self,
60        id: op::IdResult,
61        mut stream: InstructionStream<'a>
62    )
63    {
64        stream.reset();
65        let idx = id.0 as usize;
66        if idx >= self.data.len() {
67            self.data.resize_with(idx + 1, || None);
68        }
69        self.data[idx] = Some(stream);
70    }
71
72    #[inline]
73    pub fn get(&self, id: op::IdRef) -> Option<InstructionStream<'a>> {
74        self.data.get(id.0 as usize)
75            .and_then(|&value| value)
76    }
77
78    #[inline]
79    pub fn contains(&self, id: op::IdRef) -> bool {
80        self.data.get(id.0 as usize)
81            .and_then(|value| value.as_ref())
82            .is_some()
83    }
84}
85
86#[derive(Clone)]
87pub struct Module<'a> {
88    stream: Stream<'a>,
89    results: ResultStorage<'a>,
90}
91
92pub type ParseResult<T> = Result<T, ParseError>;
93
94impl<'a> Module<'a> {
95
96    #[inline]
97    pub fn new(spirv: &'a [u32]) -> Self {
98        Self {
99            stream: Stream::new(spirv),
100            results: Default::default(),
101        }
102    }
103
104    #[inline]
105    pub fn parse_next(&mut self) -> ParseResult<bool> {
106        let Some(mut instruction) = self.stream
107            .next()
108        else {
109            return Ok(false)
110        };
111        let inst_info = op::INST_INFOS[instruction.code().0 as usize];
112        if inst_info.has_id_result {
113            if inst_info.has_id_result_type {
114                let _ = instruction.read()?;
115            }
116            let id = op::IdResult(instruction.read()?);
117            self.results.insert(
118                id,
119                instruction,
120            );
121        }
122        Ok(true)
123    }
124
125    #[inline]
126    pub fn parse_full(&mut self) -> ParseResult<()> {
127        while self.parse_next()? {}
128        Ok(())
129    }
130
131    #[inline]
132    pub fn all_instructions(&self) -> impl Iterator<Item = InstructionStream<'a>> {
133        let mut stream = self.stream;
134        stream.reset();
135        stream
136    }
137
138    #[inline]
139    pub fn remaining_instructions(&self) -> impl Iterator<Item = InstructionStream<'a>> {
140        self.stream
141    }
142
143    #[inline]
144    pub fn results(&self) -> impl Iterator<Item = (op::IdRef, InstructionStream<'a>)> {
145        self.results.data
146            .iter()
147            .enumerate()
148            .filter_map(|(i, result)| {
149                result
150                    .map(|result| (op::IdRef::from_word(i as u32), result))
151            })
152    }
153
154    #[inline]
155    pub fn get_result(&self, id: op::IdRef) -> Option<InstructionStream<'a>> {
156        self.results.get(id)
157    }
158}
159
160#[derive(Default, Clone, Copy)]
161pub struct ParseContext {
162    pub result_type: Option<op::IdResultType>,
163}
164
165pub trait WordExt<'a>: Word {
166
167    #[inline]
168    fn parse_one(
169        stream: &mut InstructionStream<'a>,
170    ) -> ParseResult<Self> {
171        Ok(Self::from_word(stream.read()?))
172    }
173
174    #[inline]
175    fn parse_optional(
176        stream: &mut InstructionStream<'a>,
177    ) -> ParseResult<Option<Self>> {
178        if stream.is_eos() {
179            Ok(None)
180        } else {
181            Ok(Some(Self::from_word(stream.read()?)))
182        }
183    }
184
185    #[inline]
186    fn parse_eos(
187        stream: &mut InstructionStream<'a>,
188    ) -> ParseResult<&'a [Self]> {
189        let words = stream.read_words(None)?;
190        Ok(unsafe {
191            slice::from_raw_parts(
192                words.as_ptr().cast(),
193                words.len(),
194            )
195        })
196    }
197}
198
199impl<'a, T: Word> WordExt<'a> for T {}
200
201impl<'a> CompilerStr<'a> {
202
203    #[inline]
204    pub fn parse_one(
205        stream: &mut InstructionStream<'a>,
206    ) -> ParseResult<Self> {
207        stream.read_string()
208    }
209
210    #[inline]
211    pub fn parse_optional(
212        stream: &mut InstructionStream<'a>,
213    ) -> ParseResult<Option<Self>> {
214        if stream.is_eos() {
215            Ok(None)
216        } else {
217            Ok(Some(stream.read_string()?))
218        }
219    }
220}
221
222impl Display for ParseError {
223
224    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
225        match self {
226            Self::EndOfStream => write!(f, "end of stream"),
227            Self::InvalidType { expected, found } => write!(f,
228                "invalid type; expected {expected}, found {found}",
229            ),
230            Self::UnknownOpCode(code) => write!(f,
231                "unknown op code {code}"
232            ),
233            Self::WrongOpCode { expected, found } => write!(f,
234                "wrong op code; expected {expected}, found {found}",
235            ),
236            Self::InvalidIdResult(id) => write!(f,
237                "invalid id result {id}",
238            ),
239            Self::ExpectedTypeDeclaration(id) => write!(f,
240                "expected type declaration, found id {id}",
241            ),
242            Self::ExpectedContextResultType => write!(f,
243                "expected context result type, found None",
244            ),
245            Self::InvalidIntLiteralWidth(width) => write!(f,
246                "invalid int literal width {width}",
247            ),
248            Self::InvalidFloatLiteralWidth(width) => write!(f,
249                "invalid float literal width {width}",
250            ),
251            Self::UnknownVariant { kind, value } => write!(f,
252                "unknown {kind} variant {value}",
253            ),
254            Self::InvalidPairWordCount(count) => write!(f,
255                "invalid pair word count {count}: count is not a multiple of 2"
256            ),
257        }
258    }
259}
260
261impl Error for ParseError {}