Skip to content

Commit

Permalink
variable-width integer operations
Browse files Browse the repository at this point in the history
  • Loading branch information
enricozb committed Apr 4, 2024
1 parent 12509b2 commit 4b7e24a
Show file tree
Hide file tree
Showing 12 changed files with 425 additions and 104 deletions.
14 changes: 10 additions & 4 deletions src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ pub enum Tree {
/// A nilary eraser node.
Era,
/// A native 60-bit integer.
Num { val: u64 },
Num { val: i64 },
/// A nilary node, referencing a named net.
Ref { nam: String },
/// A n-ary interaction combinator.
Expand Down Expand Up @@ -303,9 +303,15 @@ impl<'i> Parser<'i> {
// Num = "#" Int
Some('#') => {
self.advance_char();
Ok(Tree::Num { val: self.parse_int()? })
match self.peek_char() {
Some('-') => {
self.advance_char();
Ok(Tree::Num { val: -(self.parse_int()? as i64) })
}
_ => Ok(Tree::Num { val: self.parse_int()? as i64 }),
}
}
// Op = "<" Op Tree Tree ">" | "<" Int Op Tree ">"
// Op = "<" Op Tree Tree ">"
Some('<') => {
self.advance_char();
let op = self.parse_op()?;
Expand Down Expand Up @@ -372,7 +378,7 @@ impl<'i> Parser<'i> {

/// See `ops.rs` for the available operators.
fn parse_op(&mut self) -> Result<Op, String> {
let op = self.take_while(|c| "+-=*/%<>|&^!?$".contains(c));
let op = self.take_while(|c| "ui81632.+-=*/%<>|&^!?$".contains(c));
op.parse().map_err(|_| format!("Unknown operator: {op:?}"))
}

Expand Down
2 changes: 1 addition & 1 deletion src/compile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ fn _compile_host(host: &Host) -> Result<String, fmt::Error> {
.map(|(raw_name, def)| (raw_name, sanitize_name(raw_name), def));

writeln!(code, "#![allow(non_upper_case_globals, unused_imports)]")?;
writeln!(code, "use crate::{{host::{{Host, DefRef}}, run::*, ops::Op::*}};")?;
writeln!(code, "use crate::{{host::{{Host, DefRef}}, run::*, ops::{{Op, Ty::*, IntOp::*}}}};")?;
writeln!(code)?;

writeln!(code, "pub fn host() -> Host {{")?;
Expand Down
6 changes: 3 additions & 3 deletions src/host/encode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ trait Encoder {
fn make_const(&mut self, port: Port) -> Self::Trg;
fn ctr(&mut self, lab: Lab, trg: Self::Trg) -> (Self::Trg, Self::Trg);
fn op(&mut self, op: Op, trg: Self::Trg) -> (Self::Trg, Self::Trg);
fn op_num(&mut self, op: Op, trg: Self::Trg, rhs: u64) -> Self::Trg;
fn op_num(&mut self, op: Op, trg: Self::Trg, rhs: i64) -> Self::Trg;
fn mat(&mut self, trg: Self::Trg) -> (Self::Trg, Self::Trg);
fn wires(&mut self) -> (Self::Trg, Self::Trg, Self::Trg, Self::Trg);
}
Expand Down Expand Up @@ -164,7 +164,7 @@ impl Encoder for InterpretedDef {
self.instr.push(Instruction::Op { op, trg, rhs, out });
(rhs, out)
}
fn op_num(&mut self, op: Op, trg: Self::Trg, rhs: u64) -> Self::Trg {
fn op_num(&mut self, op: Op, trg: Self::Trg, rhs: i64) -> Self::Trg {
let out = self.new_trg_id();
self.instr.push(Instruction::OpNum { op, trg, rhs, out });
out
Expand Down Expand Up @@ -202,7 +202,7 @@ impl<'a, M: Mode> Encoder for run::Net<'a, M> {
fn op(&mut self, op: Op, trg: Self::Trg) -> (Self::Trg, Self::Trg) {
self.do_op(op, trg)
}
fn op_num(&mut self, op: Op, trg: Self::Trg, rhs: u64) -> Self::Trg {
fn op_num(&mut self, op: Op, trg: Self::Trg, rhs: i64) -> Self::Trg {
self.do_op_num(op, trg, rhs)
}
fn mat(&mut self, trg: Self::Trg) -> (Self::Trg, Self::Trg) {
Expand Down
236 changes: 179 additions & 57 deletions src/ops.rs
Original file line number Diff line number Diff line change
@@ -1,30 +1,79 @@
use crate::util::bi_enum;
use std::{
cmp::{Eq, Ord},
fmt::Display,
str::FromStr,
};

trait Num: Eq + Ord + Sized {
const ZERO: Self;
const ONE: Self;

fn add(a: Self, b: Self) -> Self;
fn sub(a: Self, b: Self) -> Self;
fn mul(a: Self, b: Self) -> Self;
fn div(a: Self, b: Self) -> Self;
fn rem(a: Self, b: Self) -> Self;
fn and(a: Self, b: Self) -> Self;
fn or(a: Self, b: Self) -> Self;
fn xor(a: Self, b: Self) -> Self;
fn shl(a: Self, b: Self) -> Self;
fn shr(a: Self, b: Self) -> Self;

fn from_bool(b: bool) -> Self {
if b { Self::ONE } else { Self::ZERO }
}
}

macro_rules! impl_num {
( $($ty:ty),+ ) => {
$(
impl Num for $ty {
const ZERO: $ty = 0;
const ONE: $ty = 1;

fn add(a: $ty, b: $ty) -> $ty { a.wrapping_add(b) }
fn sub(a: $ty, b: $ty) -> $ty { a.wrapping_sub(b) }
fn mul(a: $ty, b: $ty) -> $ty { a.wrapping_mul(b) }
fn div(a: $ty, b: $ty) -> $ty { a.checked_div(b).unwrap_or(0) }
fn rem(a: $ty, b: $ty) -> $ty { a.checked_rem(b).unwrap_or(0) }
fn and(a: $ty, b: $ty) -> $ty { a & b }
fn or(a: $ty, b: $ty) -> $ty { a | b }
fn xor(a: $ty, b: $ty) -> $ty { a ^ b }
fn shl(a: $ty, b: $ty) -> $ty { a.wrapping_shl(b as u32) }
fn shr(a: $ty, b: $ty) -> $ty { a.wrapping_shr(b as u32) }
}
)*
}
}

impl_num! { u8, u16, u32, u64, i8, i16, i32 }

bi_enum! {
#[repr(u16)]
/// A native operation on 60-bit integers.
#[repr(u8)]
/// Native operations on variable-width integers.
///
/// Each operation has a swapped counterpart (accessible with `.swap()`),
/// where the order of the operands is swapped.
///
/// Operations without an already-named counterpart (e.g. `Add <-> Add` and
/// `Lt <-> Gt`) are suffixed with `$`/`S`: `(-$ 1 2) = (- 2 1) = 1`.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum Op {
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum IntOp {
"+": Add = 0,
"-": Sub = 1,
"-$": SubS = 2,
"*": Mul = 3,
"/": Div = 4,
"/$": DivS = 5,
"%": Mod = 6,
"%$": ModS = 7,
"%": Rem = 6,
"%$": RemS = 7,
"==": Eq = 8,
"!=": Ne = 9,
"<": Lt = 10,
">": Gt = 11,
"<=": Lte = 12,
">=": Gte = 13,
"<=": Le = 12,
">=": Ge = 13,
"&": And = 14,
"|": Or = 15,
"^": Xor = 16,
Expand All @@ -35,63 +84,136 @@ bi_enum! {
}
}

use Op::*;

impl Op {
impl IntOp {
/// Returns this operation's swapped counterpart.
///
/// For all `op, a, b`, `op.swap().op(a, b) == op.op(b, a)`.
#[inline]
pub fn swap(self) -> Self {
fn swap(self) -> Self {
match self {
Add => Add,
Sub => SubS,
SubS => Sub,
Mul => Mul,
Div => DivS,
DivS => Div,
Mod => ModS,
ModS => Mod,
Eq => Eq,
Ne => Ne,
Lt => Gt,
Gt => Lt,
Lte => Gte,
Gte => Lte,
And => And,
Or => Or,
Xor => Xor,
Shl => ShlS,
ShlS => Shl,
Shr => ShrS,
ShrS => Shr,
Self::Add => Self::Add,
Self::Sub => Self::SubS,
Self::SubS => Self::Sub,
Self::Mul => Self::Mul,
Self::Div => Self::DivS,
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,
Self::Shl => Self::ShlS,
Self::ShlS => Self::Shl,
Self::Shr => Self::ShrS,
Self::ShrS => Self::Shr,
}
}
#[inline]
pub fn op(self, a: u64, b: u64) -> u64 {
const U60: u64 = 0xFFF_FFFF_FFFF_FFFF;

fn op<T: Num>(self, a: T, b: T) -> T {
match self {
Add => a.wrapping_add(b) & U60,
Sub => a.wrapping_sub(b) & U60,
SubS => b.wrapping_sub(a) & U60,
Mul => a.wrapping_mul(b) & U60,
Div => a.checked_div(b).unwrap_or(0),
DivS => b.checked_div(a).unwrap_or(0),
Mod => a.checked_rem(b).unwrap_or(0),
ModS => b.checked_rem(a).unwrap_or(0),
Eq => (a == b) as u64,
Ne => (a != b) as u64,
Lt => (a < b) as u64,
Gt => (a > b) as u64,
Lte => (a <= b) as u64,
Gte => (a >= b) as u64,
And => a & b,
Or => a | b,
Xor => a ^ b,
Shl => (a << b) & U60,
ShlS => (b << a) & U60,
Shr => a >> b,
ShrS => b >> a,
Self::Add => T::add(a, b),
Self::Sub => T::sub(a, b),
Self::SubS => T::sub(b, a),
Self::Mul => T::mul(a, b),
Self::Div => T::div(a, b),
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),
Self::Shl => T::shl(a, b),
Self::ShlS => T::shl(b, a),
Self::Shr => T::shr(a, b),
Self::ShrS => T::shr(b, a),
}
}
}

bi_enum! {
#[repr(u8)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum Ty {
"u8": U8 = 0,
"u16": U16 = 1,
"u32": U32 = 2,
"u60": U60 = 3,
"i8": I8 = 4,
"i16": I16 = 5,
"i32": I32 = 6,
}
}

#[repr(C)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Op {
pub ty: Ty,
pub op: IntOp,
}

impl From<u16> for Op {
fn from(value: u16) -> Self {
unsafe { std::mem::transmute(value) }
}
}

impl From<Op> for u16 {
fn from(op: Op) -> Self {
unsafe { std::mem::transmute(op) }
}
}

impl Display for Op {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self.ty {
Ty::U60 => write!(f, "{}", self.op),
_ => write!(f, "{}.{}", self.ty, self.op),
}
}
}

impl FromStr for Op {
type Err = ();

fn from_str(s: &str) -> Result<Self, Self::Err> {
match s.split('.').collect::<Vec<_>>().as_slice() {
[ty, op] => Ok(Self { ty: Ty::from_str(ty)?, op: IntOp::from_str(op)? }),
[op] => Ok(Self { ty: Ty::U60, op: IntOp::from_str(op)? }),

_ => Err(()),
}
}
}

impl Op {
pub fn swap(self) -> Self {
Self { op: self.op.swap(), ty: self.ty }
}

#[inline]
pub fn op(self, a: i64, b: i64) -> i64 {
const U60: i64 = 0xFFF_FFFF_FFFF_FFFF;

match self.ty {
Ty::U8 => self.op.op(a as u8, b as u8) as i64,
Ty::U16 => self.op.op(a as u16, b as u16) as i64,
Ty::U32 => self.op.op(a as u32, b as u32) as i64,
Ty::U60 => self.op.op(a as u64, b as u64) as i64 & U60,
Ty::I8 => self.op.op(a as i8, b as i8) as i64,
Ty::I16 => self.op.op(a as i16, b as i16) as i64,
Ty::I32 => self.op.op(a as i32, b as i32) as i64,
}
}
}
10 changes: 5 additions & 5 deletions src/run/instruction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ pub enum Instruction {
/// ```rust,ignore
/// let out = net.do_op_num(lab, trg, rhs);
/// ```
OpNum { op: Op, trg: TrgId, rhs: u64, out: TrgId },
OpNum { op: Op, trg: TrgId, rhs: i64, out: TrgId },
/// See [`Net::do_mat`].
/// ```rust,ignore
/// let (lft, rgt) = net.do_mat(trg);
Expand Down Expand Up @@ -137,22 +137,22 @@ impl<'a, M: Mode> Net<'a, M> {
let port = trg.target();
if !M::LAZY && port.tag() == Num {
self.free_trg(trg);
let n = self.create_node(Op, op.swap() as Lab);
let n = self.create_node(Op, op.swap().into());
n.p1.wire().set_target(Port::new_num(port.num()));
(Trg::port(n.p0), Trg::port(n.p2))
} else if !M::LAZY && port == Port::ERA {
self.free_trg(trg);
(Trg::port(Port::ERA), Trg::port(Port::ERA))
} else {
let n = self.create_node(Op, op as Lab);
let n = self.create_node(Op, op.into());
self.link_trg_port(trg, n.p0);
(Trg::port(n.p1), Trg::port(n.p2))
}
}

/// `trg ~ <op #b x>`
#[inline(always)]
pub(crate) fn do_op_num(&mut self, op: Op, trg: Trg, rhs: u64) -> Trg {
pub(crate) fn do_op_num(&mut self, op: Op, trg: Trg, rhs: i64) -> Trg {
let port = trg.target();
if !M::LAZY && port.tag() == Num {
self.rwts.oper += 1;
Expand All @@ -162,7 +162,7 @@ impl<'a, M: Mode> Net<'a, M> {
self.free_trg(trg);
Trg::port(Port::ERA)
} else {
let n = self.create_node(Op, op as Lab);
let n = self.create_node(Op, op.into());
self.link_trg_port(trg, n.p0);
n.p1.wire().set_target(Port::new_num(rhs));
Trg::port(n.p2)
Expand Down
Loading

0 comments on commit 4b7e24a

Please sign in to comment.