1use std::ffi::CStr;
2use std::{fmt, ops::Range, sync::Arc};
3
4use crate::{FunctionRef, HashMap, Type, VectorField, function_args};
5use arc_slice::ArcSlice;
6use arrayvec::ArrayVec;
7#[cfg(feature = "reflect")]
8use bevy_reflect::Reflect;
9use bump_scope::{BumpAllocatorScopeExt, FixedBumpVec};
10use itertools::Itertools;
11use num::FromPrimitive as _;
12use num_derive::FromPrimitive;
13
14use crate::QCMemory;
15use crate::load::LoadFn;
16use crate::ops::Opcode;
17use crate::progs::{VmScalar, VmScalarType};
18
19pub const MAX_ARGS: usize = 8;
21
22#[derive(Debug, Copy, Clone, PartialEq, Eq)]
23#[cfg_attr(feature = "reflect", derive(Reflect))]
24pub struct Statement {
25 pub opcode: Opcode,
26 pub arg1: i16,
27 pub arg2: i16,
28 pub arg3: i16,
29}
30
31impl Statement {
32 pub fn new(op: i16, arg1: i16, arg2: i16, arg3: i16) -> anyhow::Result<Statement> {
33 let opcode = Opcode::from_i16(op)
34 .ok_or_else(|| anyhow::Error::msg(format!("Bad opcode 0x{op:x}")))?;
35
36 Ok(Statement {
37 opcode,
38 arg1,
39 arg2,
40 arg3,
41 })
42 }
43}
44
45#[derive(Copy, Clone, Debug, FromPrimitive, PartialEq, Eq)]
46#[cfg_attr(feature = "reflect", derive(Reflect))]
47#[repr(u8)]
48pub(crate) enum ArgSize {
49 Scalar = 1,
50 Vector = 3,
51}
52
53impl From<ArgSize> for Type {
54 fn from(value: ArgSize) -> Self {
55 match value {
56 ArgSize::Scalar => Type::AnyScalar,
57 ArgSize::Vector => Type::Vector,
58 }
59 }
60}
61
62impl std::fmt::Display for ArgSize {
63 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
64 match self {
65 Self::Scalar => write!(f, "1 (scalar)"),
66 Self::Vector => write!(f, "3 (vector)"),
67 }
68 }
69}
70
71#[derive(Debug)]
72pub(crate) struct FunctionExecutionCtx<'a> {
73 params: FixedBumpVec<'a, VmScalar>,
74 local_storage: FixedBumpVec<'a, VmScalar>,
75 local_range: Range<usize>,
79 statements: ArcSlice<[Statement]>,
80}
81
82impl FunctionExecutionCtx<'_> {
83 pub fn instr(&self, index: usize) -> anyhow::Result<Statement> {
84 Ok(*self
85 .statements
86 .get(index)
87 .ok_or_else(|| anyhow::Error::msg("Out-of-bounds instruction access"))?)
88 }
89}
90
91const LOCAL_STORAGE_ERR: &str =
92 "Programmer error: `local_storage` was too small for `local_range`. This is a bug!";
93
94impl QCMemory for FunctionExecutionCtx<'_> {
95 type Scalar = Option<VmScalar>;
96
97 fn get(&self, index: usize) -> anyhow::Result<Option<VmScalar>> {
98 if self.local_range.contains(&index) {
99 let index = index - self.local_range.start;
100
101 Ok(Some(
102 self.local_storage
103 .get(index)
104 .expect(LOCAL_STORAGE_ERR)
105 .clone(),
106 ))
107 } else {
108 let Ok(index) = u16::try_from(index) else {
109 return Ok(None);
110 };
111
112 match function_args().find_map(|arg| arg.try_match_scalar(index)) {
113 Some((arg_index, ofs)) => {
114 Ok(self.params.get(arg_index * 3 + ofs as usize).cloned())
115 }
116 _ => Ok(None),
117 }
118 }
119 }
120}
121
122impl FunctionExecutionCtx<'_> {
123 pub fn set(&mut self, index: usize, value: VmScalar) -> anyhow::Result<()> {
124 if value.type_() == VmScalarType::Void {
125 return Ok(());
126 }
127
128 if self.local_range.contains(&index) {
129 let index = index - self.local_range.start;
130
131 let local = self.local_storage.get_mut(index).expect(LOCAL_STORAGE_ERR);
132
133 *local = value;
134 } else {
135 let out_of_range = || anyhow::bail!("Global {index} is out of range");
136
137 let Ok(index) = u16::try_from(index) else {
138 return out_of_range();
139 };
140
141 match function_args().find_map(|arg| arg.try_match_scalar(index)) {
142 Some((arg_index, ofs)) => {
143 if let Some(param) = self.params.get_mut(arg_index * 3 + ofs as usize) {
144 *param = value;
145 } else {
146 return out_of_range();
147 }
148 }
149 _ => {
150 return out_of_range();
151 }
152 }
153 }
154
155 Ok(())
156 }
157}
158
159#[derive(Debug, Clone, PartialEq, Eq)]
160pub struct QCFunctionBody {
161 pub locals: Range<usize>,
164 pub statements: ArcSlice<[Statement]>,
165}
166
167#[derive(Debug, Clone)]
168pub enum FunctionBody {
169 Progs(QCFunctionBody),
170 Builtin,
171}
172
173impl FunctionBody {
174 pub fn try_into_qc(self) -> Result<QCFunctionBody, Builtin> {
175 match self {
176 Self::Progs(quakec) => Ok(quakec),
177 Self::Builtin => Err(Builtin),
178 }
179 }
180}
181
182pub type QCFunctionDef = FunctionDef<QCFunctionBody>;
183
184#[derive(Debug, Clone, PartialEq, Eq)]
186pub struct FunctionDef<T = FunctionBody> {
187 pub offset: i32,
191 pub name: Arc<CStr>,
193 pub source: Arc<CStr>,
195 pub args: ArrayVec<Type, MAX_ARGS>,
197 pub body: T,
199}
200
201#[derive(Debug, Copy, Clone, PartialEq, Eq)]
203pub struct Builtin;
204
205pub type BuiltinDef = FunctionDef<Builtin>;
207
208impl FunctionDef {
209 pub(crate) fn try_into_qc(self) -> Result<QCFunctionDef, FunctionDef<Builtin>> {
210 match self.body.try_into_qc() {
211 Ok(quakec) => Ok(QCFunctionDef {
212 offset: self.offset,
213 name: self.name,
214 source: self.source,
215 args: self.args,
216 body: quakec,
217 }),
218 Err(builtin) => Err(FunctionDef {
219 offset: self.offset,
220 name: self.name,
221 source: self.source,
222 args: self.args,
223 body: builtin,
224 }),
225 }
226 }
227}
228
229impl QCFunctionDef {
230 pub(crate) fn ctx<'scope>(
231 &self,
232 mut alloc: impl BumpAllocatorScopeExt<'scope>,
233 ) -> FunctionExecutionCtx<'scope> {
234 FunctionExecutionCtx {
235 params: FixedBumpVec::from_iter_exact_in(
236 std::iter::repeat_n(
237 VmScalar::Void,
238 function_args().len() * VectorField::FIELDS.len(),
239 ),
240 &mut alloc,
241 ),
242 local_storage: FixedBumpVec::from_iter_exact_in(
243 std::iter::repeat_n(VmScalar::Void, self.body.locals.len()),
244 &mut alloc,
245 ),
246 local_range: self.body.locals.clone(),
247 statements: self.body.statements.clone(),
248 }
249 }
250}
251
252#[derive(Debug, Clone)]
254pub struct FunctionRegistry {
255 by_index: HashMap<i32, FunctionDef>,
256 by_name: HashMap<Arc<CStr>, FunctionDef>,
257}
258
259impl FunctionRegistry {
260 pub(crate) fn new(
261 statements: ArcSlice<[Statement]>,
262 definitions: &[LoadFn],
263 ) -> anyhow::Result<Self> {
264 let (by_index, by_name) = definitions
265 .iter()
266 .map(Some)
267 .chain(std::iter::once(None))
268 .tuple_windows()
269 .map(|(cur, next)| -> anyhow::Result<_> {
270 match (cur, next) {
271 (Some(cur), next) => {
272 let func_def = FunctionDef {
273 name: cur.name.clone(),
274 source: cur.source.clone(),
275 args: cur.args.iter().copied().map(Into::into).collect(),
276 offset: cur.offset,
277 body: if cur.offset < 0 {
278 debug_assert_eq!(cur.locals.len(), 0);
279
280 FunctionBody::Builtin
281 } else {
282 let cur_offset = usize::try_from(cur.offset)?;
283 let next_offset = next
284 .as_ref()
285 .map(|n| usize::try_from(n.offset))
286 .unwrap_or(Ok(statements.len()))?;
287
288 FunctionBody::Progs(QCFunctionBody {
289 locals: cur.locals.clone(),
290 statements: statements.subslice(cur_offset..next_offset),
291 })
292 },
293 };
294
295 Ok(((cur.offset, func_def.clone()), (cur.name.clone(), func_def)))
296 }
297 (None, _) => unreachable!(),
298 }
299 })
300 .collect::<anyhow::Result<(_, _)>>()?;
301
302 Ok(Self { by_index, by_name })
303 }
304
305 pub fn get<F>(&self, func: F) -> anyhow::Result<&FunctionDef>
307 where
308 F: Into<FunctionRef>,
309 {
310 match func.into() {
311 FunctionRef::Offset(i) => self.get_by_index(i),
312 FunctionRef::Name(name) => self.get_by_name(name),
313 }
314 }
315
316 pub fn get_by_index<F>(&self, func: F) -> anyhow::Result<&FunctionDef>
318 where
319 F: TryInto<i32>,
320 F::Error: snafu::Error + Into<anyhow::Error> + Send + Sync + 'static,
321 {
322 let func = func.try_into()?;
323
324 self.by_index
325 .get(&func)
326 .ok_or_else(|| anyhow::format_err!("Function {func} does not exist"))
327 }
328
329 pub fn get_by_name<F>(&self, func: F) -> anyhow::Result<&FunctionDef>
331 where
332 F: AsRef<CStr>,
333 {
334 let func = func.as_ref();
335
336 self.by_name
337 .get(func)
338 .ok_or_else(|| anyhow::format_err!("Function {func:?} does not exist"))
339 }
340}