diff --git a/crates/rue-hir/src/environment.rs b/crates/rue-hir/src/environment.rs index 7d10429d..25f9637f 100644 --- a/crates/rue-hir/src/environment.rs +++ b/crates/rue-hir/src/environment.rs @@ -1,6 +1,7 @@ use std::cmp::Reverse; use indexmap::IndexMap; +use num_bigint::BigUint; use crate::SymbolId; @@ -16,7 +17,6 @@ pub enum Environment { #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum PathError { SymbolNotFound, - PathTooLarge, } impl Environment { @@ -93,23 +93,15 @@ impl Environment { ) } - pub fn path(&self, symbol_id: SymbolId) -> Result { + pub fn path(&self, symbol_id: SymbolId) -> Result { let ops = self .get_pair_ops(symbol_id) .ok_or(PathError::SymbolNotFound)?; - let mut path: u32 = 1; + let mut path = BigUint::from(1u8); for op in ops.into_iter().rev() { - match op { - PairOp::First => { - path = path.checked_mul(2).ok_or(PathError::PathTooLarge)?; - } - PairOp::Rest => { - path = path - .checked_mul(2) - .ok_or(PathError::PathTooLarge)? - .checked_add(1) - .ok_or(PathError::PathTooLarge)?; - } + path <<= 1; + if matches!(op, PairOp::Rest) { + path += 1u8; } } Ok(path) diff --git a/crates/rue-hir/src/lower.rs b/crates/rue-hir/src/lower.rs index aacdef6e..5d59d66f 100644 --- a/crates/rue-hir/src/lower.rs +++ b/crates/rue-hir/src/lower.rs @@ -136,7 +136,7 @@ impl<'d, 'a, 'g> Lowerer<'d, 'a, 'g> { let mut expr = self.lower_hir(&function_env, function.body); if symbol == self.main && function.kind == FunctionKind::External { - let entire_env = self.arena.alloc(Lir::Path(1)); + let entire_env = self.arena.alloc(Lir::Path(1u8.into())); return self.arena.alloc(Lir::Run(expr, entire_env)); } @@ -151,7 +151,7 @@ impl<'d, 'a, 'g> Lowerer<'d, 'a, 'g> { { map.insert(symbol, self.arena.alloc(Lir::Quote(expr))); let reference = self.lower_symbol_reference(&function_env, symbol); - let entire_env = self.arena.alloc(Lir::Path(1)); + let entire_env = self.arena.alloc(Lir::Path(1u8.into())); expr = self.arena.alloc(Lir::Run(reference, entire_env)); } @@ -167,7 +167,7 @@ impl<'d, 'a, 'g> Lowerer<'d, 'a, 'g> { let rest = if param_group.is_empty() { self.arena.alloc(Lir::Atom(vec![])) } else { - self.arena.alloc(Lir::Path(1)) + self.arena.alloc(Lir::Path(1u8.into())) }; let group_env = @@ -525,10 +525,6 @@ impl<'d, 'a, 'g> Lowerer<'d, 'a, 'g> { "clvm path in environment not found for symbol {}", self.db.debug_symbol(symbol) ), - PathError::PathTooLarge => panic!( - "calculated clvm path too large for symbol {}", - self.db.debug_symbol(symbol) - ), }, ))) } @@ -598,7 +594,7 @@ impl<'d, 'a, 'g> Lowerer<'d, 'a, 'g> { bind_env = Self::apply_group(bind_env, group, true); } - let rest = self.arena.alloc(Lir::Path(1)); + let rest = self.arena.alloc(Lir::Path(1u8.into())); let group_env = self.lower_group_environment(&bind_env, group, rest, false, None, true); expr = self.arena.alloc(Lir::Run(expr, group_env)); diff --git a/crates/rue-lir/src/codegen.rs b/crates/rue-lir/src/codegen.rs index 1b508914..30543138 100644 --- a/crates/rue-lir/src/codegen.rs +++ b/crates/rue-lir/src/codegen.rs @@ -1,8 +1,9 @@ use clvm_traits::{ToClvm, clvm_list, clvm_quote, clvm_tuple}; use clvmr::{Allocator, NodePtr}; use id_arena::Arena; +use num_bigint::BigUint; -use crate::{ClvmOp, Lir, LirId, Result, bigint_atom}; +use crate::{ClvmOp, Lir, LirId, Result, path_to_atom}; #[derive(Debug, Default, Clone, Copy)] pub struct CodegenOptions { @@ -41,15 +42,12 @@ fn codegen_impl( Ok(clvm_quote!(arg).to_clvm(allocator)?) } Lir::Path(path) => { - let mut atom = bigint_atom((*path).into()); - while !atom.is_empty() && atom[0] == 0 { - atom = atom[1..].to_vec(); - } + let atom = path_to_atom(path); Ok(allocator.new_atom(&atom)?) } Lir::Run(callee, env) => { if options.optimize_static_pairs - && matches!(arena[*env], Lir::Path(1)) + && matches!(&arena[*env], Lir::Path(path) if path == &BigUint::from(1u8)) && is_static_value(arena, *callee) { return codegen_static_value(arena, allocator, *callee); @@ -503,7 +501,7 @@ mod tests { #[test] fn test_path() { let mut arena = Arena::new(); - let lir = arena.alloc(Lir::Path(1)); + let lir = arena.alloc(Lir::Path(1u8.into())); check(&arena, lir, expect!["1"]); } @@ -543,7 +541,7 @@ mod tests { let args = arena.alloc(Lir::Cons(two, args)); let add = arena.alloc(Lir::Atom(vec![16])); let program = arena.alloc(Lir::Cons(add, args)); - let env = arena.alloc(Lir::Path(1)); + let env = arena.alloc(Lir::Path(1u8.into())); let lir = arena.alloc(Lir::Run(program, env)); check_with_options( @@ -558,7 +556,7 @@ mod tests { #[test] fn test_closure() { let mut arena = Arena::new(); - let path = arena.alloc(Lir::Path(2)); + let path = arena.alloc(Lir::Path(2u8.into())); let add = arena.alloc(Lir::Add(vec![path, path])); let function = arena.alloc(Lir::Quote(add)); let capture = arena.alloc(Lir::Atom(vec![0x10])); @@ -609,7 +607,7 @@ mod tests { check(&arena, lir, expect![[r#"(c (q . "first") (q . "rest"))"#]]); check_with_options(&arena, lir, true, expect![[r#"(q "first" . "rest")"#]]); - let path = arena.alloc(Lir::Path(2)); + let path = arena.alloc(Lir::Path(2u8.into())); let lir = arena.alloc(Lir::Cons(path, lir)); check_with_options( &arena, diff --git a/crates/rue-lir/src/lir.rs b/crates/rue-lir/src/lir.rs index 3af43e7a..a5fd2c09 100644 --- a/crates/rue-lir/src/lir.rs +++ b/crates/rue-lir/src/lir.rs @@ -1,6 +1,7 @@ use clvm_traits::{ToClvm, ToClvmError}; use clvmr::{Allocator, NodePtr}; use id_arena::Id; +use num_bigint::BigUint; use crate::bigint_atom; @@ -9,7 +10,7 @@ pub type LirId = Id; #[derive(Debug, Clone)] pub enum Lir { Atom(Vec), - Path(u32), + Path(BigUint), Quote(LirId), Run(LirId, LirId), Closure(LirId, Vec, bool), diff --git a/crates/rue-lir/src/optimize/ops.rs b/crates/rue-lir/src/optimize/ops.rs index 455f50d7..77d19099 100644 --- a/crates/rue-lir/src/optimize/ops.rs +++ b/crates/rue-lir/src/optimize/ops.rs @@ -1,7 +1,7 @@ use std::num::Saturating; use id_arena::Arena; -use num_bigint::{BigInt, Sign}; +use num_bigint::{BigInt, BigUint, Sign}; use num_integer::Integer; use sha2::{Digest, Sha256}; use sha3::Keccak256; @@ -18,7 +18,7 @@ pub fn opt_atom(arena: &mut Arena, atom: Vec) -> LirId { } // There's no way to optimize a path -pub fn opt_path(arena: &mut Arena, path: u32) -> LirId { +pub fn opt_path(arena: &mut Arena, path: BigUint) -> LirId { arena.alloc(Lir::Path(path)) } @@ -32,7 +32,9 @@ pub fn opt_quote(arena: &mut Arena, value: LirId) -> LirId { // We can also skip quoting if the program has no path, since it's not going to rely on the environment pub fn opt_run(arena: &mut Arena, callee: LirId, env: LirId) -> LirId { if let Lir::Quote(value) = arena[callee].clone() { - if let Lir::Path(1) = arena[env].clone() { + if let Lir::Path(path) = arena[env].clone() + && path == BigUint::from(1u8) + { return value; } @@ -66,7 +68,7 @@ pub fn opt_closure( // If the value is a divmod, we can optimize it to a div since that's the first output pub fn opt_first(arena: &mut Arena, value: LirId) -> LirId { match arena[value].clone() { - Lir::Path(path) => arena.alloc(Lir::Path(first_path(path))), + Lir::Path(path) => arena.alloc(Lir::Path(first_path(&path))), Lir::Divmod(left, right) => opt_div(arena, left, right), _ => arena.alloc(Lir::First(value)), } @@ -77,7 +79,7 @@ pub fn opt_first(arena: &mut Arena, value: LirId) -> LirId { // If the value is a divmod, we can optimize it to a remainder since that's the rest output pub fn opt_rest(arena: &mut Arena, value: LirId) -> LirId { match arena[value].clone() { - Lir::Path(path) => arena.alloc(Lir::Path(rest_path(path))), + Lir::Path(path) => arena.alloc(Lir::Path(rest_path(&path))), Lir::Divmod(left, right) => opt_mod(arena, left, right), _ => arena.alloc(Lir::Rest(value)), } @@ -87,10 +89,10 @@ pub fn opt_rest(arena: &mut Arena, value: LirId) -> LirId { pub fn opt_cons(arena: &mut Arena, first: LirId, rest: LirId) -> LirId { if let Lir::Path(f_path) = arena[first].clone() && let Lir::Path(r_path) = arena[rest].clone() - && let Some(parent) = parent_path(f_path) - && Some(parent) == parent_path(r_path) - && first_path(parent) == f_path - && rest_path(parent) == r_path + && let Some(parent) = parent_path(&f_path) + && parent_path(&r_path).as_ref() == Some(&parent) + && first_path(&parent) == f_path + && rest_path(&parent) == r_path { return arena.alloc(Lir::Path(parent)); } diff --git a/crates/rue-lir/src/path.rs b/crates/rue-lir/src/path.rs index d35eb738..52d571cf 100644 --- a/crates/rue-lir/src/path.rs +++ b/crates/rue-lir/src/path.rs @@ -1,48 +1,39 @@ -pub fn path_to_atom(path: u32) -> Vec { - let bytes = path.to_be_bytes(); - let mut slice = bytes.as_slice(); - while (!slice.is_empty()) && (slice[0] == 0) { - slice = &slice[1..]; - } - slice.to_vec() -} +use num_bigint::BigUint; -pub fn first_path(path: u32) -> u32 { - add_path(path, 2) +pub fn path_to_atom(path: &BigUint) -> Vec { + let bytes = path.to_bytes_be(); + let first_nonzero = bytes + .iter() + .position(|&byte| byte != 0) + .unwrap_or(bytes.len()); + bytes[first_nonzero..].to_vec() } -pub fn rest_path(path: u32) -> u32 { - add_path(path, 3) +pub fn first_path(path: &BigUint) -> BigUint { + add_path(path, BigUint::from(2u8)) } -fn add_path(a: u32, mut b: u32) -> u32 { - let mut mask = 1; - let mut temp_path = a; - while temp_path > 1 { - b <<= 1; - mask <<= 1; - temp_path >>= 1; - } +pub fn rest_path(path: &BigUint) -> BigUint { + add_path(path, BigUint::from(3u8)) +} - mask -= 1; - b | (a & mask) +fn add_path(a: &BigUint, b: BigUint) -> BigUint { + let depth = a.bits().saturating_sub(1); + let mask = (BigUint::from(1u8) << depth) - 1u8; + (b << depth) | (a & mask) } -pub fn parent_path(path: u32) -> Option { - let result_depth = 31u32.saturating_sub(path.leading_zeros()); +pub fn parent_path(path: &BigUint) -> Option { + let result_depth = path.bits().saturating_sub(1); if result_depth == 0 { return None; } let original_depth = result_depth - 1; - let mask = (1 << original_depth) - 1; + let mask = (BigUint::from(1u8) << original_depth) - 1u8; let lower_bits = path & mask; - let result = (1 << original_depth) | lower_bits; - - if result == 0 { - return None; - } + let result = (BigUint::from(1u8) << original_depth) | lower_bits; Some(result) } @@ -53,47 +44,68 @@ mod tests { #[test] fn test_path_to_atom() { - assert_eq!(path_to_atom(1), [0x01]); - assert_eq!(path_to_atom(3), [0x03]); - assert_eq!(path_to_atom(300), [0x01, 0x2C]); + assert_eq!(path_to_atom(&0u8.into()), Vec::::new()); + assert_eq!(path_to_atom(&1u8.into()), [0x01]); + assert_eq!(path_to_atom(&3u8.into()), [0x03]); + assert_eq!(path_to_atom(&128u8.into()), [0x80]); + assert_eq!(path_to_atom(&300u16.into()), [0x01, 0x2C]); } #[test] fn test_first_path() { - assert_eq!(first_path(1), 2); - assert_eq!(first_path(2), 4); - assert_eq!(first_path(5), 9); + assert_eq!(first_path(&1u8.into()), 2u8.into()); + assert_eq!(first_path(&2u8.into()), 4u8.into()); + assert_eq!(first_path(&5u8.into()), 9u8.into()); } #[test] fn test_rest_path() { - assert_eq!(rest_path(1), 3); - assert_eq!(rest_path(2), 6); - assert_eq!(rest_path(5), 13); + assert_eq!(rest_path(&1u8.into()), 3u8.into()); + assert_eq!(rest_path(&2u8.into()), 6u8.into()); + assert_eq!(rest_path(&5u8.into()), 13u8.into()); } #[test] fn test_parent_path() { - assert_eq!(parent_path(0), None); - assert_eq!(parent_path(1), None); - assert_eq!(parent_path(2), Some(1)); - assert_eq!(parent_path(3), Some(1)); - assert_eq!(parent_path(4), Some(2)); - assert_eq!(parent_path(6), Some(2)); - assert_eq!(parent_path(5), Some(3)); - assert_eq!(parent_path(7), Some(3)); - assert_eq!(parent_path(8), Some(4)); - assert_eq!(parent_path(12), Some(4)); - assert_eq!(parent_path(10), Some(6)); - assert_eq!(parent_path(14), Some(6)); - assert_eq!(parent_path(9), Some(5)); - assert_eq!(parent_path(13), Some(5)); - assert_eq!(parent_path(11), Some(7)); - assert_eq!(parent_path(15), Some(7)); + assert_eq!(parent_path(&0u8.into()), None); + assert_eq!(parent_path(&1u8.into()), None); + assert_eq!(parent_path(&2u8.into()), Some(1u8.into())); + assert_eq!(parent_path(&3u8.into()), Some(1u8.into())); + assert_eq!(parent_path(&4u8.into()), Some(2u8.into())); + assert_eq!(parent_path(&6u8.into()), Some(2u8.into())); + assert_eq!(parent_path(&5u8.into()), Some(3u8.into())); + assert_eq!(parent_path(&7u8.into()), Some(3u8.into())); + assert_eq!(parent_path(&8u8.into()), Some(4u8.into())); + assert_eq!(parent_path(&12u8.into()), Some(4u8.into())); + assert_eq!(parent_path(&10u8.into()), Some(6u8.into())); + assert_eq!(parent_path(&14u8.into()), Some(6u8.into())); + assert_eq!(parent_path(&9u8.into()), Some(5u8.into())); + assert_eq!(parent_path(&13u8.into()), Some(5u8.into())); + assert_eq!(parent_path(&11u8.into()), Some(7u8.into())); + assert_eq!(parent_path(&15u8.into()), Some(7u8.into())); for path in 1..u32::from(u16::MAX) { - assert_eq!(parent_path(first_path(path)), Some(path)); - assert_eq!(parent_path(rest_path(path)), Some(path)); + let path = BigUint::from(path); + assert_eq!(parent_path(&first_path(&path)), Some(path.clone())); + assert_eq!(parent_path(&rest_path(&path)), Some(path)); + } + } + + #[test] + fn test_arbitrarily_large_path() { + let mut path = BigUint::from(1u8); + + for _ in 0..100 { + path = rest_path(&path); } + + assert_eq!(path.bits(), 101); + assert_eq!(path_to_atom(&path).len(), 13); + + for _ in 0..100 { + path = parent_path(&path).unwrap(); + } + + assert_eq!(path, BigUint::from(1u8)); } }