1use serde::{Deserialize, Serialize};
16
17#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
20pub struct LaneShape {
21 pub word: u32,
22 pub mul: u32,
23}
24
25impl LaneShape {
26 pub const fn new(word: u32, mul: u32) -> LaneShape {
27 assert!(
28 word >= 2 && word <= 32 && 32 % word == 0,
29 "a lane word is 2, 4, 8, 16 or 32 bits"
30 );
31 assert!(
32 mul >= 1 && 2 * mul <= word && word % mul == 0,
33 "a lane multiplier's product fits one word"
34 );
35 LaneShape { word, mul }
36 }
37
38 pub fn mask(self) -> u32 {
40 if self.word == 32 {
41 u32::MAX
42 } else {
43 (1u32 << self.word) - 1
44 }
45 }
46
47 pub fn words_for(self, bits: u32) -> usize {
49 bits.div_ceil(self.word) as usize
50 }
51}
52
53pub type LReg = u32;
55
56#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
58pub enum LOp {
59 Const(u32),
61 Mov(LReg),
63 Add {
65 a: LReg,
66 b: LReg,
67 cin: LReg,
68 },
69 Sub {
71 a: LReg,
72 b: LReg,
73 bin: LReg,
74 },
75 Mul {
77 a: LReg,
78 b: LReg,
79 },
80 And {
81 a: LReg,
82 b: LReg,
83 },
84 Or {
85 a: LReg,
86 b: LReg,
87 },
88 Xor {
89 a: LReg,
90 b: LReg,
91 },
92 Not {
94 a: LReg,
95 },
96 Shl {
98 a: LReg,
99 k: u32,
100 },
101 Shr {
103 a: LReg,
104 k: u32,
105 },
106 Ltu {
108 a: LReg,
109 b: LReg,
110 },
111 Eq {
113 a: LReg,
114 b: LReg,
115 },
116 Sel {
118 c: LReg,
119 t: LReg,
120 f: LReg,
121 },
122}
123
124#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
126pub struct LInst {
127 pub dst: LReg,
128 pub dst2: Option<LReg>,
129 pub op: LOp,
130}
131
132#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
134pub enum LStmt {
135 Inst(LInst),
136 Repeat { times: u32, body: Vec<LStmt> },
137}
138
139#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
141pub struct Routine {
142 pub name: String,
143 pub shape: LaneShape,
144 pub regs: u32,
145 pub inputs: Vec<LReg>,
146 pub outputs: Vec<LReg>,
147 pub body: Vec<LStmt>,
148}
149
150#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
152pub struct LaneCounts {
153 pub add: u64,
154 pub sub: u64,
155 pub mul: u64,
156 pub logic: u64,
157 pub shift: u64,
158 pub compare: u64,
159 pub select: u64,
160 pub moves: u64,
161}
162
163impl LaneCounts {
164 pub fn total(&self) -> u64 {
165 self.add
166 + self.sub
167 + self.mul
168 + self.logic
169 + self.shift
170 + self.compare
171 + self.select
172 + self.moves
173 }
174
175 pub fn absorb(&mut self, o: &LaneCounts) {
176 self.add += o.add;
177 self.sub += o.sub;
178 self.mul += o.mul;
179 self.logic += o.logic;
180 self.shift += o.shift;
181 self.compare += o.compare;
182 self.select += o.select;
183 self.moves += o.moves;
184 }
185}
186
187pub trait Lanes {
190 fn shape(&self) -> LaneShape;
191 fn add(&mut self, a: u32, b: u32, cin: u32) -> (u32, u32);
193 fn sub(&mut self, a: u32, b: u32, bin: u32) -> (u32, u32);
195 fn mul(&mut self, a: u32, b: u32) -> u32;
197}
198
199#[derive(Clone, Copy, Debug)]
201pub struct ExactLanes {
202 pub shape: LaneShape,
203}
204
205impl Lanes for ExactLanes {
206 fn shape(&self) -> LaneShape {
207 self.shape
208 }
209 fn add(&mut self, a: u32, b: u32, cin: u32) -> (u32, u32) {
210 let s = a as u64 + b as u64 + cin as u64;
211 (
212 (s & self.shape.mask() as u64) as u32,
213 (s >> self.shape.word) as u32,
214 )
215 }
216 fn sub(&mut self, a: u32, b: u32, bin: u32) -> (u32, u32) {
217 let d = a as i64 - b as i64 - bin as i64;
218 let borrow = u32::from(d < 0);
219 ((d.rem_euclid(1i64 << self.shape.word)) as u32, borrow)
220 }
221 fn mul(&mut self, a: u32, b: u32) -> u32 {
222 a * b
223 }
224}
225
226#[derive(Clone, Debug, PartialEq, Eq)]
228pub enum LaneFault {
229 WideWord { op: &'static str, value: u32 },
231 WideMultiplicand { value: u32 },
233 NotABit { value: u32 },
235 Arity { expected: usize, got: usize },
237}
238
239impl std::fmt::Display for LaneFault {
240 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
241 match self {
242 LaneFault::WideWord { op, value } => {
243 write!(f, "{op}: operand {value:#x} is wider than the lane word")
244 }
245 LaneFault::WideMultiplicand { value } => {
246 write!(f, "mul: operand {value:#x} is wider than the multiplier")
247 }
248 LaneFault::NotABit { value } => write!(f, "carry {value} is not a bit"),
249 LaneFault::Arity { expected, got } => {
250 write!(f, "{got} input words where the routine takes {expected}")
251 }
252 }
253 }
254}
255
256impl std::error::Error for LaneFault {}
257
258pub fn run(
260 routine: &Routine,
261 lanes: &mut dyn Lanes,
262 inputs: &[u32],
263) -> Result<(Vec<u32>, LaneCounts), LaneFault> {
264 if inputs.len() != routine.inputs.len() {
265 return Err(LaneFault::Arity {
266 expected: routine.inputs.len(),
267 got: inputs.len(),
268 });
269 }
270 let shape = lanes.shape();
271 let mut regs = vec![0u32; routine.regs as usize];
272 for (r, v) in routine.inputs.iter().zip(inputs) {
273 if *v & !shape.mask() != 0 {
274 return Err(LaneFault::WideWord {
275 op: "input",
276 value: *v,
277 });
278 }
279 regs[*r as usize] = *v;
280 }
281 let mut counts = LaneCounts::default();
282 exec(&routine.body, &mut regs, lanes, shape, &mut counts)?;
283 Ok((
284 routine.outputs.iter().map(|r| regs[*r as usize]).collect(),
285 counts,
286 ))
287}
288
289fn exec(
290 body: &[LStmt],
291 regs: &mut [u32],
292 lanes: &mut dyn Lanes,
293 shape: LaneShape,
294 counts: &mut LaneCounts,
295) -> Result<(), LaneFault> {
296 for stmt in body {
297 match stmt {
298 LStmt::Repeat { times, body } => {
299 for _ in 0..*times {
300 exec(body, regs, lanes, shape, counts)?;
301 }
302 }
303 LStmt::Inst(inst) => step(inst, regs, lanes, shape, counts)?,
304 }
305 }
306 Ok(())
307}
308
309fn step(
310 inst: &LInst,
311 regs: &mut [u32],
312 lanes: &mut dyn Lanes,
313 shape: LaneShape,
314 counts: &mut LaneCounts,
315) -> Result<(), LaneFault> {
316 let mask = shape.mask();
317 let r = |x: LReg| regs[x as usize];
318 let bit = |v: u32| {
319 if v > 1 {
320 Err(LaneFault::NotABit { value: v })
321 } else {
322 Ok(v)
323 }
324 };
325 let (v, v2) = match inst.op {
326 LOp::Const(k) => {
327 if k & !mask != 0 {
328 return Err(LaneFault::WideWord {
329 op: "const",
330 value: k,
331 });
332 }
333 counts.moves += 1;
334 (k, None)
335 }
336 LOp::Mov(a) => {
337 counts.moves += 1;
338 (r(a), None)
339 }
340 LOp::Add { a, b, cin } => {
341 counts.add += 1;
342 let (s, c) = lanes.add(r(a), r(b), bit(r(cin))?);
343 (s, Some(c))
344 }
345 LOp::Sub { a, b, bin } => {
346 counts.sub += 1;
347 let (d, bo) = lanes.sub(r(a), r(b), bit(r(bin))?);
348 (d, Some(bo))
349 }
350 LOp::Mul { a, b } => {
351 counts.mul += 1;
352 let limit = 1u32 << shape.mul;
353 for x in [r(a), r(b)] {
354 if x >= limit {
355 return Err(LaneFault::WideMultiplicand { value: x });
356 }
357 }
358 (lanes.mul(r(a), r(b)), None)
359 }
360 LOp::And { a, b } => {
361 counts.logic += 1;
362 (r(a) & r(b), None)
363 }
364 LOp::Or { a, b } => {
365 counts.logic += 1;
366 (r(a) | r(b), None)
367 }
368 LOp::Xor { a, b } => {
369 counts.logic += 1;
370 (r(a) ^ r(b), None)
371 }
372 LOp::Not { a } => {
373 counts.logic += 1;
374 (!r(a) & mask, None)
375 }
376 LOp::Shl { a, k } => {
377 counts.shift += 1;
378 ((r(a) << k) & mask, None)
379 }
380 LOp::Shr { a, k } => {
381 counts.shift += 1;
382 (r(a) >> k, None)
383 }
384 LOp::Ltu { a, b } => {
385 counts.compare += 1;
386 (u32::from(r(a) < r(b)), None)
387 }
388 LOp::Eq { a, b } => {
389 counts.compare += 1;
390 (u32::from(r(a) == r(b)), None)
391 }
392 LOp::Sel { c, t, f } => {
393 counts.select += 1;
394 (if r(c) != 0 { r(t) } else { r(f) }, None)
395 }
396 };
397 regs[inst.dst as usize] = v;
398 if let (Some(d2), Some(x)) = (inst.dst2, v2) {
399 regs[d2 as usize] = x;
400 }
401 Ok(())
402}
403
404pub fn split_u64(shape: LaneShape, x: u64, n: usize) -> Vec<u32> {
406 (0..n)
407 .map(|i| {
408 let at = i as u32 * shape.word;
409 if at >= 64 {
410 0
411 } else {
412 ((x >> at) as u32) & shape.mask()
413 }
414 })
415 .collect()
416}
417
418pub fn join_u64(shape: LaneShape, words: &[u32]) -> u64 {
420 let mut x = 0u64;
421 for (i, w) in words.iter().enumerate() {
422 let at = i as u32 * shape.word;
423 if at < 64 {
424 x |= (*w as u64) << at;
425 }
426 }
427 x
428}
429
430pub fn split_u128(shape: LaneShape, x: u128, n: usize) -> Vec<u32> {
432 (0..n)
433 .map(|i| {
434 let at = i as u32 * shape.word;
435 if at >= 128 {
436 0
437 } else {
438 ((x >> at) as u32) & shape.mask()
439 }
440 })
441 .collect()
442}
443
444pub fn join_u128(shape: LaneShape, words: &[u32]) -> u128 {
446 let mut x = 0u128;
447 for (i, w) in words.iter().enumerate() {
448 let at = i as u32 * shape.word;
449 if at < 128 {
450 x |= (*w as u128) << at;
451 }
452 }
453 x
454}