From fb5a2a98d4ec19e4b4a4898a8124fa4a76f87ee1 Mon Sep 17 00:00:00 2001 From: Victor Maia Date: Sun, 12 Nov 2023 15:18:31 -0300 Subject: [PATCH] Initial compiler! 1.5x to 10x speedups MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit implements compilation of HVM terms. The idea, as explained in an early commit, is to modify the `@F ~ X` deref rule, so that, instead of unrolling `@F` and letting reductions happen normally, we instead pass `X` to a compiled procedure that attempts to perform some local reductions BEFORE allocating `@F`. For example, the HVM term: add = λa λb (+ a b) Is compiled to HVM as: @add = (a (b r)) & #1 ~ > So, if we apply `(add 123 100)`, we will have the following net: (#123 (#100 R)) ~ ⟦(a (b r))⟧ ⟦#1⟧ ~ ⟦>⟧ Notice that `(#123 (#100 R))` is a dynamic net, and everything inside these «angle_brackets» is part of the "source code" of `@add`, i.e., these are static nets. As such, once we send `X` to the compiled `add()`, we can immediatelly detect that `X` isn't an aux port, but is actually the main port of the `(#123 (#100 R))` tree. Furthermore, since the root of `@add` is also two CON nodes (representing `λa` and `λb`), we can immediatelly substitute `a <~ #123`, `b <~ #100`, and `r <~ R`, performing two "local annihilations" before allocating the body of `@add`. As a result, we'll have the following net: R ~ ⟦r⟧ ⟦#1⟧ ~ ⟦<#123 <#100 r>>⟧ Now, we have an OP2 node connected to the number #1. Normally, that would require 4 rewrites to reduce to normal form: R ~ r #1 ~ <#123 <#100 r>> -------------------- OP2 R ~ r #123 ~ <#1 <#100 r>> -------------------- OP1 R ~ r #+123 ~ <#100 r> ---------------- OP2 R ~ r #100 ~ <#+123 r> ---------------- OP1 R ~ r #223 ~ r -------- subst #223 ~ R Yet, the compiled `add()` function can see, on its local registers, that `op = #1`, `a = #123` and `b = #100`. As such, it doesn't need to allocate any OP2 node, and can shortcut the reduction directly to: R ~ ⟦r⟧ ⟦#1⟧ ~ ⟦<#123 <#100 r>>⟧ ------------------------ OP2 + OP1 + OP2 + OP1 R ~ #223 Which bypasses the runtime entirely, saving several allocations and redex pushing/popping/matching. Sadly, Rust functions, unlike interaction nets, obey an evaluation order. As such, keeping a mini "local interaction net runtime" on registers would be inpractical. As such, we make a choice on the order that we traverse the "static net"; specifically, we first traverse the root tree, then the redex trees, in order. This is relevant, because it means that the order matters for which optimizations are used. For example, in this case, if we first traversed the redex trees, we'd have: ⟦#1⟧ ~ ⟦>⟧ (#123 (#100 R)) ~ ⟦(a (b r))⟧ ----------------------------- alloc `>` #1 ~ > (#123 (#100 R)) ~ ⟦(a (b r))⟧ ----------------------------- alloc `(a (b r))` #1 ~ > (#123 (#100 R)) ~ (a (b r)) --------------------------- ... proceed reduction as normal I.e., when traversing `#1 ~ >`, the compiled `add()` function would see `a` and `b` (i.e., aux ports) instead of concrete numbers and, as such, it would be forced to allocate 2 OP2 nodes, `>`, and the optimization would fail, causing it to fall back to the interpreted speed. As such, it is important that tools emitting HVMC code to sort redexes in a way that allows optimizations to be performed more often. If redexes are sorted respecting the corresponding "strict evaluation" order, then functions compiled from classical paradigm should always hit the optimization case. For illustration, here is the compiled `add()` procedure: pub fn F_add(&mut self, ptr: Ptr, x: Ptr) -> bool { let xx : Ptr; let xy : Ptr; // fast apply if x.tag() == CT0 { self.anni += 1; xx = self.heap.get(x.val(), P1); xy = self.heap.get(x.val(), P2); self.heap.free(x.val()); } else { let k1 = self.heap.alloc(1); xx = Ptr::new(VR1, k1); xy = Ptr::new(VR2, k1); self.link(Ptr::new(CT0, k1), x); } let xyx : Ptr; let xyy : Ptr; // fast apply if xy.tag() == CT0 { self.anni += 1; xyx = self.heap.get(xy.val(), P1); xyy = self.heap.get(xy.val(), P2); self.heap.free(xy.val()); } else { let k2 = self.heap.alloc(1); xyx = Ptr::new(VR1, k2); xyy = Ptr::new(VR2, k2); self.link(Ptr::new(CT0, k2), xy); } let _k3 = Ptr::new(NUM, 0x1); let k4 : Ptr; // fast op if _k3.is_num() && xx.is_num() && xyx.is_num() { self.oper += 4; k4 = Ptr::new(NUM, self.op(self.op(_k3.val(),xx.val()),xyx.val())); } else { let k5 = self.heap.alloc(1); let k6 = self.heap.alloc(1); self.heap.set(k5, P2, Ptr::new(OP2, k6)); self.link(Ptr::new(VR1,k5), xx); self.link(Ptr::new(VR1,k6), xyx); self.link(Ptr::new(OP2,k5), _k3); k4 = Ptr::new(VR2, k6); } self.link(k4, xyy); return true; } Each optimization branch is labelled with a comment. The more optimization branches are hit, the faster your program will be. This commit results in a 1.55x speedup in the 'burn' benchmark (the one that decrements λ-encoded bits in parallel), a 2.94x speedup in a tree recursive sum, and a 5.64x speedup in a tail recursive sum. Note that tail recursion was NOT implemented yet, and there are still some allocations that can be skipped. With a better codegen, the maximum theoretical speedup should be of around 36x, which is what we obtain by manually polishing the generated functions. --- examples/alloc_big_tree.hvmc | 7 - examples/{dec_bits_tree.hvmc => burn.hvmc} | 6 +- examples/church.hvm2 | 12 + examples/church.hvmc | 21 ++ examples/church_exp.hvmc | 10 - examples/dec_bits.hvmc | 24 -- examples/examples.hvmc | 117 --------- examples/loop.hvmc | 46 ---- examples/num_add.hvm2 | 2 + examples/num_add.hvmc | 6 + examples/num_match.hvm2 | 6 + examples/num_match.hvmc | 6 + examples/num_mt_ex.hvmc | 5 - examples/num_op_ex.hvmc | 6 - examples/sum_rec.hvm2 | 6 + examples/sum_rec.hvmc | 9 + examples/sum_tail.hvm2 | 6 + examples/sum_tail.hvmc | 10 + src/jit.rs | 290 ++++++++++++++------- src/run.rs | 20 +- 20 files changed, 288 insertions(+), 327 deletions(-) delete mode 100644 examples/alloc_big_tree.hvmc rename examples/{dec_bits_tree.hvmc => burn.hvmc} (75%) create mode 100644 examples/church.hvm2 create mode 100644 examples/church.hvmc delete mode 100644 examples/church_exp.hvmc delete mode 100644 examples/dec_bits.hvmc delete mode 100644 examples/examples.hvmc delete mode 100644 examples/loop.hvmc create mode 100644 examples/num_add.hvm2 create mode 100644 examples/num_add.hvmc create mode 100644 examples/num_match.hvm2 create mode 100644 examples/num_match.hvmc delete mode 100644 examples/num_mt_ex.hvmc delete mode 100644 examples/num_op_ex.hvmc create mode 100644 examples/sum_rec.hvm2 create mode 100644 examples/sum_rec.hvmc create mode 100644 examples/sum_tail.hvm2 create mode 100644 examples/sum_tail.hvmc diff --git a/examples/alloc_big_tree.hvmc b/examples/alloc_big_tree.hvmc deleted file mode 100644 index b6d016a2..00000000 --- a/examples/alloc_big_tree.hvmc +++ /dev/null @@ -1,7 +0,0 @@ -@c8 = ([[[[[[[(h g) (g f)] (f e)] (e d)] (d c)] (c b)] (b a)] (a R)] (h R)) -@g_s = ({2 r0 r1} ((r0 (r1 r)) r)) -@g_z = (x x) - -@main - = R - & @c8 ~ (@g_s (@g_z R)) diff --git a/examples/dec_bits_tree.hvmc b/examples/burn.hvmc similarity index 75% rename from examples/dec_bits_tree.hvmc rename to examples/burn.hvmc index 7ddc8b96..7fc2e2f6 100644 --- a/examples/dec_bits_tree.hvmc +++ b/examples/burn.hvmc @@ -1,7 +1,11 @@ -// Decreases a tree of binary counters until they're all 0 (parallel) +// Decreases a tree of λ-encoded binary counters until they're all 0 (parallel). // Takes about ~16s on Apple M1, and ~0.5s on RTX 4090 +@c4 = ([[[(d c) (c b)] (b a)] (a R)] (d R)) +@c6 = ([[[[[(f e) (e d)] (d c)] (c b)] (b a)] (a R)] (f R)) +@c8 = ([[[[[[[(h g) (g f)] (f e)] (e d)] (d c)] (c b)] (b a)] (a R)] (h R)) @c10 = ([[[[[[[[[(j i) (i h)] (h g)] (g f)] (f e)] (e d)] (d c)] (c b)] (b a)] (a R)] (j R)) +@c12 = ([[[[[[[[[[[(l k) (k j)] (j i)] (i h)] (h g)] (g f)] (f e)] (e d)] (d c)] (c b)] (b a)] (a R)] (l R)) @c14 = ([[[[[[[[[[[[[(n m) (m l)] (l k)] (k j)] (j i)] (i h)] (h g)] (g f)] (f e)] (e d)] (d c)] (c b)] (b a)] (a R)] (n R)) @c16 = ([[[[[[[[[[[[[[[(p o) (o n)] (n m)] (m l)] (l k)] (k j)] (j i)] (i h)] (h g)] (g f)] (f e)] (e d)] (d c)] (c b)] (b a)] (a R)] (p R)) diff --git a/examples/church.hvm2 b/examples/church.hvm2 new file mode 100644 index 00000000..fefebd83 --- /dev/null +++ b/examples/church.hvm2 @@ -0,0 +1,12 @@ +S = λn λs λz (s (n s z)) +Z = λs λz z + +c2 = λf λx (f (f x)) +c3 = λf λx (f (f (f x))) +c4 = (S (S (S (S Z)))) + +add = λa λb λs λz (a s (b s z)) +mul = λa λb λs λz (a (b s) z) + +// 2 * 3 + 4 +main = (add (mul c2 c3) c4) diff --git a/examples/church.hvmc b/examples/church.hvmc new file mode 100644 index 00000000..c831d4db --- /dev/null +++ b/examples/church.hvmc @@ -0,0 +1,21 @@ +@S = ((a (b c)) ({2 a (c d)} (b d))) +@Z = (* (a a)) + +@add = ((a (b c)) ((d (e b)) ({2 d a} (e c)))) +@mul = ((a (b c)) ((d a) (d (b c)))) + +@c2 = ({2 (a b) (b c)} (a c)) +@c3 = ({2 (a b) {3 (b c) (c d)}} (a d)) + +@c4 + = a + & @S ~ (b a) + & @S ~ (c b) + & @S ~ (d c) + & @S ~ (@Z d) + +@main + = a + & @add ~ (b (@c4 a)) + & @mul ~ (@c2 (@c3 b)) + diff --git a/examples/church_exp.hvmc b/examples/church_exp.hvmc deleted file mode 100644 index db130646..00000000 --- a/examples/church_exp.hvmc +++ /dev/null @@ -1,10 +0,0 @@ -// closed net for the Church Nat 2 -@c2_a = ([(b a) (a R)] (b R)) - -// also 2, but with a different label -@c2_b = ({2 (b a) (a R)} (b R)) - -// applies 2 to 2 to exponentiate -@main - = ret - & @c2_a ~ (@c2_b ret) diff --git a/examples/dec_bits.hvmc b/examples/dec_bits.hvmc deleted file mode 100644 index 1eb3aee4..00000000 --- a/examples/dec_bits.hvmc +++ /dev/null @@ -1,24 +0,0 @@ -// Decreases a binary counter until it is 0 - -@c24 = ([[[[[[[[[[[[[[[[[[[[[[[(x w) (w v)] (v u)] (u t)] (t s)] (s r)] (r q)] (q p)] (p o)] (o n)] (n m)] (m l)] (l k)] (k j)] (j i)] (i h)] (h g)] (g f)] (f e)] (e d)] (d c)] (c b)] (b a)] (a R)] (x R)) - -@O = (xs ((xs r) (* (* r)))) -@I = (xs (* ((xs r) (* r)))) -@E = (* (* (e e))) - -@decO = (p idecp) & @I ~ (decp idecp) & @dec ~ (p decp) -@decI = (p lowp) & @low ~ (p lowp) -@dec = ((@decO (@decI (@E R))) R) - -@lowO = (p oop) & @O ~ (p op) & @O ~ (op oop) -@lowI = (p oip) & @I ~ (p ip) & @O ~ (ip oip) -@low = ((@lowO (@lowI (@E R))) R) - -@runO = (p R) & @run ~ (decop R) & @dec ~ (op decop) & @O ~ (p op) -@runI = (p R) & @run ~ (decip R) & @dec ~ (ip decip) & @I ~ (p ip) -@run = ((@runO (@runI (@E R))) R) - -@main - = R - & @c24 ~ (@I (@E nie)) - & @run ~ (nie R) diff --git a/examples/examples.hvmc b/examples/examples.hvmc deleted file mode 100644 index 6e05bd73..00000000 --- a/examples/examples.hvmc +++ /dev/null @@ -1,117 +0,0 @@ -// // Church Nat constructors -// @c_z = * (a a)) -// @c_s = (s (z k)) ([(k r) s] (z r))) -// -// // Church Nats -// @c0 = (* (a a)) -// @c1 = ((a R) (a R)) -// @c2 = ([(b a) (a R)] (b R)) -// @c3 = ([[(c b) (b a)] (a R)] (c R)) -// @c4 = ([[[(d c) (c b)] (b a)] (a R)] (d R)) -// @c5 = ([[[[(e d) (d c)] (c b)] (b a)] (a R)] (e R)) -// @c6 = ([[[[[(f e) (e d)] (d c)] (c b)] (b a)] (a R)] (f R)) -// @c7 = ([[[[[[(g f) (f e)] (e d)] (d c)] (c b)] (b a)] (a R)] (g R)) -// @c8 = ([[[[[[[(h g) (g f)] (f e)] (e d)] (d c)] (c b)] (b a)] (a R)] (h R)) -// @c9 = ([[[[[[[[(i h) (h g)] (g f)] (f e)] (e d)] (d c)] (c b)] (b a)] (a R)] (i R)) -// @c10 = ([[[[[[[[[(j i) (i h)] (h g)] (g f)] (f e)] (e d)] (d c)] (c b)] (b a)] (a R)] (j R)) -// @c11 = ([[[[[[[[[[(k j) (j i)] (i h)] (h g)] (g f)] (f e)] (e d)] (d c)] (c b)] (b a)] (a R)] (k R)) -// @c12 = ([[[[[[[[[[[(l k) (k j)] (j i)] (i h)] (h g)] (g f)] (f e)] (e d)] (d c)] (c b)] (b a)] (a R)] (l R)) -// @c13 = ([[[[[[[[[[[[(m l) (l k)] (k j)] (j i)] (i h)] (h g)] (g f)] (f e)] (e d)] (d c)] (c b)] (b a)] (a R)] (m R)) -// @c14 = ([[[[[[[[[[[[[(n m) (m l)] (l k)] (k j)] (j i)] (i h)] (h g)] (g f)] (f e)] (e d)] (d c)] (c b)] (b a)] (a R)] (n R)) -// @c15 = ([[[[[[[[[[[[[[(o n) (n m)] (m l)] (l k)] (k j)] (j i)] (i h)] (h g)] (g f)] (f e)] (e d)] (d c)] (c b)] (b a)] (a R)] (o R)) -// @c16 = ([[[[[[[[[[[[[[[(p o) (o n)] (n m)] (m l)] (l k)] (k j)] (j i)] (i h)] (h g)] (g f)] (f e)] (e d)] (d c)] (c b)] (b a)] (a R)] (p R)) -// @c17 = ([[[[[[[[[[[[[[[[(q p) (p o)] (o n)] (n m)] (m l)] (l k)] (k j)] (j i)] (i h)] (h g)] (g f)] (f e)] (e d)] (d c)] (c b)] (b a)] (a R)] (q R)) -// @c18 = ([[[[[[[[[[[[[[[[[(r q) (q p)] (p o)] (o n)] (n m)] (m l)] (l k)] (k j)] (j i)] (i h)] (h g)] (g f)] (f e)] (e d)] (d c)] (c b)] (b a)] (a R)] (r R)) -// @c19 = ([[[[[[[[[[[[[[[[[[(s r) (r q)] (q p)] (p o)] (o n)] (n m)] (m l)] (l k)] (k j)] (j i)] (i h)] (h g)] (g f)] (f e)] (e d)] (d c)] (c b)] (b a)] (a R)] (s R)) -// @c20 = ([[[[[[[[[[[[[[[[[[[(t s) (s r)] (r q)] (q p)] (p o)] (o n)] (n m)] (m l)] (l k)] (k j)] (j i)] (i h)] (h g)] (g f)] (f e)] (e d)] (d c)] (c b)] (b a)] (a R)] (t R)) -// @c21 = ([[[[[[[[[[[[[[[[[[[[(u t) (t s)] (s r)] (r q)] (q p)] (p o)] (o n)] (n m)] (m l)] (l k)] (k j)] (j i)] (i h)] (h g)] (g f)] (f e)] (e d)] (d c)] (c b)] (b a)] (a R)] (u R)) -// @c22 = ([[[[[[[[[[[[[[[[[[[[[(v u) (u t)] (t s)] (s r)] (r q)] (q p)] (p o)] (o n)] (n m)] (m l)] (l k)] (k j)] (j i)] (i h)] (h g)] (g f)] (f e)] (e d)] (d c)] (c b)] (b a)] (a R)] (v R)) -// @c23 = ([[[[[[[[[[[[[[[[[[[[[[(w v) (v u)] (u t)] (t s)] (s r)] (r q)] (q p)] (p o)] (o n)] (n m)] (m l)] (l k)] (k j)] (j i)] (i h)] (h g)] (g f)] (f e)] (e d)] (d c)] (c b)] (b a)] (a R)] (w R)) -// @c24 = ([[[[[[[[[[[[[[[[[[[[[[[(x w) (w v)] (v u)] (u t)] (t s)] (s r)] (r q)] (q p)] (p o)] (o n)] (n m)] (m l)] (l k)] (k j)] (j i)] (i h)] (h g)] (g f)] (f e)] (e d)] (d c)] (c b)] (b a)] (a R)] (x R)) -// @c25 = ([[[[[[[[[[[[[[[[[[[[[[[[(y x) (x w)] (w v)] (v u)] (u t)] (t s)] (s r)] (r q)] (q p)] (p o)] (o n)] (n m)] (m l)] (l k)] (k j)] (j i)] (i h)] (h g)] (g f)] (f e)] (e d)] (d c)] (c b)] (b a)] (a R)] (x R)) -// @c26 = ([[[[[[[[[[[[[[[[[[[[[[[[[(z y) (y x)] (x w)] (w v)] (v u)] (u t)] (t s)] (s r)] (r q)] (q p)] (p o)] (o n)] (n m)] (m l)] (l k)] (k j)] (j i)] (i h)] (h g)] (g f)] (f e)] (e d)] (d c)] (c b)] (b a)] (a R)] (x R)) -// -// // Church Nats -// @mul = ((a b) ((c a) (c b))) -// -// // Bools -// @T = (t (* t)) -// @F = (* (f f)) -// -// // Bool Fns -// @not = ((f (t r)) (t (f r))) -// @and = ((((@T (@F a)) a) (((@F (@F b)) b) c)) c) -// -// // Scott Nats -// @S = (a ((a b) (* b))) -// @Z = (* (a a)) -// -// // Generators for a big binary tree -// // λr. λt. ((t r) r) -// @g_s = ({r0 r1} ((r0 (r1 r)) r)) -// @g_z = (x x) -// -// // BitString constructors -// // O = λxs λo λi λe (o xs) -// // I = λxs λo λi λe (i xs) -// // E = λo λi λe e -// @O = (xs ((xs r) (* (* r)))) -// @I = (xs (* ((xs r) (* r)))) -// @E = (* (* (e e))) -// -// // Double -// @nidS = (p R) & @S ~ (nidp R) & @nid ~ (p nidp) -// @nid = ((@nidS (@Z R)) R) -// -// // Decrements a BitString -// // decO = λp(I (dec p)) -// // decI = λp(low p) -// // dec = λx(((x decO) decI) E) -// @decO = (p idecp) & @I ~ (decp idecp) & @dec ~ (p decp) -// @decI = (p lowp) & @low ~ (p lowp) -// @dec = ((@decO (@decI (@E R))) R) -// -// // Auxiliary function -// // lowO = λp(O (O p)) -// // lowI = λp(O (I p)) -// // low = λx(((x lowO) lowI) E) -// @lowO = (p oop) & @O ~ (p op) & @O ~ (op oop) -// @lowI = (p oip) & @I ~ (p ip) & @O ~ (ip oip) -// @low = ((@lowO (@lowI (@E R))) R) -// -// // Decrements a BitString until it is zero -// // runO = λp(run (dec (O p))) -// // runI = λp(run (dec (I p))) -// // run = λx(((x runO) runI) E) -// @runO = (p R) & @run ~ (decop R) & @dec ~ (op decop) & @O ~ (p op) -// @runI = (p R) & @run ~ (decip R) & @dec ~ (ip decip) & @I ~ (p ip) -// @run = ((@runO (@runI (@E R))) R) -// -// // Decrements 2^N BitStrings until they reach zero -// // brnZ = (run (c8 S Z)) -// // brnS = λp {(brn p) (brn p)} -// // brn = λn ((n brnS) brnZ) -// @brnZ = R & @run ~ (val R) & @c11 ~ (@I (@E val)) -// @brnS = ([p0 p1] (r0 r1)) & @brn ~ (p0 r0) & @brn ~ (p1 r1) -// @brn = ((@brnS (@brnZ r)) r) -// -// // af = λx (x afS afZ) -// // afS = λp (and (af p) (af p)) -// // afZ = T -// @af = ((@afS (@afZ a)) a) -// @afS = ([a b] c) & (b d) ~ @af & (e (d c)) ~ @and & (a e) ~ @af -// @afZ = @T -// -// // Church multiplication. -// @ex0 = root & @c2 ~ (@k2 root) -// -// // Allocates a big tree. -// @ex1 = root & @c24 ~ (@g_s (@g_z root)) -// -// // Decrease a binary counter. -// @ex2 = R & @c26 ~ (@I (@E nie)) & @run ~ (nie R) -// -// // Decreases many binary counters. -// @ex3 = R & @c16 ~ (@S (@Z dep)) & @brn ~ (dep R) - -@main = #42 diff --git a/examples/loop.hvmc b/examples/loop.hvmc deleted file mode 100644 index 55e90be2..00000000 --- a/examples/loop.hvmc +++ /dev/null @@ -1,46 +0,0 @@ -@foo = (a b) - & (a b) ~ @loop - -@loop = (? (#0 @foo) a a) - -@main = a - & (#1 a) ~ @loop - -//#0 -//RWTS : 7000006 -//- ANNI : 3000002 -//- COMM : 0 -//- ERAS : 1000001 -//- DREF : 2000002 -//- OPER : 1000001 -//TIME : 0.040 s -//RPS : 175.000 m - -//#0 -//RWTS : 7000006 -//- ANNI : 3000002 -//- COMM : 0 -//- ERAS : 1000001 -//- DREF : 2000002 -//- OPER : 1000001 -//TIME : 0.033 s -//RPS : 212.121 m - -//#0 -//RWTS : 7000006 -//- ANNI : 3000002 -//- COMM : 0 -//- ERAS : 1000001 -//- DREF : 2000002 -//- OPER : 1000001 -//TIME : 0.025 s -//RPS : 280.000 m - -//RWTS : 7000006 -//- ANNI : 3000002 -//- COMM : 0 -//- ERAS : 1000001 -//- DREF : 2000002 -//- OPER : 1000001 -//TIME : 0.006 s -//RPS : 1166.668 m diff --git a/examples/num_add.hvm2 b/examples/num_add.hvm2 new file mode 100644 index 00000000..6c13227a --- /dev/null +++ b/examples/num_add.hvm2 @@ -0,0 +1,2 @@ +add = λa λb (+ a b) +main = (add 123 100) diff --git a/examples/num_add.hvmc b/examples/num_add.hvmc new file mode 100644 index 00000000..656633a8 --- /dev/null +++ b/examples/num_add.hvmc @@ -0,0 +1,6 @@ +// #1 represents addition +@add = (a (b R)) +& #1 ~ > + +@main = R + & @add ~ (#123 (#100 R)) diff --git a/examples/num_match.hvm2 b/examples/num_match.hvm2 new file mode 100644 index 00000000..25df0990 --- /dev/null +++ b/examples/num_match.hvm2 @@ -0,0 +1,6 @@ +pred = λx match x { + 0 : 0 + 1+p : p +} + +main = (pred 10) diff --git a/examples/num_match.hvmc b/examples/num_match.hvmc new file mode 100644 index 00000000..0b2d90b1 --- /dev/null +++ b/examples/num_match.hvmc @@ -0,0 +1,6 @@ +@pred = (? (#0 (x x)) R R) + +@main + = R + & @pred ~ (#10 R) + diff --git a/examples/num_mt_ex.hvmc b/examples/num_mt_ex.hvmc deleted file mode 100644 index 41ff4380..00000000 --- a/examples/num_mt_ex.hvmc +++ /dev/null @@ -1,5 +0,0 @@ -// Performs: 'match #100 { #0: #321; #1+p: p }' - -@main - = R - & #100 ~ ? (#321 (p p)) R diff --git a/examples/num_op_ex.hvmc b/examples/num_op_ex.hvmc deleted file mode 100644 index bf172115..00000000 --- a/examples/num_op_ex.hvmc +++ /dev/null @@ -1,6 +0,0 @@ -// Performs '123 * 321'. -// '3' is used for MUL. - -@main - = R - & #3 ~ <#123 <#321 R>> diff --git a/examples/sum_rec.hvm2 b/examples/sum_rec.hvm2 new file mode 100644 index 00000000..026086b8 --- /dev/null +++ b/examples/sum_rec.hvm2 @@ -0,0 +1,6 @@ +sum = λn match n { + 0 : 1 + 1+p : (+ (sum p) (sum p)) +} + +main = (sum 24) diff --git a/examples/sum_rec.hvmc b/examples/sum_rec.hvmc new file mode 100644 index 00000000..ed1968bb --- /dev/null +++ b/examples/sum_rec.hvmc @@ -0,0 +1,9 @@ +@sum = (? (#1 @sumS) a a) + +@sumS = ({2 a b} c) + & @sum ~ (a e) + & @sum ~ (b d) + & #1 ~ > + +@main = R + & @sum ~ (#24 R) diff --git a/examples/sum_tail.hvm2 b/examples/sum_tail.hvm2 new file mode 100644 index 00000000..5939b17f --- /dev/null +++ b/examples/sum_tail.hvm2 @@ -0,0 +1,6 @@ +sum = λa match a { + 0 : λs s + 1+p : λs (sum p (+ p s)) +} + +main = (sum 10000000 0) diff --git a/examples/sum_tail.hvmc b/examples/sum_tail.hvmc new file mode 100644 index 00000000..d5056296 --- /dev/null +++ b/examples/sum_tail.hvmc @@ -0,0 +1,10 @@ +@sum = (? (@sumZ @sumS) a a) + +@sumZ = (a a) + +@sumS = ({2 a b} (c d)) + & @sum ~ (b (e d)) + & #1 ~ > + +@main = R + & @sum ~ (#10000000 (#0 R)) diff --git a/src/jit.rs b/src/jit.rs index 6684af13..61880b37 100644 --- a/src/jit.rs +++ b/src/jit.rs @@ -48,11 +48,11 @@ pub fn compile_book(book: &run::Book) -> String { } -fn ident(tab: usize) -> String { +pub fn ident(tab: usize) -> String { return " ".repeat(tab); } -fn tag(tag: run::Tag) -> &'static str { +pub fn tag(tag: run::Tag) -> &'static str { match tag { run::VR1 => "VR1", run::VR2 => "VR2", @@ -74,8 +74,23 @@ fn tag(tag: run::Tag) -> &'static str { } } -fn atom(ptr: run::Ptr) -> String { - return format!("Ptr::new({}, 0x{:x})", tag(ptr.tag()), ptr.val()); +pub fn atom(ptr: run::Ptr) -> String { + if ptr.is_ref() { + return format!("Ptr::new(REF, F_{})", ast::val_to_name(ptr.val())); + } else { + return format!("Ptr::new({}, 0x{:x})", tag(ptr.tag()), ptr.val()); + } +} + +enum Target { + Var { nam: String }, + //Val { ptr: run:: Ptr }, +} + +fn target(trg: &Target) -> String { + match trg { + Target::Var { nam } => format!("{}", nam), + } } pub fn compile_term(book: &run::Book, tab: usize, fid: run::Val) -> String { @@ -85,6 +100,30 @@ pub fn compile_term(book: &run::Book, tab: usize, fid: run::Val) -> String { *newx += 1; format!("k{}", newx) } + + fn call( + book : &run::Book, + tab : usize, + newx : &mut usize, + vars : &mut HashMap, + fid : run::Val, + x : &Target, + ) -> String { + //let newx = &mut 0; + //let vars = &mut HashMap::new(); + let def = &book.defs[fid as usize]; + let mut code = String::new(); + code.push_str(&burn(book, tab, newx, vars, def, def.node[0].1, &x)); + for (rf, rx) in &def.rdex { + let (rf, rx) = adjust_redex(*rf, *rx); + let rf_name = format!("_{}", fresh(newx)); + code.push_str(&format!("{}let {} = {};\n", ident(tab), rf_name, &atom(rf))); + code.push_str(&burn(book, tab, newx, vars, def, rx, &Target::Var { nam: rf_name })); + //code.push_str(&make(tab, newx, vars, def, rx, &atom(rf))); + } + + return code; + } // @loop = (?<(#0 (x y)) R> R) & @loop ~ (x y) fn burn( @@ -94,13 +133,13 @@ pub fn compile_term(book: &run::Book, tab: usize, fid: run::Val) -> String { vars : &mut HashMap, def : &run::Def, ptr : run::Ptr, - x : &String, + x : &Target, ) -> String { //println!("burn {:08x} {}", ptr.0, x); let mut code = String::new(); - // ( ret) <~ (#X R) - // -------------------------------- fast apply-and-match + // ( ret) ~ (#X R) + // ------------------------------- fast match // if X == 0: // ifz ~ R // ifs ~ * @@ -119,32 +158,31 @@ pub fn compile_term(book: &run::Book, tab: usize, fid: run::Val) -> String { if cse.tag() == run::CT0 && rtx.is_var() && rtx == rtz { let ifz = def.node[cse.val() as usize].0; let ifs = def.node[cse.val() as usize].1; - let c_z = fresh(newx); - let c_s = fresh(newx); - code.push_str(&format!("{}let {};\n", ident(tab), c_z)); - code.push_str(&format!("{}let {};\n", ident(tab), c_s)); - code.push_str(&format!("{}// fast match-apply\n", ident(tab))); - code.push_str(&format!("{}if {}.tag() == CT0 && self.heap.get({}.val(), P1).is_num() {{\n", ident(tab), x, x)); + let c_z = Target::Var { nam: fresh(newx) }; + let c_s = Target::Var { nam: fresh(newx) }; + let num = format!("{}x", target(x)); + let res = format!("{}y", target(x)); + let lam = fresh(newx); + let mat = fresh(newx); + let cse = fresh(newx); + code.push_str(&format!("{}let {} : Ptr;\n", ident(tab), target(&c_z))); + code.push_str(&format!("{}let {} : Ptr;\n", ident(tab), target(&c_s))); + code.push_str(&format!("{}// fast match\n", ident(tab))); + code.push_str(&format!("{}if {}.tag() == CT0 && self.heap.get({}.val(), P1).is_num() {{\n", ident(tab), target(x), target(x))); code.push_str(&format!("{}self.anni += 2;\n", ident(tab+1))); code.push_str(&format!("{}self.oper += 1;\n", ident(tab+1))); - let num = format!("{}1", x); - let res = format!("{}2", x); - code.push_str(&format!("{}let {} = self.heap.get({}.val(), P1);\n", ident(tab+1), num, x)); - code.push_str(&format!("{}let {} = self.heap.get({}.val(), P2);\n", ident(tab+1), res, x)); + code.push_str(&format!("{}let {} = self.heap.get({}.val(), P1);\n", ident(tab+1), num, target(x))); + code.push_str(&format!("{}let {} = self.heap.get({}.val(), P2);\n", ident(tab+1), res, target(x))); code.push_str(&format!("{}if {}.val() == 0 {{\n", ident(tab+1), num)); - code.push_str(&format!("{}self.heap.free({}.val());\n", ident(tab+2), x)); - code.push_str(&format!("{}{} = {};\n", ident(tab+2), c_z, res)); - code.push_str(&format!("{}{} = {};\n", ident(tab+2), c_s, "ERAS")); + code.push_str(&format!("{}self.heap.free({}.val());\n", ident(tab+2), target(x))); + code.push_str(&format!("{}{} = {};\n", ident(tab+2), target(&c_z), res)); + code.push_str(&format!("{}{} = {};\n", ident(tab+2), target(&c_s), "ERAS")); code.push_str(&format!("{}}} else {{\n", ident(tab+1))); - code.push_str(&format!("{}self.heap.set({}.val(), P1, Ptr::new(NUM, {}.val() - 1));\n", ident(tab+2), x, num)); - code.push_str(&format!("{}{} = {};\n", ident(tab+2), c_z, "ERAS")); - code.push_str(&format!("{}{} = {};\n", ident(tab+2), c_s, x)); + code.push_str(&format!("{}self.heap.set({}.val(), P1, Ptr::new(NUM, {}.val() - 1));\n", ident(tab+2), target(x), num)); + code.push_str(&format!("{}{} = {};\n", ident(tab+2), target(&c_z), "ERAS")); + code.push_str(&format!("{}{} = {};\n", ident(tab+2), target(&c_s), target(x))); code.push_str(&format!("{}}}\n", ident(tab+1))); code.push_str(&format!("{}}} else {{\n", ident(tab))); - // ( ret) - let lam = fresh(newx); - let mat = fresh(newx); - let cse = fresh(newx); code.push_str(&format!("{}let {} = self.heap.alloc(1);\n", ident(tab+1), lam)); code.push_str(&format!("{}let {} = self.heap.alloc(1);\n", ident(tab+1), mat)); code.push_str(&format!("{}let {} = self.heap.alloc(1);\n", ident(tab+1), cse)); @@ -152,9 +190,9 @@ pub fn compile_term(book: &run::Book, tab: usize, fid: run::Val) -> String { code.push_str(&format!("{}self.heap.set({}, P2, Ptr::new(VR2, {}));\n", ident(tab+1), lam, mat)); code.push_str(&format!("{}self.heap.set({}, P1, Ptr::new(CT0, {}));\n", ident(tab+1), mat, cse)); code.push_str(&format!("{}self.heap.set({}, P2, Ptr::new(VR2, {}));\n", ident(tab+1), mat, lam)); - code.push_str(&format!("{}self.link(Ptr::new(CT0, {}), {});\n", ident(tab+1), lam, x)); - code.push_str(&format!("{}{} = Ptr::new(VR1, {});\n", ident(tab+1), c_z, cse)); - code.push_str(&format!("{}{} = Ptr::new(VR2, {});\n", ident(tab+1), c_s, cse)); + code.push_str(&format!("{}self.link(Ptr::new(CT0, {}), {});\n", ident(tab+1), lam, target(x))); + code.push_str(&format!("{}{} = Ptr::new(VR1, {});\n", ident(tab+1), target(&c_z), cse)); + code.push_str(&format!("{}{} = Ptr::new(VR2, {});\n", ident(tab+1), target(&c_s), cse)); code.push_str(&format!("{}}}\n", ident(tab))); code.push_str(&burn(book, tab, newx, vars, def, ifz, &c_z)); code.push_str(&burn(book, tab, newx, vars, def, ifs, &c_s)); @@ -163,73 +201,126 @@ pub fn compile_term(book: &run::Book, tab: usize, fid: run::Val) -> String { } } + // > ~ #N + // --------------------- fast op + // r <~ #(op(op(N,x),y)) + if ptr.is_op2() { + let v_x = def.node[ptr.val() as usize].0; + let cnt = def.node[ptr.val() as usize].1; + if cnt.is_op2() { + let v_y = def.node[cnt.val() as usize].0; + let ret = def.node[cnt.val() as usize].1; + if let (Some(v_x), Some(v_y)) = (got(vars, def, v_x), got(vars, def, v_y)) { + let nxt = Target::Var { nam: fresh(newx) }; + let opx = fresh(newx); + let opy = fresh(newx); + code.push_str(&format!("{}let {} : Ptr;\n", ident(tab), target(&nxt))); + code.push_str(&format!("{}// fast op\n", ident(tab))); + code.push_str(&format!("{}if {}.is_num() && {}.is_num() && {}.is_num() {{\n", ident(tab), target(x), v_x, v_y)); + code.push_str(&format!("{}self.oper += 4;\n", ident(tab+1))); // OP2 + OP1 + OP2 + OP1 + code.push_str(&format!("{}{} = Ptr::new(NUM, self.op(self.op({}.val(),{}.val()),{}.val()));\n", ident(tab+1), target(&nxt), target(x), v_x, v_y)); + code.push_str(&format!("{}}} else {{\n", ident(tab))); + code.push_str(&format!("{}let {} = self.heap.alloc(1);\n", ident(tab+1), opx)); + code.push_str(&format!("{}let {} = self.heap.alloc(1);\n", ident(tab+1), opy)); + code.push_str(&format!("{}self.heap.set({}, P2, Ptr::new(OP2, {}));\n", ident(tab+1), opx, opy)); + code.push_str(&format!("{}self.link(Ptr::new(VR1,{}), {});\n", ident(tab+1), opx, v_x)); + code.push_str(&format!("{}self.link(Ptr::new(VR1,{}), {});\n", ident(tab+1), opy, v_y)); + code.push_str(&format!("{}self.link(Ptr::new(OP2,{}), {});\n", ident(tab+1), opx, target(x))); + code.push_str(&format!("{}{} = Ptr::new(VR2, {});\n", ident(tab+1), target(&nxt), opy)); + code.push_str(&format!("{}}}\n", ident(tab))); + code.push_str(&burn(book, tab, newx, vars, def, ret, &nxt)); + return code; + } + } + } + + // {p1 p2} <~ #N + // ------------- fast copy + // p1 <~ #N + // p2 <~ #N + if ptr.is_ctr() && ptr.tag() > run::CT0 { + let x1 = Target::Var { nam: format!("{}x", target(x)) }; + let x2 = Target::Var { nam: format!("{}y", target(x)) }; + let p1 = def.node[ptr.val() as usize].0; + let p2 = def.node[ptr.val() as usize].1; + let lc = fresh(newx); + code.push_str(&format!("{}let {} : Ptr;\n", ident(tab), target(&x1))); + code.push_str(&format!("{}let {} : Ptr;\n", ident(tab), target(&x2))); + code.push_str(&format!("{}// fast copy\n", ident(tab))); + code.push_str(&format!("{}if {}.tag() == NUM {{\n", ident(tab), target(x))); + code.push_str(&format!("{}self.comm += 1;\n", ident(tab+1))); + code.push_str(&format!("{}{} = {};\n", ident(tab+1), target(&x1), target(x))); + code.push_str(&format!("{}{} = {};\n", ident(tab+1), target(&x2), target(x))); + code.push_str(&format!("{}}} else {{\n", ident(tab))); + code.push_str(&format!("{}let {} = self.heap.alloc(1);\n", ident(tab+1), lc)); + code.push_str(&format!("{}{} = Ptr::new(VR1, {});\n", ident(tab+1), target(&x1), lc)); + code.push_str(&format!("{}{} = Ptr::new(VR2, {});\n", ident(tab+1), target(&x2), lc)); + code.push_str(&format!("{}self.link(Ptr::new({}, {}), {});\n", ident(tab+1), tag(ptr.tag()), lc, target(x))); + code.push_str(&format!("{}}}\n", ident(tab))); + code.push_str(&burn(book, tab, newx, vars, def, p1, &x1)); + code.push_str(&burn(book, tab, newx, vars, def, p2, &x2)); + return code; + } + // (p1 p2) <~ (x1 x2) // ------------------ fast apply // p1 <~ x1 // p2 <~ x2 - //if ptr.is_ctr() { - //let x1 = format!("{}1", x); - //let x2 = format!("{}2", x); - //code.push_str(&format!("{}let {};\n", ident(tab), x1)); - //code.push_str(&format!("{}let {};\n", ident(tab), x2)); - //code.push_str(&format!("{}if false && {}.tag() == {} {{\n", ident(tab), x, tag(ptr.tag()))); - //let p1 = def.node[ptr.val() as usize].0; - //let p2 = def.node[ptr.val() as usize].1; - //code.push_str(&format!("{}self.anni += 1;\n", ident(tab+1))); - //code.push_str(&format!("{}{} = self.heap.get({}.val(), P1);\n", ident(tab+1), x1, x)); - //code.push_str(&format!("{}{} = self.heap.get({}.val(), P2);\n", ident(tab+1), x2, x)); + if ptr.is_ctr() && ptr.tag() == run::CT0 { + let x1 = Target::Var { nam: format!("{}x", target(x)) }; + let x2 = Target::Var { nam: format!("{}y", target(x)) }; + let p1 = def.node[ptr.val() as usize].0; + let p2 = def.node[ptr.val() as usize].1; + let lc = fresh(newx); + code.push_str(&format!("{}let {} : Ptr;\n", ident(tab), target(&x1))); + code.push_str(&format!("{}let {} : Ptr;\n", ident(tab), target(&x2))); + code.push_str(&format!("{}// fast apply\n", ident(tab))); + code.push_str(&format!("{}if {}.tag() == {} {{\n", ident(tab), target(x), tag(ptr.tag()))); + code.push_str(&format!("{}self.anni += 1;\n", ident(tab+1))); + code.push_str(&format!("{}{} = self.heap.get({}.val(), P1);\n", ident(tab+1), target(&x1), target(x))); + code.push_str(&format!("{}{} = self.heap.get({}.val(), P2);\n", ident(tab+1), target(&x2), target(x))); + code.push_str(&format!("{}self.heap.free({}.val());\n", ident(tab+1), target(x))); + code.push_str(&format!("{}}} else {{\n", ident(tab))); + code.push_str(&format!("{}let {} = self.heap.alloc(1);\n", ident(tab+1), lc)); + code.push_str(&format!("{}{} = Ptr::new(VR1, {});\n", ident(tab+1), target(&x1), lc)); + code.push_str(&format!("{}{} = Ptr::new(VR2, {});\n", ident(tab+1), target(&x2), lc)); + code.push_str(&format!("{}self.link(Ptr::new({}, {}), {});\n", ident(tab+1), tag(ptr.tag()), lc, target(x))); + code.push_str(&format!("{}}}\n", ident(tab))); + code.push_str(&burn(book, tab, newx, vars, def, p1, &x1)); + code.push_str(&burn(book, tab, newx, vars, def, p2, &x2)); + return code; + } + + // TODO: implement inlining correctly + // NOTE: enabling this makes dec_bits_tree hang; investigate + //if ptr.is_ref() { + //code.push_str(&format!("{}// inline @{}\n", ident(tab), ast::val_to_name(ptr.val()))); + //code.push_str(&format!("{}if !{}.is_skp() {{\n", ident(tab), target(x))); + //code.push_str(&format!("{}self.dref += 1;\n", ident(tab+1))); + //code.push_str(&call(book, tab+1, newx, &mut HashMap::new(), ptr.val(), x)); //code.push_str(&format!("{}}} else {{\n", ident(tab))); - //let lc = fresh(newx); - //code.push_str(&format!("{}let {} = self.heap.alloc(1);\n", ident(tab+1), lc)); - //code.push_str(&format!("{}{} = Ptr::new(VR1, {});\n", ident(tab+1), x1, lc)); - //code.push_str(&format!("{}{} = Ptr::new(VR2, {});\n", ident(tab+1), x2, lc)); - //code.push_str(&format!("{}self.link(Ptr::new({}, {}), {});\n", ident(tab+1), tag(ptr.tag()), lc, x)); + //code.push_str(&make(tab+1, newx, vars, def, ptr, &target(x))); //code.push_str(&format!("{}}}\n", ident(tab))); - //code.push_str(&burn(book, tab, newx, vars, def, p1, &x1)); - //code.push_str(&burn(book, tab, newx, vars, def, p2, &x2)); //return code; //} - // TODO: fast numeric operations + // ATOM <~ * + // --------- fast erase + // nothing + if ptr.is_num() || ptr.is_era() { + code.push_str(&format!("{}// fast erase\n", ident(tab))); + code.push_str(&format!("{}if {}.is_skp() {{\n", ident(tab), target(x))); + code.push_str(&format!("{}self.eras += 1;\n", ident(tab+1))); + code.push_str(&format!("{}}} else {{\n", ident(tab))); + code.push_str(&make(tab+1, newx, vars, def, ptr, &target(x))); + code.push_str(&format!("{}}}\n", ident(tab))); + return code; + } - code.push_str(&make(tab, newx, vars, def, ptr, &x)); + code.push_str(&make(tab, newx, vars, def, ptr, &target(x))); return code; } - //fn era( - //tab : usize, - //newx : &mut usize, - //vars : &mut HashMap, - //def : &run::Def, - //ptr : run::Ptr, - //) -> String { - //let mut code = String::new(); - - //// (p1 p2) <~ * - //// ------------ - //// p1 ~ * - //// p2 ~ * - //if ptr.is_ctr() { - //let p1 = def.node[ptr.val() as usize].0; - //let p2 = def.node[ptr.val() as usize].1; - //code.push_str(&format!("{}self.eras += 1;\n", ident(tab))); - //code.push_str(&era(tab, newx, vars, def, p1)); - //code.push_str(&era(tab, newx, vars, def, p2)); - //return code; - //} - - //// (atom) <~ * - //// ----------- - //// (nothing) - //if ptr.is_skp() { - //code.push_str(&format!("{}self.eras += 1;\n", ident(tab))); - //return code; - //} - - //code.push_str(&make(tab, newx, vars, def, ptr, &format!("ERAS"))); - //return code; - //} - fn make( tab : usize, newx : &mut usize, @@ -249,10 +340,7 @@ pub fn compile_term(book: &run::Book, tab: usize, fid: run::Val) -> String { code.push_str(&make(tab, newx, vars, def, p2, &format!("Ptr::new(VR2, {})", lc))); code.push_str(&format!("{}self.link(Ptr::new({}, {}), {});\n", ident(tab), tag(ptr.tag()), lc, x)); } else if ptr.is_var() { - let got = def.node[ptr.val() as usize]; - let slf = if ptr.tag() == run::VR1 { got.0 } else { got.1 }; - //println!("-var {:08x} -- {:08x}", slf.0, ptr.0); - match vars.get(&slf) { + match got(vars, def, ptr) { None => { //println!("-var fst"); vars.insert(ptr, x.clone()); @@ -268,29 +356,31 @@ pub fn compile_term(book: &run::Book, tab: usize, fid: run::Val) -> String { return code; } - let mut newx = 0; - let mut vars = HashMap::new(); + fn got( + vars : &HashMap, + def : &run::Def, + ptr : run::Ptr, + ) -> Option { + if ptr.is_var() { + let got = def.node[ptr.val() as usize]; + let slf = if ptr.tag() == run::VR1 { got.0 } else { got.1 }; + return vars.get(&slf).cloned(); + } else { + return None; + } + } let fun = ast::val_to_name(fid); - let def = &book.defs[fid as usize]; let mut code = String::new(); - code.push_str(&format!("{}pub fn F_{}(&mut self, ptr: Ptr, x: Ptr) -> bool {{\n", ident(tab), fun)); - code.push_str(&burn(book, tab+1, &mut newx, &mut vars, def, def.node[0].1, &"x".to_string())); - for (rf, rx) in &def.rdex { - let (rf, rx) = adjust_redex(*rf, *rx); - code.push_str(&make(tab+1, &mut newx, &mut vars, def, rx, &atom(rf))); - } + code.push_str(&call(book, tab+1, &mut 0, &mut HashMap::new(), fid, &Target::Var { nam: "x".to_string() })); code.push_str(&format!("{}return true;\n", ident(tab+1))); code.push_str(&format!("{}}}\n", ident(tab))); return code; } -// NOTE: the compile_term function has been refactored to use immutable context instead of newx/vars - - // TODO: HVM-Lang must always output in this form. fn adjust_redex(rf: run::Ptr, rx: run::Ptr) -> (run::Ptr, run::Ptr) { if rf.is_skp() && !rx.is_skp() { diff --git a/src/run.rs b/src/run.rs index 69278b88..5e69b44d 100644 --- a/src/run.rs +++ b/src/run.rs @@ -62,10 +62,9 @@ pub struct Ptr(pub Val); #[derive(Clone, Debug, Eq, PartialEq, Hash)] pub struct Heap { - pub(crate) data: Vec<(Ptr, Ptr)>, - next: usize, - used: usize, - full: bool, + pub data: Vec<(Ptr, Ptr)>, + pub next: usize, + pub full: bool, } // A interaction combinator net. @@ -228,17 +227,17 @@ impl Heap { return Heap { data: vec![(NULL, NULL); size], next: INIT + 1, - used: 0, full: false, }; } #[inline(always)] pub fn alloc(&mut self, size: usize) -> Val { + //self.next += size; + //return (self.next - size) as Val; if size == 0 { return 0; } else if !self.full && self.next + size <= self.data.len() { - self.used += size; self.next += size; return (self.next - size) as Val; } else { @@ -256,7 +255,6 @@ impl Heap { } self.next += 1; if space == size { - self.used += size; return (self.next - space) as Val; } } @@ -265,7 +263,6 @@ impl Heap { #[inline(always)] pub fn free(&mut self, index: Val) { - self.used -= 1; self.set(index, P1, NULL); self.set(index, P2, NULL); } @@ -521,7 +518,7 @@ impl Net { let mut p2 = self.heap.get(a.val(), P2); loop { self.oper += 1; - rt = self.prim(rt, p1.val()); + rt = self.op(rt, p1.val()); // If P2 is OP2, keep looping if p2.is_op2() { p1 = self.heap.get(p2.val(), P1); @@ -554,12 +551,13 @@ impl Net { let p2 = self.heap.get(a.val(), P2); let v0 = p1.val() as Val; let v1 = b.val() as Val; - let v2 = self.prim(v0, v1); + let v2 = self.op(v0, v1); self.link(Ptr::new(NUM, v2), p2); self.heap.free(a.val()); } - pub fn prim(&mut self, a: Val, b: Val) -> Val { + #[inline(always)] + pub fn op(&self, a: Val, b: Val) -> Val { let a_opr = (a >> 24) & 0xF; let b_opr = (b >> 24) & 0xF; // not used yet let a_val = a & 0xFFFFFF;