Repository: openvm-org/openvm
Pull request #1486: fix: alignment in unsafe rust
Author: login: jonathanpwang
Created: 2025-03-19T19:18:59Z | Merged: 2025-03-20T00:57:11Z

Rust treats `[u8; LEN]` as having alignment of `1`, so if you unsafely transmute or convert a slice to `[T; LEN]` which expects alignment of `align_of<T>()`, then this may result in misaligned load/store instructions, often during operations like memcpy. OpenVM RV32 does not support misaligned access (see https://github.com/openvm-org/openvm/pull/1474), so we make sure all our use of unsafe Rust has proper alignment.


Diff:
diff --git a/benchmarks/programs/kitchen-sink/src/main.rs b/benchmarks/programs/kitchen-sink/src/main.rs
index 1892787158..8c12e165f7 100644
--- a/benchmarks/programs/kitchen-sink/src/main.rs
+++ b/benchmarks/programs/kitchen-sink/src/main.rs
@@ -82,8 +82,8 @@ pub fn main() {
         hash.extend_from_slice(&digest2);
 
         // SAFETY: internally I256 is represented as [u8; 32]
-        let i1 = unsafe { transmute::<[u8; 32], I256>(digest1) };
-        let i2 = unsafe { transmute::<[u8; 32], I256>(digest2) };
+        let i1 = I256::from_le_bytes(digest1);
+        let i2 = I256::from_le_bytes(digest2);
 
         black_box(&i1 + &i2);
         black_box(&i1 - &i2);
diff --git a/crates/toolchain/openvm/src/io/read.rs b/crates/toolchain/openvm/src/io/read.rs
index d5305f761c..39b2166e39 100644
--- a/crates/toolchain/openvm/src/io/read.rs
+++ b/crates/toolchain/openvm/src/io/read.rs
@@ -63,10 +63,11 @@ impl WordRead for Reader {
         let remainder = bytes.chunks_exact_mut(WORD_SIZE).into_remainder();
         if !remainder.is_empty() {
             num_padded_bytes += WORD_SIZE - remainder.len();
-            let mut padded = MaybeUninit::<[u8; WORD_SIZE]>::uninit();
-            hint_store_word(padded.as_mut_ptr() as *mut u32);
+            let mut padded = MaybeUninit::<u32>::uninit();
+            hint_store_word(padded.as_mut_ptr());
             let padded = unsafe { padded.assume_init() };
-            remainder.copy_from_slice(&padded[..remainder.len()]);
+            // We use native endian so its equivalent to transmuting u32 to [u8; 4]
+            remainder.copy_from_slice(&padded.to_ne_bytes()[..remainder.len()]);
         }
         // If we reached EOF, then we set to 0.
         // Otherwise, we need to subtract the padding as well.
diff --git a/extensions/algebra/moduli-macros/src/lib.rs b/extensions/algebra/moduli-macros/src/lib.rs
index ea49bef8a0..5e1958ccfc 100644
--- a/extensions/algebra/moduli-macros/src/lib.rs
+++ b/extensions/algebra/moduli-macros/src/lib.rs
@@ -751,15 +751,20 @@ pub fn moduli_init(input: TokenStream) -> TokenStream {
 
         let modulus_bytes = string_to_bytes(&modulus);
         let mut limbs = modulus_bytes.len();
+        let mut block_size = 32;
 
         if limbs <= 32 {
             limbs = 32;
         } else if limbs <= 48 {
             limbs = 48;
+            block_size = 16;
         } else {
             panic!("limbs must be at most 48");
         }
 
+        let block_size = proc_macro::Literal::usize_unsuffixed(block_size);
+        let block_size = syn::Lit::new(block_size.to_string().parse::<_>().unwrap());
+
         let modulus_bytes = modulus_bytes
             .into_iter()
             .chain(vec![0u8; limbs])
@@ -861,9 +866,13 @@ pub fn moduli_init(input: TokenStream) -> TokenStream {
                     ptr += 4;
                     let remaining = &#serialized_name[ptr..];
 
+                    // To avoid importing #struct_name, we create a placeholder struct with the same size and alignment.
+                    #[repr(C, align(#block_size))]
+                    struct AlignedPlaceholder([u8; #limbs]);
+
                     // We are going to use the numeric representation of the `rs2` register to distinguish the chip to setup.
                     // The transpiler will transform this instruction, based on whether `rs2` is `x0`, `x1` or `x2`, into a `SETUP_ADDSUB`, `SETUP_MULDIV` or `SETUP_ISEQ` instruction.
-                    let mut uninit: core::mem::MaybeUninit<[u8; #limbs]> = core::mem::MaybeUninit::uninit();
+                    let mut uninit: core::mem::MaybeUninit<AlignedPlaceholder> = core::mem::MaybeUninit::uninit();
                     openvm::platform::custom_insn_r!(
                         opcode = ::openvm_algebra_guest::OPCODE,
                         funct3 = ::openvm_algebra_guest::MODULAR_ARITHMETIC_FUNCT3,
diff --git a/extensions/bigint/guest/src/i256.rs b/extensions/bigint/guest/src/i256.rs
index 43421b4eca..6b104029e7 100644
--- a/extensions/bigint/guest/src/i256.rs
+++ b/extensions/bigint/guest/src/i256.rs
@@ -34,6 +34,11 @@ impl I256 {
     /// The zero constant.
     pub const ZERO: Self = Self { limbs: [0u8; 32] };
 
+    /// Construct [I256] from little-endian bytes.
+    pub const fn from_le_bytes(bytes: [u8; 32]) -> Self {
+        Self { limbs: bytes }
+    }
+
     /// Value of this I256 as a BigInt.
     #[cfg(not(target_os = "zkvm"))]
     pub fn as_bigint(&self) -> BigInt {
diff --git a/extensions/bigint/guest/src/u256.rs b/extensions/bigint/guest/src/u256.rs
index c637c66c9d..feeffce4ac 100644
--- a/extensions/bigint/guest/src/u256.rs
+++ b/extensions/bigint/guest/src/u256.rs
@@ -36,6 +36,11 @@ impl U256 {
     /// The zero constant.
     pub const ZERO: Self = Self { limbs: [0u8; 32] };
 
+    /// Construct [U256] from little-endian bytes.
+    pub const fn from_le_bytes(bytes: [u8; 32]) -> Self {
+        Self { limbs: bytes }
+    }
+
     /// Value of this U256 as a BigUint.
     #[cfg(not(target_os = "zkvm"))]
     pub fn as_biguint(&self) -> BigUint {
diff --git a/extensions/pairing/tests/programs/examples/fp12_mul.rs b/extensions/pairing/tests/programs/examples/fp12_mul.rs
index a8874c5081..fcee30c094 100644
--- a/extensions/pairing/tests/programs/examples/fp12_mul.rs
+++ b/extensions/pairing/tests/programs/examples/fp12_mul.rs
@@ -32,8 +32,8 @@ mod bn254 {
         let f1 = &io[32 * 12..32 * 24];
         let r_cmp = &io[32 * 24..32 * 36];
 
-        let f0_cast = unsafe { &*(f0.as_ptr() as *const Fp12) };
-        let f1_cast = unsafe { &*(f1.as_ptr() as *const Fp12) };
+        let f0_cast = Fp12::from_bytes(f0);
+        let f1_cast = Fp12::from_bytes(f1);
 
         let r = f0_cast * f1_cast;
         let mut r_bytes = [0u8; 32 * 12];
@@ -71,8 +71,8 @@ mod bls12_381 {
         let f1 = &io[48 * 12..48 * 24];
         let r_cmp = &io[48 * 24..48 * 36];
 
-        let f0_cast = unsafe { &*(f0.as_ptr() as *const Fp12) };
-        let f1_cast = unsafe { &*(f1.as_ptr() as *const Fp12) };
+        let f0_cast = Fp12::from_bytes(f0);
+        let f1_cast = Fp12::from_bytes(f1);
 
         let r = f0_cast * f1_cast;
         let mut r_bytes = [0u8; 48 * 12];
diff --git a/extensions/pairing/tests/programs/examples/pairing_check.rs b/extensions/pairing/tests/programs/examples/pairing_check.rs
index 3ef0fc285e..840caf4610 100644
--- a/extensions/pairing/tests/programs/examples/pairing_check.rs
+++ b/extensions/pairing/tests/programs/examples/pairing_check.rs
@@ -15,7 +15,7 @@ openvm::entry!(main);
 mod bn254 {
     use alloc::format;
 
-    use openvm_algebra_guest::IntMod;
+    use openvm_algebra_guest::{field::FieldExtension, IntMod};
     use openvm_pairing_guest::bn254::{Bn254, Fp, Fp2};
 
     use super::*;
@@ -37,10 +37,12 @@ mod bn254 {
         let q0 = &io[32 * 4..32 * 8];
         let q1 = &io[32 * 8..32 * 12];
 
-        let s0_cast = unsafe { &*(s0.as_ptr() as *const AffinePoint<Fp>) };
-        let s1_cast = unsafe { &*(s1.as_ptr() as *const AffinePoint<Fp>) };
-        let q0_cast = unsafe { &*(q0.as_ptr() as *const AffinePoint<Fp2>) };
-        let q1_cast = unsafe { &*(q1.as_ptr() as *const AffinePoint<Fp2>) };
+        let s0_cast =
+            AffinePoint::new(Fp::from_le_bytes(&s0[..32]), Fp::from_le_bytes(&s0[32..64]));
+        let s1_cast =
+            AffinePoint::new(Fp::from_le_bytes(&s1[..32]), Fp::from_le_bytes(&s1[32..64]));
+        let q0_cast = AffinePoint::new(Fp2::from_bytes(&q0[..64]), Fp2::from_bytes(&q0[64..128]));
+        let q1_cast = AffinePoint::new(Fp2::from_bytes(&q1[..64]), Fp2::from_bytes(&q1[64..128]));
 
         let f = Bn254::pairing_check(
             &[s0_cast.clone(), s1_cast.clone()],
@@ -55,7 +57,7 @@ mod bls12_381 {
 
     use alloc::format;
 
-    use openvm_algebra_guest::IntMod;
+    use openvm_algebra_guest::{field::FieldExtension, IntMod};
     use openvm_pairing_guest::bls12_381::{Bls12_381, Fp, Fp2};
 
     use super::*;
@@ -77,10 +79,12 @@ mod bls12_381 {
         let q0 = &io[48 * 4..48 * 8];
         let q1 = &io[48 * 8..48 * 12];
 
-        let s0_cast = unsafe { &*(s0.as_ptr() as *const AffinePoint<Fp>) };
-        let s1_cast = unsafe { &*(s1.as_ptr() as *const AffinePoint<Fp>) };
-        let q0_cast = unsafe { &*(q0.as_ptr() as *const AffinePoint<Fp2>) };
-        let q1_cast = unsafe { &*(q1.as_ptr() as *const AffinePoint<Fp2>) };
+        let s0_cast =
+            AffinePoint::new(Fp::from_le_bytes(&s0[..48]), Fp::from_le_bytes(&s0[48..96]));
+        let s1_cast =
+            AffinePoint::new(Fp::from_le_bytes(&s1[..48]), Fp::from_le_bytes(&s1[48..96]));
+        let q0_cast = AffinePoint::new(Fp2::from_bytes(&q0[..96]), Fp2::from_bytes(&q0[96..192]));
+        let q1_cast = AffinePoint::new(Fp2::from_bytes(&q1[..96]), Fp2::from_bytes(&q1[96..192]));
 
         let f = Bls12_381::pairing_check(
             &[s0_cast.clone(), s1_cast.clone()],
diff --git a/extensions/pairing/tests/programs/examples/pairing_check_fallback.rs b/extensions/pairing/tests/programs/examples/pairing_check_fallback.rs
index 9758fdad4a..e6df0201db 100644
--- a/extensions/pairing/tests/programs/examples/pairing_check_fallback.rs
+++ b/extensions/pairing/tests/programs/examples/pairing_check_fallback.rs
@@ -109,10 +109,12 @@ mod bn254 {
         let q0 = &io[32 * 4..32 * 8];
         let q1 = &io[32 * 8..32 * 12];
 
-        let s0_cast = unsafe { &*(s0.as_ptr() as *const AffinePoint<Fp>) };
-        let s1_cast = unsafe { &*(s1.as_ptr() as *const AffinePoint<Fp>) };
-        let q0_cast = unsafe { &*(q0.as_ptr() as *const AffinePoint<Fp2>) };
-        let q1_cast = unsafe { &*(q1.as_ptr() as *const AffinePoint<Fp2>) };
+        let s0_cast =
+            AffinePoint::new(Fp::from_le_bytes(&s0[..32]), Fp::from_le_bytes(&s0[32..64]));
+        let s1_cast =
+            AffinePoint::new(Fp::from_le_bytes(&s1[..32]), Fp::from_le_bytes(&s1[32..64]));
+        let q0_cast = AffinePoint::new(Fp2::from_bytes(&q0[..64]), Fp2::from_bytes(&q0[64..128]));
+        let q1_cast = AffinePoint::new(Fp2::from_bytes(&q1[..64]), Fp2::from_bytes(&q1[64..128]));
 
         let f = Bn254Wrapper::pairing_check(
             &[s0_cast.clone(), s1_cast.clone()],
@@ -222,10 +224,12 @@ mod bls12_381 {
         let q0 = &io[48 * 4..48 * 8];
         let q1 = &io[48 * 8..48 * 12];
 
-        let s0_cast = unsafe { &*(s0.as_ptr() as *const AffinePoint<Fp>) };
-        let s1_cast = unsafe { &*(s1.as_ptr() as *const AffinePoint<Fp>) };
-        let q0_cast = unsafe { &*(q0.as_ptr() as *const AffinePoint<Fp2>) };
-        let q1_cast = unsafe { &*(q1.as_ptr() as *const AffinePoint<Fp2>) };
+        let s0_cast =
+            AffinePoint::new(Fp::from_le_bytes(&s0[..48]), Fp::from_le_bytes(&s0[48..96]));
+        let s1_cast =
+            AffinePoint::new(Fp::from_le_bytes(&s1[..48]), Fp::from_le_bytes(&s1[48..96]));
+        let q0_cast = AffinePoint::new(Fp2::from_bytes(&q0[..96]), Fp2::from_bytes(&q0[96..192]));
+        let q1_cast = AffinePoint::new(Fp2::from_bytes(&q1[..96]), Fp2::from_bytes(&q1[96..192]));
 
         let f = Bls12_381Wrapper::pairing_check(
             &[s0_cast.clone(), s1_cast.clone()],
diff --git a/extensions/pairing/tests/programs/examples/pairing_line.rs b/extensions/pairing/tests/programs/examples/pairing_line.rs
index 9789421a3d..087b15eb29 100644
--- a/extensions/pairing/tests/programs/examples/pairing_line.rs
+++ b/extensions/pairing/tests/programs/examples/pairing_line.rs
@@ -30,10 +30,16 @@ mod bn254 {
         let l1 = &io[32 * 4..32 * 8];
         let expected = &io[32 * 8..32 * 18];
 
-        let l0_cast = unsafe { &*(l0.as_ptr() as *const EvaluatedLine<Fp2>) };
-        let l1_cast = unsafe { &*(l1.as_ptr() as *const EvaluatedLine<Fp2>) };
-
-        let r = Bn254::mul_013_by_013(l0_cast, l1_cast);
+        let l0_cast = EvaluatedLine {
+            b: Fp2::from_bytes(&l0[..64]),
+            c: Fp2::from_bytes(&l0[64..128]),
+        };
+        let l1_cast = EvaluatedLine {
+            b: Fp2::from_bytes(&l1[..64]),
+            c: Fp2::from_bytes(&l1[64..128]),
+        };
+
+        let r = Bn254::mul_013_by_013(&l0_cast, &l1_cast);
         let mut r_bytes = [0u8; 32 * 10];
         let mut i = 0;
         for x in r {
@@ -50,10 +56,16 @@ mod bn254 {
         let x = &io[32 * 12..32 * 22];
         let expected = &io[32 * 22..32 * 34];
 
-        let f_cast = unsafe { &*(f.as_ptr() as *const Fp12) };
-        let x_cast = unsafe { &*(x.as_ptr() as *const [Fp2; 5]) };
+        let f_cast = Fp12::from_bytes(f);
+        let x_cast = [
+            Fp2::from_bytes(&x[..64]),
+            Fp2::from_bytes(&x[64..128]),
+            Fp2::from_bytes(&x[128..192]),
+            Fp2::from_bytes(&x[192..256]),
+            Fp2::from_bytes(&x[256..320]),
+        ];
 
-        let r = Bn254::mul_by_01234(f_cast, x_cast);
+        let r = Bn254::mul_by_01234(&f_cast, &x_cast);
         let mut r_bytes = [0u8; 32 * 12];
         let mut i = 0;
         for x in r.to_coeffs() {
@@ -86,10 +98,16 @@ mod bls12_381 {
         let l1 = &io[48 * 4..48 * 8];
         let expected = &io[48 * 8..48 * 18];
 
-        let l0_cast = unsafe { &*(l0.as_ptr() as *const EvaluatedLine<Fp2>) };
-        let l1_cast = unsafe { &*(l1.as_ptr() as *const EvaluatedLine<Fp2>) };
+        let l0_cast = EvaluatedLine {
+            b: Fp2::from_bytes(&l0[..48 * 2]),
+            c: Fp2::from_bytes(&l0[48 * 2..48 * 4]),
+        };
+        let l1_cast = EvaluatedLine {
+            b: Fp2::from_bytes(&l1[..48 * 2]),
+            c: Fp2::from_bytes(&l1[48 * 2..48 * 4]),
+        };
 
-        let r = Bls12_381::mul_023_by_023(l0_cast, l1_cast);
+        let r = Bls12_381::mul_023_by_023(&l0_cast, &l1_cast);
         let mut r_bytes = [0u8; 48 * 10];
         let mut i = 0;
         for x in r {
@@ -106,10 +124,16 @@ mod bls12_381 {
         let x = &io[48 * 12..48 * 22];
         let expected = &io[48 * 22..48 * 34];
 
-        let f_cast = unsafe { &*(f.as_ptr() as *const Fp12) };
-        let x_cast = unsafe { &*(x.as_ptr() as *const [Fp2; 5]) };
+        let f_cast = Fp12::from_bytes(f);
+        let x_cast = [
+            Fp2::from_bytes(&x[..48 * 2]),
+            Fp2::from_bytes(&x[48 * 2..48 * 4]),
+            Fp2::from_bytes(&x[48 * 4..48 * 6]),
+            Fp2::from_bytes(&x[48 * 6..48 * 8]),
+            Fp2::from_bytes(&x[48 * 8..48 * 10]),
+        ];
 
-        let r = Bls12_381::mul_by_02345(f_cast, x_cast);
+        let r = Bls12_381::mul_by_02345(&f_cast, &x_cast);
         let mut r_bytes = [0u8; 48 * 12];
         let mut i = 0;
         for x in r.to_coeffs() {
diff --git a/extensions/pairing/tests/programs/examples/pairing_miller_loop.rs b/extensions/pairing/tests/programs/examples/pairing_miller_loop.rs
index 985c766e33..df7da880db 100644
--- a/extensions/pairing/tests/programs/examples/pairing_miller_loop.rs
+++ b/extensions/pairing/tests/programs/examples/pairing_miller_loop.rs
@@ -36,15 +36,14 @@ mod bn254 {
         let q1 = &io[32 * 8..32 * 12];
         let f_cmp = &io[32 * 12..32 * 24];
 
-        let s0_cast = unsafe { &*(s0.as_ptr() as *const AffinePoint<Fp>) };
-        let s1_cast = unsafe { &*(s1.as_ptr() as *const AffinePoint<Fp>) };
-        let q0_cast = unsafe { &*(q0.as_ptr() as *const AffinePoint<Fp2>) };
-        let q1_cast = unsafe { &*(q1.as_ptr() as *const AffinePoint<Fp2>) };
-
-        let f = Bn254::multi_miller_loop(
-            &[s0_cast.clone(), s1_cast.clone()],
-            &[q0_cast.clone(), q1_cast.clone()],
-        );
+        let s0_cast =
+            AffinePoint::new(Fp::from_le_bytes(&s0[..32]), Fp::from_le_bytes(&s0[32..64]));
+        let s1_cast =
+            AffinePoint::new(Fp::from_le_bytes(&s1[..32]), Fp::from_le_bytes(&s1[32..64]));
+        let q0_cast = AffinePoint::new(Fp2::from_bytes(&q0[..64]), Fp2::from_bytes(&q0[64..128]));
+        let q1_cast = AffinePoint::new(Fp2::from_bytes(&q1[..64]), Fp2::from_bytes(&q1[64..128]));
+
+        let f = Bn254::multi_miller_loop(&[s0_cast, s1_cast], &[q0_cast, q1_cast]);
         let mut f_bytes = [0u8; 32 * 12];
         f.to_coeffs()
             .iter()
@@ -80,10 +79,12 @@ mod bls12_381 {
         let q1 = &io[48 * 8..48 * 12];
         let f_cmp = &io[48 * 12..48 * 24];
 
-        let s0_cast = unsafe { &*(s0.as_ptr() as *const AffinePoint<Fp>) };
-        let s1_cast = unsafe { &*(s1.as_ptr() as *const AffinePoint<Fp>) };
-        let q0_cast = unsafe { &*(q0.as_ptr() as *const AffinePoint<Fp2>) };
-        let q1_cast = unsafe { &*(q1.as_ptr() as *const AffinePoint<Fp2>) };
+        let s0_cast =
+            AffinePoint::new(Fp::from_le_bytes(&s0[..48]), Fp::from_le_bytes(&s0[48..96]));
+        let s1_cast =
+            AffinePoint::new(Fp::from_le_bytes(&s1[..48]), Fp::from_le_bytes(&s1[48..96]));
+        let q0_cast = AffinePoint::new(Fp2::from_bytes(&q0[..96]), Fp2::from_bytes(&q0[96..192]));
+        let q1_cast = AffinePoint::new(Fp2::from_bytes(&q1[..96]), Fp2::from_bytes(&q1[96..192]));
 
         let f = Bls12_381::multi_miller_loop(
             &[s0_cast.clone(), s1_cast.clone()],
diff --git a/extensions/pairing/tests/programs/examples/pairing_miller_step.rs b/extensions/pairing/tests/programs/examples/pairing_miller_step.rs
index 68301c92e4..fd61931d98 100644
--- a/extensions/pairing/tests/programs/examples/pairing_miller_step.rs
+++ b/extensions/pairing/tests/programs/examples/pairing_miller_step.rs
@@ -12,6 +12,7 @@ openvm::entry!(main);
 
 #[cfg(feature = "bn254")]
 mod bn254 {
+    use openvm_algebra_guest::field::FieldExtension;
     use openvm_pairing_guest::bn254::{Bn254, Fp, Fp2};
 
     use super::*;
@@ -31,9 +32,9 @@ mod bn254 {
         let pt = &io[32 * 4..32 * 8];
         let l = &io[32 * 8..32 * 12];
 
-        let s_cast = unsafe { &*(s.as_ptr() as *const AffinePoint<Fp2>) };
+        let s_cast = AffinePoint::new(Fp2::from_bytes(&s[..64]), Fp2::from_bytes(&s[64..128]));
 
-        let (pt_cmp, l_cmp) = Bn254::miller_double_step(s_cast);
+        let (pt_cmp, l_cmp) = Bn254::miller_double_step(&s_cast);
         let mut pt_bytes = [0u8; 32 * 4];
         let mut l_bytes = [0u8; 32 * 4];
 
@@ -60,9 +61,9 @@ mod bn254 {
         let l0 = &io[32 * 12..32 * 16];
         let l1 = &io[32 * 16..32 * 20];
 
-        let s_cast = unsafe { &*(s.as_ptr() as *const AffinePoint<Fp2>) };
-        let q_cast = unsafe { &*(q.as_ptr() as *const AffinePoint<Fp2>) };
-        let (pt_cmp, l0_cmp, l1_cmp) = Bn254::miller_double_and_add_step(s_cast, q_cast);
+        let s_cast = AffinePoint::new(Fp2::from_bytes(&s[..64]), Fp2::from_bytes(&s[64..128]));
+        let q_cast = AffinePoint::new(Fp2::from_bytes(&q[..64]), Fp2::from_bytes(&q[64..128]));
+        let (pt_cmp, l0_cmp, l1_cmp) = Bn254::miller_double_and_add_step(&s_cast, &q_cast);
         let mut pt_bytes = [0u8; 32 * 4];
         let mut l0_bytes = [0u8; 32 * 4];
         let mut l1_bytes = [0u8; 32 * 4];
@@ -90,6 +91,7 @@ mod bn254 {
 
 #[cfg(feature = "bls12_381")]
 mod bls12_381 {
+    use openvm_algebra_guest::field::FieldExtension;
     use openvm_pairing_guest::bls12_381::{Bls12_381, Fp, Fp2};
 
     use super::*;
@@ -109,9 +111,9 @@ mod bls12_381 {
         let pt = &io[48 * 4..48 * 8];
         let l = &io[48 * 8..48 * 12];
 
-        let s_cast = unsafe { &*(s.as_ptr() as *const AffinePoint<Fp2>) };
+        let s_cast = AffinePoint::new(Fp2::from_bytes(&s[..96]), Fp2::from_bytes(&s[96..192]));
 
-        let (pt_cmp, l_cmp) = Bls12_381::miller_double_step(s_cast);
+        let (pt_cmp, l_cmp) = Bls12_381::miller_double_step(&s_cast);
         let mut pt_bytes = [0u8; 48 * 4];
         let mut l_bytes = [0u8; 48 * 4];
 
@@ -136,9 +138,9 @@ mod bls12_381 {
         let l0 = &io[48 * 12..48 * 16];
         let l1 = &io[48 * 16..48 * 20];
 
-        let s_cast = unsafe { &*(s.as_ptr() as *const AffinePoint<Fp2>) };
-        let q_cast = unsafe { &*(q.as_ptr() as *const AffinePoint<Fp2>) };
-        let (pt_cmp, l0_cmp, l1_cmp) = Bls12_381::miller_double_and_add_step(s_cast, q_cast);
+        let s_cast = AffinePoint::new(Fp2::from_bytes(&s[..96]), Fp2::from_bytes(&s[96..192]));
+        let q_cast = AffinePoint::new(Fp2::from_bytes(&q[..96]), Fp2::from_bytes(&q[96..192]));
+        let (pt_cmp, l0_cmp, l1_cmp) = Bls12_381::miller_double_and_add_step(&s_cast, &q_cast);
         let mut pt_bytes = [0u8; 48 * 4];
         let mut l0_bytes = [0u8; 48 * 4];
         let mut l1_bytes = [0u8; 48 * 4];


Reviews:
user: login: Golovanov399
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-20T00:44:52Z
user: login: Golovanov399
body: Hope it's all and hope no major performance issues are introduced
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-03-20T00:48:18Z
user: login: jonathanpwang
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-20T00:56:55Z
user: login: Golovanov399
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-20T01:01:39Z

Review comments:
diff_hunk: @@ -751,15 +751,20 @@ pub fn moduli_init(input: TokenStream) -> TokenStream {
 
         let modulus_bytes = string_to_bytes(&modulus);
         let mut limbs = modulus_bytes.len();
+        let mut block_size = 32;
 
         if limbs <= 32 {
             limbs = 32;
         } else if limbs <= 48 {
             limbs = 48;
+            block_size = 16;
         } else {
             panic!("limbs must be at most 48");
         }
 
+        let block_size = proc_macro::Literal::usize_unsuffixed(block_size);
path: extensions/algebra/moduli-macros/src/lib.rs
user: login: Golovanov399
body: oh wow good to know this exists
created_at: 2025-03-20T00:44:51Z
author_association: CONTRIBUTOR
diff_hunk: @@ -751,15 +751,20 @@ pub fn moduli_init(input: TokenStream) -> TokenStream {
 
         let modulus_bytes = string_to_bytes(&modulus);
         let mut limbs = modulus_bytes.len();
+        let mut block_size = 32;
 
         if limbs <= 32 {
             limbs = 32;
         } else if limbs <= 48 {
             limbs = 48;
+            block_size = 16;
         } else {
             panic!("limbs must be at most 48");
         }
 
+        let block_size = proc_macro::Literal::usize_unsuffixed(block_size);
path: extensions/algebra/moduli-macros/src/lib.rs
user: login: jonathanpwang
body: wait, I copied this from code you wrote!
created_at: 2025-03-20T00:56:55Z
author_association: CONTRIBUTOR
diff_hunk: @@ -751,15 +751,20 @@ pub fn moduli_init(input: TokenStream) -> TokenStream {
 
         let modulus_bytes = string_to_bytes(&modulus);
         let mut limbs = modulus_bytes.len();
+        let mut block_size = 32;
 
         if limbs <= 32 {
             limbs = 32;
         } else if limbs <= 48 {
             limbs = 48;
+            block_size = 16;
         } else {
             panic!("limbs must be at most 48");
         }
 
+        let block_size = proc_macro::Literal::usize_unsuffixed(block_size);
path: extensions/algebra/moduli-macros/src/lib.rs
user: login: Golovanov399
body: Hmm I thought I used some hacks for this, but maybe this was the hack...
created_at: 2025-03-20T01:01:39Z
author_association: CONTRIBUTOR

PR comments:
user: login: github-actions[bot]
created_at: 2025-03-19T19:25:45Z
body: <!--Benchmark Results-->
| group | app.proof_time_ms | app.cycles | app.cells_used | leaf.proof_time_ms | leaf.cycles | leaf.cells_used |
| -- | -- | -- | -- | -- | -- | -- |
| [verify_fibair](https://github.com/openvm-org/openvm/blob/benchmark-results/benchmarks-pr/1486/verify_fibair-bb00107680cb89e77cf05ed390f52eb4583d9b3c.md) |<span style='color: red'>(+20 [+1.6%])</span> 1,254 |  334,048 |  17,902,648 |- | - | - |
| [fibonacci_program](https://github.com/openvm-org/openvm/blob/benchmark-results/benchmarks-pr/1486/fibonacci-bb00107680cb89e77cf05ed390f52eb4583d9b3c.md) |<span style='color: green'>(-44 [-1.6%])</span> 2,648 |  1,500,096 |  51,485,167 |- | - | - |
| [regex_program](https://github.com/openvm-org/openvm/blob/benchmark-results/benchmarks-pr/1486/regex-bb00107680cb89e77cf05ed390f52eb4583d9b3c.md) |<span style='color: red'>(+45 [+0.6%])</span> 7,765 |  4,140,164 |  167,389,450 |- | - | - |
| [ecrecover_program](https://github.com/openvm-org/openvm/blob/benchmark-results/benchmarks-pr/1486/ecrecover-bb00107680cb89e77cf05ed390f52eb4583d9b3c.md) |<span style='color: red'>(+3 [+0.2%])</span> 1,418 |  295,291 |  15,598,160 |- | - | - |
| [pairing](https://github.com/openvm-org/openvm/blob/benchmark-results/benchmarks-pr/1486/pairing-bb00107680cb89e77cf05ed390f52eb4583d9b3c.md) |<span style='color: green'>(-30 [-0.6%])</span> 4,706 |  1,711,640 |  92,620,923 |- | - | - |


Commit: https://github.com/openvm-org/openvm/commit/bb00107680cb89e77cf05ed390f52eb4583d9b3c

[Benchmark Workflow](https://github.com/openvm-org/openvm/actions/runs/13954935642)

author_association: NONE
user: login: github-actions[bot]
created_at: 2025-03-19T21:02:26Z
body: <!--Benchmark Results-->
| group | app.proof_time_ms | app.cycles | app.cells_used | leaf.proof_time_ms | leaf.cycles | leaf.cells_used |
| -- | -- | -- | -- | -- | -- | -- |
| [verify_fibair](https://github.com/openvm-org/openvm/blob/benchmark-results/benchmarks-pr/1486/verify_fibair-b6704d75225d983960765ac1fd11b5c691f0a450.md) |<span style='color: red'>(+8 [+0.7%])</span> 1,234 |  333,997 |  17,902,036 |- | - | - |
| [fibonacci_program](https://github.com/openvm-org/openvm/blob/benchmark-results/benchmarks-pr/1486/fibonacci-b6704d75225d983960765ac1fd11b5c691f0a450.md) | 2,677 |  1,500,096 |  51,485,167 |- | - | - |
| [regex_program](https://github.com/openvm-org/openvm/blob/benchmark-results/benchmarks-pr/1486/regex-b6704d75225d983960765ac1fd11b5c691f0a450.md) |<span style='color: red'>(+9 [+0.1%])</span> 7,792 |  4,140,164 |  167,389,450 |- | - | - |
| [ecrecover_program](https://github.com/openvm-org/openvm/blob/benchmark-results/benchmarks-pr/1486/ecrecover-b6704d75225d983960765ac1fd11b5c691f0a450.md) |<span style='color: red'>(+13 [+0.9%])</span> 1,430 |  295,291 |  15,598,160 |- | - | - |
| [pairing](https://github.com/openvm-org/openvm/blob/benchmark-results/benchmarks-pr/1486/pairing-b6704d75225d983960765ac1fd11b5c691f0a450.md) | 4,742 |  1,711,640 |  92,620,923 |- | - | - |


Commit: https://github.com/openvm-org/openvm/commit/b6704d75225d983960765ac1fd11b5c691f0a450

[Benchmark Workflow](https://github.com/openvm-org/openvm/actions/runs/13956542472)

author_association: NONE
user: login: github-actions[bot]
created_at: 2025-03-19T21:38:31Z
body: <!--Benchmark Results-->
| group | app.proof_time_ms | app.cycles | app.cells_used | leaf.proof_time_ms | leaf.cycles | leaf.cells_used |
| -- | -- | -- | -- | -- | -- | -- |
| [verify_fibair](https://github.com/openvm-org/openvm/blob/benchmark-results/benchmarks-pr/1486/verify_fibair-a75f53994bbe24dcbf7b573444745de139480077.md) |<span style='color: red'>(+22 [+1.8%])</span> 1,248 |  333,944 |  17,901,400 |- | - | - |
| [fibonacci_program](https://github.com/openvm-org/openvm/blob/benchmark-results/benchmarks-pr/1486/fibonacci-a75f53994bbe24dcbf7b573444745de139480077.md) |<span style='color: red'>(+30 [+1.1%])</span> 2,707 |  1,500,096 |  51,485,167 |- | - | - |
| [regex_program](https://github.com/openvm-org/openvm/blob/benchmark-results/benchmarks-pr/1486/regex-a75f53994bbe24dcbf7b573444745de139480077.md) |<span style='color: green'>(-46 [-0.6%])</span> 7,737 |  4,140,164 |  167,389,450 |- | - | - |
| [ecrecover_program](https://github.com/openvm-org/openvm/blob/benchmark-results/benchmarks-pr/1486/ecrecover-a75f53994bbe24dcbf7b573444745de139480077.md) |<span style='color: red'>(+3 [+0.2%])</span> 1,420 |  295,291 |  15,598,160 |- | - | - |
| [pairing](https://github.com/openvm-org/openvm/blob/benchmark-results/benchmarks-pr/1486/pairing-a75f53994bbe24dcbf7b573444745de139480077.md) |<span style='color: green'>(-11 [-0.2%])</span> 4,733 |  1,711,640 |  92,620,923 |- | - | - |


Commit: https://github.com/openvm-org/openvm/commit/a75f53994bbe24dcbf7b573444745de139480077

[Benchmark Workflow](https://github.com/openvm-org/openvm/actions/runs/13957137619)

author_association: NONE
user: login: github-actions[bot]
created_at: 2025-03-19T21:48:21Z
body: <!--Benchmark Results-->
| group | app.proof_time_ms | app.cycles | app.cells_used | leaf.proof_time_ms | leaf.cycles | leaf.cells_used |
| -- | -- | -- | -- | -- | -- | -- |
| [verify_fibair](https://github.com/openvm-org/openvm/blob/benchmark-results/benchmarks-pr/1486/verify_fibair-80b1789e2a6eca1f80ab16bffbe6a1e394beb1a1.md) |<span style='color: red'>(+12 [+1.0%])</span> 1,238 |  334,022 |  17,902,336 |- | - | - |
| [fibonacci_program](https://github.com/openvm-org/openvm/blob/benchmark-results/benchmarks-pr/1486/fibonacci-80b1789e2a6eca1f80ab16bffbe6a1e394beb1a1.md) |<span style='color: red'>(+7 [+0.3%])</span> 2,684 |  1,500,096 |  51,485,167 |- | - | - |
| [regex_program](https://github.com/openvm-org/openvm/blob/benchmark-results/benchmarks-pr/1486/regex-80b1789e2a6eca1f80ab16bffbe6a1e394beb1a1.md) |<span style='color: green'>(-42 [-0.5%])</span> 7,741 |  4,140,164 |  167,389,450 |- | - | - |
| [ecrecover_program](https://github.com/openvm-org/openvm/blob/benchmark-results/benchmarks-pr/1486/ecrecover-80b1789e2a6eca1f80ab16bffbe6a1e394beb1a1.md) |<span style='color: red'>(+17 [+1.2%])</span> 1,434 |  295,291 |  15,598,160 |- | - | - |
| [pairing](https://github.com/openvm-org/openvm/blob/benchmark-results/benchmarks-pr/1486/pairing-80b1789e2a6eca1f80ab16bffbe6a1e394beb1a1.md) |<span style='color: red'>(+9 [+0.2%])</span> 4,753 |  1,711,640 |  92,620,923 |- | - | - |


Commit: https://github.com/openvm-org/openvm/commit/80b1789e2a6eca1f80ab16bffbe6a1e394beb1a1

[Benchmark Workflow](https://github.com/openvm-org/openvm/actions/runs/13957284801)

author_association: NONE
user: login: github-actions[bot]
created_at: 2025-03-19T21:59:58Z
body: <!--Benchmark Results-->
| group | app.proof_time_ms | app.cycles | app.cells_used | leaf.proof_time_ms | leaf.cycles | leaf.cells_used |
| -- | -- | -- | -- | -- | -- | -- |
| [verify_fibair](https://github.com/openvm-org/openvm/blob/benchmark-results/benchmarks-pr/1486/verify_fibair-92d0bba5c6dba4282171eb2d7dc8a141b91afb05.md) |<span style='color: red'>(+14 [+1.1%])</span> 1,234 |  333,944 |  17,901,400 |- | - | - |
| [fibonacci_program](https://github.com/openvm-org/openvm/blob/benchmark-results/benchmarks-pr/1486/fibonacci-92d0bba5c6dba4282171eb2d7dc8a141b91afb05.md) |<span style='color: green'>(-7 [-0.3%])</span> 2,683 |  1,500,096 |  51,485,167 |- | - | - |
| [regex_program](https://github.com/openvm-org/openvm/blob/benchmark-results/benchmarks-pr/1486/regex-92d0bba5c6dba4282171eb2d7dc8a141b91afb05.md) |<span style='color: red'>(+46 [+0.6%])</span> 7,759 |  4,140,240 |  167,400,358 |- | - | - |
| [ecrecover_program](https://github.com/openvm-org/openvm/blob/benchmark-results/benchmarks-pr/1486/ecrecover-92d0bba5c6dba4282171eb2d7dc8a141b91afb05.md) | 1,417 |  295,291 |  15,598,160 |- | - | - |
| [pairing](https://github.com/openvm-org/openvm/blob/benchmark-results/benchmarks-pr/1486/pairing-92d0bba5c6dba4282171eb2d7dc8a141b91afb05.md) |<span style='color: green'>(-40 [-0.8%])</span> 4,739 |  1,711,640 |  92,620,923 |- | - | - |


Commit: https://github.com/openvm-org/openvm/commit/92d0bba5c6dba4282171eb2d7dc8a141b91afb05

[Benchmark Workflow](https://github.com/openvm-org/openvm/actions/runs/13957439650)

author_association: NONE