From e313223b4442700db0cce2354f3b944cb67798b5 Mon Sep 17 00:00:00 2001 From: Dr Maxim Orlovsky Date: Mon, 21 Oct 2024 21:33:53 +0200 Subject: [PATCH] zkaluvm: complete initial GPA implementation --- src/core/core.rs | 12 ++++----- src/core/microcode/base.rs | 8 ++++++ src/core/microcode/gfa.rs | 28 ++++++++++---------- src/isa/gfa/bytecode.rs | 53 +++++++++++++++++++++++++++++++++++--- src/isa/gfa/exec.rs | 53 +++++++++++++++++++++++++++++++++++--- 5 files changed, 126 insertions(+), 28 deletions(-) diff --git a/src/core/core.rs b/src/core/core.rs index 62732ad..4aa6a42 100644 --- a/src/core/core.rs +++ b/src/core/core.rs @@ -26,7 +26,7 @@ use core::fmt::{self, Debug, Formatter}; use super::{Site, SiteId, Status}; #[cfg(feature = "GFA")] -use crate::core::gfa::Zp; +use crate::core::gfa::Fq; /// Maximal size of call stack. /// @@ -38,7 +38,7 @@ pub const CALL_STACK_SIZE_MAX: u16 = 0xFF; pub struct Core { #[cfg(feature = "GFA")] /// Finite field order. - pub(super) zp: Zp, + pub(super) fq: Fq, // ============================================================================================ // Arithmetic integer registers (ALU64 ISA). @@ -161,7 +161,7 @@ pub struct CoreConfig { pub call_stack_size: u16, #[cfg(feature = "GFA")] /// Order of the finite field for modulo arithmetics. - pub field_order: Zp, + pub field_order: Fq, } impl Default for CoreConfig { @@ -169,7 +169,7 @@ impl Default for CoreConfig { /// - [`CoreConfig::halt`] to `true`, /// - [`CoreConfig::complexity_lim`] to `None` /// - [`CoreConfig::call_stack_size`] to [`CALL_STACK_SIZE_MAX`], - /// - [`CoreConfig::field_order`] to [`Zp::F1137119`] (if `GFA` feature is set). + /// - [`CoreConfig::field_order`] to [`Fq::F1137119`] (if `GFA` feature is set). /// /// # See also /// @@ -183,7 +183,7 @@ impl Default for CoreConfig { complexity_lim: None, call_stack_size: CALL_STACK_SIZE_MAX, #[cfg(feature = "GFA")] - field_order: Zp::F1137119, + field_order: Fq::F1137119, } } } @@ -200,7 +200,7 @@ impl Core { pub fn with(config: CoreConfig) -> Self { Core { #[cfg(feature = "GFA")] - zp: config.field_order, + fq: config.field_order, a8: Default::default(), a16: Default::default(), a32: Default::default(), diff --git a/src/core/microcode/base.rs b/src/core/microcode/base.rs index 124dbf9..b9fcf16 100644 --- a/src/core/microcode/base.rs +++ b/src/core/microcode/base.rs @@ -132,6 +132,14 @@ impl RegA { } } +impl From for RegA { + fn from(val: u8) -> Self { + let a = u3::with(val >> 5); + let idx = u5::with(val & 0x1F); + RegA::with(A::from(a), IdxA::from(idx)) + } +} + #[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, Display)] #[display(inner)] pub struct IdxA(Idx32); diff --git a/src/core/microcode/gfa.rs b/src/core/microcode/gfa.rs index 3543c89..1151de0 100644 --- a/src/core/microcode/gfa.rs +++ b/src/core/microcode/gfa.rs @@ -31,7 +31,7 @@ const F1289: u128 = u128::MAX - 8; // it should be 9, but `u128::MAX` is 2^128-1 /// Finite field orders. #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Display)] -pub enum Zp { +pub enum Fq { #[display("M31", alt = "2^31-1")] M31, // 2^31-1 #[display("F1137119", alt = "1+11*37*2^119")] @@ -42,29 +42,29 @@ pub enum Zp { Other(u128), } -impl From for u128 { - fn from(zp: Zp) -> Self { zp.to_u128() } +impl From for u128 { + fn from(fq: Fq) -> Self { fq.to_u128() } } -impl Zp { +impl Fq { pub fn to_u128(self) -> u128 { match self { - Zp::M31 => M31, - Zp::F1137119 => F1137119, - Zp::F1289 => F1289, - Zp::Other(val) => val, + Fq::M31 => M31, + Fq::F1137119 => F1137119, + Fq::F1289 => F1289, + Fq::Other(val) => val, } } } /// Microcode for finite field arithmetics. impl Core { - pub fn zp(&self) -> Zp { self.zp } - pub fn zp_u128(&self) -> u128 { self.zp.to_u128() } + pub fn fq(&self) -> Fq { self.fq } + pub fn fq_u128(&self) -> u128 { self.fq.to_u128() } #[inline] pub fn add_mod(&mut self, a: u128, b: u128) -> Option { - let order = self.zp.to_u128(); + let order = self.fq.to_u128(); if a >= order || b >= order { return None; } @@ -81,7 +81,7 @@ impl Core { #[inline] pub fn mul_mod(&mut self, a: u128, b: u128) -> Option { - let order = self.zp.to_u128(); + let order = self.fq.to_u128(); if a >= order || b >= order { return None; } @@ -93,7 +93,7 @@ impl Core { } fn mul_mod_int(&mut self, a: u128, b: u128) -> (u128, bool) { - let order = self.zp.to_u128(); + let order = self.fq.to_u128(); let (mut res, overflow) = a.overflowing_mul(b); if overflow { let rem = u128::MAX - order; @@ -104,7 +104,7 @@ impl Core { #[inline] pub fn neg_mod(&self, a: u128) -> Option { - let order = self.zp_u128(); + let order = self.fq.to_u128(); if a >= order { return None; } diff --git a/src/isa/gfa/bytecode.rs b/src/isa/gfa/bytecode.rs index b3262ba..dcbc859 100644 --- a/src/isa/gfa/bytecode.rs +++ b/src/isa/gfa/bytecode.rs @@ -27,13 +27,30 @@ use std::ops::RangeInclusive; use amplify::num::u1; use super::FieldInstr; -use crate::core::SiteId; +use crate::core::{IdxAl, RegA, SiteId, A}; use crate::isa::{Bytecode, BytecodeRead, BytecodeWrite, CodeEofError}; +impl FieldInstr { + const START: u8 = 64; + const END: u8 = Self::START + Self::ADD_MUL; + const INC_MOD: u8 = 0; + const DEC_MOD: u8 = 1; + const NEG_MOD: u8 = 2; + const ADD_MUL: u8 = 3; +} + impl Bytecode for FieldInstr { - fn op_range() -> RangeInclusive { todo!() } + fn op_range() -> RangeInclusive { Self::START..=Self::END } - fn opcode_byte(&self) -> u8 { todo!() } + fn opcode_byte(&self) -> u8 { + Self::START + + match *self { + FieldInstr::IncMod { .. } => Self::INC_MOD, + FieldInstr::DecMod { .. } => Self::DEC_MOD, + FieldInstr::NegMod { .. } => Self::NEG_MOD, + FieldInstr::AddMod { .. } | FieldInstr::MulMod { .. } => Self::ADD_MUL, + } + } fn encode_operands(&self, writer: &mut W) -> Result<(), W::Error> where W: BytecodeWrite { @@ -72,6 +89,34 @@ impl Bytecode for FieldInstr { Self: Sized, R: BytecodeRead, { - todo!() + Ok(match opcode - Self::START { + Self::INC_MOD => { + let src_dst = RegA::from(reader.read_u8()?); + let val = reader.read_u8()?; + FieldInstr::IncMod { src_dst, val } + } + Self::DEC_MOD => { + let src_dst = RegA::from(reader.read_u8()?); + let val = reader.read_u8()?; + FieldInstr::IncMod { src_dst, val } + } + Self::NEG_MOD => { + let src_dst = RegA::from(reader.read_u8()?); + FieldInstr::NegMod { src_dst } + } + Self::ADD_MUL => { + let subop = reader.read_u1()?; + let reg = A::from(reader.read_u3()?); + let dst = IdxAl::from(reader.read_u4()?); + let src1 = IdxAl::from(reader.read_u4()?); + let src2 = IdxAl::from(reader.read_u4()?); + match subop { + u1::ZERO => FieldInstr::AddMod { reg, dst, src1, src2 }, + u1::ONE => FieldInstr::MulMod { reg, dst, src1, src2 }, + _ => unreachable!(), + } + } + _ => unreachable!(), + }) } } diff --git a/src/isa/gfa/exec.rs b/src/isa/gfa/exec.rs index 7268eee..b78b356 100644 --- a/src/isa/gfa/exec.rs +++ b/src/isa/gfa/exec.rs @@ -58,13 +58,58 @@ macro_rules! checked { impl Instruction for FieldInstr { type Context<'ctx> = (); - fn src_regs(&self) -> BTreeSet { todo!() } + fn src_regs(&self) -> BTreeSet { + match *self { + FieldInstr::IncMod { src_dst, val: _ } + | FieldInstr::DecMod { src_dst, val: _ } + | FieldInstr::NegMod { src_dst } => { + bset![src_dst.into()] + } + FieldInstr::AddMod { + reg, + dst, + src1: _, + src2: _, + } + | FieldInstr::MulMod { + reg, + dst, + src1: _, + src2: _, + } => bset![RegA::with(reg, dst.into()).into()], + } + } - fn dst_regs(&self) -> BTreeSet { todo!() } + fn dst_regs(&self) -> BTreeSet { + match *self { + FieldInstr::IncMod { src_dst, val: _ } + | FieldInstr::DecMod { src_dst, val: _ } + | FieldInstr::NegMod { src_dst } => { + bset![src_dst.into()] + } + FieldInstr::AddMod { + reg, + dst: _, + src1, + src2, + } + | FieldInstr::MulMod { + reg, + dst: _, + src1, + src2, + } => bset![RegA::with(reg, src1.into()).into(), RegA::with(reg, src2.into()).into()], + } + } - fn op_data_bytes(&self) -> u16 { todo!() } + fn op_data_bytes(&self) -> u16 { + match self { + FieldInstr::IncMod { .. } | FieldInstr::DecMod { .. } => 1, + FieldInstr::NegMod { .. } | FieldInstr::AddMod { .. } | FieldInstr::MulMod { .. } => 0, + } + } - fn ext_data_bytes(&self) -> u16 { todo!() } + fn ext_data_bytes(&self) -> u16 { 0 } fn complexity(&self) -> u64 { // Double the default complexity since each instruction performs two operations (and each arithmetic