From 5d3bb02e8e5814e2fef8a3aec99c31da217aaa93 Mon Sep 17 00:00:00 2001 From: Enrico Zandomeni Borba Date: Mon, 15 Apr 2024 16:18:45 +0200 Subject: [PATCH] comparisons return ints --- Cargo.lock | 2 +- Cargo.toml | 2 +- src/ast.rs | 9 +++-- src/ops.rs | 70 ++++++++++++++++++++++--------------- src/ops/num.rs | 2 +- src/ops/word.rs | 25 ++----------- src/run/instruction.rs | 4 +-- src/run/interact.rs | 4 +-- src/run/port.rs | 6 ++-- src/transform/eta_reduce.rs | 2 +- tests/programs/f32.hvmc | 2 ++ 11 files changed, 61 insertions(+), 67 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a5601ca8..b649ad4a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -295,7 +295,7 @@ checksum = "809e18805660d7b6b2e2b9f316a5099521b5998d5cba4dda11b5157a21aaef03" [[package]] name = "hvm-core" -version = "0.2.24" +version = "0.2.25" dependencies = [ "TSPL", "arrayvec", diff --git a/Cargo.toml b/Cargo.toml index 4e9f7793..9a951cc7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "hvm-core" -version = "0.2.24" +version = "0.2.25" edition = "2021" description = "HVM-Core is a massively parallel Interaction Combinator evaluator." license = "MIT" diff --git a/src/ast.rs b/src/ast.rs index dac75fd3..eab8b09b 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -10,18 +10,17 @@ //! //! [interaction calculus]: https://en.wikipedia.org/wiki/Interaction_nets#Interaction_calculus -use crate::prelude::*; -use arrayvec::ArrayVec; -use ordered_float::OrderedFloat; - use crate::{ ops::TypedOp as Op, + prelude::*, run::Lab, util::{array_vec, deref, maybe_grow}, }; + use alloc::collections::BTreeMap; use arrayvec::ArrayVec; use core::str::FromStr; +use ordered_float::OrderedFloat; use TSPL::{new_parser, Parser}; /// The top level AST node, representing a collection of named nets. @@ -311,7 +310,7 @@ impl<'i> HvmcParser<'i> { // Int = "#" [-] Int // F32 = "#" [-] ( Int "." Int | "NaN" | "inf" ) Some('#') => { - self.advance_char(); + self.advance_one(); let is_neg = self.consume("-").is_ok(); let num = self.take_while(|c| c.is_alphanumeric() || c == '.'); diff --git a/src/ops.rs b/src/ops.rs index 411b2af8..44823daa 100644 --- a/src/ops.rs +++ b/src/ops.rs @@ -51,19 +51,20 @@ bi_enum! { "/$": DivS = 5, "%": Rem = 6, "%$": RemS = 7, - "==": Eq = 8, - "!=": Ne = 9, - "<": Lt = 10, - ">": Gt = 11, - "<=": Le = 12, - ">=": Ge = 13, - "&": And = 14, - "|": Or = 15, - "^": Xor = 16, - "<<": Shl = 17, - "<<$": ShlS = 18, - ">>": Shr = 19, - ">>$": ShrS = 20, + "&": And = 8, + "|": Or = 9, + "^": Xor = 10, + "<<": Shl = 11, + "<<$": ShlS = 12, + ">>": Shr = 13, + ">>$": ShrS = 14, + // operators returning ints should go after `Eq` + "==": Eq = 15, + "!=": Ne = 16, + "<": Lt = 17, + ">": Gt = 18, + "<=": Le = 19, + ">=": Ge = 20, } } @@ -82,12 +83,6 @@ impl Op { Self::DivS => Self::Div, Self::Rem => Self::RemS, Self::RemS => Self::Rem, - Self::Eq => Self::Eq, - Self::Ne => Self::Ne, - Self::Lt => Self::Gt, - Self::Gt => Self::Lt, - Self::Le => Self::Ge, - Self::Ge => Self::Le, Self::And => Self::And, Self::Or => Self::Or, Self::Xor => Self::Xor, @@ -95,10 +90,16 @@ impl Op { Self::ShlS => Self::Shl, Self::Shr => Self::ShrS, Self::ShrS => Self::Shr, + Self::Eq => Self::Eq, + Self::Ne => Self::Ne, + Self::Lt => Self::Gt, + Self::Gt => Self::Lt, + Self::Le => Self::Ge, + Self::Ge => Self::Le, } } - fn op(self, a: T, b: T) -> T { + fn op_monoid(self, a: T, b: T) -> T { match self { Self::Add => T::add(a, b), Self::Sub => T::sub(a, b), @@ -108,12 +109,6 @@ impl Op { Self::DivS => T::div(b, a), Self::Rem => T::rem(a, b), Self::RemS => T::rem(b, a), - Self::Eq => T::from_bool(a == b), - Self::Ne => T::from_bool(a != b), - Self::Lt => T::from_bool(a < b), - Self::Le => T::from_bool(a <= b), - Self::Gt => T::from_bool(a > b), - Self::Ge => T::from_bool(a >= b), Self::And => T::and(a, b), Self::Or => T::or(a, b), Self::Xor => T::xor(a, b), @@ -121,11 +116,27 @@ impl Op { Self::ShlS => T::shl(b, a), Self::Shr => T::shr(a, b), Self::ShrS => T::shr(b, a), + + Self::Eq | Self::Ne | Self::Lt | Self::Le | Self::Gt | Self::Ge => unreachable!("non-monoid operation {self}"), + } + } + + fn op_word(self, a: u64, b: u64) -> u64 { + match self { + Self::Eq => (T::from_word(a) == T::from_word(b)).into(), + Self::Ne => (T::from_word(a) != T::from_word(b)).into(), + Self::Lt => (T::from_word(a) < T::from_word(b)).into(), + Self::Le => (T::from_word(a) <= T::from_word(b)).into(), + Self::Gt => (T::from_word(a) > T::from_word(b)).into(), + Self::Ge => (T::from_word(a) >= T::from_word(b)).into(), + + op => op.op_monoid(T::from_word(a), T::from_word(b)).to_word(), } } - fn op_word(self, a: u64, b: u64) -> u64 { - self.op(T::from_word(a), T::from_word(b)).to_word() + #[inline(always)] + fn is_int(&self) -> bool { + self >= &Self::Eq } } @@ -144,8 +155,9 @@ impl TypedOp { std::mem::transmute(val) } + #[inline(always)] pub fn is_int(&self) -> bool { - self.ty < Ty::F32 + (self.ty < Ty::F32) || self.op.is_int() } pub fn swap(self) -> Self { diff --git a/src/ops/num.rs b/src/ops/num.rs index 983a97e9..8e910ec7 100644 --- a/src/ops/num.rs +++ b/src/ops/num.rs @@ -1,5 +1,5 @@ #[rustfmt::skip] -pub trait Numeric: Sized { +pub trait Numeric: PartialEq + PartialOrd + Sized { const ZERO: Self; const ONE: Self; diff --git a/src/ops/word.rs b/src/ops/word.rs index b1dc601f..23894afe 100644 --- a/src/ops/word.rs +++ b/src/ops/word.rs @@ -6,27 +6,7 @@ pub trait ToWord { fn to_word(self) -> u64; } -macro_rules! impl_signed { - ( $($ty:ty),+ ) => { - $( - impl FromWord for $ty { - #[inline(always)] - fn from_word(bits: u64) -> Self { - unsafe { std::mem::transmute::<_, i64>(bits) as Self } - } - } - - impl ToWord for $ty { - #[inline(always)] - fn to_word(self) -> u64 { - unsafe { std::mem::transmute(self as i64) } - } - } - )* - }; -} - -macro_rules! impl_unsigned { +macro_rules! impl_word { ( $($ty:ty),+ ) => { $( impl FromWord for $ty { @@ -46,8 +26,7 @@ macro_rules! impl_unsigned { }; } -impl_signed! { i8, i16, i32 } -impl_unsigned! { u8, u16, u32, u64 } +impl_word! { u8, u16, u32, u64, i8, i16, i32 } impl FromWord for f32 { #[inline(always)] diff --git a/src/run/instruction.rs b/src/run/instruction.rs index d1ea16a1..28c3bb13 100644 --- a/src/run/instruction.rs +++ b/src/run/instruction.rs @@ -120,7 +120,7 @@ impl<'a, M: Mode> Net<'a, M> { self.rwts.anni += 1; (Trg::wire(node.p1), Trg::wire(node.p2)) // TODO: fast copy? - } else if false && !M::LAZY && (port.is_num() == Int || port.tag() == Ref && lab >= port.lab()) { + } else if false && !M::LAZY && (port.is_num() || port.tag() == Ref && lab >= port.lab()) { self.rwts.comm += 1; self.free_trg(trg); (Trg::port(port.clone()), Trg::port(port)) @@ -136,7 +136,7 @@ impl<'a, M: Mode> Net<'a, M> { pub(crate) fn do_op(&mut self, op: Op, trg: Trg) -> (Trg, Trg) { trace!(self.tracer, op, trg); let port = trg.target(); - if !M::LAZY && (port.tag() == Int || port.tag() == F32) { + if !M::LAZY && port.is_num() { self.free_trg(trg); let n = self.create_node(Op, op.swap().into()); n.p1.wire().set_target(port); diff --git a/src/run/interact.rs b/src/run/interact.rs index 6f4cdac9..358e576e 100644 --- a/src/run/interact.rs +++ b/src/run/interact.rs @@ -222,7 +222,7 @@ impl<'a, M: Mode> Net<'a, M> { } } - /// Interacts an int and a binary numeric operation node. + /// Interacts a number and a binary numeric operation node. /// /// ```text /// | @@ -253,7 +253,7 @@ impl<'a, M: Mode> Net<'a, M> { let a = a.consume_node(); let op = unsafe { Op::try_from(a.lab).unwrap_unchecked() }; let a1 = a.p1.load_target(); - if a1.tag() == Int || a1.tag() == F32 { + if a1.is_num() { self.rwts.oper += 1; self.half_free(a.p1.addr()); diff --git a/src/run/port.rs b/src/run/port.rs index af0b1ec4..bfda878a 100644 --- a/src/run/port.rs +++ b/src/run/port.rs @@ -209,16 +209,18 @@ impl Port { /// Accesses the float value of this port; this is valid for [`F32`] ports. #[inline(always)] pub fn float(&self) -> f32 { - f32::from_bits((self.0 >> 4) as u32) + f32::from_bits(self.num() as u32) } /// Accesses the numeric value of this port; this is valid for [`Int`] or /// [`F32`] ports. This is meant for numeric operations to defer /// interpreting this port as an integer or as a float until the operation /// type is known. + /// + /// The intermediate cast to `i64` is to sign-extend during bit-shift. #[inline(always)] pub const fn num(&self) -> u64 { - self.0 >> 4 + ((self.0 as i64) >> 4) as u64 } /// Accesses the wire leaving this port; this is valid for [`Var`] ports and diff --git a/src/transform/eta_reduce.rs b/src/transform/eta_reduce.rs index 318924c1..11e4c23b 100644 --- a/src/transform/eta_reduce.rs +++ b/src/transform/eta_reduce.rs @@ -145,7 +145,7 @@ impl Phase2 { if a == b { let reducible = match a { NodeType::Var(delta) => self.nodes[head_index.wrapping_add_signed(delta)] == NodeType::Ctr(lab), - NodeType::Era | NodeType::Int(_) => true, + NodeType::Era | NodeType::Int(_) | NodeType::F32(_) => true, _ => false, }; if reducible { diff --git a/tests/programs/f32.hvmc b/tests/programs/f32.hvmc index c2602ebf..ed8da372 100644 --- a/tests/programs/f32.hvmc +++ b/tests/programs/f32.hvmc @@ -13,6 +13,8 @@ & x ~ [n x] & @half ~ // 1.15 & x ~ [n x] & @half ~ // 0.25 & x ~ [n x] & @half ~ // 0.5 + + // comparisons (returning ints) & x ~ [n x] & @half ~ // 0 & x ~ [n x] & @half ~ // 1 & x ~ [n x] & @half ~ // 1