use nox::noun::{Order, NounId, Noun};
pub mod wasm;
pub mod arm64;
pub mod rv32;
pub mod rv64;
pub mod rvv;
pub mod x86_64;
pub mod ebpf;
pub mod ptx;
pub mod tensor_cores;
pub mod wgsl;
pub mod spirv;
pub mod amx;
pub mod ane;
pub mod thumb2;
pub mod hexagon;
pub mod verilog;
pub mod systemverilog;
pub mod vhdl;
pub mod qasm;
pub mod xla;
pub mod mir2nox;
pub mod qir;
pub mod onnx;
pub mod cerebras;
pub mod upmem;
pub mod intel_amx;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CompileError {
UnsupportedPattern(u64),
Malformed,
AxisError(u64),
NoParams,
}
pub fn axis_to_param(axis: u64) -> Result<u32, CompileError> {
if axis < 2 {
return Err(CompileError::AxisError(axis));
}
let bits = 64 - axis.leading_zeros() - 1; let mut depth = 0u32;
for i in (0..bits).rev() {
let bit = (axis >> i) & 1;
if bit == 0 {
if i == 0 {
return Ok(depth);
}
return Err(CompileError::AxisError(axis));
} else {
depth += 1;
}
}
Err(CompileError::AxisError(axis))
}
pub fn formula_parts<const N: usize>(order: &Order<N>, formula: NounId) -> Result<(u64, NounId), CompileError> {
match order.get(formula).inner {
Noun::Cell { left, right } => {
match order.atom_value(left) {
Some((v, _)) => Ok((v.as_u64(), right)),
None => Err(CompileError::Malformed),
}
}
Noun::Atom { .. } => Err(CompileError::Malformed),
}
}
pub fn body_pair<const N: usize>(order: &Order<N>, body: NounId) -> Result<(NounId, NounId), CompileError> {
match order.get(body).inner {
Noun::Cell { left, right } => Ok((left, right)),
_ => Err(CompileError::Malformed),
}
}
pub fn body_triple<const N: usize>(order: &Order<N>, body: NounId) -> Result<(NounId, NounId, NounId), CompileError> {
let (test, rest) = body_pair(order, body)?;
let (yes, no) = body_pair(order, rest)?;
Ok((test, yes, no))
}
pub fn atom_u64<const N: usize>(order: &Order<N>, r: NounId) -> Result<u64, CompileError> {
match order.get(r).inner {
Noun::Atom { value, .. } => Ok(value.as_u64()),
_ => Err(CompileError::Malformed),
}
}
pub fn is_quote_atom<const N: usize>(order: &Order<N>, formula: NounId) -> Option<u64> {
let (tag, body) = formula_parts(order, formula).ok()?;
if tag != 1 { return None; }
atom_u64(order, body).ok()
}
pub fn detect_loop_setup<const N: usize>(
order: &Order<N>, compose_body: NounId,
) -> Option<(NounId, Vec<NounId>)> {
let (a, b) = body_pair(order, compose_body).ok()?;
let (b_tag, loop_body) = formula_parts(order, b).ok()?;
if b_tag != 1 { return None; }
let (a_tag, a_body) = formula_parts(order, a).ok()?;
if a_tag != 3 { return None; }
let (formula_slot, rest) = body_pair(order, a_body).ok()?;
let (fs_tag, _) = formula_parts(order, formula_slot).ok()?;
if fs_tag != 1 { return None; }
let mut inits = Vec::new();
let mut cur = rest;
loop {
let (tag, body) = formula_parts(order, cur).ok()?;
if tag == 0 { break; } if tag != 3 { return None; }
let (val, tail) = body_pair(order, body).ok()?;
inits.push(val);
cur = tail;
}
Some((loop_body, inits))
}
pub fn detect_back_edge<const N: usize>(
order: &Order<N>, compose_body: NounId,
) -> Option<(NounId, u64)> {
let (a, b) = body_pair(order, compose_body).ok()?;
let (b_tag, b_body) = formula_parts(order, b).ok()?;
if b_tag != 0 { return None; } let axis = atom_u64(order, b_body).ok()?;
Some((a, axis))
}
pub fn count_loop_slots(inits: &[NounId]) -> u32 {
inits.len() as u32
}
#[cfg(test)]
mod tests {
use super::*;
use nox::noun::{Order, Tag};
use nebu::Goldilocks;
fn g(v: u64) -> Goldilocks { Goldilocks::new(v) }
fn make_cell<const N: usize>(order: &mut Order<N>, left: NounId, right: NounId) -> NounId {
order.cell(left, right).unwrap()
}
fn make_atom<const N: usize>(order: &mut Order<N>, v: u64) -> NounId {
order.atom(g(v), Tag::Field).unwrap()
}
fn make_formula<const N: usize>(order: &mut Order<N>, tag: u64, body: NounId) -> NounId {
let t = make_atom(order, tag);
make_cell(order, t, body)
}
fn make_binary<const N: usize>(order: &mut Order<N>, tag: u64, left: NounId, right: NounId) -> NounId {
let body = make_cell(order, left, right);
make_formula(order, tag, body)
}
#[test]
fn axis_param_mapping() {
assert_eq!(axis_to_param(2), Ok(0));
assert_eq!(axis_to_param(6), Ok(1));
assert_eq!(axis_to_param(14), Ok(2));
assert_eq!(axis_to_param(30), Ok(3));
assert_eq!(axis_to_param(62), Ok(4));
}
#[test]
fn axis_errors() {
assert!(axis_to_param(0).is_err());
assert!(axis_to_param(1).is_err());
assert!(axis_to_param(3).is_err());
}
#[test]
fn wasm_quote_compiles() {
let mut order = Order::<1024>::new();
let body = make_atom(&mut order, 42);
let formula = make_formula(&mut order, 1, body);
let wasm = wasm::compile_to_wasm(&order, formula, 0).unwrap();
assert_eq!(&wasm[0..4], b"\0asm");
assert!(wasm.len() > 8);
}
#[test]
fn wasm_add_compiles() {
let mut o = Order::<1024>::new();
let a3 = make_atom(&mut o, 3);
let q3 = make_formula(&mut o, 1, a3);
let a5 = make_atom(&mut o, 5);
let q5 = make_formula(&mut o, 1, a5);
let formula = make_binary(&mut o, 5, q3, q5);
let wasm = wasm::compile_to_wasm(&o, formula, 0).unwrap();
assert_eq!(&wasm[0..4], b"\0asm");
}
#[test]
fn wasm_axis_param_compiles() {
let mut o = Order::<1024>::new();
let ax2 = make_atom(&mut o, 2);
let a0 = make_formula(&mut o, 0, ax2);
let ax6 = make_atom(&mut o, 6);
let a1 = make_formula(&mut o, 0, ax6);
let formula = make_binary(&mut o, 5, a0, a1);
let wasm = wasm::compile_to_wasm(&o, formula, 2).unwrap();
assert_eq!(&wasm[0..4], b"\0asm");
}
#[test]
fn wasm_branch_compiles() {
let mut o = Order::<1024>::new();
let v0 = make_atom(&mut o, 0);
let test = make_formula(&mut o, 1, v0);
let v10 = make_atom(&mut o, 10);
let yes = make_formula(&mut o, 1, v10);
let v20 = make_atom(&mut o, 20);
let no = make_formula(&mut o, 1, v20);
let yes_no = make_cell(&mut o, yes, no);
let body = make_cell(&mut o, test, yes_no);
let formula = make_formula(&mut o, 4, body);
let wasm = wasm::compile_to_wasm(&o, formula, 0).unwrap();
assert_eq!(&wasm[0..4], b"\0asm");
}
#[test]
fn wasm_mul_compiles() {
let mut o = Order::<1024>::new();
let ax2 = make_atom(&mut o, 2);
let a0 = make_formula(&mut o, 0, ax2);
let ax6 = make_atom(&mut o, 6);
let a1 = make_formula(&mut o, 0, ax6);
let formula = make_binary(&mut o, 7, a0, a1);
let wasm = wasm::compile_to_wasm(&o, formula, 2).unwrap();
assert_eq!(&wasm[0..4], b"\0asm");
}
#[test]
fn arm64_add_compiles() {
let mut o = Order::<1024>::new();
let ax2 = make_atom(&mut o, 2);
let a0 = make_formula(&mut o, 0, ax2);
let ax6 = make_atom(&mut o, 6);
let a1 = make_formula(&mut o, 0, ax6);
let formula = make_binary(&mut o, 5, a0, a1);
let code = arm64::compile_to_arm64(&o, formula, 2).unwrap();
let len = code.len();
assert!(len >= 4);
let last4 = u32::from_le_bytes([code[len-4], code[len-3], code[len-2], code[len-1]]);
assert_eq!(last4, 0xD65F03C0, "should end with RET");
}
#[test]
fn arm64_mul_compiles() {
let mut o = Order::<1024>::new();
let ax2 = make_atom(&mut o, 2);
let a0 = make_formula(&mut o, 0, ax2);
let ax6 = make_atom(&mut o, 6);
let a1 = make_formula(&mut o, 0, ax6);
let formula = make_binary(&mut o, 7, a0, a1);
let code = arm64::compile_to_arm64(&o, formula, 2).unwrap();
let len = code.len();
let last4 = u32::from_le_bytes([code[len-4], code[len-3], code[len-2], code[len-1]]);
assert_eq!(last4, 0xD65F03C0, "should end with RET");
}
#[test]
fn x86_64_add_compiles() {
let mut o = Order::<1024>::new();
let ax2 = make_atom(&mut o, 2);
let a0 = make_formula(&mut o, 0, ax2);
let ax6 = make_atom(&mut o, 6);
let a1 = make_formula(&mut o, 0, ax6);
let formula = make_binary(&mut o, 5, a0, a1);
let code = x86_64::compile_to_x86_64(&o, formula, 2).unwrap();
assert!(!code.is_empty());
assert_eq!(*code.last().unwrap(), 0xC3, "should end with RET");
}
#[test]
fn x86_64_mul_compiles() {
let mut o = Order::<1024>::new();
let ax2 = make_atom(&mut o, 2);
let a0 = make_formula(&mut o, 0, ax2);
let ax6 = make_atom(&mut o, 6);
let a1 = make_formula(&mut o, 0, ax6);
let formula = make_binary(&mut o, 7, a0, a1);
let code = x86_64::compile_to_x86_64(&o, formula, 2).unwrap();
assert_eq!(*code.last().unwrap(), 0xC3, "should end with RET");
}
#[test]
fn rv32_add_compiles() {
let mut o = Order::<1024>::new();
let ax2 = make_atom(&mut o, 2);
let a0 = make_formula(&mut o, 0, ax2);
let ax6 = make_atom(&mut o, 6);
let a1 = make_formula(&mut o, 0, ax6);
let formula = make_binary(&mut o, 5, a0, a1);
let code = rv32::compile_to_rv32(&o, formula, 2).unwrap();
let len = code.len();
assert!(len >= 4);
let last4 = u32::from_le_bytes([code[len-4], code[len-3], code[len-2], code[len-1]]);
assert_eq!(last4, 0x00008067, "should end with RET");
}
#[test]
fn rv64_add_compiles() {
let mut o = Order::<1024>::new();
let ax2 = make_atom(&mut o, 2);
let a0 = make_formula(&mut o, 0, ax2);
let ax6 = make_atom(&mut o, 6);
let a1 = make_formula(&mut o, 0, ax6);
let formula = make_binary(&mut o, 5, a0, a1);
let code = rv64::compile_to_rv64(&o, formula, 2).unwrap();
let len = code.len();
assert!(len >= 4);
let last4 = u32::from_le_bytes([code[len-4], code[len-3], code[len-2], code[len-1]]);
assert_eq!(last4, 0x00008067, "should end with RET");
}
#[test]
fn ebpf_add_compiles() {
let mut o = Order::<1024>::new();
let ax2 = make_atom(&mut o, 2);
let a0 = make_formula(&mut o, 0, ax2);
let ax6 = make_atom(&mut o, 6);
let a1 = make_formula(&mut o, 0, ax6);
let formula = make_binary(&mut o, 5, a0, a1);
let code = ebpf::compile_to_ebpf(&o, formula, 2).unwrap();
let len = code.len();
assert!(len >= 8);
let last8 = u64::from_le_bytes(code[len-8..len].try_into().unwrap());
assert_eq!(last8 & 0xFF, 0x95, "should end with EXIT");
}
#[test]
fn unsupported_pattern_errors() {
let mut o = Order::<1024>::new();
let a1 = make_atom(&mut o, 1);
let body = make_formula(&mut o, 0, a1);
let formula = make_formula(&mut o, 15, body);
assert!(matches!(
wasm::compile_to_wasm(&o, formula, 1),
Err(CompileError::UnsupportedPattern(15))
));
}
}