spirq_core/parse/
instr.rs1use 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 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 pub fn opcode(&self) -> u32 {
79 self.inner[0] & 0xFFFF
80 }
81 pub fn op(&self) -> Op {
83 Op::from_u32(self.opcode()).unwrap()
84 }
85 pub fn word_count(&self) -> usize {
88 self.inner.len()
89 }
90 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 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 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}