From 3433a073e83182e133354c5a2493d57f353860f0 Mon Sep 17 00:00:00 2001 From: Mansur Mukimbekov Date: Fri, 26 Jun 2026 17:14:46 -0400 Subject: [PATCH 01/10] refactor(rvr-algebra): use common pad_modulus --- extensions/algebra/rvr/src/fp2.rs | 11 ++--------- extensions/algebra/rvr/src/lib.rs | 16 ++++++++++++++++ extensions/algebra/rvr/src/modular.rs | 11 ++--------- 3 files changed, 20 insertions(+), 18 deletions(-) diff --git a/extensions/algebra/rvr/src/fp2.rs b/extensions/algebra/rvr/src/fp2.rs index b3ba48ed4f..821671d855 100644 --- a/extensions/algebra/rvr/src/fp2.rs +++ b/extensions/algebra/rvr/src/fp2.rs @@ -8,7 +8,7 @@ use rvr_openvm_ir::{ExtEmitCtx, ExtInstr, Instr, InstrAt, LiftedInstr, Reg}; use rvr_openvm_lift::{helpers::decode_reg, RvrExtension}; use strum::EnumCount; -use crate::{detect_known_field, format_c_byte_array, ModOp}; +use crate::{detect_known_field, format_c_byte_array, pad_modulus, ModOp}; /// Per-modulus info for the Fp2 extension. Fp2 lifting never consults a /// non-QR, so we only carry the padded modulus and limb count. @@ -22,14 +22,7 @@ fn make_moduli(moduli: Vec) -> Vec { } fn make_modulus_info(modulus: BigUint) -> ModulusInfo { - let bytes = modulus.bits().div_ceil(8) as usize; - assert!( - bytes <= 48, - "modulus exceeds maximum supported size of 384 bits" - ); - let num_limbs = if bytes <= 32 { 32u32 } else { 48u32 }; - let mut modulus_bytes = modulus.to_bytes_le(); - modulus_bytes.resize(num_limbs as usize, 0); + let (modulus_bytes, num_limbs) = pad_modulus(&modulus); ModulusInfo { modulus_bytes, num_limbs, diff --git a/extensions/algebra/rvr/src/lib.rs b/extensions/algebra/rvr/src/lib.rs index d5152b9016..7b7c144a0d 100644 --- a/extensions/algebra/rvr/src/lib.rs +++ b/extensions/algebra/rvr/src/lib.rs @@ -13,6 +13,22 @@ pub use fp2::{Fp2ArithInstr, Fp2RvrExtension, Fp2SetupInstr}; pub use modular::{ HintNonQrInstr, HintSqrtInstr, ModArithInstr, ModIsEqInstr, ModSetupInstr, ModularRvrExtension, }; +use num_bigint::BigUint; + +/// Zero-pad `modulus` to the canonical limb boundary (32 or 48 bytes). +/// +/// Returns `(padded_bytes_le, num_limbs)`. Panics if the modulus exceeds 384 bits. +fn pad_modulus(modulus: &BigUint) -> (Vec, u32) { + let bytes = modulus.bits().div_ceil(8) as usize; + assert!( + bytes <= 48, + "modulus exceeds maximum supported size of 384 bits" + ); + let num_limbs = if bytes <= 32 { 32u32 } else { 48u32 }; + let mut modulus_bytes = modulus.to_bytes_le(); + modulus_bytes.resize(num_limbs as usize, 0); + (modulus_bytes, num_limbs) +} // ── Modular arithmetic operations ──────────────────────────────────────────── diff --git a/extensions/algebra/rvr/src/modular.rs b/extensions/algebra/rvr/src/modular.rs index db92684485..67ebc2a0ff 100644 --- a/extensions/algebra/rvr/src/modular.rs +++ b/extensions/algebra/rvr/src/modular.rs @@ -11,7 +11,7 @@ use rvr_openvm_ir::{ExtEmitCtx, ExtInstr, Instr, InstrAt, LiftedInstr, Reg}; use rvr_openvm_lift::{helpers::decode_reg, RvrExtension}; use strum::EnumCount; -use crate::{detect_known_field, format_c_byte_array, ModOp}; +use crate::{detect_known_field, format_c_byte_array, pad_modulus, ModOp}; include!(concat!(env!("OUT_DIR"), "/secp256k1_files.rs")); @@ -35,14 +35,7 @@ fn make_moduli(moduli: Vec) -> Vec { } fn make_modulus_info(modulus: &BigUint, rng: &mut StdRng) -> ModulusInfo { - let bytes = modulus.bits().div_ceil(8) as usize; - assert!( - bytes <= 48, - "modulus exceeds maximum supported size of 384 bits" - ); - let num_limbs = if bytes <= 32 { 32u32 } else { 48u32 }; - let mut modulus_bytes = modulus.to_bytes_le(); - modulus_bytes.resize(num_limbs as usize, 0); + let (modulus_bytes, num_limbs) = pad_modulus(modulus); let non_qr = find_non_qr(modulus, rng); let mut non_qr_bytes = non_qr.to_bytes_le(); non_qr_bytes.resize(num_limbs as usize, 0); From 0a3598a159305c5e50528c774c2efd9ac212f71f Mon Sep 17 00:00:00 2001 From: Mansur Mukimbekov Date: Fri, 26 Jun 2026 17:59:48 -0400 Subject: [PATCH 02/10] refactor(rvr-algebra): create common IR and trates for modular and fp2 --- extensions/algebra/rvr/src/field_arith.rs | 77 +++++++++++++ extensions/algebra/rvr/src/fp2.rs | 93 ++++------------ extensions/algebra/rvr/src/lib.rs | 12 ++- extensions/algebra/rvr/src/modular.rs | 125 +++++----------------- 4 files changed, 137 insertions(+), 170 deletions(-) create mode 100644 extensions/algebra/rvr/src/field_arith.rs diff --git a/extensions/algebra/rvr/src/field_arith.rs b/extensions/algebra/rvr/src/field_arith.rs new file mode 100644 index 0000000000..d3cc5d36bd --- /dev/null +++ b/extensions/algebra/rvr/src/field_arith.rs @@ -0,0 +1,77 @@ +use std::{fmt::Debug, marker::PhantomData}; + +use rvr_openvm_ir::{ExtEmitCtx, ExtInstr, Reg}; + +use crate::{detect_known_field, format_c_byte_array, KnownField, ModOp}; + +/// Marker trait implemented by zero-sized types that parameterize +/// [`FieldArithInstr`], supplying the C prefix and known-field suffix lookup. +pub trait ArithKind: Clone + Debug + Send + Sync + 'static { + fn opname() -> &'static str; + fn c_prefix() -> &'static str; + /// Returns the C suffix for a known field, or `None` to fall through to the + /// generic path. + fn known_suffix(field: KnownField) -> Option<&'static str>; +} + +/// Generic IR node for field arithmetic (ADD, SUB, MUL, DIV). +/// Use the type aliases [`crate::ModArithInstr`] / [`crate::Fp2ArithInstr`] +/// rather than naming this type directly. +#[derive(Debug, Clone)] +pub struct FieldArithInstr { + _kind: PhantomData, + pub op: ModOp, + pub rd_reg: Reg, + pub rs1_reg: Reg, + pub rs2_reg: Reg, + pub num_limbs: u32, + pub modulus: Vec, +} + +impl FieldArithInstr { + pub fn new( + op: ModOp, + rd_reg: Reg, + rs1_reg: Reg, + rs2_reg: Reg, + num_limbs: u32, + modulus: Vec, + ) -> Self { + Self { _kind: PhantomData, op, rd_reg, rs1_reg, rs2_reg, num_limbs, modulus } + } +} + +impl ExtInstr for FieldArithInstr { + fn opname(&self) -> &str { + K::opname() + } + + fn emit_c(&self, ctx: &mut dyn ExtEmitCtx) { + let rd = ctx.read_reg(self.rd_reg); + let rs1 = ctx.read_reg(self.rs1_reg); + let rs2 = ctx.read_reg(self.rs2_reg); + let op_name = self.op.c_name(); + let prefix = K::c_prefix(); + let known_suffix = detect_known_field(&self.modulus).and_then(K::known_suffix); + if let Some(suffix) = known_suffix { + let name = format!("rvr_ext_{prefix}_{op_name}_{suffix}"); + ctx.extern_call(&name, &["state", &rd, &rs1, &rs2]); + } else { + let mod_literal = format_c_byte_array(&self.modulus); + ctx.write_line("{"); + ctx.write_line(&format!("static const uint8_t mod_[] = {mod_literal};")); + let name = format!("rvr_ext_{prefix}_{op_name}"); + let num_limbs = format!("{}u", self.num_limbs); + ctx.extern_call(&name, &["state", &rd, &rs1, &rs2, &num_limbs, "mod_"]); + ctx.write_line("}"); + } + } + + fn clone_box(&self) -> Box { + Box::new(self.clone()) + } + + fn is_block_end(&self) -> bool { + false + } +} diff --git a/extensions/algebra/rvr/src/fp2.rs b/extensions/algebra/rvr/src/fp2.rs index 821671d855..e872612a17 100644 --- a/extensions/algebra/rvr/src/fp2.rs +++ b/extensions/algebra/rvr/src/fp2.rs @@ -8,7 +8,7 @@ use rvr_openvm_ir::{ExtEmitCtx, ExtInstr, Instr, InstrAt, LiftedInstr, Reg}; use rvr_openvm_lift::{helpers::decode_reg, RvrExtension}; use strum::EnumCount; -use crate::{detect_known_field, format_c_byte_array, pad_modulus, ModOp}; +use crate::{pad_modulus, ArithKind, FieldArithInstr, KnownField, ModOp}; /// Per-modulus info for the Fp2 extension. Fp2 lifting never consults a /// non-QR, so we only carry the padded modulus and limb count. @@ -31,51 +31,24 @@ fn make_modulus_info(modulus: BigUint) -> ModulusInfo { // ── Fp2 arithmetic IR ──────────────────────────────────────────────────────── -/// IR node for Fp2 arithmetic (ADD, SUB, MUL, DIV). #[derive(Debug, Clone)] -pub struct Fp2ArithInstr { - pub op: ModOp, - pub rd_reg: Reg, - pub rs1_reg: Reg, - pub rs2_reg: Reg, - pub num_limbs: u32, - pub modulus: Vec, -} +pub struct Fp2ArithKind; -impl ExtInstr for Fp2ArithInstr { - fn opname(&self) -> &str { +impl ArithKind for Fp2ArithKind { + fn opname() -> &'static str { "fp2_arith" } - - fn emit_c(&self, ctx: &mut dyn ExtEmitCtx) { - let rd = ctx.read_reg(self.rd_reg); - let rs1 = ctx.read_reg(self.rs1_reg); - let rs2 = ctx.read_reg(self.rs2_reg); - let op_name = self.op.c_name(); - let fp2_suffix = detect_known_field(&self.modulus).and_then(|f| f.fp2_c_suffix()); - if let Some(suffix) = fp2_suffix { - let name = format!("rvr_ext_fp2_{op_name}_{suffix}"); - ctx.extern_call(&name, &["state", &rd, &rs1, &rs2]); - } else { - let mod_literal = format_c_byte_array(&self.modulus); - ctx.write_line("{"); - ctx.write_line(&format!("static const uint8_t mod_[] = {mod_literal};")); - let name = format!("rvr_ext_fp2_{op_name}"); - let num_limbs = format!("{}u", self.num_limbs); - ctx.extern_call(&name, &["state", &rd, &rs1, &rs2, &num_limbs, "mod_"]); - ctx.write_line("}"); - } - } - - fn clone_box(&self) -> Box { - Box::new(self.clone()) + fn c_prefix() -> &'static str { + "fp2" } - - fn is_block_end(&self) -> bool { - false + fn known_suffix(field: KnownField) -> Option<&'static str> { + field.fp2_c_suffix() } } +/// IR node for Fp2 arithmetic (ADD, SUB, MUL, DIV). +pub type Fp2ArithInstr = FieldArithInstr; + /// IR node for Fp2 SETUP (SETUP_ADDSUB, SETUP_MULDIV). #[derive(Debug, Clone)] pub struct Fp2SetupInstr { @@ -169,44 +142,24 @@ impl Fp2RvrExtension { let rs2_reg = decode_reg(insn.c); let instr: Instr = match local { - x if x == Fp2Opcode::ADD as usize => Instr::Ext(Box::new(Fp2ArithInstr { - op: ModOp::Add, - rd_reg, - rs1_reg, - rs2_reg, - num_limbs: info.num_limbs, - modulus: info.modulus_bytes.clone(), - })), - x if x == Fp2Opcode::SUB as usize => Instr::Ext(Box::new(Fp2ArithInstr { - op: ModOp::Sub, - rd_reg, - rs1_reg, - rs2_reg, - num_limbs: info.num_limbs, - modulus: info.modulus_bytes.clone(), - })), + x if x == Fp2Opcode::ADD as usize => Instr::Ext(Box::new( + Fp2ArithInstr::new(ModOp::Add, rd_reg, rs1_reg, rs2_reg, info.num_limbs, info.modulus_bytes.clone()), + )), + x if x == Fp2Opcode::SUB as usize => Instr::Ext(Box::new( + Fp2ArithInstr::new(ModOp::Sub, rd_reg, rs1_reg, rs2_reg, info.num_limbs, info.modulus_bytes.clone()), + )), x if x == Fp2Opcode::SETUP_ADDSUB as usize => Instr::Ext(Box::new(Fp2SetupInstr { rd_reg, rs1_reg, rs2_reg, num_limbs: info.num_limbs, })), - x if x == Fp2Opcode::MUL as usize => Instr::Ext(Box::new(Fp2ArithInstr { - op: ModOp::Mul, - rd_reg, - rs1_reg, - rs2_reg, - num_limbs: info.num_limbs, - modulus: info.modulus_bytes.clone(), - })), - x if x == Fp2Opcode::DIV as usize => Instr::Ext(Box::new(Fp2ArithInstr { - op: ModOp::Div, - rd_reg, - rs1_reg, - rs2_reg, - num_limbs: info.num_limbs, - modulus: info.modulus_bytes.clone(), - })), + x if x == Fp2Opcode::MUL as usize => Instr::Ext(Box::new( + Fp2ArithInstr::new(ModOp::Mul, rd_reg, rs1_reg, rs2_reg, info.num_limbs, info.modulus_bytes.clone()), + )), + x if x == Fp2Opcode::DIV as usize => Instr::Ext(Box::new( + Fp2ArithInstr::new(ModOp::Div, rd_reg, rs1_reg, rs2_reg, info.num_limbs, info.modulus_bytes.clone()), + )), x if x == Fp2Opcode::SETUP_MULDIV as usize => Instr::Ext(Box::new(Fp2SetupInstr { rd_reg, rs1_reg, diff --git a/extensions/algebra/rvr/src/lib.rs b/extensions/algebra/rvr/src/lib.rs index 7b7c144a0d..3dd4b724e1 100644 --- a/extensions/algebra/rvr/src/lib.rs +++ b/extensions/algebra/rvr/src/lib.rs @@ -6,14 +6,18 @@ //! (modular + phantoms; ships the lift-time C and libsecp256k1 inputs for //! k256) and [`Fp2RvrExtension`] (fp2 ops only; Rust-only). +mod field_arith; mod fp2; mod modular; -pub use fp2::{Fp2ArithInstr, Fp2RvrExtension, Fp2SetupInstr}; +use num_bigint::BigUint; + +pub use field_arith::{ArithKind, FieldArithInstr}; +pub use fp2::{Fp2ArithInstr, Fp2ArithKind, Fp2RvrExtension, Fp2SetupInstr}; pub use modular::{ - HintNonQrInstr, HintSqrtInstr, ModArithInstr, ModIsEqInstr, ModSetupInstr, ModularRvrExtension, + HintNonQrInstr, HintSqrtInstr, ModArithInstr, ModArithKind, ModIsEqInstr, ModSetupInstr, + ModularRvrExtension, }; -use num_bigint::BigUint; /// Zero-pad `modulus` to the canonical limb boundary (32 or 48 bytes). /// @@ -58,7 +62,7 @@ impl ModOp { /// Known field types that have optimized native FFI implementations. #[derive(Debug, Clone, Copy)] -pub(crate) enum KnownField { +pub enum KnownField { K256Coord, K256Scalar, P256Coord, diff --git a/extensions/algebra/rvr/src/modular.rs b/extensions/algebra/rvr/src/modular.rs index 67ebc2a0ff..6d405d21c1 100644 --- a/extensions/algebra/rvr/src/modular.rs +++ b/extensions/algebra/rvr/src/modular.rs @@ -11,7 +11,10 @@ use rvr_openvm_ir::{ExtEmitCtx, ExtInstr, Instr, InstrAt, LiftedInstr, Reg}; use rvr_openvm_lift::{helpers::decode_reg, RvrExtension}; use strum::EnumCount; -use crate::{detect_known_field, format_c_byte_array, pad_modulus, ModOp}; +use crate::{ + detect_known_field, format_c_byte_array, pad_modulus, ArithKind, FieldArithInstr, KnownField, + ModOp, +}; include!(concat!(env!("OUT_DIR"), "/secp256k1_files.rs")); @@ -48,51 +51,24 @@ fn make_modulus_info(modulus: &BigUint, rng: &mut StdRng) -> ModulusInfo { // ── Modular arithmetic IR ──────────────────────────────────────────────────── -/// IR node for modular arithmetic (ADD, SUB, MUL, DIV). #[derive(Debug, Clone)] -pub struct ModArithInstr { - pub op: ModOp, - pub rd_reg: Reg, - pub rs1_reg: Reg, - pub rs2_reg: Reg, - pub num_limbs: u32, - pub modulus: Vec, -} +pub struct ModArithKind; -impl ExtInstr for ModArithInstr { - fn opname(&self) -> &str { +impl ArithKind for ModArithKind { + fn opname() -> &'static str { "mod_arith" } - - fn emit_c(&self, ctx: &mut dyn ExtEmitCtx) { - let rd = ctx.read_reg(self.rd_reg); - let rs1 = ctx.read_reg(self.rs1_reg); - let rs2 = ctx.read_reg(self.rs2_reg); - let op_name = self.op.c_name(); - if let Some(field) = detect_known_field(&self.modulus) { - let suffix = field.c_suffix(); - let name = format!("rvr_ext_mod_{op_name}_{suffix}"); - ctx.extern_call(&name, &["state", &rd, &rs1, &rs2]); - } else { - let mod_literal = format_c_byte_array(&self.modulus); - ctx.write_line("{"); - ctx.write_line(&format!("static const uint8_t mod_[] = {mod_literal};")); - let name = format!("rvr_ext_mod_{op_name}"); - let num_limbs = format!("{}u", self.num_limbs); - ctx.extern_call(&name, &["state", &rd, &rs1, &rs2, &num_limbs, "mod_"]); - ctx.write_line("}"); - } + fn c_prefix() -> &'static str { + "mod" } - - fn clone_box(&self) -> Box { - Box::new(self.clone()) - } - - fn is_block_end(&self) -> bool { - false + fn known_suffix(field: KnownField) -> Option<&'static str> { + Some(field.c_suffix()) } } +/// IR node for modular arithmetic (ADD, SUB, MUL, DIV). +pub type ModArithInstr = FieldArithInstr; + /// IR node for modular IS_EQ. #[derive(Debug, Clone)] pub struct ModIsEqInstr { @@ -358,61 +334,23 @@ impl ModularRvrExtension { let rs2_reg = decode_reg(insn.c); let instr: Instr = match local { - x if x == Rv64ModularArithmeticOpcode::ADD as usize => { - Instr::Ext(Box::new(ModArithInstr { - op: ModOp::Add, - rd_reg, - rs1_reg, - rs2_reg, - num_limbs: info.num_limbs, - modulus: info.modulus_bytes.clone(), - })) - } - x if x == Rv64ModularArithmeticOpcode::SUB as usize => { - Instr::Ext(Box::new(ModArithInstr { - op: ModOp::Sub, - rd_reg, - rs1_reg, - rs2_reg, - num_limbs: info.num_limbs, - modulus: info.modulus_bytes.clone(), - })) - } + x if x == Rv64ModularArithmeticOpcode::ADD as usize => Instr::Ext(Box::new( + ModArithInstr::new(ModOp::Add, rd_reg, rs1_reg, rs2_reg, info.num_limbs, info.modulus_bytes.clone()), + )), + x if x == Rv64ModularArithmeticOpcode::SUB as usize => Instr::Ext(Box::new( + ModArithInstr::new(ModOp::Sub, rd_reg, rs1_reg, rs2_reg, info.num_limbs, info.modulus_bytes.clone()), + )), x if x == Rv64ModularArithmeticOpcode::SETUP_ADDSUB as usize => { - Instr::Ext(Box::new(ModSetupInstr { - rd_reg, - rs1_reg, - rs2_reg, - num_limbs: info.num_limbs, - })) - } - x if x == Rv64ModularArithmeticOpcode::MUL as usize => { - Instr::Ext(Box::new(ModArithInstr { - op: ModOp::Mul, - rd_reg, - rs1_reg, - rs2_reg, - num_limbs: info.num_limbs, - modulus: info.modulus_bytes.clone(), - })) - } - x if x == Rv64ModularArithmeticOpcode::DIV as usize => { - Instr::Ext(Box::new(ModArithInstr { - op: ModOp::Div, - rd_reg, - rs1_reg, - rs2_reg, - num_limbs: info.num_limbs, - modulus: info.modulus_bytes.clone(), - })) + Instr::Ext(Box::new(ModSetupInstr { rd_reg, rs1_reg, rs2_reg, num_limbs: info.num_limbs })) } + x if x == Rv64ModularArithmeticOpcode::MUL as usize => Instr::Ext(Box::new( + ModArithInstr::new(ModOp::Mul, rd_reg, rs1_reg, rs2_reg, info.num_limbs, info.modulus_bytes.clone()), + )), + x if x == Rv64ModularArithmeticOpcode::DIV as usize => Instr::Ext(Box::new( + ModArithInstr::new(ModOp::Div, rd_reg, rs1_reg, rs2_reg, info.num_limbs, info.modulus_bytes.clone()), + )), x if x == Rv64ModularArithmeticOpcode::SETUP_MULDIV as usize => { - Instr::Ext(Box::new(ModSetupInstr { - rd_reg, - rs1_reg, - rs2_reg, - num_limbs: info.num_limbs, - })) + Instr::Ext(Box::new(ModSetupInstr { rd_reg, rs1_reg, rs2_reg, num_limbs: info.num_limbs })) } x if x == Rv64ModularArithmeticOpcode::IS_EQ as usize => { Instr::Ext(Box::new(ModIsEqInstr { @@ -424,12 +362,7 @@ impl ModularRvrExtension { })) } x if x == Rv64ModularArithmeticOpcode::SETUP_ISEQ as usize => { - Instr::Ext(Box::new(ModSetupInstr { - rd_reg, - rs1_reg, - rs2_reg, - num_limbs: info.num_limbs, - })) + Instr::Ext(Box::new(ModSetupInstr { rd_reg, rs1_reg, rs2_reg, num_limbs: info.num_limbs })) } _ => return None, }; From c4ef1103c26be4406b606f258acd12a44bfe1e1d Mon Sep 17 00:00:00 2001 From: Mansur Mukimbekov Date: Mon, 29 Jun 2026 11:11:16 -0400 Subject: [PATCH 03/10] refactor(rvr-algebra): common IR for setup instructions --- .../rvr/src/{field_arith.rs => common.rs} | 40 ++++++++++++++++ extensions/algebra/rvr/src/fp2.rs | 48 ++----------------- extensions/algebra/rvr/src/lib.rs | 4 +- extensions/algebra/rvr/src/modular.rs | 40 +++------------- 4 files changed, 53 insertions(+), 79 deletions(-) rename extensions/algebra/rvr/src/{field_arith.rs => common.rs} (69%) diff --git a/extensions/algebra/rvr/src/field_arith.rs b/extensions/algebra/rvr/src/common.rs similarity index 69% rename from extensions/algebra/rvr/src/field_arith.rs rename to extensions/algebra/rvr/src/common.rs index d3cc5d36bd..420bcc67e7 100644 --- a/extensions/algebra/rvr/src/field_arith.rs +++ b/extensions/algebra/rvr/src/common.rs @@ -41,6 +41,46 @@ impl FieldArithInstr { } } +/// Generic IR node for field setup (SETUP_ADDSUB, SETUP_MULDIV, SETUP_ISEQ). +/// Use the type aliases [`crate::ModSetupInstr`] / [`crate::Fp2SetupInstr`] +/// rather than naming this type directly. +#[derive(Debug, Clone)] +pub struct FieldSetupInstr { + c_fn: &'static str, + pub rd_reg: Reg, + pub rs1_reg: Reg, + pub rs2_reg: Reg, + pub num_limbs: u32, +} + +impl FieldSetupInstr { + pub fn new(c_fn: &'static str, rd_reg: Reg, rs1_reg: Reg, rs2_reg: Reg, num_limbs: u32) -> Self { + Self { c_fn, rd_reg, rs1_reg, rs2_reg, num_limbs } + } +} + +impl ExtInstr for FieldSetupInstr { + fn opname(&self) -> &str { + &self.c_fn["rvr_ext_".len()..] + } + + fn emit_c(&self, ctx: &mut dyn ExtEmitCtx) { + let rd = ctx.read_reg(self.rd_reg); + let rs1 = ctx.read_reg(self.rs1_reg); + let rs2 = ctx.read_reg(self.rs2_reg); + let num_limbs = format!("{}u", self.num_limbs); + ctx.extern_call(self.c_fn, &["state", &rd, &rs1, &rs2, &num_limbs]); + } + + fn clone_box(&self) -> Box { + Box::new(self.clone()) + } + + fn is_block_end(&self) -> bool { + false + } +} + impl ExtInstr for FieldArithInstr { fn opname(&self) -> &str { K::opname() diff --git a/extensions/algebra/rvr/src/fp2.rs b/extensions/algebra/rvr/src/fp2.rs index e872612a17..5fc7d1d3fb 100644 --- a/extensions/algebra/rvr/src/fp2.rs +++ b/extensions/algebra/rvr/src/fp2.rs @@ -4,11 +4,11 @@ use num_bigint::BigUint; use openvm_algebra_transpiler::Fp2Opcode; use openvm_instructions::{instruction::Instruction, LocalOpcode}; use openvm_stark_backend::p3_field::PrimeField32; -use rvr_openvm_ir::{ExtEmitCtx, ExtInstr, Instr, InstrAt, LiftedInstr, Reg}; +use rvr_openvm_ir::{Instr, InstrAt, LiftedInstr}; use rvr_openvm_lift::{helpers::decode_reg, RvrExtension}; use strum::EnumCount; -use crate::{pad_modulus, ArithKind, FieldArithInstr, KnownField, ModOp}; +use crate::{pad_modulus, ArithKind, FieldArithInstr, FieldSetupInstr, KnownField, ModOp}; /// Per-modulus info for the Fp2 extension. Fp2 lifting never consults a /// non-QR, so we only carry the padded modulus and limb count. @@ -50,35 +50,7 @@ impl ArithKind for Fp2ArithKind { pub type Fp2ArithInstr = FieldArithInstr; /// IR node for Fp2 SETUP (SETUP_ADDSUB, SETUP_MULDIV). -#[derive(Debug, Clone)] -pub struct Fp2SetupInstr { - pub rd_reg: Reg, - pub rs1_reg: Reg, - pub rs2_reg: Reg, - pub num_limbs: u32, -} - -impl ExtInstr for Fp2SetupInstr { - fn opname(&self) -> &str { - "fp2_setup" - } - - fn emit_c(&self, ctx: &mut dyn ExtEmitCtx) { - let rd = ctx.read_reg(self.rd_reg); - let rs1 = ctx.read_reg(self.rs1_reg); - let rs2 = ctx.read_reg(self.rs2_reg); - let num_limbs = format!("{}u", self.num_limbs); - ctx.extern_call("rvr_ext_fp2_setup", &["state", &rd, &rs1, &rs2, &num_limbs]); - } - - fn clone_box(&self) -> Box { - Box::new(self.clone()) - } - - fn is_block_end(&self) -> bool { - false - } -} +pub type Fp2SetupInstr = FieldSetupInstr; // ── Fp2 extension ──────────────────────────────────────────────────────────── @@ -148,24 +120,14 @@ impl Fp2RvrExtension { x if x == Fp2Opcode::SUB as usize => Instr::Ext(Box::new( Fp2ArithInstr::new(ModOp::Sub, rd_reg, rs1_reg, rs2_reg, info.num_limbs, info.modulus_bytes.clone()), )), - x if x == Fp2Opcode::SETUP_ADDSUB as usize => Instr::Ext(Box::new(Fp2SetupInstr { - rd_reg, - rs1_reg, - rs2_reg, - num_limbs: info.num_limbs, - })), + x if x == Fp2Opcode::SETUP_ADDSUB as usize => Instr::Ext(Box::new(Fp2SetupInstr::new("rvr_ext_fp2_setup", rd_reg, rs1_reg, rs2_reg, info.num_limbs))), x if x == Fp2Opcode::MUL as usize => Instr::Ext(Box::new( Fp2ArithInstr::new(ModOp::Mul, rd_reg, rs1_reg, rs2_reg, info.num_limbs, info.modulus_bytes.clone()), )), x if x == Fp2Opcode::DIV as usize => Instr::Ext(Box::new( Fp2ArithInstr::new(ModOp::Div, rd_reg, rs1_reg, rs2_reg, info.num_limbs, info.modulus_bytes.clone()), )), - x if x == Fp2Opcode::SETUP_MULDIV as usize => Instr::Ext(Box::new(Fp2SetupInstr { - rd_reg, - rs1_reg, - rs2_reg, - num_limbs: info.num_limbs, - })), + x if x == Fp2Opcode::SETUP_MULDIV as usize => Instr::Ext(Box::new(Fp2SetupInstr::new("rvr_ext_fp2_setup", rd_reg, rs1_reg, rs2_reg, info.num_limbs))), _ => return None, }; diff --git a/extensions/algebra/rvr/src/lib.rs b/extensions/algebra/rvr/src/lib.rs index 3dd4b724e1..e8c63fbaec 100644 --- a/extensions/algebra/rvr/src/lib.rs +++ b/extensions/algebra/rvr/src/lib.rs @@ -6,13 +6,13 @@ //! (modular + phantoms; ships the lift-time C and libsecp256k1 inputs for //! k256) and [`Fp2RvrExtension`] (fp2 ops only; Rust-only). -mod field_arith; +mod common; mod fp2; mod modular; use num_bigint::BigUint; -pub use field_arith::{ArithKind, FieldArithInstr}; +pub use common::{ArithKind, FieldArithInstr, FieldSetupInstr}; pub use fp2::{Fp2ArithInstr, Fp2ArithKind, Fp2RvrExtension, Fp2SetupInstr}; pub use modular::{ HintNonQrInstr, HintSqrtInstr, ModArithInstr, ModArithKind, ModIsEqInstr, ModSetupInstr, diff --git a/extensions/algebra/rvr/src/modular.rs b/extensions/algebra/rvr/src/modular.rs index 6d405d21c1..9b76f028fc 100644 --- a/extensions/algebra/rvr/src/modular.rs +++ b/extensions/algebra/rvr/src/modular.rs @@ -12,8 +12,8 @@ use rvr_openvm_lift::{helpers::decode_reg, RvrExtension}; use strum::EnumCount; use crate::{ - detect_known_field, format_c_byte_array, pad_modulus, ArithKind, FieldArithInstr, KnownField, - ModOp, + detect_known_field, format_c_byte_array, pad_modulus, ArithKind, FieldArithInstr, + FieldSetupInstr, KnownField, ModOp, }; include!(concat!(env!("OUT_DIR"), "/secp256k1_files.rs")); @@ -117,35 +117,7 @@ impl ExtInstr for ModIsEqInstr { } /// IR node for modular SETUP (SETUP_ADDSUB, SETUP_MULDIV, SETUP_ISEQ). -#[derive(Debug, Clone)] -pub struct ModSetupInstr { - pub rd_reg: Reg, - pub rs1_reg: Reg, - pub rs2_reg: Reg, - pub num_limbs: u32, -} - -impl ExtInstr for ModSetupInstr { - fn opname(&self) -> &str { - "mod_setup" - } - - fn emit_c(&self, ctx: &mut dyn ExtEmitCtx) { - let rd = ctx.read_reg(self.rd_reg); - let rs1 = ctx.read_reg(self.rs1_reg); - let rs2 = ctx.read_reg(self.rs2_reg); - let num_limbs = format!("{}u", self.num_limbs); - ctx.extern_call("rvr_ext_mod_setup", &["state", &rd, &rs1, &rs2, &num_limbs]); - } - - fn clone_box(&self) -> Box { - Box::new(self.clone()) - } - - fn is_block_end(&self) -> bool { - false - } -} +pub type ModSetupInstr = FieldSetupInstr; // ── Phantom instructions (HintNonQr, HintSqrt) ────────────────────────────── @@ -341,7 +313,7 @@ impl ModularRvrExtension { ModArithInstr::new(ModOp::Sub, rd_reg, rs1_reg, rs2_reg, info.num_limbs, info.modulus_bytes.clone()), )), x if x == Rv64ModularArithmeticOpcode::SETUP_ADDSUB as usize => { - Instr::Ext(Box::new(ModSetupInstr { rd_reg, rs1_reg, rs2_reg, num_limbs: info.num_limbs })) + Instr::Ext(Box::new(ModSetupInstr::new("rvr_ext_mod_setup", rd_reg, rs1_reg, rs2_reg, info.num_limbs))) } x if x == Rv64ModularArithmeticOpcode::MUL as usize => Instr::Ext(Box::new( ModArithInstr::new(ModOp::Mul, rd_reg, rs1_reg, rs2_reg, info.num_limbs, info.modulus_bytes.clone()), @@ -350,7 +322,7 @@ impl ModularRvrExtension { ModArithInstr::new(ModOp::Div, rd_reg, rs1_reg, rs2_reg, info.num_limbs, info.modulus_bytes.clone()), )), x if x == Rv64ModularArithmeticOpcode::SETUP_MULDIV as usize => { - Instr::Ext(Box::new(ModSetupInstr { rd_reg, rs1_reg, rs2_reg, num_limbs: info.num_limbs })) + Instr::Ext(Box::new(ModSetupInstr::new("rvr_ext_mod_setup", rd_reg, rs1_reg, rs2_reg, info.num_limbs))) } x if x == Rv64ModularArithmeticOpcode::IS_EQ as usize => { Instr::Ext(Box::new(ModIsEqInstr { @@ -362,7 +334,7 @@ impl ModularRvrExtension { })) } x if x == Rv64ModularArithmeticOpcode::SETUP_ISEQ as usize => { - Instr::Ext(Box::new(ModSetupInstr { rd_reg, rs1_reg, rs2_reg, num_limbs: info.num_limbs })) + Instr::Ext(Box::new(ModSetupInstr::new("rvr_ext_mod_setup", rd_reg, rs1_reg, rs2_reg, info.num_limbs))) } _ => return None, }; From 9eeb13f16b0ec82dc08e47a5eb8aa01e374f5e71 Mon Sep 17 00:00:00 2001 From: Mansur Mukimbekov Date: Mon, 29 Jun 2026 11:21:21 -0400 Subject: [PATCH 04/10] refactor(rvr-algebra): make common macro for unknown field op in ffi --- extensions/algebra/rvr/ffi/common/src/lib.rs | 30 +++++++++++++++++++ extensions/algebra/rvr/ffi/fp2/src/lib.rs | 30 ++++--------------- extensions/algebra/rvr/ffi/modular/src/lib.rs | 30 ++++--------------- 3 files changed, 40 insertions(+), 50 deletions(-) diff --git a/extensions/algebra/rvr/ffi/common/src/lib.rs b/extensions/algebra/rvr/ffi/common/src/lib.rs index d1e5e5e309..ae7d9684b9 100644 --- a/extensions/algebra/rvr/ffi/common/src/lib.rs +++ b/extensions/algebra/rvr/ffi/common/src/lib.rs @@ -182,6 +182,36 @@ pub fn mod_inverse(a: &BigUint, p: &BigUint) -> BigUint { .unwrap() } +// ── FFI generation macro (shared by modular and fp2) ──────────────────────── + +/// Generate a generic (unknown-modulus) field-op FFI function. +/// +/// `$field_ty` must be a struct with `modulus: BigUint` and `num_limbs: u32` +/// fields that implements [`FieldArith`]. +#[macro_export] +macro_rules! unknown_field_op_fn { + ($name:ident, $field_ty:ident, $op:ident) => { + /// # Safety + /// `state` must be a valid `RvState` pointer. `modulus_ptr` must point + /// to `num_limbs` bytes. + #[no_mangle] + pub unsafe extern "C" fn $name( + state: *mut ::std::ffi::c_void, + rd_ptr: u32, + rs1_ptr: u32, + rs2_ptr: u32, + num_limbs: u32, + modulus_ptr: *const u8, + ) { + let modulus = ::num_bigint::BigUint::from_bytes_le( + ::std::slice::from_raw_parts(modulus_ptr, num_limbs as usize), + ); + let f = $field_ty { modulus, num_limbs }; + $crate::exec_op(&f, state, rd_ptr, rs1_ptr, rs2_ptr, |f, a, b| f.$op(a, b)); + } + }; +} + // ── Instruction execution helper ───────────────────────────────────────────── /// Read both operands, apply `op`, write the result. diff --git a/extensions/algebra/rvr/ffi/fp2/src/lib.rs b/extensions/algebra/rvr/ffi/fp2/src/lib.rs index 857cc4d5c2..cdb9973d78 100644 --- a/extensions/algebra/rvr/ffi/fp2/src/lib.rs +++ b/extensions/algebra/rvr/ffi/fp2/src/lib.rs @@ -190,32 +190,12 @@ define_fp2_ffi!(blstrs::Fp2, bls12_381); // ── Generic FFI (fallback for unknown moduli) ──────────────────────────────── -macro_rules! unknown_field_op_fn { - ($name:ident, $op:ident) => { - /// # Safety - /// `state` must be a valid `RvState` pointer. `modulus_ptr` must point - /// to `num_limbs` bytes. - #[no_mangle] - pub unsafe extern "C" fn $name( - state: *mut c_void, - rd_ptr: u32, - rs1_ptr: u32, - rs2_ptr: u32, - num_limbs: u32, - modulus_ptr: *const u8, - ) { - let modulus = - BigUint::from_bytes_le(std::slice::from_raw_parts(modulus_ptr, num_limbs as usize)); - let f = UnknownComplexField { modulus, num_limbs }; - exec_op(&f, state, rd_ptr, rs1_ptr, rs2_ptr, |f, a, b| f.$op(a, b)); - } - }; -} +use rvr_openvm_ext_algebra_ffi_common::unknown_field_op_fn; -unknown_field_op_fn!(rvr_ext_fp2_add, add); -unknown_field_op_fn!(rvr_ext_fp2_sub, sub); -unknown_field_op_fn!(rvr_ext_fp2_mul, mul); -unknown_field_op_fn!(rvr_ext_fp2_div, div); +unknown_field_op_fn!(rvr_ext_fp2_add, UnknownComplexField, add); +unknown_field_op_fn!(rvr_ext_fp2_sub, UnknownComplexField, sub); +unknown_field_op_fn!(rvr_ext_fp2_mul, UnknownComplexField, mul); +unknown_field_op_fn!(rvr_ext_fp2_div, UnknownComplexField, div); /// # Safety /// `state` must be a valid `RvState` pointer. diff --git a/extensions/algebra/rvr/ffi/modular/src/lib.rs b/extensions/algebra/rvr/ffi/modular/src/lib.rs index 44f5a18f9b..f76f4d9312 100644 --- a/extensions/algebra/rvr/ffi/modular/src/lib.rs +++ b/extensions/algebra/rvr/ffi/modular/src/lib.rs @@ -203,32 +203,12 @@ define_mod_ffi!(halo2curves_axiom::bls12_381::Fr, bls12_381_fr); // ── Generic FFI (fallback for unknown moduli) ──────────────────────────────── -macro_rules! unknown_field_op_fn { - ($name:ident, $op:ident) => { - /// # Safety - /// `state` must be a valid `RvState` pointer. `modulus_ptr` must point - /// to `num_limbs` bytes. - #[no_mangle] - pub unsafe extern "C" fn $name( - state: *mut c_void, - rd_ptr: u32, - rs1_ptr: u32, - rs2_ptr: u32, - num_limbs: u32, - modulus_ptr: *const u8, - ) { - let modulus = - BigUint::from_bytes_le(std::slice::from_raw_parts(modulus_ptr, num_limbs as usize)); - let f = UnknownPrimeField { modulus, num_limbs }; - exec_op(&f, state, rd_ptr, rs1_ptr, rs2_ptr, |f, a, b| f.$op(a, b)); - } - }; -} +use rvr_openvm_ext_algebra_ffi_common::unknown_field_op_fn; -unknown_field_op_fn!(rvr_ext_mod_add, add); -unknown_field_op_fn!(rvr_ext_mod_sub, sub); -unknown_field_op_fn!(rvr_ext_mod_mul, mul); -unknown_field_op_fn!(rvr_ext_mod_div, div); +unknown_field_op_fn!(rvr_ext_mod_add, UnknownPrimeField, add); +unknown_field_op_fn!(rvr_ext_mod_sub, UnknownPrimeField, sub); +unknown_field_op_fn!(rvr_ext_mod_mul, UnknownPrimeField, mul); +unknown_field_op_fn!(rvr_ext_mod_div, UnknownPrimeField, div); /// # Safety /// `state` must be a valid `RvState` pointer. `modulus_ptr` must point to `num_limbs` bytes. From ba2322e54aa939b1dd68326fa36df973ca49f4c7 Mon Sep 17 00:00:00 2001 From: Mansur Mukimbekov Date: Mon, 29 Jun 2026 13:08:19 -0400 Subject: [PATCH 05/10] refactor(rvr-algebra): reduce code duplication for KnownField's arithmetic operations --- extensions/algebra/rvr/ffi/common/src/lib.rs | 44 ++++++++++++- extensions/algebra/rvr/ffi/fp2/src/lib.rs | 56 ++-------------- extensions/algebra/rvr/ffi/modular/src/lib.rs | 64 +++---------------- 3 files changed, 56 insertions(+), 108 deletions(-) diff --git a/extensions/algebra/rvr/ffi/common/src/lib.rs b/extensions/algebra/rvr/ffi/common/src/lib.rs index ae7d9684b9..6515489b97 100644 --- a/extensions/algebra/rvr/ffi/common/src/lib.rs +++ b/extensions/algebra/rvr/ffi/common/src/lib.rs @@ -2,7 +2,7 @@ use std::ffi::c_void; -use halo2curves_axiom::ff::PrimeField; +use halo2curves_axiom::ff::{Field, PrimeField}; use num_bigint::{BigInt, BigUint}; use num_integer::Integer; use rvr_openvm_ext_ffi_common::{rd_mem_words_traced, wr_mem_words_traced, WORD_SIZE}; @@ -37,6 +37,48 @@ pub trait FieldArith { fn is_eq(&self, a: &Self::Elem, b: &Self::Elem) -> bool; } +// ── KnownFieldArith trait ───────────────────────────────────────────────────── + +/// Narrower trait for known (native) field types whose element type supports +/// standard arithmetic via operator overloads. Implementing this trait +/// automatically provides a full [`FieldArith`] impl via a blanket impl — +/// only `read_elem` and `write_elem` need to be supplied. +pub trait KnownFieldArith { + type Elem: Field; + + /// # Safety + /// `state` must be a valid pointer to the C `RvState` struct. + unsafe fn read_elem(&self, state: *mut c_void, ptr: u32) -> Self::Elem; + /// # Safety + /// `state` must be a valid pointer to the C `RvState` struct. + unsafe fn write_elem(&self, state: *mut c_void, ptr: u32, val: &Self::Elem); +} + +impl FieldArith for T { + type Elem = T::Elem; + + #[inline(always)] + unsafe fn read_elem(&self, state: *mut c_void, ptr: u32) -> Self::Elem { + KnownFieldArith::read_elem(self, state, ptr) + } + + #[inline(always)] + unsafe fn write_elem(&self, state: *mut c_void, ptr: u32, val: &Self::Elem) { + KnownFieldArith::write_elem(self, state, ptr, val) + } + + #[inline(always)] + fn add(&self, a: Self::Elem, b: Self::Elem) -> Self::Elem { a + b } + #[inline(always)] + fn sub(&self, a: Self::Elem, b: Self::Elem) -> Self::Elem { a - b } + #[inline(always)] + fn mul(&self, a: Self::Elem, b: Self::Elem) -> Self::Elem { a * b } + #[inline(always)] + fn div(&self, a: Self::Elem, b: Self::Elem) -> Self::Elem { a * b.invert().unwrap() } + #[inline(always)] + fn is_eq(&self, a: &Self::Elem, b: &Self::Elem) -> bool { a == b } +} + // ── 256-bit / 384-bit field I/O ───────────────────────────────────────────── /// Read a 256-bit field element from guest memory (traced). diff --git a/extensions/algebra/rvr/ffi/fp2/src/lib.rs b/extensions/algebra/rvr/ffi/fp2/src/lib.rs index cdb9973d78..49471b4784 100644 --- a/extensions/algebra/rvr/ffi/fp2/src/lib.rs +++ b/extensions/algebra/rvr/ffi/fp2/src/lib.rs @@ -7,11 +7,11 @@ use std::{ffi::c_void, marker::PhantomData}; -use halo2curves_axiom::ff::Field; use num_bigint::BigUint; use rvr_openvm_ext_algebra_ffi_common::{ exec_op, mod_inverse, read_bigint, read_bls12_381_fq, read_field_256, write_bigint, - write_bls12_381_fq, write_field_256, FieldArith, BLS12_381_ELEM_BYTES, FIELD_256_BYTES, + write_bls12_381_fq, write_field_256, FieldArith, KnownFieldArith, BLS12_381_ELEM_BYTES, + FIELD_256_BYTES, }; use rvr_openvm_ext_ffi_common::{ rd_mem_words_traced, trace_mem_access_range, wr_mem_words_traced, AS_MEMORY, WORD_SIZE, @@ -28,7 +28,7 @@ struct UnknownComplexField { // ── KnownComplexField impls ───────────────────────────────────────────────── -impl FieldArith for KnownComplexField { +impl KnownFieldArith for KnownComplexField { type Elem = halo2curves_axiom::bn256::Fq2; #[inline(always)] @@ -42,36 +42,11 @@ impl FieldArith for KnownComplexField { #[inline(always)] unsafe fn write_elem(&self, state: *mut c_void, ptr: u32, val: &Self::Elem) { write_field_256::(state, ptr, &val.c0); - write_field_256::( - state, - ptr + FIELD_256_BYTES as u32, - &val.c1, - ); - } - - #[inline(always)] - fn add(&self, a: Self::Elem, b: Self::Elem) -> Self::Elem { - a + b - } - #[inline(always)] - fn sub(&self, a: Self::Elem, b: Self::Elem) -> Self::Elem { - a - b - } - #[inline(always)] - fn mul(&self, a: Self::Elem, b: Self::Elem) -> Self::Elem { - a * b - } - #[inline(always)] - fn div(&self, a: Self::Elem, b: Self::Elem) -> Self::Elem { - a * b.invert().unwrap() - } - #[inline(always)] - fn is_eq(&self, a: &Self::Elem, b: &Self::Elem) -> bool { - a == b + write_field_256::(state, ptr + FIELD_256_BYTES as u32, &val.c1); } } -impl FieldArith for KnownComplexField { +impl KnownFieldArith for KnownComplexField { type Elem = blstrs::Fp2; #[inline(always)] @@ -87,27 +62,6 @@ impl FieldArith for KnownComplexField { write_bls12_381_fq(state, ptr, &val.c0()); write_bls12_381_fq(state, ptr + BLS12_381_ELEM_BYTES as u32, &val.c1()); } - - #[inline(always)] - fn add(&self, a: Self::Elem, b: Self::Elem) -> Self::Elem { - a + b - } - #[inline(always)] - fn sub(&self, a: Self::Elem, b: Self::Elem) -> Self::Elem { - a - b - } - #[inline(always)] - fn mul(&self, a: Self::Elem, b: Self::Elem) -> Self::Elem { - a * b - } - #[inline(always)] - fn div(&self, a: Self::Elem, b: Self::Elem) -> Self::Elem { - a * b.invert().unwrap() - } - #[inline(always)] - fn is_eq(&self, a: &Self::Elem, b: &Self::Elem) -> bool { - a == b - } } // ── UnknownComplexField impl ──────────────────────────────────────────────── diff --git a/extensions/algebra/rvr/ffi/modular/src/lib.rs b/extensions/algebra/rvr/ffi/modular/src/lib.rs index f76f4d9312..622118859b 100644 --- a/extensions/algebra/rvr/ffi/modular/src/lib.rs +++ b/extensions/algebra/rvr/ffi/modular/src/lib.rs @@ -12,12 +12,12 @@ use std::{ffi::c_void, marker::PhantomData}; -use halo2curves_axiom::ff::{Field, PrimeField}; +use halo2curves_axiom::ff::PrimeField; use num_bigint::BigUint; use num_traits::One; use rvr_openvm_ext_algebra_ffi_common::{ exec_op, mod_inverse, read_bigint, read_bls12_381_fq, read_field_256, write_bigint, - write_bls12_381_fq, write_field_256, FieldArith, + write_bls12_381_fq, write_field_256, FieldArith, KnownFieldArith, }; use rvr_openvm_ext_ffi_common::{ ext_hint_stream_set, rd_mem_u64_range_wrapper, rd_mem_words_traced, trace_mem_access_range, @@ -45,70 +45,22 @@ struct UnknownPrimeField { // ── KnownPrimeField impl (256-bit via generic bound) ──────────────────────── -impl> FieldArith for KnownPrimeField { +impl> KnownFieldArith for KnownPrimeField { type Elem = F; #[inline(always)] - unsafe fn read_elem(&self, state: *mut c_void, ptr: u32) -> Self::Elem { - read_field_256(state, ptr) - } - #[inline(always)] - unsafe fn write_elem(&self, state: *mut c_void, ptr: u32, val: &Self::Elem) { - write_field_256(state, ptr, val) - } + unsafe fn read_elem(&self, state: *mut c_void, ptr: u32) -> F { read_field_256(state, ptr) } #[inline(always)] - fn add(&self, a: Self::Elem, b: Self::Elem) -> Self::Elem { - a + b - } - #[inline(always)] - fn sub(&self, a: Self::Elem, b: Self::Elem) -> Self::Elem { - a - b - } - #[inline(always)] - fn mul(&self, a: Self::Elem, b: Self::Elem) -> Self::Elem { - a * b - } - #[inline(always)] - fn div(&self, a: Self::Elem, b: Self::Elem) -> Self::Elem { - a * b.invert().unwrap() - } - #[inline(always)] - fn is_eq(&self, a: &Self::Elem, b: &Self::Elem) -> bool { - a == b - } + unsafe fn write_elem(&self, state: *mut c_void, ptr: u32, val: &F) { write_field_256(state, ptr, val) } } // ── KnownPrimeField impl (BLS12-381 Fq, 48 bytes) ────────────────────────── -impl FieldArith for KnownPrimeField { +impl KnownFieldArith for KnownPrimeField { type Elem = blstrs::Fp; #[inline(always)] - unsafe fn read_elem(&self, state: *mut c_void, ptr: u32) -> Self::Elem { - read_bls12_381_fq(state, ptr) - } - #[inline(always)] - unsafe fn write_elem(&self, state: *mut c_void, ptr: u32, val: &Self::Elem) { - write_bls12_381_fq(state, ptr, val) - } + unsafe fn read_elem(&self, state: *mut c_void, ptr: u32) -> blstrs::Fp { read_bls12_381_fq(state, ptr) } #[inline(always)] - fn add(&self, a: Self::Elem, b: Self::Elem) -> Self::Elem { - a + b - } - #[inline(always)] - fn sub(&self, a: Self::Elem, b: Self::Elem) -> Self::Elem { - a - b - } - #[inline(always)] - fn mul(&self, a: Self::Elem, b: Self::Elem) -> Self::Elem { - a * b - } - #[inline(always)] - fn div(&self, a: Self::Elem, b: Self::Elem) -> Self::Elem { - a * b.invert().unwrap() - } - #[inline(always)] - fn is_eq(&self, a: &Self::Elem, b: &Self::Elem) -> bool { - a == b - } + unsafe fn write_elem(&self, state: *mut c_void, ptr: u32, val: &blstrs::Fp) { write_bls12_381_fq(state, ptr, val) } } // ── UnknownPrimeField impl ────────────────────────────────────────────────── From 846f02af33a476c73245d72d591b7bd562803a0b Mon Sep 17 00:00:00 2001 From: Mansur Mukimbekov Date: Mon, 29 Jun 2026 13:39:37 -0400 Subject: [PATCH 06/10] refactor(rvr-algebra): use common known_field_op_fn macro isntead of seperate it. --- extensions/algebra/rvr/ffi/common/src/lib.rs | 22 +++++++++++++++- extensions/algebra/rvr/ffi/fp2/src/lib.rs | 26 +++++-------------- extensions/algebra/rvr/ffi/modular/src/lib.rs | 24 +++++------------ 3 files changed, 34 insertions(+), 38 deletions(-) diff --git a/extensions/algebra/rvr/ffi/common/src/lib.rs b/extensions/algebra/rvr/ffi/common/src/lib.rs index 6515489b97..adf3d2461e 100644 --- a/extensions/algebra/rvr/ffi/common/src/lib.rs +++ b/extensions/algebra/rvr/ffi/common/src/lib.rs @@ -224,7 +224,27 @@ pub fn mod_inverse(a: &BigUint, p: &BigUint) -> BigUint { .unwrap() } -// ── FFI generation macro (shared by modular and fp2) ──────────────────────── +// ── FFI generation macros (shared by modular and fp2) ─────────────────────── + +/// Generate a known-field FFI function. `$wrapper` must be a +/// `PhantomData`-newtype that implements [`KnownFieldArith`] for `$field`. +#[macro_export] +macro_rules! known_field_op_fn { + ($name:ident, $wrapper:ident, $field:ty, $op:ident) => { + /// # Safety + /// `state` must be a valid `RvState` pointer. + #[no_mangle] + pub unsafe extern "C" fn $name( + state: *mut ::std::ffi::c_void, + rd: u32, + rs1: u32, + rs2: u32, + ) { + let f = $wrapper::<$field>(::std::marker::PhantomData); + $crate::exec_op(&f, state, rd, rs1, rs2, |f, a, b| f.$op(a, b)); + } + }; +} /// Generate a generic (unknown-modulus) field-op FFI function. /// diff --git a/extensions/algebra/rvr/ffi/fp2/src/lib.rs b/extensions/algebra/rvr/ffi/fp2/src/lib.rs index 49471b4784..6a60a4a136 100644 --- a/extensions/algebra/rvr/ffi/fp2/src/lib.rs +++ b/extensions/algebra/rvr/ffi/fp2/src/lib.rs @@ -9,9 +9,9 @@ use std::{ffi::c_void, marker::PhantomData}; use num_bigint::BigUint; use rvr_openvm_ext_algebra_ffi_common::{ - exec_op, mod_inverse, read_bigint, read_bls12_381_fq, read_field_256, write_bigint, - write_bls12_381_fq, write_field_256, FieldArith, KnownFieldArith, BLS12_381_ELEM_BYTES, - FIELD_256_BYTES, + known_field_op_fn, mod_inverse, read_bigint, read_bls12_381_fq, read_field_256, + write_bigint, write_bls12_381_fq, write_field_256, FieldArith, KnownFieldArith, + BLS12_381_ELEM_BYTES, FIELD_256_BYTES, }; use rvr_openvm_ext_ffi_common::{ rd_mem_words_traced, trace_mem_access_range, wr_mem_words_traced, AS_MEMORY, WORD_SIZE, @@ -116,25 +116,13 @@ impl FieldArith for UnknownComplexField { // ── Macros ────────────────────────────────────────────────────────────────── -macro_rules! field_op_fn { - ($name:ident, $field:ty, $op:ident) => { - /// # Safety - /// `state` must be a valid `RvState` pointer. - #[no_mangle] - pub unsafe extern "C" fn $name(state: *mut c_void, rd: u32, rs1: u32, rs2: u32) { - let f = KnownComplexField::<$field>(PhantomData); - exec_op(&f, state, rd, rs1, rs2, |f, a, b| f.$op(a, b)); - } - }; -} - macro_rules! define_fp2_ffi { ($field:ty, $suffix:ident) => { paste::paste! { - field_op_fn!([], $field, add); - field_op_fn!([], $field, sub); - field_op_fn!([], $field, mul); - field_op_fn!([], $field, div); + known_field_op_fn!([], KnownComplexField, $field, add); + known_field_op_fn!([], KnownComplexField, $field, sub); + known_field_op_fn!([], KnownComplexField, $field, mul); + known_field_op_fn!([], KnownComplexField, $field, div); } }; } diff --git a/extensions/algebra/rvr/ffi/modular/src/lib.rs b/extensions/algebra/rvr/ffi/modular/src/lib.rs index 622118859b..81ebcd3273 100644 --- a/extensions/algebra/rvr/ffi/modular/src/lib.rs +++ b/extensions/algebra/rvr/ffi/modular/src/lib.rs @@ -16,8 +16,8 @@ use halo2curves_axiom::ff::PrimeField; use num_bigint::BigUint; use num_traits::One; use rvr_openvm_ext_algebra_ffi_common::{ - exec_op, mod_inverse, read_bigint, read_bls12_381_fq, read_field_256, write_bigint, - write_bls12_381_fq, write_field_256, FieldArith, KnownFieldArith, + known_field_op_fn, mod_inverse, read_bigint, read_bls12_381_fq, read_field_256, + write_bigint, write_bls12_381_fq, write_field_256, FieldArith, KnownFieldArith, }; use rvr_openvm_ext_ffi_common::{ ext_hint_stream_set, rd_mem_u64_range_wrapper, rd_mem_words_traced, trace_mem_access_range, @@ -113,25 +113,13 @@ unsafe fn exec_iseq(f: &F, state: *mut c_void, rs1_ptr: u32, rs2_ // ── FFI generation macros ──────────────────────────────────────────────────── -macro_rules! field_op_fn { - ($name:ident, $field:ty, $op:ident) => { - /// # Safety - /// `state` must be a valid `RvState` pointer. - #[no_mangle] - pub unsafe extern "C" fn $name(state: *mut c_void, rd: u32, rs1: u32, rs2: u32) { - let f = KnownPrimeField::<$field>(PhantomData); - exec_op(&f, state, rd, rs1, rs2, |f, a, b| f.$op(a, b)); - } - }; -} - macro_rules! define_mod_ffi { ($field:ty, $suffix:ident) => { paste::paste! { - field_op_fn!([], $field, add); - field_op_fn!([], $field, sub); - field_op_fn!([], $field, mul); - field_op_fn!([], $field, div); + known_field_op_fn!([], KnownPrimeField, $field, add); + known_field_op_fn!([], KnownPrimeField, $field, sub); + known_field_op_fn!([], KnownPrimeField, $field, mul); + known_field_op_fn!([], KnownPrimeField, $field, div); /// # Safety /// `state` must be a valid `RvState` pointer. #[no_mangle] From d74395cace54521e6402673f920d221953ee89fb Mon Sep 17 00:00:00 2001 From: Mansur Mukimbekov Date: Mon, 29 Jun 2026 13:53:28 -0400 Subject: [PATCH 07/10] refactor(rvr-pairing): create a common function for reading --- extensions/pairing/rvr/ffi/src/lib.rs | 102 +++++++++++--------------- 1 file changed, 43 insertions(+), 59 deletions(-) diff --git a/extensions/pairing/rvr/ffi/src/lib.rs b/extensions/pairing/rvr/ffi/src/lib.rs index fd478227aa..e008eadc46 100644 --- a/extensions/pairing/rvr/ffi/src/lib.rs +++ b/extensions/pairing/rvr/ffi/src/lib.rs @@ -6,7 +6,7 @@ use std::ffi::c_void; use halo2curves_axiom::{bls12_381, bn256, ff::PrimeField}; -use openvm_ecc_guest::{algebra::field::FieldExtension, AffinePoint}; +use openvm_ecc_guest::{algebra::{field::FieldExtension, Field}, AffinePoint}; use openvm_pairing_guest::{ halo2curves_shims::{bls12_381::Bls12_381, bn254::Bn254}, pairing::{FinalExp, MultiMillerLoop}, @@ -73,9 +73,21 @@ fn point_base(ptr: u64, idx: u64, words_per_point: u32) -> Option { ptr.checked_add(offset)?.try_into().ok() } -// ── BN254 pairing hint ────────────────────────────────────────────────── +// ── Shared scaffolding ────────────────────────────────────────────────── -unsafe fn hint_bn254(state: *mut c_void, rs1_val: u32, rs2_val: u32) -> Option> { +/// Read G1 and G2 point vectors from guest memory. +/// +/// `read_fq` reads one base-field element. `make_fq2` constructs an Fq2 +/// from two consecutive Fq elements; use `Fq2::new` or `|c0, c1| Fq2 { c0, c1 }` +/// depending on the curve's API. +unsafe fn read_pairing_points( + state: *mut c_void, + rs1_val: u32, + rs2_val: u32, + fq_bytes: u32, + read_fq: impl Fn(*mut c_void, u32) -> Option, + make_fq2: impl Fn(Fq, Fq) -> Fq2, +) -> Option<(Vec>, Vec>)> { let p_ptr = rd_mem_u64_wrapper(state, rs1_val); let p_len = rd_mem_u64_wrapper(state, rs1_val + SLICE_LEN_OFFSET); let q_ptr = rd_mem_u64_wrapper(state, rs2_val); @@ -87,38 +99,42 @@ unsafe fn hint_bn254(state: *mut c_void, rs1_val: u32, rs2_val: u32) -> Option = (0..p_len) .map(|i| { - let base = point_base(p_ptr, i, G1_AFFINE_COORDS * BN254_FQ_BYTES)?; + let base = point_base(p_ptr, i, G1_AFFINE_COORDS * fq_bytes)?; Some(AffinePoint::new( - read_bn254_fq(state, base)?, - read_bn254_fq(state, base + BN254_FQ_BYTES)?, + read_fq(state, base)?, + read_fq(state, base + fq_bytes)?, )) }) .collect::>()?; let q: Vec<_> = (0..q_len) .map(|i| { - let base = point_base(q_ptr, i, G2_AFFINE_COORDS * BN254_FQ_BYTES)?; - // BN254 Fq2 exposes a constructor helper. - let x = bn256::Fq2::new( - read_bn254_fq(state, base)?, - read_bn254_fq(state, base + BN254_FQ_BYTES)?, - ); - let y = bn256::Fq2::new( - read_bn254_fq(state, base + 2 * BN254_FQ_BYTES)?, - read_bn254_fq(state, base + 3 * BN254_FQ_BYTES)?, + let base = point_base(q_ptr, i, G2_AFFINE_COORDS * fq_bytes)?; + let x = make_fq2(read_fq(state, base)?, read_fq(state, base + fq_bytes)?); + let y = make_fq2( + read_fq(state, base + 2 * fq_bytes)?, + read_fq(state, base + 3 * fq_bytes)?, ); Some(AffinePoint::new(x, y)) }) .collect::>()?; + Some((p, q)) +} + +// ── BN254 pairing hint ────────────────────────────────────────────────── + +unsafe fn hint_bn254(state: *mut c_void, rs1_val: u32, rs2_val: u32) -> Option> { + let (p, q) = read_pairing_points( + state, rs1_val, rs2_val, + BN254_FQ_BYTES, + |s, ptr| unsafe { read_bn254_fq(s, ptr) }, + bn256::Fq2::new, + )?; let f: bn256::Fq12 = Bn254::multi_miller_loop(&p, &q); let (c, u) = Bn254::final_exp_hint(&f); - - // Serialize hint: c (Fp12) then u (Fp12), each as 12 Fq elements Some( - c.to_coeffs() - .into_iter() - .chain(u.to_coeffs()) + c.to_coeffs().into_iter().chain(u.to_coeffs()) .flat_map(|fp2| fp2.to_coeffs()) .flat_map(|fp| fp.to_bytes()) .collect(), @@ -128,48 +144,16 @@ unsafe fn hint_bn254(state: *mut c_void, rs1_val: u32, rs2_val: u32) -> Option Option> { - let p_ptr = rd_mem_u64_wrapper(state, rs1_val); - let p_len = rd_mem_u64_wrapper(state, rs1_val + SLICE_LEN_OFFSET); - let q_ptr = rd_mem_u64_wrapper(state, rs2_val); - let q_len = rd_mem_u64_wrapper(state, rs2_val + SLICE_LEN_OFFSET); - - if p_len != q_len { - return None; - } - - let p: Vec<_> = (0..p_len) - .map(|i| { - let base = point_base(p_ptr, i, G1_AFFINE_COORDS * BLS12_381_FQ_BYTES)?; - Some(AffinePoint::new( - read_bls12_381_fq(state, base)?, - read_bls12_381_fq(state, base + BLS12_381_FQ_BYTES)?, - )) - }) - .collect::>()?; - - let q: Vec<_> = (0..q_len) - .map(|i| { - let base = point_base(q_ptr, i, G2_AFFINE_COORDS * BLS12_381_FQ_BYTES)?; - // BLS12-381 Fq2 uses struct fields instead of an `Fq2::new` helper. - let x = bls12_381::Fq2 { - c0: read_bls12_381_fq(state, base)?, - c1: read_bls12_381_fq(state, base + BLS12_381_FQ_BYTES)?, - }; - let y = bls12_381::Fq2 { - c0: read_bls12_381_fq(state, base + 2 * BLS12_381_FQ_BYTES)?, - c1: read_bls12_381_fq(state, base + 3 * BLS12_381_FQ_BYTES)?, - }; - Some(AffinePoint::new(x, y)) - }) - .collect::>()?; - + let (p, q) = read_pairing_points( + state, rs1_val, rs2_val, + BLS12_381_FQ_BYTES, + |s, ptr| unsafe { read_bls12_381_fq(s, ptr) }, + |c0, c1| bls12_381::Fq2 { c0, c1 }, + )?; let f: bls12_381::Fq12 = Bls12_381::multi_miller_loop(&p, &q); let (c, u) = Bls12_381::final_exp_hint(&f); - Some( - c.to_coeffs() - .into_iter() - .chain(u.to_coeffs()) + c.to_coeffs().into_iter().chain(u.to_coeffs()) .flat_map(|fp2| fp2.to_coeffs()) .flat_map(|fp| fp.to_bytes()) .collect(), From 09874ee71cf2e9d95a41285c2ac7a40a847cd840 Mon Sep 17 00:00:00 2001 From: Mansur Mukimbekov Date: Mon, 29 Jun 2026 15:21:06 -0400 Subject: [PATCH 08/10] refactor(rvr-algebra): add common IR for IsEq --- extensions/algebra/rvr/src/common.rs | 67 ++++++++++++++++++++++++++- extensions/algebra/rvr/src/fp2.rs | 4 +- extensions/algebra/rvr/src/lib.rs | 12 ++--- extensions/algebra/rvr/src/modular.rs | 62 ++++--------------------- 4 files changed, 81 insertions(+), 64 deletions(-) diff --git a/extensions/algebra/rvr/src/common.rs b/extensions/algebra/rvr/src/common.rs index 420bcc67e7..e269c79e8a 100644 --- a/extensions/algebra/rvr/src/common.rs +++ b/extensions/algebra/rvr/src/common.rs @@ -6,7 +6,7 @@ use crate::{detect_known_field, format_c_byte_array, KnownField, ModOp}; /// Marker trait implemented by zero-sized types that parameterize /// [`FieldArithInstr`], supplying the C prefix and known-field suffix lookup. -pub trait ArithKind: Clone + Debug + Send + Sync + 'static { +pub(crate) trait ArithKind: Clone + Debug + Send + Sync + 'static { fn opname() -> &'static str; fn c_prefix() -> &'static str; /// Returns the C suffix for a known field, or `None` to fall through to the @@ -18,7 +18,7 @@ pub trait ArithKind: Clone + Debug + Send + Sync + 'static { /// Use the type aliases [`crate::ModArithInstr`] / [`crate::Fp2ArithInstr`] /// rather than naming this type directly. #[derive(Debug, Clone)] -pub struct FieldArithInstr { +pub(crate) struct FieldArithInstr { _kind: PhantomData, pub op: ModOp, pub rd_reg: Reg, @@ -41,6 +41,69 @@ impl FieldArithInstr { } } +/// Generic IR node for field IS_EQ. +/// Use the type alias [`crate::ModIsEqInstr`] rather than naming this type directly. +#[derive(Debug, Clone)] +pub(crate) struct FieldIsEqInstr { + _kind: PhantomData, + pub rd_reg: Reg, + pub rs1_reg: Reg, + pub rs2_reg: Reg, + pub num_limbs: u32, + pub modulus: Vec, +} + +impl FieldIsEqInstr { + pub fn new( + rd_reg: Reg, + rs1_reg: Reg, + rs2_reg: Reg, + num_limbs: u32, + modulus: Vec, + ) -> Self { + Self { _kind: PhantomData, rd_reg, rs1_reg, rs2_reg, num_limbs, modulus } + } +} + +impl ExtInstr for FieldIsEqInstr { + fn opname(&self) -> &str { + "iseq_opname" + } + + fn emit_c(&self, ctx: &mut dyn ExtEmitCtx) { + let rs1 = ctx.read_reg(self.rs1_reg); + let rs2 = ctx.read_reg(self.rs2_reg); + let prefix = K::c_prefix(); + let known_suffix = detect_known_field(&self.modulus).and_then(K::known_suffix); + if let Some(suffix) = known_suffix { + let name = format!("rvr_ext_{prefix}_iseq_{suffix}"); + let val = ctx.extern_call_expr("uint32_t", &name, &["state", &rs1, &rs2]); + ctx.write_reg(self.rd_reg, &val); + } else { + let mod_literal = format_c_byte_array(&self.modulus); + ctx.write_line("{"); + ctx.write_line(&format!("static const uint8_t mod_[] = {mod_literal};")); + let name = format!("rvr_ext_{prefix}_iseq"); + let num_limbs = format!("{}u", self.num_limbs); + let val = ctx.extern_call_expr( + "uint32_t", + &name, + &["state", &rs1, &rs2, &num_limbs, "mod_"], + ); + ctx.write_reg(self.rd_reg, &val); + ctx.write_line("}"); + } + } + + fn clone_box(&self) -> Box { + Box::new(self.clone()) + } + + fn is_block_end(&self) -> bool { + false + } +} + /// Generic IR node for field setup (SETUP_ADDSUB, SETUP_MULDIV, SETUP_ISEQ). /// Use the type aliases [`crate::ModSetupInstr`] / [`crate::Fp2SetupInstr`] /// rather than naming this type directly. diff --git a/extensions/algebra/rvr/src/fp2.rs b/extensions/algebra/rvr/src/fp2.rs index 5fc7d1d3fb..880d84b745 100644 --- a/extensions/algebra/rvr/src/fp2.rs +++ b/extensions/algebra/rvr/src/fp2.rs @@ -32,7 +32,7 @@ fn make_modulus_info(modulus: BigUint) -> ModulusInfo { // ── Fp2 arithmetic IR ──────────────────────────────────────────────────────── #[derive(Debug, Clone)] -pub struct Fp2ArithKind; +pub(crate) struct Fp2ArithKind; impl ArithKind for Fp2ArithKind { fn opname() -> &'static str { @@ -47,7 +47,7 @@ impl ArithKind for Fp2ArithKind { } /// IR node for Fp2 arithmetic (ADD, SUB, MUL, DIV). -pub type Fp2ArithInstr = FieldArithInstr; +pub(crate) type Fp2ArithInstr = FieldArithInstr; /// IR node for Fp2 SETUP (SETUP_ADDSUB, SETUP_MULDIV). pub type Fp2SetupInstr = FieldSetupInstr; diff --git a/extensions/algebra/rvr/src/lib.rs b/extensions/algebra/rvr/src/lib.rs index e8c63fbaec..e11d535328 100644 --- a/extensions/algebra/rvr/src/lib.rs +++ b/extensions/algebra/rvr/src/lib.rs @@ -12,12 +12,10 @@ mod modular; use num_bigint::BigUint; -pub use common::{ArithKind, FieldArithInstr, FieldSetupInstr}; -pub use fp2::{Fp2ArithInstr, Fp2ArithKind, Fp2RvrExtension, Fp2SetupInstr}; -pub use modular::{ - HintNonQrInstr, HintSqrtInstr, ModArithInstr, ModArithKind, ModIsEqInstr, ModSetupInstr, - ModularRvrExtension, -}; +pub(crate) use common::{ArithKind, FieldArithInstr, FieldIsEqInstr}; +pub use common::FieldSetupInstr; +pub use fp2::{Fp2RvrExtension, Fp2SetupInstr}; +pub use modular::{HintNonQrInstr, HintSqrtInstr, ModSetupInstr, ModularRvrExtension}; /// Zero-pad `modulus` to the canonical limb boundary (32 or 48 bytes). /// @@ -62,7 +60,7 @@ impl ModOp { /// Known field types that have optimized native FFI implementations. #[derive(Debug, Clone, Copy)] -pub enum KnownField { +pub(crate) enum KnownField { K256Coord, K256Scalar, P256Coord, diff --git a/extensions/algebra/rvr/src/modular.rs b/extensions/algebra/rvr/src/modular.rs index 9b76f028fc..183f48b784 100644 --- a/extensions/algebra/rvr/src/modular.rs +++ b/extensions/algebra/rvr/src/modular.rs @@ -12,8 +12,8 @@ use rvr_openvm_lift::{helpers::decode_reg, RvrExtension}; use strum::EnumCount; use crate::{ - detect_known_field, format_c_byte_array, pad_modulus, ArithKind, FieldArithInstr, - FieldSetupInstr, KnownField, ModOp, + format_c_byte_array, pad_modulus, ArithKind, FieldArithInstr, FieldIsEqInstr, FieldSetupInstr, + KnownField, ModOp, }; include!(concat!(env!("OUT_DIR"), "/secp256k1_files.rs")); @@ -52,7 +52,7 @@ fn make_modulus_info(modulus: &BigUint, rng: &mut StdRng) -> ModulusInfo { // ── Modular arithmetic IR ──────────────────────────────────────────────────── #[derive(Debug, Clone)] -pub struct ModArithKind; +pub(crate) struct ModArithKind; impl ArithKind for ModArithKind { fn opname() -> &'static str { @@ -67,54 +67,10 @@ impl ArithKind for ModArithKind { } /// IR node for modular arithmetic (ADD, SUB, MUL, DIV). -pub type ModArithInstr = FieldArithInstr; +pub(crate) type ModArithInstr = FieldArithInstr; /// IR node for modular IS_EQ. -#[derive(Debug, Clone)] -pub struct ModIsEqInstr { - pub rd_reg: Reg, - pub rs1_reg: Reg, - pub rs2_reg: Reg, - pub num_limbs: u32, - pub modulus: Vec, -} - -impl ExtInstr for ModIsEqInstr { - fn opname(&self) -> &str { - "mod_iseq" - } - - fn emit_c(&self, ctx: &mut dyn ExtEmitCtx) { - let rs1 = ctx.read_reg(self.rs1_reg); - let rs2 = ctx.read_reg(self.rs2_reg); - if let Some(field) = detect_known_field(&self.modulus) { - let suffix = field.c_suffix(); - let name = format!("rvr_ext_mod_iseq_{suffix}"); - let val = ctx.extern_call_expr("uint32_t", &name, &["state", &rs1, &rs2]); - ctx.write_reg(self.rd_reg, &val); - } else { - let mod_literal = format_c_byte_array(&self.modulus); - ctx.write_line("{"); - ctx.write_line(&format!("static const uint8_t mod_[] = {mod_literal};")); - let num_limbs = format!("{}u", self.num_limbs); - let val = ctx.extern_call_expr( - "uint32_t", - "rvr_ext_mod_iseq", - &["state", &rs1, &rs2, &num_limbs, "mod_"], - ); - ctx.write_reg(self.rd_reg, &val); - ctx.write_line("}"); - } - } - - fn clone_box(&self) -> Box { - Box::new(self.clone()) - } - - fn is_block_end(&self) -> bool { - false - } -} +pub(crate) type ModIsEqInstr = FieldIsEqInstr; /// IR node for modular SETUP (SETUP_ADDSUB, SETUP_MULDIV, SETUP_ISEQ). pub type ModSetupInstr = FieldSetupInstr; @@ -325,13 +281,13 @@ impl ModularRvrExtension { Instr::Ext(Box::new(ModSetupInstr::new("rvr_ext_mod_setup", rd_reg, rs1_reg, rs2_reg, info.num_limbs))) } x if x == Rv64ModularArithmeticOpcode::IS_EQ as usize => { - Instr::Ext(Box::new(ModIsEqInstr { + Instr::Ext(Box::new(ModIsEqInstr::new( rd_reg, rs1_reg, rs2_reg, - num_limbs: info.num_limbs, - modulus: info.modulus_bytes.clone(), - })) + info.num_limbs, + info.modulus_bytes.clone(), + ))) } x if x == Rv64ModularArithmeticOpcode::SETUP_ISEQ as usize => { Instr::Ext(Box::new(ModSetupInstr::new("rvr_ext_mod_setup", rd_reg, rs1_reg, rs2_reg, info.num_limbs))) From 90998169819ca531b6812fc94ba46a1404fee36a Mon Sep 17 00:00:00 2001 From: Mansur Mukimbekov Date: Mon, 29 Jun 2026 15:56:16 -0400 Subject: [PATCH 09/10] refactor(rvr-algebra): create common FieldKind trait and modify IR to add ArithKind, SetupKind and IsEqKind --- extensions/algebra/rvr/src/common.rs | 116 +++++++++++++++----------- extensions/algebra/rvr/src/fp2.rs | 25 ++++-- extensions/algebra/rvr/src/lib.rs | 7 +- extensions/algebra/rvr/src/modular.rs | 35 +++++--- 4 files changed, 113 insertions(+), 70 deletions(-) diff --git a/extensions/algebra/rvr/src/common.rs b/extensions/algebra/rvr/src/common.rs index e269c79e8a..b779c99e7a 100644 --- a/extensions/algebra/rvr/src/common.rs +++ b/extensions/algebra/rvr/src/common.rs @@ -4,16 +4,34 @@ use rvr_openvm_ir::{ExtEmitCtx, ExtInstr, Reg}; use crate::{detect_known_field, format_c_byte_array, KnownField, ModOp}; -/// Marker trait implemented by zero-sized types that parameterize -/// [`FieldArithInstr`], supplying the C prefix and known-field suffix lookup. -pub(crate) trait ArithKind: Clone + Debug + Send + Sync + 'static { - fn opname() -> &'static str; +/// Base trait for zero-sized marker types that identify a field kind (modular +/// or Fp2). Provides the C function prefix and known-field suffix lookup used +/// by all three instruction families (arith, iseq, setup). +pub(crate) trait FieldKind: Clone + Debug + Send + Sync + 'static { fn c_prefix() -> &'static str; /// Returns the C suffix for a known field, or `None` to fall through to the /// generic path. fn known_suffix(field: KnownField) -> Option<&'static str>; } +/// Extension of [`FieldKind`] for the arithmetic instruction family. Adds the +/// IR opname used by [`FieldArithInstr`]. +pub(crate) trait ArithKind: FieldKind { + fn opname() -> &'static str; +} + +/// Extension of [`FieldKind`] for the setup instruction family. Adds the +/// IR opname used by [`FieldSetupInstr`]. +pub(crate) trait SetupKind: FieldKind { + fn opname() -> &'static str; +} + +/// Extension of [`FieldKind`] for the IS_EQ instruction family. Adds the +/// IR opname used by [`FieldIsEqInstr`]. Not implemented for Fp2 — fp2 has no IS_EQ. +pub(crate) trait IsEqKind: FieldKind { + fn opname() -> &'static str; +} + /// Generic IR node for field arithmetic (ADD, SUB, MUL, DIV). /// Use the type aliases [`crate::ModArithInstr`] / [`crate::Fp2ArithInstr`] /// rather than naming this type directly. @@ -44,7 +62,7 @@ impl FieldArithInstr { /// Generic IR node for field IS_EQ. /// Use the type alias [`crate::ModIsEqInstr`] rather than naming this type directly. #[derive(Debug, Clone)] -pub(crate) struct FieldIsEqInstr { +pub(crate) struct FieldIsEqInstr { _kind: PhantomData, pub rd_reg: Reg, pub rs1_reg: Reg, @@ -53,7 +71,7 @@ pub(crate) struct FieldIsEqInstr { pub modulus: Vec, } -impl FieldIsEqInstr { +impl FieldIsEqInstr { pub fn new( rd_reg: Reg, rs1_reg: Reg, @@ -65,9 +83,51 @@ impl FieldIsEqInstr { } } -impl ExtInstr for FieldIsEqInstr { +/// Generic IR node for field setup (SETUP_ADDSUB, SETUP_MULDIV, SETUP_ISEQ). +/// The C function name is derived from `K::c_prefix()` as `rvr_ext_{prefix}_setup`. +/// Use the type aliases [`crate::ModSetupInstr`] / [`crate::Fp2SetupInstr`] +/// rather than naming this type directly. +#[derive(Debug, Clone)] +pub(crate) struct FieldSetupInstr { + _kind: PhantomData, + pub rd_reg: Reg, + pub rs1_reg: Reg, + pub rs2_reg: Reg, + pub num_limbs: u32, +} + +impl FieldSetupInstr { + pub fn new(rd_reg: Reg, rs1_reg: Reg, rs2_reg: Reg, num_limbs: u32) -> Self { + Self { _kind: PhantomData, rd_reg, rs1_reg, rs2_reg, num_limbs } + } +} + +impl ExtInstr for FieldSetupInstr { fn opname(&self) -> &str { - "iseq_opname" + K::opname() + } + + fn emit_c(&self, ctx: &mut dyn ExtEmitCtx) { + let rd = ctx.read_reg(self.rd_reg); + let rs1 = ctx.read_reg(self.rs1_reg); + let rs2 = ctx.read_reg(self.rs2_reg); + let num_limbs = format!("{}u", self.num_limbs); + let name = format!("rvr_ext_{}_setup", K::c_prefix()); + ctx.extern_call(&name, &["state", &rd, &rs1, &rs2, &num_limbs]); + } + + fn clone_box(&self) -> Box { + Box::new(self.clone()) + } + + fn is_block_end(&self) -> bool { + false + } +} + +impl ExtInstr for FieldIsEqInstr { + fn opname(&self) -> &str { + K::opname() } fn emit_c(&self, ctx: &mut dyn ExtEmitCtx) { @@ -104,46 +164,6 @@ impl ExtInstr for FieldIsEqInstr { } } -/// Generic IR node for field setup (SETUP_ADDSUB, SETUP_MULDIV, SETUP_ISEQ). -/// Use the type aliases [`crate::ModSetupInstr`] / [`crate::Fp2SetupInstr`] -/// rather than naming this type directly. -#[derive(Debug, Clone)] -pub struct FieldSetupInstr { - c_fn: &'static str, - pub rd_reg: Reg, - pub rs1_reg: Reg, - pub rs2_reg: Reg, - pub num_limbs: u32, -} - -impl FieldSetupInstr { - pub fn new(c_fn: &'static str, rd_reg: Reg, rs1_reg: Reg, rs2_reg: Reg, num_limbs: u32) -> Self { - Self { c_fn, rd_reg, rs1_reg, rs2_reg, num_limbs } - } -} - -impl ExtInstr for FieldSetupInstr { - fn opname(&self) -> &str { - &self.c_fn["rvr_ext_".len()..] - } - - fn emit_c(&self, ctx: &mut dyn ExtEmitCtx) { - let rd = ctx.read_reg(self.rd_reg); - let rs1 = ctx.read_reg(self.rs1_reg); - let rs2 = ctx.read_reg(self.rs2_reg); - let num_limbs = format!("{}u", self.num_limbs); - ctx.extern_call(self.c_fn, &["state", &rd, &rs1, &rs2, &num_limbs]); - } - - fn clone_box(&self) -> Box { - Box::new(self.clone()) - } - - fn is_block_end(&self) -> bool { - false - } -} - impl ExtInstr for FieldArithInstr { fn opname(&self) -> &str { K::opname() diff --git a/extensions/algebra/rvr/src/fp2.rs b/extensions/algebra/rvr/src/fp2.rs index 880d84b745..107def3629 100644 --- a/extensions/algebra/rvr/src/fp2.rs +++ b/extensions/algebra/rvr/src/fp2.rs @@ -8,7 +8,7 @@ use rvr_openvm_ir::{Instr, InstrAt, LiftedInstr}; use rvr_openvm_lift::{helpers::decode_reg, RvrExtension}; use strum::EnumCount; -use crate::{pad_modulus, ArithKind, FieldArithInstr, FieldSetupInstr, KnownField, ModOp}; +use crate::{pad_modulus, ArithKind, FieldArithInstr, FieldKind, FieldSetupInstr, KnownField, ModOp, SetupKind}; /// Per-modulus info for the Fp2 extension. Fp2 lifting never consults a /// non-QR, so we only carry the padded modulus and limb count. @@ -34,10 +34,7 @@ fn make_modulus_info(modulus: BigUint) -> ModulusInfo { #[derive(Debug, Clone)] pub(crate) struct Fp2ArithKind; -impl ArithKind for Fp2ArithKind { - fn opname() -> &'static str { - "fp2_arith" - } +impl FieldKind for Fp2ArithKind { fn c_prefix() -> &'static str { "fp2" } @@ -46,11 +43,23 @@ impl ArithKind for Fp2ArithKind { } } +impl ArithKind for Fp2ArithKind { + fn opname() -> &'static str { + "fp2_arith" + } +} + +impl SetupKind for Fp2ArithKind { + fn opname() -> &'static str { + "fp2_setup" + } +} + /// IR node for Fp2 arithmetic (ADD, SUB, MUL, DIV). pub(crate) type Fp2ArithInstr = FieldArithInstr; /// IR node for Fp2 SETUP (SETUP_ADDSUB, SETUP_MULDIV). -pub type Fp2SetupInstr = FieldSetupInstr; +pub(crate) type Fp2SetupInstr = FieldSetupInstr; // ── Fp2 extension ──────────────────────────────────────────────────────────── @@ -120,14 +129,14 @@ impl Fp2RvrExtension { x if x == Fp2Opcode::SUB as usize => Instr::Ext(Box::new( Fp2ArithInstr::new(ModOp::Sub, rd_reg, rs1_reg, rs2_reg, info.num_limbs, info.modulus_bytes.clone()), )), - x if x == Fp2Opcode::SETUP_ADDSUB as usize => Instr::Ext(Box::new(Fp2SetupInstr::new("rvr_ext_fp2_setup", rd_reg, rs1_reg, rs2_reg, info.num_limbs))), + x if x == Fp2Opcode::SETUP_ADDSUB as usize => Instr::Ext(Box::new(Fp2SetupInstr::new(rd_reg, rs1_reg, rs2_reg, info.num_limbs))), x if x == Fp2Opcode::MUL as usize => Instr::Ext(Box::new( Fp2ArithInstr::new(ModOp::Mul, rd_reg, rs1_reg, rs2_reg, info.num_limbs, info.modulus_bytes.clone()), )), x if x == Fp2Opcode::DIV as usize => Instr::Ext(Box::new( Fp2ArithInstr::new(ModOp::Div, rd_reg, rs1_reg, rs2_reg, info.num_limbs, info.modulus_bytes.clone()), )), - x if x == Fp2Opcode::SETUP_MULDIV as usize => Instr::Ext(Box::new(Fp2SetupInstr::new("rvr_ext_fp2_setup", rd_reg, rs1_reg, rs2_reg, info.num_limbs))), + x if x == Fp2Opcode::SETUP_MULDIV as usize => Instr::Ext(Box::new(Fp2SetupInstr::new(rd_reg, rs1_reg, rs2_reg, info.num_limbs))), _ => return None, }; diff --git a/extensions/algebra/rvr/src/lib.rs b/extensions/algebra/rvr/src/lib.rs index e11d535328..ae37afe748 100644 --- a/extensions/algebra/rvr/src/lib.rs +++ b/extensions/algebra/rvr/src/lib.rs @@ -12,10 +12,9 @@ mod modular; use num_bigint::BigUint; -pub(crate) use common::{ArithKind, FieldArithInstr, FieldIsEqInstr}; -pub use common::FieldSetupInstr; -pub use fp2::{Fp2RvrExtension, Fp2SetupInstr}; -pub use modular::{HintNonQrInstr, HintSqrtInstr, ModSetupInstr, ModularRvrExtension}; +pub(crate) use common::{ArithKind, FieldArithInstr, FieldIsEqInstr, FieldKind, FieldSetupInstr, IsEqKind, SetupKind}; +pub use fp2::Fp2RvrExtension; +pub use modular::{HintNonQrInstr, HintSqrtInstr, ModularRvrExtension}; /// Zero-pad `modulus` to the canonical limb boundary (32 or 48 bytes). /// diff --git a/extensions/algebra/rvr/src/modular.rs b/extensions/algebra/rvr/src/modular.rs index 183f48b784..621bf810ca 100644 --- a/extensions/algebra/rvr/src/modular.rs +++ b/extensions/algebra/rvr/src/modular.rs @@ -12,8 +12,8 @@ use rvr_openvm_lift::{helpers::decode_reg, RvrExtension}; use strum::EnumCount; use crate::{ - format_c_byte_array, pad_modulus, ArithKind, FieldArithInstr, FieldIsEqInstr, FieldSetupInstr, - KnownField, ModOp, + format_c_byte_array, pad_modulus, ArithKind, FieldArithInstr, FieldIsEqInstr, FieldKind, + FieldSetupInstr, IsEqKind, KnownField, ModOp, SetupKind, }; include!(concat!(env!("OUT_DIR"), "/secp256k1_files.rs")); @@ -54,10 +54,7 @@ fn make_modulus_info(modulus: &BigUint, rng: &mut StdRng) -> ModulusInfo { #[derive(Debug, Clone)] pub(crate) struct ModArithKind; -impl ArithKind for ModArithKind { - fn opname() -> &'static str { - "mod_arith" - } +impl FieldKind for ModArithKind { fn c_prefix() -> &'static str { "mod" } @@ -66,6 +63,24 @@ impl ArithKind for ModArithKind { } } +impl ArithKind for ModArithKind { + fn opname() -> &'static str { + "mod_arith" + } +} + +impl SetupKind for ModArithKind { + fn opname() -> &'static str { + "mod_setup" + } +} + +impl IsEqKind for ModArithKind { + fn opname() -> &'static str { + "mod_iseq" + } +} + /// IR node for modular arithmetic (ADD, SUB, MUL, DIV). pub(crate) type ModArithInstr = FieldArithInstr; @@ -73,7 +88,7 @@ pub(crate) type ModArithInstr = FieldArithInstr; pub(crate) type ModIsEqInstr = FieldIsEqInstr; /// IR node for modular SETUP (SETUP_ADDSUB, SETUP_MULDIV, SETUP_ISEQ). -pub type ModSetupInstr = FieldSetupInstr; +pub(crate) type ModSetupInstr = FieldSetupInstr; // ── Phantom instructions (HintNonQr, HintSqrt) ────────────────────────────── @@ -269,7 +284,7 @@ impl ModularRvrExtension { ModArithInstr::new(ModOp::Sub, rd_reg, rs1_reg, rs2_reg, info.num_limbs, info.modulus_bytes.clone()), )), x if x == Rv64ModularArithmeticOpcode::SETUP_ADDSUB as usize => { - Instr::Ext(Box::new(ModSetupInstr::new("rvr_ext_mod_setup", rd_reg, rs1_reg, rs2_reg, info.num_limbs))) + Instr::Ext(Box::new(ModSetupInstr::new(rd_reg, rs1_reg, rs2_reg, info.num_limbs))) } x if x == Rv64ModularArithmeticOpcode::MUL as usize => Instr::Ext(Box::new( ModArithInstr::new(ModOp::Mul, rd_reg, rs1_reg, rs2_reg, info.num_limbs, info.modulus_bytes.clone()), @@ -278,7 +293,7 @@ impl ModularRvrExtension { ModArithInstr::new(ModOp::Div, rd_reg, rs1_reg, rs2_reg, info.num_limbs, info.modulus_bytes.clone()), )), x if x == Rv64ModularArithmeticOpcode::SETUP_MULDIV as usize => { - Instr::Ext(Box::new(ModSetupInstr::new("rvr_ext_mod_setup", rd_reg, rs1_reg, rs2_reg, info.num_limbs))) + Instr::Ext(Box::new(ModSetupInstr::new(rd_reg, rs1_reg, rs2_reg, info.num_limbs))) } x if x == Rv64ModularArithmeticOpcode::IS_EQ as usize => { Instr::Ext(Box::new(ModIsEqInstr::new( @@ -290,7 +305,7 @@ impl ModularRvrExtension { ))) } x if x == Rv64ModularArithmeticOpcode::SETUP_ISEQ as usize => { - Instr::Ext(Box::new(ModSetupInstr::new("rvr_ext_mod_setup", rd_reg, rs1_reg, rs2_reg, info.num_limbs))) + Instr::Ext(Box::new(ModSetupInstr::new(rd_reg, rs1_reg, rs2_reg, info.num_limbs))) } _ => return None, }; From b1609a1db1793910ed96dfc9fc5f9296a3d36d48 Mon Sep 17 00:00:00 2001 From: Mansur Mukimbekov Date: Mon, 29 Jun 2026 16:15:48 -0400 Subject: [PATCH 10/10] refactor: apply fmt --- extensions/algebra/rvr/ffi/common/src/lib.rs | 27 +++++--- extensions/algebra/rvr/ffi/fp2/src/lib.rs | 12 ++-- extensions/algebra/rvr/ffi/modular/src/lib.rs | 20 ++++-- extensions/algebra/rvr/src/common.rs | 35 +++++++--- extensions/algebra/rvr/src/fp2.rs | 64 +++++++++++++------ extensions/algebra/rvr/src/lib.rs | 7 +- extensions/algebra/rvr/src/modular.rs | 64 +++++++++++++------ extensions/pairing/rvr/ffi/src/lib.rs | 21 ++++-- 8 files changed, 178 insertions(+), 72 deletions(-) diff --git a/extensions/algebra/rvr/ffi/common/src/lib.rs b/extensions/algebra/rvr/ffi/common/src/lib.rs index adf3d2461e..81b849e649 100644 --- a/extensions/algebra/rvr/ffi/common/src/lib.rs +++ b/extensions/algebra/rvr/ffi/common/src/lib.rs @@ -68,15 +68,25 @@ impl FieldArith for T { } #[inline(always)] - fn add(&self, a: Self::Elem, b: Self::Elem) -> Self::Elem { a + b } + fn add(&self, a: Self::Elem, b: Self::Elem) -> Self::Elem { + a + b + } #[inline(always)] - fn sub(&self, a: Self::Elem, b: Self::Elem) -> Self::Elem { a - b } + fn sub(&self, a: Self::Elem, b: Self::Elem) -> Self::Elem { + a - b + } #[inline(always)] - fn mul(&self, a: Self::Elem, b: Self::Elem) -> Self::Elem { a * b } + fn mul(&self, a: Self::Elem, b: Self::Elem) -> Self::Elem { + a * b + } #[inline(always)] - fn div(&self, a: Self::Elem, b: Self::Elem) -> Self::Elem { a * b.invert().unwrap() } + fn div(&self, a: Self::Elem, b: Self::Elem) -> Self::Elem { + a * b.invert().unwrap() + } #[inline(always)] - fn is_eq(&self, a: &Self::Elem, b: &Self::Elem) -> bool { a == b } + fn is_eq(&self, a: &Self::Elem, b: &Self::Elem) -> bool { + a == b + } } // ── 256-bit / 384-bit field I/O ───────────────────────────────────────────── @@ -265,9 +275,10 @@ macro_rules! unknown_field_op_fn { num_limbs: u32, modulus_ptr: *const u8, ) { - let modulus = ::num_bigint::BigUint::from_bytes_le( - ::std::slice::from_raw_parts(modulus_ptr, num_limbs as usize), - ); + let modulus = ::num_bigint::BigUint::from_bytes_le(::std::slice::from_raw_parts( + modulus_ptr, + num_limbs as usize, + )); let f = $field_ty { modulus, num_limbs }; $crate::exec_op(&f, state, rd_ptr, rs1_ptr, rs2_ptr, |f, a, b| f.$op(a, b)); } diff --git a/extensions/algebra/rvr/ffi/fp2/src/lib.rs b/extensions/algebra/rvr/ffi/fp2/src/lib.rs index 6a60a4a136..9fef4e9152 100644 --- a/extensions/algebra/rvr/ffi/fp2/src/lib.rs +++ b/extensions/algebra/rvr/ffi/fp2/src/lib.rs @@ -9,9 +9,9 @@ use std::{ffi::c_void, marker::PhantomData}; use num_bigint::BigUint; use rvr_openvm_ext_algebra_ffi_common::{ - known_field_op_fn, mod_inverse, read_bigint, read_bls12_381_fq, read_field_256, - write_bigint, write_bls12_381_fq, write_field_256, FieldArith, KnownFieldArith, - BLS12_381_ELEM_BYTES, FIELD_256_BYTES, + known_field_op_fn, mod_inverse, read_bigint, read_bls12_381_fq, read_field_256, write_bigint, + write_bls12_381_fq, write_field_256, FieldArith, KnownFieldArith, BLS12_381_ELEM_BYTES, + FIELD_256_BYTES, }; use rvr_openvm_ext_ffi_common::{ rd_mem_words_traced, trace_mem_access_range, wr_mem_words_traced, AS_MEMORY, WORD_SIZE, @@ -42,7 +42,11 @@ impl KnownFieldArith for KnownComplexField { #[inline(always)] unsafe fn write_elem(&self, state: *mut c_void, ptr: u32, val: &Self::Elem) { write_field_256::(state, ptr, &val.c0); - write_field_256::(state, ptr + FIELD_256_BYTES as u32, &val.c1); + write_field_256::( + state, + ptr + FIELD_256_BYTES as u32, + &val.c1, + ); } } diff --git a/extensions/algebra/rvr/ffi/modular/src/lib.rs b/extensions/algebra/rvr/ffi/modular/src/lib.rs index 81ebcd3273..d7db3af0b3 100644 --- a/extensions/algebra/rvr/ffi/modular/src/lib.rs +++ b/extensions/algebra/rvr/ffi/modular/src/lib.rs @@ -16,8 +16,8 @@ use halo2curves_axiom::ff::PrimeField; use num_bigint::BigUint; use num_traits::One; use rvr_openvm_ext_algebra_ffi_common::{ - known_field_op_fn, mod_inverse, read_bigint, read_bls12_381_fq, read_field_256, - write_bigint, write_bls12_381_fq, write_field_256, FieldArith, KnownFieldArith, + known_field_op_fn, mod_inverse, read_bigint, read_bls12_381_fq, read_field_256, write_bigint, + write_bls12_381_fq, write_field_256, FieldArith, KnownFieldArith, }; use rvr_openvm_ext_ffi_common::{ ext_hint_stream_set, rd_mem_u64_range_wrapper, rd_mem_words_traced, trace_mem_access_range, @@ -48,9 +48,13 @@ struct UnknownPrimeField { impl> KnownFieldArith for KnownPrimeField { type Elem = F; #[inline(always)] - unsafe fn read_elem(&self, state: *mut c_void, ptr: u32) -> F { read_field_256(state, ptr) } + unsafe fn read_elem(&self, state: *mut c_void, ptr: u32) -> F { + read_field_256(state, ptr) + } #[inline(always)] - unsafe fn write_elem(&self, state: *mut c_void, ptr: u32, val: &F) { write_field_256(state, ptr, val) } + unsafe fn write_elem(&self, state: *mut c_void, ptr: u32, val: &F) { + write_field_256(state, ptr, val) + } } // ── KnownPrimeField impl (BLS12-381 Fq, 48 bytes) ────────────────────────── @@ -58,9 +62,13 @@ impl> KnownFieldArith for KnownPrimeField { impl KnownFieldArith for KnownPrimeField { type Elem = blstrs::Fp; #[inline(always)] - unsafe fn read_elem(&self, state: *mut c_void, ptr: u32) -> blstrs::Fp { read_bls12_381_fq(state, ptr) } + unsafe fn read_elem(&self, state: *mut c_void, ptr: u32) -> blstrs::Fp { + read_bls12_381_fq(state, ptr) + } #[inline(always)] - unsafe fn write_elem(&self, state: *mut c_void, ptr: u32, val: &blstrs::Fp) { write_bls12_381_fq(state, ptr, val) } + unsafe fn write_elem(&self, state: *mut c_void, ptr: u32, val: &blstrs::Fp) { + write_bls12_381_fq(state, ptr, val) + } } // ── UnknownPrimeField impl ────────────────────────────────────────────────── diff --git a/extensions/algebra/rvr/src/common.rs b/extensions/algebra/rvr/src/common.rs index b779c99e7a..f7efaee06d 100644 --- a/extensions/algebra/rvr/src/common.rs +++ b/extensions/algebra/rvr/src/common.rs @@ -55,7 +55,15 @@ impl FieldArithInstr { num_limbs: u32, modulus: Vec, ) -> Self { - Self { _kind: PhantomData, op, rd_reg, rs1_reg, rs2_reg, num_limbs, modulus } + Self { + _kind: PhantomData, + op, + rd_reg, + rs1_reg, + rs2_reg, + num_limbs, + modulus, + } } } @@ -72,14 +80,15 @@ pub(crate) struct FieldIsEqInstr { } impl FieldIsEqInstr { - pub fn new( - rd_reg: Reg, - rs1_reg: Reg, - rs2_reg: Reg, - num_limbs: u32, - modulus: Vec, - ) -> Self { - Self { _kind: PhantomData, rd_reg, rs1_reg, rs2_reg, num_limbs, modulus } + pub fn new(rd_reg: Reg, rs1_reg: Reg, rs2_reg: Reg, num_limbs: u32, modulus: Vec) -> Self { + Self { + _kind: PhantomData, + rd_reg, + rs1_reg, + rs2_reg, + num_limbs, + modulus, + } } } @@ -98,7 +107,13 @@ pub(crate) struct FieldSetupInstr { impl FieldSetupInstr { pub fn new(rd_reg: Reg, rs1_reg: Reg, rs2_reg: Reg, num_limbs: u32) -> Self { - Self { _kind: PhantomData, rd_reg, rs1_reg, rs2_reg, num_limbs } + Self { + _kind: PhantomData, + rd_reg, + rs1_reg, + rs2_reg, + num_limbs, + } } } diff --git a/extensions/algebra/rvr/src/fp2.rs b/extensions/algebra/rvr/src/fp2.rs index 107def3629..898c1fe851 100644 --- a/extensions/algebra/rvr/src/fp2.rs +++ b/extensions/algebra/rvr/src/fp2.rs @@ -8,7 +8,10 @@ use rvr_openvm_ir::{Instr, InstrAt, LiftedInstr}; use rvr_openvm_lift::{helpers::decode_reg, RvrExtension}; use strum::EnumCount; -use crate::{pad_modulus, ArithKind, FieldArithInstr, FieldKind, FieldSetupInstr, KnownField, ModOp, SetupKind}; +use crate::{ + pad_modulus, ArithKind, FieldArithInstr, FieldKind, FieldSetupInstr, KnownField, ModOp, + SetupKind, +}; /// Per-modulus info for the Fp2 extension. Fp2 lifting never consults a /// non-QR, so we only carry the padded modulus and limb count. @@ -122,23 +125,48 @@ impl Fp2RvrExtension { let rs1_reg = decode_reg(insn.b); let rs2_reg = decode_reg(insn.c); - let instr: Instr = match local { - x if x == Fp2Opcode::ADD as usize => Instr::Ext(Box::new( - Fp2ArithInstr::new(ModOp::Add, rd_reg, rs1_reg, rs2_reg, info.num_limbs, info.modulus_bytes.clone()), - )), - x if x == Fp2Opcode::SUB as usize => Instr::Ext(Box::new( - Fp2ArithInstr::new(ModOp::Sub, rd_reg, rs1_reg, rs2_reg, info.num_limbs, info.modulus_bytes.clone()), - )), - x if x == Fp2Opcode::SETUP_ADDSUB as usize => Instr::Ext(Box::new(Fp2SetupInstr::new(rd_reg, rs1_reg, rs2_reg, info.num_limbs))), - x if x == Fp2Opcode::MUL as usize => Instr::Ext(Box::new( - Fp2ArithInstr::new(ModOp::Mul, rd_reg, rs1_reg, rs2_reg, info.num_limbs, info.modulus_bytes.clone()), - )), - x if x == Fp2Opcode::DIV as usize => Instr::Ext(Box::new( - Fp2ArithInstr::new(ModOp::Div, rd_reg, rs1_reg, rs2_reg, info.num_limbs, info.modulus_bytes.clone()), - )), - x if x == Fp2Opcode::SETUP_MULDIV as usize => Instr::Ext(Box::new(Fp2SetupInstr::new(rd_reg, rs1_reg, rs2_reg, info.num_limbs))), - _ => return None, - }; + let instr: Instr = + match local { + x if x == Fp2Opcode::ADD as usize => Instr::Ext(Box::new(Fp2ArithInstr::new( + ModOp::Add, + rd_reg, + rs1_reg, + rs2_reg, + info.num_limbs, + info.modulus_bytes.clone(), + ))), + x if x == Fp2Opcode::SUB as usize => Instr::Ext(Box::new(Fp2ArithInstr::new( + ModOp::Sub, + rd_reg, + rs1_reg, + rs2_reg, + info.num_limbs, + info.modulus_bytes.clone(), + ))), + x if x == Fp2Opcode::SETUP_ADDSUB as usize => Instr::Ext(Box::new( + Fp2SetupInstr::new(rd_reg, rs1_reg, rs2_reg, info.num_limbs), + )), + x if x == Fp2Opcode::MUL as usize => Instr::Ext(Box::new(Fp2ArithInstr::new( + ModOp::Mul, + rd_reg, + rs1_reg, + rs2_reg, + info.num_limbs, + info.modulus_bytes.clone(), + ))), + x if x == Fp2Opcode::DIV as usize => Instr::Ext(Box::new(Fp2ArithInstr::new( + ModOp::Div, + rd_reg, + rs1_reg, + rs2_reg, + info.num_limbs, + info.modulus_bytes.clone(), + ))), + x if x == Fp2Opcode::SETUP_MULDIV as usize => Instr::Ext(Box::new( + Fp2SetupInstr::new(rd_reg, rs1_reg, rs2_reg, info.num_limbs), + )), + _ => return None, + }; Some(LiftedInstr::Body(InstrAt { pc, diff --git a/extensions/algebra/rvr/src/lib.rs b/extensions/algebra/rvr/src/lib.rs index ae37afe748..d555181db6 100644 --- a/extensions/algebra/rvr/src/lib.rs +++ b/extensions/algebra/rvr/src/lib.rs @@ -10,11 +10,12 @@ mod common; mod fp2; mod modular; -use num_bigint::BigUint; - -pub(crate) use common::{ArithKind, FieldArithInstr, FieldIsEqInstr, FieldKind, FieldSetupInstr, IsEqKind, SetupKind}; +pub(crate) use common::{ + ArithKind, FieldArithInstr, FieldIsEqInstr, FieldKind, FieldSetupInstr, IsEqKind, SetupKind, +}; pub use fp2::Fp2RvrExtension; pub use modular::{HintNonQrInstr, HintSqrtInstr, ModularRvrExtension}; +use num_bigint::BigUint; /// Zero-pad `modulus` to the canonical limb boundary (32 or 48 bytes). /// diff --git a/extensions/algebra/rvr/src/modular.rs b/extensions/algebra/rvr/src/modular.rs index 621bf810ca..5e19b55fc7 100644 --- a/extensions/algebra/rvr/src/modular.rs +++ b/extensions/algebra/rvr/src/modular.rs @@ -277,24 +277,52 @@ impl ModularRvrExtension { let rs2_reg = decode_reg(insn.c); let instr: Instr = match local { - x if x == Rv64ModularArithmeticOpcode::ADD as usize => Instr::Ext(Box::new( - ModArithInstr::new(ModOp::Add, rd_reg, rs1_reg, rs2_reg, info.num_limbs, info.modulus_bytes.clone()), - )), - x if x == Rv64ModularArithmeticOpcode::SUB as usize => Instr::Ext(Box::new( - ModArithInstr::new(ModOp::Sub, rd_reg, rs1_reg, rs2_reg, info.num_limbs, info.modulus_bytes.clone()), - )), - x if x == Rv64ModularArithmeticOpcode::SETUP_ADDSUB as usize => { - Instr::Ext(Box::new(ModSetupInstr::new(rd_reg, rs1_reg, rs2_reg, info.num_limbs))) + x if x == Rv64ModularArithmeticOpcode::ADD as usize => { + Instr::Ext(Box::new(ModArithInstr::new( + ModOp::Add, + rd_reg, + rs1_reg, + rs2_reg, + info.num_limbs, + info.modulus_bytes.clone(), + ))) } - x if x == Rv64ModularArithmeticOpcode::MUL as usize => Instr::Ext(Box::new( - ModArithInstr::new(ModOp::Mul, rd_reg, rs1_reg, rs2_reg, info.num_limbs, info.modulus_bytes.clone()), - )), - x if x == Rv64ModularArithmeticOpcode::DIV as usize => Instr::Ext(Box::new( - ModArithInstr::new(ModOp::Div, rd_reg, rs1_reg, rs2_reg, info.num_limbs, info.modulus_bytes.clone()), + x if x == Rv64ModularArithmeticOpcode::SUB as usize => { + Instr::Ext(Box::new(ModArithInstr::new( + ModOp::Sub, + rd_reg, + rs1_reg, + rs2_reg, + info.num_limbs, + info.modulus_bytes.clone(), + ))) + } + x if x == Rv64ModularArithmeticOpcode::SETUP_ADDSUB as usize => Instr::Ext(Box::new( + ModSetupInstr::new(rd_reg, rs1_reg, rs2_reg, info.num_limbs), )), - x if x == Rv64ModularArithmeticOpcode::SETUP_MULDIV as usize => { - Instr::Ext(Box::new(ModSetupInstr::new(rd_reg, rs1_reg, rs2_reg, info.num_limbs))) + x if x == Rv64ModularArithmeticOpcode::MUL as usize => { + Instr::Ext(Box::new(ModArithInstr::new( + ModOp::Mul, + rd_reg, + rs1_reg, + rs2_reg, + info.num_limbs, + info.modulus_bytes.clone(), + ))) + } + x if x == Rv64ModularArithmeticOpcode::DIV as usize => { + Instr::Ext(Box::new(ModArithInstr::new( + ModOp::Div, + rd_reg, + rs1_reg, + rs2_reg, + info.num_limbs, + info.modulus_bytes.clone(), + ))) } + x if x == Rv64ModularArithmeticOpcode::SETUP_MULDIV as usize => Instr::Ext(Box::new( + ModSetupInstr::new(rd_reg, rs1_reg, rs2_reg, info.num_limbs), + )), x if x == Rv64ModularArithmeticOpcode::IS_EQ as usize => { Instr::Ext(Box::new(ModIsEqInstr::new( rd_reg, @@ -304,9 +332,9 @@ impl ModularRvrExtension { info.modulus_bytes.clone(), ))) } - x if x == Rv64ModularArithmeticOpcode::SETUP_ISEQ as usize => { - Instr::Ext(Box::new(ModSetupInstr::new(rd_reg, rs1_reg, rs2_reg, info.num_limbs))) - } + x if x == Rv64ModularArithmeticOpcode::SETUP_ISEQ as usize => Instr::Ext(Box::new( + ModSetupInstr::new(rd_reg, rs1_reg, rs2_reg, info.num_limbs), + )), _ => return None, }; diff --git a/extensions/pairing/rvr/ffi/src/lib.rs b/extensions/pairing/rvr/ffi/src/lib.rs index e008eadc46..2cd3492155 100644 --- a/extensions/pairing/rvr/ffi/src/lib.rs +++ b/extensions/pairing/rvr/ffi/src/lib.rs @@ -6,7 +6,10 @@ use std::ffi::c_void; use halo2curves_axiom::{bls12_381, bn256, ff::PrimeField}; -use openvm_ecc_guest::{algebra::{field::FieldExtension, Field}, AffinePoint}; +use openvm_ecc_guest::{ + algebra::{field::FieldExtension, Field}, + AffinePoint, +}; use openvm_pairing_guest::{ halo2curves_shims::{bls12_381::Bls12_381, bn254::Bn254}, pairing::{FinalExp, MultiMillerLoop}, @@ -126,7 +129,9 @@ unsafe fn read_pairing_points( unsafe fn hint_bn254(state: *mut c_void, rs1_val: u32, rs2_val: u32) -> Option> { let (p, q) = read_pairing_points( - state, rs1_val, rs2_val, + state, + rs1_val, + rs2_val, BN254_FQ_BYTES, |s, ptr| unsafe { read_bn254_fq(s, ptr) }, bn256::Fq2::new, @@ -134,7 +139,9 @@ unsafe fn hint_bn254(state: *mut c_void, rs1_val: u32, rs2_val: u32) -> Option Option Option> { let (p, q) = read_pairing_points( - state, rs1_val, rs2_val, + state, + rs1_val, + rs2_val, BLS12_381_FQ_BYTES, |s, ptr| unsafe { read_bls12_381_fq(s, ptr) }, |c0, c1| bls12_381::Fq2 { c0, c1 }, @@ -153,7 +162,9 @@ unsafe fn hint_bls12_381(state: *mut c_void, rs1_val: u32, rs2_val: u32) -> Opti let f: bls12_381::Fq12 = Bls12_381::multi_miller_loop(&p, &q); let (c, u) = Bls12_381::final_exp_hint(&f); Some( - c.to_coeffs().into_iter().chain(u.to_coeffs()) + c.to_coeffs() + .into_iter() + .chain(u.to_coeffs()) .flat_map(|fp2| fp2.to_coeffs()) .flat_map(|fp| fp.to_bytes()) .collect(),