1use std::collections::{BTreeMap, BTreeSet};
16
17use serde::{Deserialize, Serialize};
18
19use crate::codegen_sva::fabric_rtl::{AluOp, CmpKind, Divergence, FuncUnit};
20use crate::semantics::builtins::BuiltinId;
21use crate::vm::{CompiledProgram, Constant, Op, ParamKind, SlotKind};
22
23use super::cells::{routine, CellOp};
24use super::lane::{join_u64, run, split_u64, LaneCounts, Lanes};
25
26pub type VReg = u16;
28
29#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
31pub enum Kind {
32 Int,
33 Float,
34 Bool,
35}
36
37impl Kind {
38 pub fn slot(self) -> SlotKind {
40 match self {
41 Kind::Int => SlotKind::Int,
42 Kind::Float => SlotKind::Float,
43 Kind::Bool => SlotKind::Bool,
44 }
45 }
46}
47
48#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
50pub enum RStmt {
51 Const { dst: VReg, value: i64 },
53 Copy { dst: VReg, src: VReg },
55 Cell { dst: VReg, op: CellOp, a: VReg, b: VReg },
57 Select { dst: VReg, cmp: CmpKind, a: VReg, b: VReg, t: VReg, e: VReg },
59}
60
61#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
63pub enum RTerm {
64 Jump(u32),
65 Branch { cond: VReg, then_: u32, else_: u32 },
67 Return(VReg),
68}
69
70#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
71pub struct RBlock {
72 pub stmts: Vec<RStmt>,
73 pub term: RTerm,
74}
75
76#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
78pub struct RegionCfg {
79 pub name: String,
80 pub func: u16,
81 pub params: Vec<Kind>,
82 pub ret: Kind,
83 pub regs: u16,
84 pub blocks: Vec<RBlock>,
85}
86
87#[derive(Clone, Debug, PartialEq, Eq)]
89pub enum Halt {
90 Declined { divergence: Divergence, op: CellOp, a: i64, b: i64 },
92 OutOfFuel,
94}
95
96#[derive(Clone, Debug, PartialEq, Eq)]
98pub struct HostReason {
99 pub func: u16,
100 pub name: String,
101 pub reason: String,
102}
103
104#[derive(Clone, Debug, PartialEq, Eq)]
106pub enum Discovered {
107 Region(RegionCfg),
108 Host(HostReason),
109}
110
111#[derive(Clone, Copy, Debug, PartialEq, Eq)]
113enum Ak {
114 Int,
115 Float,
116 Bool,
117 Range(usize),
119 Conflict,
121}
122
123impl Ak {
124 fn of(k: Kind) -> Ak {
125 match k {
126 Kind::Int => Ak::Int,
127 Kind::Float => Ak::Float,
128 Kind::Bool => Ak::Bool,
129 }
130 }
131 fn scalar(self) -> Option<Kind> {
132 match self {
133 Ak::Int => Some(Kind::Int),
134 Ak::Float => Some(Kind::Float),
135 Ak::Bool => Some(Kind::Bool),
136 _ => None,
137 }
138 }
139}
140
141#[derive(Clone, Debug, PartialEq, Eq)]
144struct State {
145 kinds: BTreeMap<u16, Ak>,
146 iters: Vec<usize>,
147}
148
149fn join(a: &State, b: &State) -> Result<State, String> {
150 if a.iters != b.iters {
151 return Err("paths reach one point with different live loops".to_string());
152 }
153 let mut kinds = a.kinds.clone();
154 for (r, k) in &b.kinds {
155 kinds
156 .entry(*r)
157 .and_modify(|cur| {
158 if *cur != *k {
159 *cur = Ak::Conflict
160 }
161 })
162 .or_insert(*k);
163 }
164 let both: BTreeSet<u16> = a.kinds.keys().filter(|r| b.kinds.contains_key(r)).copied().collect();
166 kinds.retain(|r, _| both.contains(r));
167 Ok(State { kinds, iters: a.iters.clone() })
168}
169
170fn leaders(code: &[Op], entry: usize, end: usize) -> Result<BTreeSet<usize>, String> {
172 let mut set = BTreeSet::new();
173 set.insert(entry);
174 for pc in entry..end {
175 let within = |t: usize| -> Result<usize, String> {
176 if t < entry || t >= end {
177 Err(format!("a jump at pc {pc} leaves the function (to {t})"))
178 } else {
179 Ok(t)
180 }
181 };
182 match code[pc] {
183 Op::Jump { target } => {
184 set.insert(within(target)?);
185 set.insert(pc + 1);
186 }
187 Op::JumpIfFalse { target, .. } | Op::JumpIfTrue { target, .. } => {
188 set.insert(within(target)?);
189 set.insert(pc + 1);
190 }
191 Op::IterNext { exit, .. } => {
192 set.insert(within(exit)?);
193 set.insert(pc + 1);
194 }
195 Op::Return { .. } | Op::ReturnNothing => {
196 set.insert(pc + 1);
197 }
198 _ => {}
199 }
200 }
201 set.retain(|p| *p < end);
202 Ok(set)
203}
204
205fn param_kinds(program: &CompiledProgram, fi: usize) -> Result<Vec<Kind>, String> {
207 let f = &program.functions[fi];
208 (0..f.param_count as usize)
209 .map(|i| match f.param_kinds.get(i).copied().flatten() {
210 Some(ParamKind::Scalar(SlotKind::Int)) => Ok(Kind::Int),
211 Some(ParamKind::Scalar(SlotKind::Float)) => Ok(Kind::Float),
212 Some(ParamKind::Scalar(SlotKind::Bool)) => Ok(Kind::Bool),
213 other => Err(format!("parameter {} is not an Int, Float or Bool ({other:?})", i + 1)),
214 })
215 .collect()
216}
217
218fn function_range(program: &CompiledProgram, fi: usize) -> (usize, usize) {
220 let entry = program.functions[fi].entry_pc;
221 let end = program.functions.iter().map(|g| g.entry_pc).filter(|&e| e > entry).min().unwrap_or(program.code.len());
222 (entry, end)
223}
224
225pub fn lower(program: &CompiledProgram, fi: usize, name: &str) -> Result<RegionCfg, HostReason> {
227 let host = |reason: String| HostReason { func: fi as u16, name: name.to_string(), reason };
228 let f = &program.functions[fi];
229 if !f.captures.is_empty() {
230 return Err(host("a closure body (it captures variables)".to_string()));
231 }
232 if program.code.iter().any(|op| matches!(op, Op::MakeClosure { func, .. } if *func as usize == fi)) {
233 return Err(host("a function the program also uses as a closure value".to_string()));
234 }
235 let params = param_kinds(program, fi).map_err(host)?;
236 let (entry, end) = function_range(program, fi);
237 let code = &program.code;
238 let starts = leaders(code, entry, end).map_err(host)?;
239 let starts: Vec<usize> = starts.into_iter().collect();
240 let block_of: BTreeMap<usize, usize> = starts.iter().enumerate().map(|(i, p)| (*p, i)).collect();
241 let block_end = |i: usize| starts.get(i + 1).copied().unwrap_or(end);
242 let mut entry_state: Vec<Option<State>> = vec![None; starts.len()];
244 entry_state[0] = Some(State {
245 kinds: params.iter().enumerate().map(|(i, k)| (i as u16, Ak::of(*k))).collect(),
246 iters: Vec::new(),
247 });
248 let mut work: Vec<usize> = vec![0];
249 let mut guard = 0usize;
250 while let Some(bi) = work.pop() {
251 guard += 1;
252 if guard > 100_000 {
253 return Err(host("the kind analysis did not settle".to_string()));
254 }
255 let mut st = entry_state[bi].clone().expect("a reached block has a state");
256 let mut succ: Vec<usize> = Vec::new();
257 let mut falls = true;
258 for pc in starts[bi]..block_end(bi) {
259 match transfer(program, pc, &mut st).map_err(host)? {
260 Flow::Next => {}
261 Flow::Jump(t) => {
262 succ.push(t);
263 falls = false;
264 }
265 Flow::Branch(t) => succ.push(t),
266 Flow::End => falls = false,
267 }
268 }
269 if falls && block_end(bi) < end {
270 succ.push(block_end(bi));
271 }
272 for t in succ {
273 let ti = *block_of.get(&t).ok_or_else(|| host(format!("pc {t} does not start a block")))?;
274 let merged = match &entry_state[ti] {
275 None => st.clone(),
276 Some(cur) => join(cur, &st).map_err(host)?,
277 };
278 if entry_state[ti].as_ref() != Some(&merged) {
279 entry_state[ti] = Some(merged);
280 work.push(ti);
281 }
282 }
283 }
284 let mut tr = Translator::new(program, f.register_count.max(params.len()));
286 let region_blocks: Vec<u32> = (0..starts.len()).map(|_| tr.new_block()).collect();
287 let mut ret: Option<Kind> = None;
288 for bi in 0..starts.len() {
289 let Some(mut st) = entry_state[bi].clone() else { continue };
290 let at = region_blocks[bi];
291 tr.at = at;
292 let mut ended = false;
293 for pc in starts[bi]..block_end(bi) {
294 let before = st.clone();
295 transfer(program, pc, &mut st).map_err(host)?;
296 let term = tr.op(pc, &before, ®ion_blocks, &block_of, &mut ret).map_err(host)?;
297 if term {
298 ended = true;
299 break;
300 }
301 }
302 if !ended {
303 let next = block_end(bi);
304 if next >= end {
305 return Err(host("control falls off the end of the function".to_string()));
306 }
307 tr.terminate(RTerm::Jump(region_blocks[block_of[&next]]));
308 }
309 }
310 let ret = ret.ok_or_else(|| host("no path returns a value".to_string()))?;
311 let regs = u16::try_from(tr.next_reg).map_err(|_| host("more registers than a region holds".to_string()))?;
312 Ok(RegionCfg { name: name.to_string(), func: fi as u16, params, ret, regs, blocks: tr.finish() })
313}
314
315pub fn discover(program: &CompiledProgram, names: &[String]) -> Vec<Discovered> {
317 (0..program.functions.len())
318 .map(|fi| match lower(program, fi, &names[fi]) {
319 Ok(r) => Discovered::Region(r),
320 Err(h) => Discovered::Host(h),
321 })
322 .collect()
323}
324
325enum Flow {
327 Next,
328 Jump(usize),
330 Branch(usize),
332 End,
334}
335
336fn scalar(st: &State, r: u16, pc: usize) -> Result<Kind, String> {
337 match st.kinds.get(&r) {
338 Some(k) => k.scalar().ok_or_else(|| format!("pc {pc}: register {r} is not a scalar here ({k:?})")),
339 None => Err(format!("pc {pc}: register {r} is read where no path defines it")),
340 }
341}
342
343fn numeric(a: Kind, b: Kind, pc: usize, what: &str) -> Result<Kind, String> {
345 match (a, b) {
346 (Kind::Int, Kind::Int) => Ok(Kind::Int),
347 (Kind::Float, Kind::Float) | (Kind::Int, Kind::Float) | (Kind::Float, Kind::Int) => Ok(Kind::Float),
348 _ => Err(format!("pc {pc}: {what} of {a:?} and {b:?}")),
349 }
350}
351
352fn transfer(program: &CompiledProgram, pc: usize, st: &mut State) -> Result<Flow, String> {
354 let set = |st: &mut State, r: u16, k: Ak| {
355 st.kinds.insert(r, k);
356 };
357 match program.code[pc] {
358 Op::LoadConst { dst, idx } => {
359 let k = match &program.constants[idx as usize] {
360 Constant::Int(_) => Ak::Int,
361 Constant::Float(_) => Ak::Float,
362 Constant::Bool(_) => Ak::Bool,
363 other => return Err(format!("pc {pc}: a {} constant", constant_kind(other))),
364 };
365 set(st, dst, k);
366 }
367 Op::Move { dst, src } => {
368 let k = *st.kinds.get(&src).ok_or_else(|| format!("pc {pc}: register {src} is read where no path defines it"))?;
369 set(st, dst, k);
370 }
371 Op::Add { dst, lhs, rhs } | Op::Sub { dst, lhs, rhs } | Op::Mul { dst, lhs, rhs } | Op::Div { dst, lhs, rhs } => {
372 let k = numeric(scalar(st, lhs, pc)?, scalar(st, rhs, pc)?, pc, program.code[pc].name())?;
373 set(st, dst, Ak::of(k));
374 }
375 Op::AddAssign { dst, src } => {
376 let k = numeric(scalar(st, dst, pc)?, scalar(st, src, pc)?, pc, "AddAssign")?;
377 set(st, dst, Ak::of(k));
378 }
379 Op::FloorDiv { dst, lhs, rhs } | Op::Mod { dst, lhs, rhs } | Op::Pow { dst, lhs, rhs } | Op::Shl { dst, lhs, rhs } | Op::Shr { dst, lhs, rhs } => {
380 let (a, b) = (scalar(st, lhs, pc)?, scalar(st, rhs, pc)?);
381 if (a, b) != (Kind::Int, Kind::Int) {
382 return Err(format!("pc {pc}: {} of {a:?} and {b:?}", program.code[pc].name()));
383 }
384 set(st, dst, Ak::Int);
385 }
386 Op::DivPow2 { dst, lhs, .. } | Op::MagicDivU { dst, lhs, .. } => {
387 if scalar(st, lhs, pc)? != Kind::Int {
388 return Err(format!("pc {pc}: {} of a non-Int", program.code[pc].name()));
389 }
390 set(st, dst, Ak::Int);
391 }
392 Op::Lt { dst, lhs, rhs } | Op::Gt { dst, lhs, rhs } | Op::LtEq { dst, lhs, rhs } | Op::GtEq { dst, lhs, rhs } => {
393 let (a, b) = (scalar(st, lhs, pc)?, scalar(st, rhs, pc)?);
394 if a != b || a == Kind::Bool {
395 return Err(format!("pc {pc}: an ordering of {a:?} and {b:?}"));
396 }
397 set(st, dst, Ak::Bool);
398 }
399 Op::Eq { dst, lhs, rhs } | Op::NotEq { dst, lhs, rhs } => {
400 let (a, b) = (scalar(st, lhs, pc)?, scalar(st, rhs, pc)?);
401 if a != b {
402 return Err(format!("pc {pc}: an equality of {a:?} and {b:?}"));
403 }
404 set(st, dst, Ak::Bool);
405 }
406 Op::ApproxEq { dst, lhs, rhs } => {
407 let (a, b) = (scalar(st, lhs, pc)?, scalar(st, rhs, pc)?);
408 if (a, b) != (Kind::Int, Kind::Int) {
409 return Err(format!("pc {pc}: `is approximately` of {a:?} and {b:?}"));
410 }
411 set(st, dst, Ak::Bool);
412 }
413 Op::Not { dst, src } => {
414 scalar(st, src, pc)?;
415 set(st, dst, Ak::Bool);
416 }
417 Op::BitXor { dst, lhs, rhs } | Op::BitAnd { dst, lhs, rhs } | Op::BitOr { dst, lhs, rhs } => {
418 let (a, b) = (scalar(st, lhs, pc)?, scalar(st, rhs, pc)?);
419 let k = match (a, b) {
420 (Kind::Int, Kind::Int) => Ak::Int,
421 (Kind::Bool, Kind::Bool) => Ak::Bool,
422 _ => return Err(format!("pc {pc}: {} of {a:?} and {b:?}", program.code[pc].name())),
423 };
424 set(st, dst, k);
425 }
426 Op::CallBuiltin { dst, builtin, args_start, arg_count } => {
427 let k = builtin_kind(st, builtin, args_start, arg_count, pc)?;
428 set(st, dst, Ak::of(k));
429 }
430 Op::Jump { target } => return Ok(Flow::Jump(target)),
431 Op::JumpIfFalse { cond, target } | Op::JumpIfTrue { cond, target } => {
432 scalar(st, cond, pc)?;
433 return Ok(Flow::Branch(target));
434 }
435 Op::Return { src } => {
436 scalar(st, src, pc)?;
437 return Ok(Flow::End);
438 }
439 Op::ReturnNothing => return Err(format!("pc {pc}: a path returns nothing")),
440 Op::NewRange { dst, start, end } => {
441 if scalar(st, start, pc)? != Kind::Int || scalar(st, end, pc)? != Kind::Int {
442 return Err(format!("pc {pc}: a range over non-Int bounds"));
443 }
444 set(st, dst, Ak::Range(pc));
445 }
446 Op::IterPrepare { iterable } => match st.kinds.get(&iterable) {
447 Some(Ak::Range(_)) => st.iters.push(pc),
448 other => return Err(format!("pc {pc}: iterating over {other:?}, not a range")),
449 },
450 Op::IterNext { dst, exit } => {
451 if st.iters.is_empty() {
452 return Err(format!("pc {pc}: IterNext with no live loop"));
453 }
454 set(st, dst, Ak::Int);
455 return Ok(Flow::Branch(exit));
456 }
457 Op::IterPop => {
458 st.iters.pop().ok_or_else(|| format!("pc {pc}: IterPop with no live loop"))?;
459 }
460 ref other => return Err(format!("pc {pc}: `{}` is not scalar region computation", other.name())),
461 }
462 Ok(Flow::Next)
463}
464
465fn constant_kind(c: &Constant) -> &'static str {
466 match c {
467 Constant::Int(_) => "Int",
468 Constant::Float(_) => "Float",
469 Constant::Bool(_) => "Bool",
470 Constant::Text(_) => "Text",
471 Constant::Char(_) => "Char",
472 Constant::Nothing => "Nothing",
473 Constant::Duration(_) => "Duration",
474 Constant::Date(_) => "Date",
475 Constant::Moment(_) => "Moment",
476 Constant::Span { .. } => "Span",
477 Constant::Time(_) => "Time",
478 }
479}
480
481fn builtin_kind(st: &State, b: BuiltinId, start: u16, count: u16, pc: usize) -> Result<Kind, String> {
483 if count != 1 {
484 return Err(format!("pc {pc}: builtin {b:?} with {count} arguments"));
485 }
486 let a = scalar(st, start, pc)?;
487 match (b, a) {
488 (BuiltinId::Sqrt, Kind::Int | Kind::Float) => Ok(Kind::Float),
489 (BuiltinId::Floor, Kind::Int | Kind::Float) => Ok(Kind::Int),
490 (BuiltinId::CountOnes, Kind::Int) => Ok(Kind::Int),
491 _ => Err(format!("pc {pc}: builtin {b:?} of {a:?}")),
492 }
493}
494
495struct Translator<'a> {
497 program: &'a CompiledProgram,
498 next_reg: usize,
499 blocks: Vec<RBlock>,
500 open: Vec<Vec<RStmt>>,
501 terms: Vec<Option<RTerm>>,
502 at: u32,
503 consts: BTreeMap<i64, VReg>,
504 const_stmts: Vec<RStmt>,
505 ranges: BTreeMap<usize, (VReg, VReg)>,
507 iters: BTreeMap<usize, (VReg, VReg, VReg)>,
509}
510
511impl<'a> Translator<'a> {
512 fn new(program: &'a CompiledProgram, vm_regs: usize) -> Self {
513 Translator {
514 program,
515 next_reg: vm_regs,
516 blocks: Vec::new(),
517 open: Vec::new(),
518 terms: Vec::new(),
519 at: 0,
520 consts: BTreeMap::new(),
521 const_stmts: Vec::new(),
522 ranges: BTreeMap::new(),
523 iters: BTreeMap::new(),
524 }
525 }
526
527 fn reg(&mut self) -> VReg {
528 let r = self.next_reg as VReg;
529 self.next_reg += 1;
530 r
531 }
532
533 fn new_block(&mut self) -> u32 {
534 self.open.push(Vec::new());
535 self.terms.push(None);
536 (self.open.len() - 1) as u32
537 }
538
539 fn push(&mut self, s: RStmt) {
540 self.open[self.at as usize].push(s);
541 }
542
543 fn terminate(&mut self, t: RTerm) {
544 self.terms[self.at as usize] = Some(t);
545 }
546
547 fn k(&mut self, v: i64) -> VReg {
549 if let Some(r) = self.consts.get(&v) {
550 return *r;
551 }
552 let r = self.reg();
553 self.const_stmts.push(RStmt::Const { dst: r, value: v });
554 self.consts.insert(v, r);
555 r
556 }
557
558 fn cell(&mut self, op: CellOp, a: VReg, b: VReg) -> VReg {
559 let dst = self.reg();
560 self.push(RStmt::Cell { dst, op, a, b });
561 dst
562 }
563
564 fn float(&mut self, x: VReg, k: Kind) -> VReg {
566 match k {
567 Kind::Float => x,
568 _ => {
569 let zero = self.k(0);
570 self.cell(CellOp::Unit(FuncUnit::IntToF), x, zero)
571 }
572 }
573 }
574
575 fn truthy(&mut self, c: VReg, k: Kind) -> VReg {
578 match k {
579 Kind::Int | Kind::Bool => c,
580 Kind::Float => {
581 let zero = self.k(0);
582 self.cell(CellOp::Unit(FuncUnit::FNeF), c, zero)
583 }
584 }
585 }
586
587 fn finish(mut self) -> Vec<RBlock> {
588 let mut entry_consts = std::mem::take(&mut self.const_stmts);
589 let mut blocks = Vec::with_capacity(self.open.len());
590 for (i, stmts) in self.open.into_iter().enumerate() {
591 let term = self.terms[i].unwrap_or(RTerm::Jump(i as u32));
592 let stmts = if i == 0 {
593 let mut s = std::mem::take(&mut entry_consts);
594 s.extend(stmts);
595 s
596 } else {
597 stmts
598 };
599 blocks.push(RBlock { stmts, term });
600 }
601 self.blocks = blocks;
602 self.blocks
603 }
604
605 fn op(
608 &mut self,
609 pc: usize,
610 before: &State,
611 region_blocks: &[u32],
612 block_of: &BTreeMap<usize, usize>,
613 ret: &mut Option<Kind>,
614 ) -> Result<bool, String> {
615 let blk = |p: usize| -> Result<u32, String> {
616 block_of.get(&p).map(|i| region_blocks[*i]).ok_or_else(|| format!("pc {p} does not start a block"))
617 };
618 let kind = |r: u16| scalar(before, r, pc);
619 match self.program.code[pc] {
620 Op::LoadConst { dst, idx } => {
621 let v = match &self.program.constants[idx as usize] {
622 Constant::Int(n) => *n,
623 Constant::Bool(b) => i64::from(*b),
624 Constant::Float(f) => f.to_bits() as i64,
625 other => return Err(format!("pc {pc}: a {} constant", constant_kind(other))),
626 };
627 let c = self.k(v);
628 self.push(RStmt::Copy { dst, src: c });
629 }
630 Op::Move { dst, src } => match before.kinds.get(&src) {
631 Some(Ak::Range(_)) => {}
632 _ => self.push(RStmt::Copy { dst, src }),
633 },
634 Op::Add { dst, lhs, rhs } => self.arith(dst, lhs, rhs, kind(lhs)?, kind(rhs)?, AluOp::Add, FuncUnit::FAddF),
635 Op::AddAssign { dst, src } => self.arith(dst, dst, src, kind(dst)?, kind(src)?, AluOp::Add, FuncUnit::FAddF),
636 Op::Sub { dst, lhs, rhs } => self.arith(dst, lhs, rhs, kind(lhs)?, kind(rhs)?, AluOp::Sub, FuncUnit::FSubF),
637 Op::Mul { dst, lhs, rhs } => self.arith(dst, lhs, rhs, kind(lhs)?, kind(rhs)?, AluOp::Mul, FuncUnit::FMulF),
638 Op::Div { dst, lhs, rhs } => {
639 let (a, b) = (kind(lhs)?, kind(rhs)?);
640 if (a, b) == (Kind::Int, Kind::Int) {
641 let v = self.cell(CellOp::Unit(FuncUnit::DivS), lhs, rhs);
642 self.push(RStmt::Copy { dst, src: v });
643 } else {
644 let (x, y) = (self.float(lhs, a), self.float(rhs, b));
645 let v = self.cell(CellOp::Unit(FuncUnit::FDivF), x, y);
646 self.push(RStmt::Copy { dst, src: v });
647 }
648 }
649 Op::FloorDiv { dst, lhs, rhs } => self.int_unit(dst, lhs, rhs, FuncUnit::FloorDivS),
650 Op::Mod { dst, lhs, rhs } => self.int_unit(dst, lhs, rhs, FuncUnit::ModS),
651 Op::Pow { dst, lhs, rhs } => self.int_unit(dst, lhs, rhs, FuncUnit::Pow),
652 Op::Shl { dst, lhs, rhs } => self.alu(dst, lhs, rhs, AluOp::Shl),
653 Op::Shr { dst, lhs, rhs } => self.alu(dst, lhs, rhs, AluOp::Shr),
654 Op::BitXor { dst, lhs, rhs } => self.alu(dst, lhs, rhs, AluOp::Xor),
655 Op::BitAnd { dst, lhs, rhs } => self.alu(dst, lhs, rhs, AluOp::And),
656 Op::BitOr { dst, lhs, rhs } => self.alu(dst, lhs, rhs, AluOp::Or),
657 Op::DivPow2 { dst, lhs, k } => {
658 let d = self.k(1i64 << k);
659 self.int_unit(dst, lhs, d, FuncUnit::DivS);
660 }
661 Op::MagicDivU { dst, lhs, mul_back, divisor, .. } => {
662 let d = self.k(divisor);
663 self.int_unit(dst, lhs, d, if mul_back == 0 { FuncUnit::DivS } else { FuncUnit::ModS });
664 }
665 Op::Lt { dst, lhs, rhs } => self.compare(dst, lhs, rhs, kind(lhs)?, CmpKind::Lt, FuncUnit::FLtF),
666 Op::Gt { dst, lhs, rhs } => self.compare(dst, lhs, rhs, kind(lhs)?, CmpKind::Gt, FuncUnit::FGtF),
667 Op::LtEq { dst, lhs, rhs } => self.compare(dst, lhs, rhs, kind(lhs)?, CmpKind::Le, FuncUnit::FLeF),
668 Op::GtEq { dst, lhs, rhs } => self.compare(dst, lhs, rhs, kind(lhs)?, CmpKind::Ge, FuncUnit::FGeF),
669 Op::Eq { dst, lhs, rhs } => self.compare(dst, lhs, rhs, kind(lhs)?, CmpKind::Eq, FuncUnit::FEqF),
670 Op::NotEq { dst, lhs, rhs } => self.compare(dst, lhs, rhs, kind(lhs)?, CmpKind::Ne, FuncUnit::FNeF),
671 Op::ApproxEq { dst, lhs, rhs } => {
672 let v = self.cell(CellOp::Unit(FuncUnit::ApproxEqF), lhs, rhs);
673 self.push(RStmt::Copy { dst, src: v });
674 }
675 Op::Not { dst, src } => {
676 let t = self.truthy(src, kind(src)?);
677 let (zero, one) = (self.k(0), self.k(1));
678 self.push(RStmt::Select { dst, cmp: CmpKind::Eq, a: t, b: zero, t: one, e: zero });
679 }
680 Op::CallBuiltin { dst, builtin, args_start, .. } => {
681 let a = kind(args_start)?;
682 match builtin {
683 BuiltinId::Sqrt => {
684 let x = self.float(args_start, a);
685 let zero = self.k(0);
686 let v = self.cell(CellOp::Unit(FuncUnit::FSqrtF), x, zero);
687 self.push(RStmt::Copy { dst, src: v });
688 }
689 BuiltinId::Floor if a == Kind::Int => self.push(RStmt::Copy { dst, src: args_start }),
690 BuiltinId::Floor => {
691 let zero = self.k(0);
692 let v = self.cell(CellOp::Unit(FuncUnit::FFloorF), args_start, zero);
693 self.push(RStmt::Copy { dst, src: v });
694 }
695 BuiltinId::CountOnes => {
696 let zero = self.k(0);
697 let v = self.cell(CellOp::Unit(FuncUnit::PopCount), args_start, zero);
698 self.push(RStmt::Copy { dst, src: v });
699 }
700 other => return Err(format!("pc {pc}: builtin {other:?}")),
701 }
702 }
703 Op::Jump { target } => {
704 self.terminate(RTerm::Jump(blk(target)?));
705 return Ok(true);
706 }
707 Op::JumpIfFalse { cond, target } => {
708 let c = self.truthy(cond, kind(cond)?);
709 self.terminate(RTerm::Branch { cond: c, then_: blk(pc + 1)?, else_: blk(target)? });
710 return Ok(true);
711 }
712 Op::JumpIfTrue { cond, target } => {
713 let c = self.truthy(cond, kind(cond)?);
714 self.terminate(RTerm::Branch { cond: c, then_: blk(target)?, else_: blk(pc + 1)? });
715 return Ok(true);
716 }
717 Op::Return { src } => {
718 let k = kind(src)?;
719 match ret {
720 None => *ret = Some(k),
721 Some(r) if *r == k => {}
722 Some(r) => return Err(format!("pc {pc}: returns a {k:?} where another path returns a {r:?}")),
723 }
724 self.terminate(RTerm::Return(src));
725 return Ok(true);
726 }
727 Op::NewRange { start, end, .. } => {
728 let (s, e) = (self.reg(), self.reg());
729 self.push(RStmt::Copy { dst: s, src: start });
730 self.push(RStmt::Copy { dst: e, src: end });
731 self.ranges.insert(pc, (s, e));
732 }
733 Op::IterPrepare { iterable } => {
734 let Some(Ak::Range(rp)) = before.kinds.get(&iterable).copied() else {
735 return Err(format!("pc {pc}: iterating over a non-range"));
736 };
737 let (s, e) = *self.ranges.get(&rp).ok_or_else(|| format!("pc {pc}: the range's NewRange was not translated first"))?;
738 let (cur, end, done) = (self.reg(), self.reg(), self.reg());
739 let (zero, one) = (self.k(0), self.k(1));
740 self.push(RStmt::Copy { dst: cur, src: s });
741 self.push(RStmt::Copy { dst: end, src: e });
742 self.push(RStmt::Select { dst: done, cmp: CmpKind::Gt, a: cur, b: end, t: one, e: zero });
743 self.iters.insert(pc, (cur, end, done));
744 }
745 Op::IterNext { dst, exit } => {
746 let q = *before.iters.last().ok_or_else(|| format!("pc {pc}: IterNext with no live loop"))?;
747 let (cur, end, done) = *self.iters.get(&q).ok_or_else(|| format!("pc {pc}: the loop's IterPrepare was not translated first"))?;
748 let body = blk(pc + 1)?;
749 let exit_b = blk(exit)?;
750 let step = self.new_block();
751 let last = self.new_block();
752 let inc = self.new_block();
753 self.terminate(RTerm::Branch { cond: done, then_: exit_b, else_: step });
754 let (zero, one) = (self.k(0), self.k(1));
755 self.at = step;
756 self.push(RStmt::Copy { dst, src: cur });
757 let is_last = self.reg();
758 self.push(RStmt::Select { dst: is_last, cmp: CmpKind::Eq, a: cur, b: end, t: one, e: zero });
759 self.terminate(RTerm::Branch { cond: is_last, then_: last, else_: inc });
760 self.at = last;
761 self.push(RStmt::Copy { dst: done, src: one });
762 self.terminate(RTerm::Jump(body));
763 self.at = inc;
764 let next = self.cell(CellOp::Alu(AluOp::Add), cur, one);
765 self.push(RStmt::Copy { dst: cur, src: next });
766 self.terminate(RTerm::Jump(body));
767 return Ok(true);
768 }
769 Op::IterPop => {}
770 ref other => return Err(format!("pc {pc}: `{}` is not scalar region computation", other.name())),
771 }
772 Ok(false)
773 }
774
775 fn arith(&mut self, dst: VReg, lhs: VReg, rhs: VReg, a: Kind, b: Kind, int: AluOp, float: FuncUnit) {
776 let v = if (a, b) == (Kind::Int, Kind::Int) {
777 self.cell(CellOp::Alu(int), lhs, rhs)
778 } else {
779 let (x, y) = (self.float(lhs, a), self.float(rhs, b));
780 self.cell(CellOp::Unit(float), x, y)
781 };
782 self.push(RStmt::Copy { dst, src: v });
783 }
784
785 fn int_unit(&mut self, dst: VReg, lhs: VReg, rhs: VReg, unit: FuncUnit) {
786 let v = self.cell(CellOp::Unit(unit), lhs, rhs);
787 self.push(RStmt::Copy { dst, src: v });
788 }
789
790 fn alu(&mut self, dst: VReg, lhs: VReg, rhs: VReg, op: AluOp) {
791 let v = self.cell(CellOp::Alu(op), lhs, rhs);
792 self.push(RStmt::Copy { dst, src: v });
793 }
794
795 fn compare(&mut self, dst: VReg, lhs: VReg, rhs: VReg, k: Kind, cmp: CmpKind, float: FuncUnit) {
796 if k == Kind::Float {
797 let v = self.cell(CellOp::Unit(float), lhs, rhs);
798 self.push(RStmt::Copy { dst, src: v });
799 } else {
800 let (zero, one) = (self.k(0), self.k(1));
801 self.push(RStmt::Select { dst, cmp, a: lhs, b: rhs, t: one, e: zero });
802 }
803 }
804}
805
806impl RegionCfg {
807 pub fn eval_checked(&self, args: &[i64], fuel: u64) -> Result<i64, Halt> {
810 assert_eq!(args.len(), self.params.len(), "{}: arity", self.name);
811 let mut regs = vec![0i64; self.regs as usize];
812 regs[..args.len()].copy_from_slice(args);
813 let mut block = 0usize;
814 let mut left = fuel;
815 loop {
816 if left == 0 {
817 return Err(Halt::OutOfFuel);
818 }
819 left -= 1;
820 let b = &self.blocks[block];
821 for s in &b.stmts {
822 match *s {
823 RStmt::Const { dst, value } => regs[dst as usize] = value,
824 RStmt::Copy { dst, src } => regs[dst as usize] = regs[src as usize],
825 RStmt::Cell { dst, op, a, b } => {
826 let (x, y) = (regs[a as usize], regs[b as usize]);
827 let r = match op {
828 CellOp::Alu(alu) => alu.eval_checked(x, y),
829 CellOp::Unit(u) => u.eval_checked(x, y),
830 CellOp::Select(c) => Ok(i64::from(c.eval(x, y))),
831 };
832 regs[dst as usize] = r.map_err(|d| Halt::Declined { divergence: d, op, a: x, b: y })?;
833 }
834 RStmt::Select { dst, cmp, a, b, t, e } => {
835 regs[dst as usize] = if cmp.eval(regs[a as usize], regs[b as usize]) { regs[t as usize] } else { regs[e as usize] };
836 }
837 }
838 }
839 match b.term {
840 RTerm::Jump(t) => block = t as usize,
841 RTerm::Branch { cond, then_, else_ } => block = if regs[cond as usize] != 0 { then_ } else { else_ } as usize,
842 RTerm::Return(v) => return Ok(regs[v as usize]),
843 }
844 }
845 }
846
847 pub fn eval_on_lanes(&self, lanes: &mut dyn Lanes, args: &[i64], fuel: u64) -> Result<(i64, LaneCounts), Halt> {
850 assert_eq!(args.len(), self.params.len(), "{}: arity", self.name);
851 let shape = lanes.shape();
852 let n = shape.words_for(64);
853 let mut regs: Vec<Vec<u32>> = vec![vec![0; n]; self.regs as usize];
854 for (i, a) in args.iter().enumerate() {
855 regs[i] = split_u64(shape, *a as u64, n);
856 }
857 let mut counts = LaneCounts::default();
858 let mut block = 0usize;
859 let mut left = fuel;
860 let lane_fault = |e: super::lane::LaneFault| -> Halt { panic!("{}: a lane routine refused its inputs: {e}", self.name) };
861 loop {
862 if left == 0 {
863 return Err(Halt::OutOfFuel);
864 }
865 left -= 1;
866 let b = &self.blocks[block];
867 for s in &b.stmts {
868 match *s {
869 RStmt::Const { dst, value } => regs[dst as usize] = split_u64(shape, value as u64, n),
870 RStmt::Copy { dst, src } => regs[dst as usize] = regs[src as usize].clone(),
871 RStmt::Cell { dst, op, a, b } => {
872 let r = routine(shape, op);
873 let mut input = regs[a as usize].clone();
874 input.extend_from_slice(®s[b as usize]);
875 let (out, c) = run(&r, lanes, &input).map_err(lane_fault)?;
876 counts.absorb(&c);
877 let value = out[..n].to_vec();
878 if op.faults() {
879 let code = join_u64(shape, &out[n..]) as u32;
880 if code != 0 {
881 let divergence = Divergence::from_code(code).unwrap_or_else(|| panic!("{}: fault code {code}", self.name));
882 let (x, y) = (join_u64(shape, ®s[a as usize]) as i64, join_u64(shape, ®s[b as usize]) as i64);
883 return Err(Halt::Declined { divergence, op, a: x, b: y });
884 }
885 }
886 regs[dst as usize] = value;
887 }
888 RStmt::Select { dst, cmp, a, b, t, e } => {
889 let r = routine(shape, CellOp::Select(cmp));
890 let mut input = regs[a as usize].clone();
891 input.extend_from_slice(®s[b as usize]);
892 input.extend_from_slice(®s[t as usize]);
893 input.extend_from_slice(®s[e as usize]);
894 let (out, c) = run(&r, lanes, &input).map_err(lane_fault)?;
895 counts.absorb(&c);
896 regs[dst as usize] = out;
897 }
898 }
899 }
900 match b.term {
901 RTerm::Jump(t) => block = t as usize,
902 RTerm::Branch { cond, then_, else_ } => {
903 block = if regs[cond as usize].iter().any(|w| *w != 0) { then_ } else { else_ } as usize
904 }
905 RTerm::Return(v) => return Ok((join_u64(shape, ®s[v as usize]) as i64, counts)),
906 }
907 }
908 }
909}