spq_core/parse/
instr.rs

1//!  SPIR-V instruction parser.
2use anyhow::bail;
3use spirv::Op;
4use std::{borrow::Borrow, fmt, ops::Deref};
5
6use crate::error::{anyhow, Result};
7
8pub struct Instrs<'a> {
9    inner: &'a [u32],
10    cache: Option<&'a Instr>,
11}
12impl<'a> Instrs<'a> {
13    pub fn new(spv: &'a [u32]) -> Result<Instrs<'a>> {
14        let mut out = Instrs {
15            inner: &spv,
16            cache: None,
17        };
18        out.load_next()?;
19        Ok(out)
20    }
21
22    fn load_next(&mut self) -> Result<()> {
23        let mut new_cache = None;
24        while let Some(head) = self.inner.first() {
25            let len = ((*head as u32) >> 16) as usize;
26            // Report zero-length instructions.
27            if len == 0 {
28                bail!("instruction length is zero");
29            }
30
31            if len <= self.inner.len() {
32                let instr = Instr::new(&self.inner[..len])?;
33                self.inner = &self.inner[len..];
34                new_cache = Some(instr);
35            } else {
36                if len < self.inner.len() {
37                    bail!("instruction is truncated");
38                }
39            }
40            break;
41        }
42
43        self.cache = new_cache;
44        Ok(())
45    }
46
47    pub fn peek(&self) -> Option<&'a Instr> {
48        self.cache.clone()
49    }
50    pub fn next(&mut self) -> Result<Option<&'a Instr>> {
51        let last_cache = self.cache.take();
52        self.load_next()?;
53        return Ok(last_cache);
54    }
55    pub fn next_non_nop(&mut self) -> Result<Option<&'a Instr>> {
56        while let Some(instr) = self.next()? {
57            if instr.opcode() != Op::Nop as u32 {
58                return Ok(Some(instr));
59            }
60        }
61        Ok(None)
62    }
63}
64
65pub struct Instr {
66    inner: [u32],
67}
68impl Instr {
69    pub fn new(x: &[u32]) -> Result<&Instr> {
70        if x.len() >= 1 {
71            Ok(unsafe { std::mem::transmute(x) })
72        } else {
73            Err(anyhow!("instruction is too short"))
74        }
75    }
76
77    /// Get the instruction opcode.
78    pub fn opcode(&self) -> u32 {
79        self.inner[0] & 0xFFFF
80    }
81    /// Get the instruction op.
82    pub fn op(&self) -> Op {
83        Op::from_u32(self.opcode()).unwrap()
84    }
85    /// Get the word count of the instruction, including the first word
86    /// containing the word count and opcode.
87    pub fn word_count(&self) -> usize {
88        self.inner.len()
89    }
90    /// Get an instruction operand reader. The reader does NO boundary checking
91    /// so the user code MUST make sure the implementation follows the
92    /// specification.
93    pub fn operands(&self) -> Operands<'_> {
94        Operands(&self.inner[1..])
95    }
96}
97impl AsRef<[u32]> for Instr {
98    fn as_ref(&self) -> &[u32] {
99        &self.inner
100    }
101}
102impl ToOwned for Instr {
103    type Owned = Instruction;
104    fn to_owned(&self) -> Self::Owned {
105        Instruction::from(&self.inner)
106    }
107}
108impl fmt::Debug for Instr {
109    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::fmt::Result {
110        write!(f, "{:?} {:?}", self.op(), &self.inner[1..])
111    }
112}
113
114#[derive(Debug, Clone)]
115pub struct Instruction {
116    inner: Vec<u32>,
117}
118impl From<Vec<u32>> for Instruction {
119    fn from(x: Vec<u32>) -> Instruction {
120        Instruction { inner: x }
121    }
122}
123impl From<&[u32]> for Instruction {
124    fn from(x: &[u32]) -> Instruction {
125        Instruction::from(x.to_owned())
126    }
127}
128impl AsRef<[u32]> for Instruction {
129    fn as_ref(&self) -> &[u32] {
130        &self.inner
131    }
132}
133impl Borrow<Instr> for Instruction {
134    fn borrow(&self) -> &Instr {
135        Instr::new(self.inner.as_ref()).unwrap()
136    }
137}
138impl Deref for Instruction {
139    type Target = Instr;
140    fn deref(&self) -> &Instr {
141        self.borrow()
142    }
143}
144impl Instruction {
145    pub fn builder(op: Op) -> InstructionBuilder {
146        InstructionBuilder::new(op)
147    }
148    pub fn into_words(self) -> Vec<u32> {
149        self.inner
150    }
151}
152
153pub struct InstructionBuilder {
154    inner: Vec<u32>,
155}
156impl InstructionBuilder {
157    pub fn new(op: Op) -> InstructionBuilder {
158        InstructionBuilder {
159            inner: vec![(op as u32) & 0xFFFF],
160        }
161    }
162    pub fn push(mut self, x: u32) -> Self {
163        self.inner.push(x);
164        self
165    }
166    pub fn push_list(mut self, x: &[u32]) -> Self {
167        self.inner.extend_from_slice(x);
168        self
169    }
170    pub fn push_str(mut self, x: &str) -> Self {
171        use std::ffi::CString;
172        let cstr = CString::new(x).unwrap();
173        let bytes = cstr.as_bytes();
174        let words = bytes.len() / 4 + 1;
175        // Pad the string with zeros.
176        let bytes = {
177            let mut out = bytes.to_owned();
178            out.resize(words * 4, 0);
179            out
180        };
181        let slice: &[u32] = bytemuck::cast_slice(&bytes);
182        self.inner.extend_from_slice(slice);
183        self
184    }
185    pub fn build(mut self) -> Instruction {
186        self.inner[0] |= (self.inner.len() as u32) << 16;
187        Instruction::from(self.inner)
188    }
189}
190
191#[derive(Clone)]
192pub struct Operands<'a>(&'a [u32]);
193impl<'a> Operands<'a> {
194    pub fn read_bool(&mut self) -> Result<bool> {
195        self.read_u32().map(|x| x != 0)
196    }
197    pub fn read_u32(&mut self) -> Result<u32> {
198        if let Some(x) = self.0.first() {
199            self.0 = &self.0[1..];
200            Ok(*x)
201        } else {
202            Err(anyhow!("operand is too short"))
203        }
204    }
205    pub fn read_f32(&mut self) -> Result<f32> {
206        self.read_u32().map(|x| f32::from_bits(x))
207    }
208    pub fn read_id(&mut self) -> Result<u32> {
209        self.read_u32()
210    }
211    pub fn read_str(&mut self) -> Result<&'a str> {
212        use std::ffi::CStr;
213        // Find the word with a trailing zero.
214        let ieos = self
215            .0
216            .iter()
217            .position(|x| (x >> 24) == 0)
218            .ok_or(anyhow!("string is not null-terminated"))?;
219
220        let slice: &[u32] = &self.0[..ieos + 1];
221        self.0 = &self.0[ieos + 1..];
222        let bytes: &[u8] = bytemuck::cast_slice(slice);
223        let cstr = CStr::from_bytes_until_nul(bytes)?;
224        Ok(cstr.to_str()?)
225    }
226    pub fn read_list(&mut self) -> Result<&'a [u32]> {
227        let rv = self.0;
228        self.0 = &[];
229        Ok(rv)
230    }
231}
232impl<'a> Iterator for Operands<'a> {
233    type Item = u32;
234    fn next(&mut self) -> Option<Self::Item> {
235        self.read_u32().ok()
236    }
237}
238impl<'a> ExactSizeIterator for Operands<'a> {
239    fn len(&self) -> usize {
240        self.0.len()
241    }
242}