1use super::sched::Item;
5use super::{BinOp, CmpOp, Elem, IId, INode, IType, IntGraph, UnOp};
6
7pub(crate) fn bin(op: BinOp, t: IType, a: u32, b: u32) -> u32 {
8 let s = t == IType::I32;
9 match op {
10 BinOp::Add => a.wrapping_add(b),
11 BinOp::Sub => a.wrapping_sub(b),
12 BinOp::Mul => a.wrapping_mul(b),
13 BinOp::MulHi if s => ((a as i32 as i64 * b as i32 as i64) >> 32) as u32,
14 BinOp::MulHi => ((a as u64 * b as u64) >> 32) as u32,
15 BinOp::And => a & b,
16 BinOp::Or => a | b,
17 BinOp::Xor => a ^ b,
18 BinOp::Shl => a.wrapping_shl(b),
19 BinOp::Shr if s => (a as i32).wrapping_shr(b) as u32,
20 BinOp::Shr => a.wrapping_shr(b),
21 BinOp::Min if s => (a as i32).min(b as i32) as u32,
22 BinOp::Min => a.min(b),
23 BinOp::Max if s => (a as i32).max(b as i32) as u32,
24 BinOp::Max => a.max(b),
25 BinOp::Div if b == 0 => a,
26 BinOp::Div if s => (a as i32).wrapping_div(b as i32) as u32,
27 BinOp::Div => a / b,
28 BinOp::Rem if b == 0 => 0,
29 BinOp::Rem if s => (a as i32).wrapping_rem(b as i32) as u32,
30 BinOp::Rem => a % b,
31 }
32}
33
34pub(crate) fn un(op: UnOp, t: IType, a: u32) -> u32 {
35 match (op, t) {
36 (UnOp::Neg, _) => a.wrapping_neg(),
37 (UnOp::Not, IType::Bool) => a ^ 1,
38 (UnOp::Not, _) => !a,
39 (UnOp::Clz, _) => a.leading_zeros(),
40 }
41}
42
43pub(crate) fn cmp(op: CmpOp, t: IType, a: u32, b: u32) -> u32 {
44 let o = if t == IType::I32 {
45 (a as i32).cmp(&(b as i32))
46 } else {
47 a.cmp(&b)
48 };
49 (match op {
50 CmpOp::Eq => o.is_eq(),
51 CmpOp::Ne => o.is_ne(),
52 CmpOp::Lt => o.is_lt(),
53 CmpOp::Le => o.is_le(),
54 }) as u32
55}
56
57pub(crate) fn load(elem: Elem, buf: &[u32], i: u32) -> u32 {
58 let i = i as usize;
60 let w = |j: usize| buf.get(j).copied().unwrap_or(0);
61 match elem {
62 Elem::I32 | Elem::U32 => w(i),
63 Elem::U8 => (w(i >> 2) >> ((i & 3) * 8)) & 0xff,
64 Elem::I8 => ((w(i >> 2) >> ((i & 3) * 8)) as u8 as i8) as i32 as u32,
65 }
66}
67
68#[derive(Clone, Copy)]
69enum Op {
70 Bin(BinOp, IType, u32, u32, u32),
71 Un(UnOp, IType, u32, u32),
72 Cmp(CmpOp, IType, u32, u32, u32),
73 Sel(u32, u32, u32, u32),
74 Mov(u32, u32),
75 Load(Elem, u16, u32, u32),
76 LoadRw(u16, u32, u32, u32),
78 Store(u16, u32, u32, u32, u32),
80 JmpIfZero(u32, u32),
81 Jmp(u32),
82}
83
84#[derive(Clone)]
89pub struct Program {
90 ops: Vec<Op>,
91 regs: Vec<u32>,
92 params: Vec<(u16, u32)>,
93 outs: Vec<u32>,
94}
95
96impl Program {
97 pub fn new(g: &IntGraph, outputs: &[IId]) -> Self {
99 let sched = g.schedule(outputs);
100 let n = g.nodes.len();
101 let mut regs = vec![0u32; n];
102 let mut params = Vec::new();
103 for (i, node) in g.nodes.iter().enumerate() {
104 match *node {
105 INode::Const(_, v) => regs[i] = v,
106 INode::Param(p, _) => params.push((p, i as u32)),
107 _ => {}
108 }
109 }
110 let mut state = vec![Vec::new(); g.loops.len()];
112 for (i, node) in g.nodes.iter().enumerate() {
113 if let INode::State(l, k) = *node {
114 let s = &mut state[l as usize];
115 if s.len() <= k as usize {
116 s.resize(k as usize + 1, u32::MAX);
117 }
118 s[k as usize] = i as u32;
119 }
120 }
121 let (mut nro, mut nrw) = (0u16, 0u16);
123 let bidx = g
124 .bufs
125 .iter()
126 .map(|b| {
127 let c = if b.rw.is_some() { &mut nrw } else { &mut nro };
128 *c += 1;
129 *c - 1
130 })
131 .collect();
132 let mut c = Compiler {
133 g,
134 ops: Vec::new(),
135 state,
136 nregs: n as u32 + 1,
137 consts: vec![(n as u32, 1)],
138 one: n as u32,
139 bidx,
140 };
141 c.items(&sched.items);
142 regs.resize(c.nregs as usize, 0);
143 for &(r, v) in &c.consts {
144 regs[r as usize] = v;
145 }
146 Self {
147 ops: c.ops,
148 regs,
149 params,
150 outs: outputs.iter().map(|o| o.0).collect(),
151 }
152 }
153
154 pub fn run(&mut self, params: &[u32], bufs: &[&[u32]], out: &mut [u32]) {
157 self.run_rw(params, bufs, &mut [], out)
158 }
159
160 pub fn run_rw(
163 &mut self,
164 params: &[u32],
165 bufs: &[&[u32]],
166 rw: &mut [&mut [u32]],
167 out: &mut [u32],
168 ) {
169 let r = &mut self.regs;
170 for &(p, i) in &self.params {
171 r[i as usize] = params[p as usize];
172 }
173 let mut pc = 0usize;
174 while pc < self.ops.len() {
175 match self.ops[pc] {
176 Op::Bin(op, t, d, a, b) => r[d as usize] = bin(op, t, r[a as usize], r[b as usize]),
177 Op::Un(op, t, d, a) => r[d as usize] = un(op, t, r[a as usize]),
178 Op::Cmp(op, t, d, a, b) => r[d as usize] = cmp(op, t, r[a as usize], r[b as usize]),
179 Op::Sel(d, c, a, b) => {
180 r[d as usize] = if r[c as usize] != 0 {
181 r[a as usize]
182 } else {
183 r[b as usize]
184 }
185 }
186 Op::Mov(d, a) => r[d as usize] = r[a as usize],
187 Op::Load(e, b, d, i) => r[d as usize] = load(e, bufs[b as usize], r[i as usize]),
188 Op::LoadRw(b, n, d, i) => {
189 let i = r[i as usize];
190 r[d as usize] = if i < n {
191 rw[b as usize].get(i as usize).copied().unwrap_or(0)
192 } else {
193 0
194 };
195 }
196 Op::Store(b, n, i, v, d) => {
197 let i = r[i as usize];
198 if i < n {
199 if let Some(w) = rw[b as usize].get_mut(i as usize) {
200 *w = r[v as usize];
201 }
202 }
203 r[d as usize] = 0;
204 }
205 Op::JmpIfZero(c, t) => {
206 if r[c as usize] == 0 {
207 pc = t as usize;
208 continue;
209 }
210 }
211 Op::Jmp(t) => {
212 pc = t as usize;
213 continue;
214 }
215 }
216 pc += 1;
217 }
218 for (o, &i) in out.iter_mut().zip(&self.outs) {
219 *o = r[i as usize];
220 }
221 }
222}
223
224struct Compiler<'a> {
225 g: &'a IntGraph,
226 ops: Vec<Op>,
227 state: Vec<Vec<u32>>,
228 nregs: u32,
229 consts: Vec<(u32, u32)>,
231 one: u32,
233 bidx: Vec<u16>,
235}
236
237impl Compiler<'_> {
238 fn items(&mut self, items: &[Item]) {
239 for it in items {
240 match it {
241 Item::Node(id) => self.node(*id),
242 Item::Loop { l, pre, post } => self.lp(*l, pre, post),
243 }
244 }
245 }
246
247 fn node(&mut self, id: IId) {
248 let g = self.g;
249 let d = id.0;
250 let op = match g.node(id) {
251 INode::Const(..) | INode::Param(..) | INode::State(..) | INode::Tok => return,
252 INode::Bin(op, a, b) => Op::Bin(op, g.ty(a), d, a.0, b.0),
253 INode::Un(op, a) => Op::Un(op, g.ty(a), d, a.0),
254 INode::Cmp(op, a, b) => Op::Cmp(op, g.ty(a), d, a.0, b.0),
255 INode::Select(c, a, b) => Op::Sel(d, c.0, a.0, b.0),
256 INode::Bitcast(_, a) => Op::Mov(d, a.0),
257 INode::Load(b, i) => Op::Load(g.bufs[b as usize].elem, self.bidx[b as usize], d, i.0),
258 INode::LoadRw(b, i, _) => Op::LoadRw(
259 self.bidx[b as usize],
260 g.bufs[b as usize].rw.as_ref().unwrap().0,
261 d,
262 i.0,
263 ),
264 INode::Store(b, i, v, _) => Op::Store(
265 self.bidx[b as usize],
266 g.bufs[b as usize].rw.as_ref().unwrap().0,
267 i.0,
268 v.0,
269 d,
270 ),
271 INode::LoopOut(l, k) => Op::Mov(d, self.state[l as usize][k as usize]),
272 };
273 self.ops.push(op);
274 }
275
276 fn lp(&mut self, l: u32, pre: &[Item], post: &[Item]) {
277 let lp = &self.g.loops[l as usize];
278 let st = self.state[l as usize].clone();
279 for (k, &i) in lp.init.iter().enumerate() {
280 self.ops.push(Op::Mov(st[k], i.0));
281 }
282 let capr = lp.cap.map(|c| {
284 let (ctr, lim, ok) = (self.nregs, self.nregs + 1, self.nregs + 2);
285 self.nregs += 3;
286 self.consts.push((lim, c));
287 self.ops
288 .push(Op::Bin(BinOp::Xor, IType::U32, ctr, ctr, ctr));
289 (ctr, lim, ok)
290 });
291 let top = self.ops.len() as u32;
292 let jcap = capr.map(|(ctr, lim, ok)| {
293 self.ops.push(Op::Cmp(CmpOp::Lt, IType::U32, ok, ctr, lim));
294 let j = self.ops.len();
295 self.ops.push(Op::JmpIfZero(ok, 0));
296 self.ops
297 .push(Op::Bin(BinOp::Add, IType::U32, ctr, ctr, self.one));
298 j
299 });
300 self.items(pre);
301 let jz = self.ops.len();
302 self.ops.push(Op::JmpIfZero(lp.cond.0, 0));
303 self.items(post);
304 let tmp = self.nregs;
306 self.nregs += lp.next.len() as u32;
307 for (k, &x) in lp.next.iter().enumerate() {
308 self.ops.push(Op::Mov(tmp + k as u32, x.0));
309 }
310 for (k, &r) in st.iter().enumerate().take(lp.next.len()) {
311 self.ops.push(Op::Mov(r, tmp + k as u32));
312 }
313 self.ops.push(Op::Jmp(top));
314 let end = self.ops.len() as u32;
315 self.ops[jz] = Op::JmpIfZero(lp.cond.0, end);
316 if let (Some(j), Some((_, _, ok))) = (jcap, capr) {
317 self.ops[j] = Op::JmpIfZero(ok, end);
318 }
319 }
320}
321
322impl IntGraph {
323 pub fn eval(&self, outputs: &[IId], params: &[u32], bufs: &[&[u32]]) -> Vec<u32> {
325 let mut p = Program::new(self, outputs);
326 let mut out = vec![0; outputs.len()];
327 p.run(params, bufs, &mut out);
328 out
329 }
330}