Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
105 changes: 104 additions & 1 deletion extensions/algebra/rvr/ffi/common/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -37,6 +37,58 @@ 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<T: KnownFieldArith> 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).
Expand Down Expand Up @@ -182,6 +234,57 @@ pub fn mod_inverse(a: &BigUint, p: &BigUint) -> BigUint {
.unwrap()
}

// ── 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.
///
/// `$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.
Expand Down
102 changes: 14 additions & 88 deletions extensions/algebra/rvr/ffi/fp2/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
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,
Expand All @@ -28,7 +28,7 @@ struct UnknownComplexField {

// ── KnownComplexField impls ─────────────────────────────────────────────────

impl FieldArith for KnownComplexField<halo2curves_axiom::bn256::Fq2> {
impl KnownFieldArith for KnownComplexField<halo2curves_axiom::bn256::Fq2> {
type Elem = halo2curves_axiom::bn256::Fq2;

#[inline(always)]
Expand All @@ -48,30 +48,9 @@ impl FieldArith for KnownComplexField<halo2curves_axiom::bn256::Fq2> {
&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
}
}

impl FieldArith for KnownComplexField<blstrs::Fp2> {
impl KnownFieldArith for KnownComplexField<blstrs::Fp2> {
type Elem = blstrs::Fp2;

#[inline(always)]
Expand All @@ -87,27 +66,6 @@ impl FieldArith for KnownComplexField<blstrs::Fp2> {
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 ────────────────────────────────────────────────
Expand Down Expand Up @@ -162,25 +120,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!([<rvr_ext_fp2_add_ $suffix>], $field, add);
field_op_fn!([<rvr_ext_fp2_sub_ $suffix>], $field, sub);
field_op_fn!([<rvr_ext_fp2_mul_ $suffix>], $field, mul);
field_op_fn!([<rvr_ext_fp2_div_ $suffix>], $field, div);
known_field_op_fn!([<rvr_ext_fp2_add_ $suffix>], KnownComplexField, $field, add);
known_field_op_fn!([<rvr_ext_fp2_sub_ $suffix>], KnownComplexField, $field, sub);
known_field_op_fn!([<rvr_ext_fp2_mul_ $suffix>], KnownComplexField, $field, mul);
known_field_op_fn!([<rvr_ext_fp2_div_ $suffix>], KnownComplexField, $field, div);
}
};
}
Expand All @@ -190,32 +136,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.
Expand Down
Loading
Loading