Repository: risc0/risc0
Pull request #2975: ZKVM-1227: extern C for rust bigint2 blob fns
Author: login: iddo-bentov
Created: 2025-03-14T20:40:01Z | Merged: 2025-03-19T21:11:35Z

Tested with external Rust and C projects (it doesn't seem to affect the cycle count of Rust programs)
For C code, this requires `use risc0_bigint2::field::*` in the guest program.


Diff:
diff --git a/risc0/bigint2/src/field/ffi_field.rs b/risc0/bigint2/src/field/ffi_field.rs
new file mode 100644
index 0000000000..0208c20261
--- /dev/null
+++ b/risc0/bigint2/src/field/ffi_field.rs
@@ -0,0 +1,323 @@
+// Copyright 2025 RISC Zero, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+use crate::field::*;
+
+#[stability::unstable]
+#[no_mangle]
+pub extern "C" fn risc0_bigint_modadd_256_unchecked(
+    lhs: &[u32; FIELD_256_WIDTH_WORDS],
+    rhs: &[u32; FIELD_256_WIDTH_WORDS],
+    modulus: &[u32; FIELD_256_WIDTH_WORDS],
+    result: &mut [u32; FIELD_256_WIDTH_WORDS],
+) {
+    modadd_256_unchecked(lhs, rhs, modulus, result);
+}
+
+#[stability::unstable]
+#[no_mangle]
+pub extern "C" fn risc0_bigint_modadd_384_unchecked(
+    lhs: &[u32; FIELD_384_WIDTH_WORDS],
+    rhs: &[u32; FIELD_384_WIDTH_WORDS],
+    modulus: &[u32; FIELD_384_WIDTH_WORDS],
+    result: &mut [u32; FIELD_384_WIDTH_WORDS],
+) {
+    modadd_384_unchecked(lhs, rhs, modulus, result);
+}
+
+#[stability::unstable]
+#[no_mangle]
+pub extern "C" fn risc0_bigint_modinv_256_unchecked(
+    inp: &[u32; FIELD_256_WIDTH_WORDS],
+    modulus: &[u32; FIELD_256_WIDTH_WORDS],
+    result: &mut [u32; FIELD_256_WIDTH_WORDS],
+) {
+    modinv_256_unchecked(inp, modulus, result);
+}
+
+#[stability::unstable]
+#[no_mangle]
+pub extern "C" fn risc0_bigint_modinv_384_unchecked(
+    inp: &[u32; FIELD_384_WIDTH_WORDS],
+    modulus: &[u32; FIELD_384_WIDTH_WORDS],
+    result: &mut [u32; FIELD_384_WIDTH_WORDS],
+) {
+    modinv_384_unchecked(inp, modulus, result);
+}
+
+#[stability::unstable]
+#[no_mangle]
+pub extern "C" fn risc0_bigint_modmul_256_unchecked(
+    lhs: &[u32; FIELD_256_WIDTH_WORDS],
+    rhs: &[u32; FIELD_256_WIDTH_WORDS],
+    modulus: &[u32; FIELD_256_WIDTH_WORDS],
+    result: &mut [u32; FIELD_256_WIDTH_WORDS],
+) {
+    modmul_256_unchecked(lhs, rhs, modulus, result);
+}
+
+#[stability::unstable]
+#[no_mangle]
+pub extern "C" fn risc0_bigint_modmul_384_unchecked(
+    lhs: &[u32; FIELD_384_WIDTH_WORDS],
+    rhs: &[u32; FIELD_384_WIDTH_WORDS],
+    modulus: &[u32; FIELD_384_WIDTH_WORDS],
+    result: &mut [u32; FIELD_384_WIDTH_WORDS],
+) {
+    modmul_384_unchecked(lhs, rhs, modulus, result);
+}
+
+#[stability::unstable]
+#[no_mangle]
+pub extern "C" fn risc0_bigint_modsub_256_unchecked(
+    lhs: &[u32; FIELD_256_WIDTH_WORDS],
+    rhs: &[u32; FIELD_256_WIDTH_WORDS],
+    modulus: &[u32; FIELD_256_WIDTH_WORDS],
+    result: &mut [u32; FIELD_256_WIDTH_WORDS],
+) {
+    modsub_256_unchecked(lhs, rhs, modulus, result);
+}
+
+#[stability::unstable]
+#[no_mangle]
+pub extern "C" fn risc0_bigint_modsub_384_unchecked(
+    lhs: &[u32; FIELD_384_WIDTH_WORDS],
+    rhs: &[u32; FIELD_384_WIDTH_WORDS],
+    modulus: &[u32; FIELD_384_WIDTH_WORDS],
+    result: &mut [u32; FIELD_384_WIDTH_WORDS],
+) {
+    modsub_384_unchecked(lhs, rhs, modulus, result);
+}
+
+#[stability::unstable]
+#[no_mangle]
+pub extern "C" fn risc0_bigint_extfield_deg2_add_256_unchecked(
+    lhs: &[[u32; FIELD_256_WIDTH_WORDS]; EXT_DEGREE_2],
+    rhs: &[[u32; FIELD_256_WIDTH_WORDS]; EXT_DEGREE_2],
+    modulus: &[u32; FIELD_256_WIDTH_WORDS],
+    result: &mut [[u32; FIELD_256_WIDTH_WORDS]; EXT_DEGREE_2],
+) {
+    extfield_deg2_add_256_unchecked(lhs, rhs, modulus, result);
+}
+
+#[stability::unstable]
+#[no_mangle]
+pub extern "C" fn risc0_bigint_extfield_deg2_add_384_unchecked(
+    lhs: &[[u32; FIELD_384_WIDTH_WORDS]; EXT_DEGREE_2],
+    rhs: &[[u32; FIELD_384_WIDTH_WORDS]; EXT_DEGREE_2],
+    modulus: &[u32; FIELD_384_WIDTH_WORDS],
+    result: &mut [[u32; FIELD_384_WIDTH_WORDS]; EXT_DEGREE_2],
+) {
+    extfield_deg2_add_384_unchecked(lhs, rhs, modulus, result);
+}
+
+#[stability::unstable]
+#[no_mangle]
+pub extern "C" fn risc0_bigint_extfield_deg2_sub_256_unchecked(
+    lhs: &[[u32; FIELD_256_WIDTH_WORDS]; EXT_DEGREE_2],
+    rhs: &[[u32; FIELD_256_WIDTH_WORDS]; EXT_DEGREE_2],
+    modulus: &[u32; FIELD_256_WIDTH_WORDS],
+    result: &mut [[u32; FIELD_256_WIDTH_WORDS]; EXT_DEGREE_2],
+) {
+    extfield_deg2_sub_256_unchecked(lhs, rhs, modulus, result);
+}
+
+#[stability::unstable]
+#[no_mangle]
+pub extern "C" fn risc0_bigint_extfield_deg2_sub_384_unchecked(
+    lhs: &[[u32; FIELD_384_WIDTH_WORDS]; EXT_DEGREE_2],
+    rhs: &[[u32; FIELD_384_WIDTH_WORDS]; EXT_DEGREE_2],
+    modulus: &[u32; FIELD_384_WIDTH_WORDS],
+    result: &mut [[u32; FIELD_384_WIDTH_WORDS]; EXT_DEGREE_2],
+) {
+    extfield_deg2_sub_384_unchecked(lhs, rhs, modulus, result);
+}
+
+#[stability::unstable]
+#[no_mangle]
+pub extern "C" fn risc0_bigint_extfield_xxone_mul_256_unchecked(
+    lhs: &[[u32; FIELD_256_WIDTH_WORDS]; EXT_DEGREE_2],
+    rhs: &[[u32; FIELD_256_WIDTH_WORDS]; EXT_DEGREE_2],
+    modulus: &[u32; FIELD_256_WIDTH_WORDS],
+    modsqr: &[u32; 2 * FIELD_256_WIDTH_WORDS],
+    result: &mut [[u32; FIELD_256_WIDTH_WORDS]; EXT_DEGREE_2],
+) {
+    extfield_xxone_mul_256_unchecked(lhs, rhs, modulus, modsqr, result);
+}
+
+#[stability::unstable]
+#[no_mangle]
+pub extern "C" fn risc0_bigint_extfield_xxone_mul_384_unchecked(
+    lhs: &[[u32; FIELD_384_WIDTH_WORDS]; EXT_DEGREE_2],
+    rhs: &[[u32; FIELD_384_WIDTH_WORDS]; EXT_DEGREE_2],
+    modulus: &[u32; FIELD_384_WIDTH_WORDS],
+    modsqr: &[u32; 2 * FIELD_384_WIDTH_WORDS],
+    result: &mut [[u32; FIELD_384_WIDTH_WORDS]; EXT_DEGREE_2],
+) {
+    extfield_xxone_mul_384_unchecked(lhs, rhs, modulus, modsqr, result);
+}
+
+#[stability::unstable]
+#[no_mangle]
+pub extern "C" fn risc0_bigint_modadd_256(
+    lhs: &[u32; FIELD_256_WIDTH_WORDS],
+    rhs: &[u32; FIELD_256_WIDTH_WORDS],
+    modulus: &[u32; FIELD_256_WIDTH_WORDS],
+    result: &mut [u32; FIELD_256_WIDTH_WORDS],
+) {
+    modadd_256(lhs, rhs, modulus, result);
+}
+
+#[stability::unstable]
+#[no_mangle]
+pub extern "C" fn risc0_bigint_modadd_384(
+    lhs: &[u32; FIELD_384_WIDTH_WORDS],
+    rhs: &[u32; FIELD_384_WIDTH_WORDS],
+    modulus: &[u32; FIELD_384_WIDTH_WORDS],
+    result: &mut [u32; FIELD_384_WIDTH_WORDS],
+) {
+    modadd_384(lhs, rhs, modulus, result);
+}
+
+#[stability::unstable]
+#[no_mangle]
+pub extern "C" fn risc0_bigint_modinv_256(
+    inp: &[u32; FIELD_256_WIDTH_WORDS],
+    modulus: &[u32; FIELD_256_WIDTH_WORDS],
+    result: &mut [u32; FIELD_256_WIDTH_WORDS],
+) {
+    modinv_256(inp, modulus, result);
+}
+
+#[stability::unstable]
+#[no_mangle]
+pub extern "C" fn risc0_bigint_modinv_384(
+    inp: &[u32; FIELD_384_WIDTH_WORDS],
+    modulus: &[u32; FIELD_384_WIDTH_WORDS],
+    result: &mut [u32; FIELD_384_WIDTH_WORDS],
+) {
+    modinv_384(inp, modulus, result);
+}
+
+#[stability::unstable]
+#[no_mangle]
+pub extern "C" fn risc0_bigint_modmul_256(
+    lhs: &[u32; FIELD_256_WIDTH_WORDS],
+    rhs: &[u32; FIELD_256_WIDTH_WORDS],
+    modulus: &[u32; FIELD_256_WIDTH_WORDS],
+    result: &mut [u32; FIELD_256_WIDTH_WORDS],
+) {
+    modmul_256(lhs, rhs, modulus, result);
+}
+
+#[stability::unstable]
+#[no_mangle]
+pub extern "C" fn risc0_bigint_modmul_384(
+    lhs: &[u32; FIELD_384_WIDTH_WORDS],
+    rhs: &[u32; FIELD_384_WIDTH_WORDS],
+    modulus: &[u32; FIELD_384_WIDTH_WORDS],
+    result: &mut [u32; FIELD_384_WIDTH_WORDS],
+) {
+    modmul_384(lhs, rhs, modulus, result);
+}
+
+#[stability::unstable]
+#[no_mangle]
+pub extern "C" fn risc0_bigint_modsub_256(
+    lhs: &[u32; FIELD_256_WIDTH_WORDS],
+    rhs: &[u32; FIELD_256_WIDTH_WORDS],
+    modulus: &[u32; FIELD_256_WIDTH_WORDS],
+    result: &mut [u32; FIELD_256_WIDTH_WORDS],
+) {
+    modsub_256(lhs, rhs, modulus, result);
+}
+
+#[stability::unstable]
+#[no_mangle]
+pub extern "C" fn risc0_bigint_modsub_384(
+    lhs: &[u32; FIELD_384_WIDTH_WORDS],
+    rhs: &[u32; FIELD_384_WIDTH_WORDS],
+    modulus: &[u32; FIELD_384_WIDTH_WORDS],
+    result: &mut [u32; FIELD_384_WIDTH_WORDS],
+) {
+    modsub_384(lhs, rhs, modulus, result);
+}
+
+#[stability::unstable]
+#[no_mangle]
+pub extern "C" fn risc0_bigint_extfield_deg2_add_256(
+    lhs: &[[u32; FIELD_256_WIDTH_WORDS]; EXT_DEGREE_2],
+    rhs: &[[u32; FIELD_256_WIDTH_WORDS]; EXT_DEGREE_2],
+    modulus: &[u32; FIELD_256_WIDTH_WORDS],
+    result: &mut [[u32; FIELD_256_WIDTH_WORDS]; EXT_DEGREE_2],
+) {
+    extfield_deg2_add_256(lhs, rhs, modulus, result);
+}
+
+#[stability::unstable]
+#[no_mangle]
+pub extern "C" fn risc0_bigint_extfield_deg2_add_384(
+    lhs: &[[u32; FIELD_384_WIDTH_WORDS]; EXT_DEGREE_2],
+    rhs: &[[u32; FIELD_384_WIDTH_WORDS]; EXT_DEGREE_2],
+    modulus: &[u32; FIELD_384_WIDTH_WORDS],
+    result: &mut [[u32; FIELD_384_WIDTH_WORDS]; EXT_DEGREE_2],
+) {
+    extfield_deg2_add_384(lhs, rhs, modulus, result);
+}
+
+#[stability::unstable]
+#[no_mangle]
+pub extern "C" fn risc0_bigint_extfield_deg2_sub_256(
+    lhs: &[[u32; FIELD_256_WIDTH_WORDS]; EXT_DEGREE_2],
+    rhs: &[[u32; FIELD_256_WIDTH_WORDS]; EXT_DEGREE_2],
+    modulus: &[u32; FIELD_256_WIDTH_WORDS],
+    result: &mut [[u32; FIELD_256_WIDTH_WORDS]; EXT_DEGREE_2],
+) {
+    extfield_deg2_sub_256(lhs, rhs, modulus, result);
+}
+
+#[stability::unstable]
+#[no_mangle]
+pub extern "C" fn risc0_bigint_extfield_deg2_sub_384(
+    lhs: &[[u32; FIELD_384_WIDTH_WORDS]; EXT_DEGREE_2],
+    rhs: &[[u32; FIELD_384_WIDTH_WORDS]; EXT_DEGREE_2],
+    modulus: &[u32; FIELD_384_WIDTH_WORDS],
+    result: &mut [[u32; FIELD_384_WIDTH_WORDS]; EXT_DEGREE_2],
+) {
+    extfield_deg2_sub_384(lhs, rhs, modulus, result);
+}
+
+#[stability::unstable]
+#[no_mangle]
+pub extern "C" fn risc0_bigint_extfield_xxone_mul_256(
+    lhs: &[[u32; FIELD_256_WIDTH_WORDS]; EXT_DEGREE_2],
+    rhs: &[[u32; FIELD_256_WIDTH_WORDS]; EXT_DEGREE_2],
+    modulus: &[u32; FIELD_256_WIDTH_WORDS],
+    modsqr: &[u32; 2 * FIELD_256_WIDTH_WORDS],
+    result: &mut [[u32; FIELD_256_WIDTH_WORDS]; EXT_DEGREE_2],
+) {
+    extfield_xxone_mul_256(lhs, rhs, modulus, modsqr, result);
+}
+
+#[stability::unstable]
+#[no_mangle]
+pub extern "C" fn risc0_bigint_extfield_xxone_mul_384(
+    lhs: &[[u32; FIELD_384_WIDTH_WORDS]; EXT_DEGREE_2],
+    rhs: &[[u32; FIELD_384_WIDTH_WORDS]; EXT_DEGREE_2],
+    modulus: &[u32; FIELD_384_WIDTH_WORDS],
+    modsqr: &[u32; 2 * FIELD_384_WIDTH_WORDS],
+    result: &mut [[u32; FIELD_384_WIDTH_WORDS]; EXT_DEGREE_2],
+) {
+    extfield_xxone_mul_384(lhs, rhs, modulus, modsqr, result);
+}
diff --git a/risc0/bigint2/src/field/mod.rs b/risc0/bigint2/src/field/mod.rs
index efeb4a7236..a056d03f1d 100644
--- a/risc0/bigint2/src/field/mod.rs
+++ b/risc0/bigint2/src/field/mod.rs
@@ -15,6 +15,8 @@
 #[cfg(test)]
 mod tests;
 
+pub mod ffi_field;
+
 use include_bytes_aligned::include_bytes_aligned;
 
 use crate::{


Reviews:
user: login: flaub
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-14T20:42:23Z
user: login: tzerrell
body: Great, and thank you for checking this works on Rust programs too.

I think we should probably expose the "unchecked" versions externally as well, they are sometimes necessary for good performance and we may end up needing them if we end up patching precompiles into more C code.
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-14T20:44:18Z
user: login: iddo-bentov
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-14T20:53:24Z
user: login: flaub
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-14T21:03:31Z
user: login: flaub
body: Need to make extern "C" symbols globally unique. Prefixing them is a common practice to help ensure this. Otherwise, downstream users get confusing and conflicting linking issues.
state: CHANGES_REQUESTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-14T21:05:24Z
user: login: iddo-bentov
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-18T18:38:52Z
user: login: tzerrell
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-19T18:23:02Z
user: login: iddo-bentov
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-19T18:33:23Z
user: login: iddo-bentov
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-19T18:33:25Z
user: login: flaub
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-19T18:37:24Z
user: login: flaub
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-03-19T18:38:07Z
user: login: flaub
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-19T18:38:22Z
user: login: iddo-bentov
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-19T18:46:37Z
user: login: tzerrell
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-03-19T19:04:33Z
user: login: tzerrell
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-19T19:05:46Z
user: login: flaub
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-19T22:42:11Z

Review comments:
diff_hunk: @@ -182,7 +182,9 @@ pub fn modsub_384_unchecked(
 
 // These "checked" versions verify that `result < modulus`
 
-pub fn modadd_256(
+#[stability::unstable]
+#[no_mangle]
+pub extern "C" fn modadd_256(
path: risc0/bigint2/src/field/mod.rs
user: login: flaub
body: I suggest we prefix extern "C" definitions to make them globally unique.

```suggestion
pub extern "C" fn risc0_bigint_modadd_256(
```
created_at: 2025-03-14T20:42:23Z
author_association: CONTRIBUTOR
diff_hunk: @@ -182,7 +182,9 @@ pub fn modsub_384_unchecked(
 
 // These "checked" versions verify that `result < modulus`
 
-pub fn modadd_256(
+#[stability::unstable]
+#[no_mangle]
+pub extern "C" fn modadd_256(
path: risc0/bigint2/src/field/mod.rs
user: login: iddo-bentov
body: Ok, but then it won't work with patched crates (that are pure Rust) that are already using these functions, such as bn254
An alternative is to duplicate these functions (that is, have one version that's extern "C", and another version that's just for rust, where the body of these two versions is the same). @SchmErik also looked at the current approach and so far it seemed ok to him.
created_at: 2025-03-14T20:53:24Z
author_association: CONTRIBUTOR
diff_hunk: @@ -182,7 +182,9 @@ pub fn modsub_384_unchecked(
 
 // These "checked" versions verify that `result < modulus`
 
-pub fn modadd_256(
+#[stability::unstable]
+#[no_mangle]
+pub extern "C" fn modadd_256(
path: risc0/bigint2/src/field/mod.rs
user: login: flaub
body: This will break users downstream if the symbol names are not unique enough. 

Extern "C" functions need to be globally unique. You can have the extern "C" functions call the rust functions, that would be fine.
created_at: 2025-03-14T21:03:31Z
author_association: CONTRIBUTOR
diff_hunk: @@ -182,7 +182,9 @@ pub fn modsub_384_unchecked(
 
 // These "checked" versions verify that `result < modulus`
 
-pub fn modadd_256(
+#[stability::unstable]
+#[no_mangle]
+pub extern "C" fn modadd_256(
path: risc0/bigint2/src/field/mod.rs
user: login: iddo-bentov
body: Ok, I changed the extern "C" functions to use risc0_bigint_ prefix.
@tzerrell I did it in the risc0-bigint2 crate as you recommended (and not in zkvm-platform), though as mentioned the setup for 3rd-party crates will be more involved.
created_at: 2025-03-18T18:38:51Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,375 @@
+// Copyright 2025 RISC Zero, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+use crate::field::*;
+
+#[stability::unstable]
+#[no_mangle]
+pub extern "C" fn risc0_bigint_modadd_256_unchecked(
+    lhs: &[u32; FIELD_256_WIDTH_WORDS],
+    rhs: &[u32; FIELD_256_WIDTH_WORDS],
+    modulus: &[u32; FIELD_256_WIDTH_WORDS],
+    result: &mut [u32; FIELD_256_WIDTH_WORDS],
+) {
+    modadd_256_unchecked(lhs, rhs, modulus, result);
+}
+
+#[stability::unstable]
+#[no_mangle]
+pub extern "C" fn risc0_bigint_modadd_384_unchecked(
+    lhs: &[u32; FIELD_384_WIDTH_WORDS],
+    rhs: &[u32; FIELD_384_WIDTH_WORDS],
+    modulus: &[u32; FIELD_384_WIDTH_WORDS],
+    result: &mut [u32; FIELD_384_WIDTH_WORDS],
+) {
+    modadd_384_unchecked(lhs, rhs, modulus, result);
+}
+
+#[stability::unstable]
+#[no_mangle]
+pub extern "C" fn risc0_bigint_modinv_256_unchecked(
+    inp: &[u32; FIELD_256_WIDTH_WORDS],
+    modulus: &[u32; FIELD_256_WIDTH_WORDS],
+    result: &mut [u32; FIELD_256_WIDTH_WORDS],
+) {
+    modinv_256_unchecked(inp, modulus, result);
+}
+
+#[stability::unstable]
+#[no_mangle]
+pub extern "C" fn risc0_bigint_modinv_384_unchecked(
+    inp: &[u32; FIELD_384_WIDTH_WORDS],
+    modulus: &[u32; FIELD_384_WIDTH_WORDS],
+    result: &mut [u32; FIELD_384_WIDTH_WORDS],
+) {
+    modinv_384_unchecked(inp, modulus, result);
+}
+
+#[stability::unstable]
+#[no_mangle]
+pub extern "C" fn risc0_bigint_modmul_256_unchecked(
+    lhs: &[u32; FIELD_256_WIDTH_WORDS],
+    rhs: &[u32; FIELD_256_WIDTH_WORDS],
+    modulus: &[u32; FIELD_256_WIDTH_WORDS],
+    result: &mut [u32; FIELD_256_WIDTH_WORDS],
+) {
+    modmul_256_unchecked(lhs, rhs, modulus, result);
+}
+
+#[stability::unstable]
+#[no_mangle]
+pub extern "C" fn risc0_bigint_modmul_384_unchecked(
+    lhs: &[u32; FIELD_384_WIDTH_WORDS],
+    rhs: &[u32; FIELD_384_WIDTH_WORDS],
+    modulus: &[u32; FIELD_384_WIDTH_WORDS],
+    result: &mut [u32; FIELD_384_WIDTH_WORDS],
+) {
+    modmul_384_unchecked(lhs, rhs, modulus, result);
+}
+
+#[stability::unstable]
+#[no_mangle]
+pub extern "C" fn risc0_bigint_modsub_256_unchecked(
+    lhs: &[u32; FIELD_256_WIDTH_WORDS],
+    rhs: &[u32; FIELD_256_WIDTH_WORDS],
+    modulus: &[u32; FIELD_256_WIDTH_WORDS],
+    result: &mut [u32; FIELD_256_WIDTH_WORDS],
+) {
+    modsub_256_unchecked(lhs, rhs, modulus, result);
+}
+
+#[stability::unstable]
+#[no_mangle]
+pub extern "C" fn risc0_bigint_modsub_384_unchecked(
+    lhs: &[u32; FIELD_384_WIDTH_WORDS],
+    rhs: &[u32; FIELD_384_WIDTH_WORDS],
+    modulus: &[u32; FIELD_384_WIDTH_WORDS],
+    result: &mut [u32; FIELD_384_WIDTH_WORDS],
+) {
+    modsub_384_unchecked(lhs, rhs, modulus, result);
+}
+
+#[stability::unstable]
+#[no_mangle]
+pub extern "C" fn risc0_bigint_extfield_deg2_add_256_unchecked(
+    lhs: &[[u32; FIELD_256_WIDTH_WORDS]; EXT_DEGREE_2],
+    rhs: &[[u32; FIELD_256_WIDTH_WORDS]; EXT_DEGREE_2],
+    modulus: &[u32; FIELD_256_WIDTH_WORDS],
+    result: &mut [[u32; FIELD_256_WIDTH_WORDS]; EXT_DEGREE_2],
+) {
+    extfield_deg2_add_256_unchecked(lhs, rhs, modulus, result);
+}
+
+#[stability::unstable]
+#[no_mangle]
+pub extern "C" fn risc0_bigint_extfield_deg2_add_384_unchecked(
+    lhs: &[[u32; FIELD_384_WIDTH_WORDS]; EXT_DEGREE_2],
+    rhs: &[[u32; FIELD_384_WIDTH_WORDS]; EXT_DEGREE_2],
+    modulus: &[u32; FIELD_384_WIDTH_WORDS],
+    result: &mut [[u32; FIELD_384_WIDTH_WORDS]; EXT_DEGREE_2],
+) {
+    extfield_deg2_add_384_unchecked(lhs, rhs, modulus, result);
+}
+
+/*
+#[stability::unstable]
+#[no_mangle]
+pub extern "C" fn risc0_bigint_extfield_deg2_mul_256_unchecked(
path: risc0/bigint2/src/field/syscall_field.rs
user: login: tzerrell
body: Why are the (non-specialized) extension field multiplies commented out? I think we should either add them or drop them (and replace with a comment about why they're not supported)
created_at: 2025-03-19T18:19:19Z
author_association: CONTRIBUTOR
path: risc0/bigint2/src/field/syscall_field.rs
user: login: tzerrell
body: To me `syscall_field` is an unintuitive name for this file, because the plain `field` file also just provides useful wrappers for syscalls. Perhaps `extern_field` or `field_externs` would be better?
created_at: 2025-03-19T18:22:29Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,375 @@
+// Copyright 2025 RISC Zero, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+use crate::field::*;
+
+#[stability::unstable]
+#[no_mangle]
+pub extern "C" fn risc0_bigint_modadd_256_unchecked(
+    lhs: &[u32; FIELD_256_WIDTH_WORDS],
+    rhs: &[u32; FIELD_256_WIDTH_WORDS],
+    modulus: &[u32; FIELD_256_WIDTH_WORDS],
+    result: &mut [u32; FIELD_256_WIDTH_WORDS],
+) {
+    modadd_256_unchecked(lhs, rhs, modulus, result);
+}
+
+#[stability::unstable]
+#[no_mangle]
+pub extern "C" fn risc0_bigint_modadd_384_unchecked(
+    lhs: &[u32; FIELD_384_WIDTH_WORDS],
+    rhs: &[u32; FIELD_384_WIDTH_WORDS],
+    modulus: &[u32; FIELD_384_WIDTH_WORDS],
+    result: &mut [u32; FIELD_384_WIDTH_WORDS],
+) {
+    modadd_384_unchecked(lhs, rhs, modulus, result);
+}
+
+#[stability::unstable]
+#[no_mangle]
+pub extern "C" fn risc0_bigint_modinv_256_unchecked(
+    inp: &[u32; FIELD_256_WIDTH_WORDS],
+    modulus: &[u32; FIELD_256_WIDTH_WORDS],
+    result: &mut [u32; FIELD_256_WIDTH_WORDS],
+) {
+    modinv_256_unchecked(inp, modulus, result);
+}
+
+#[stability::unstable]
+#[no_mangle]
+pub extern "C" fn risc0_bigint_modinv_384_unchecked(
+    inp: &[u32; FIELD_384_WIDTH_WORDS],
+    modulus: &[u32; FIELD_384_WIDTH_WORDS],
+    result: &mut [u32; FIELD_384_WIDTH_WORDS],
+) {
+    modinv_384_unchecked(inp, modulus, result);
+}
+
+#[stability::unstable]
+#[no_mangle]
+pub extern "C" fn risc0_bigint_modmul_256_unchecked(
+    lhs: &[u32; FIELD_256_WIDTH_WORDS],
+    rhs: &[u32; FIELD_256_WIDTH_WORDS],
+    modulus: &[u32; FIELD_256_WIDTH_WORDS],
+    result: &mut [u32; FIELD_256_WIDTH_WORDS],
+) {
+    modmul_256_unchecked(lhs, rhs, modulus, result);
+}
+
+#[stability::unstable]
+#[no_mangle]
+pub extern "C" fn risc0_bigint_modmul_384_unchecked(
+    lhs: &[u32; FIELD_384_WIDTH_WORDS],
+    rhs: &[u32; FIELD_384_WIDTH_WORDS],
+    modulus: &[u32; FIELD_384_WIDTH_WORDS],
+    result: &mut [u32; FIELD_384_WIDTH_WORDS],
+) {
+    modmul_384_unchecked(lhs, rhs, modulus, result);
+}
+
+#[stability::unstable]
+#[no_mangle]
+pub extern "C" fn risc0_bigint_modsub_256_unchecked(
+    lhs: &[u32; FIELD_256_WIDTH_WORDS],
+    rhs: &[u32; FIELD_256_WIDTH_WORDS],
+    modulus: &[u32; FIELD_256_WIDTH_WORDS],
+    result: &mut [u32; FIELD_256_WIDTH_WORDS],
+) {
+    modsub_256_unchecked(lhs, rhs, modulus, result);
+}
+
+#[stability::unstable]
+#[no_mangle]
+pub extern "C" fn risc0_bigint_modsub_384_unchecked(
+    lhs: &[u32; FIELD_384_WIDTH_WORDS],
+    rhs: &[u32; FIELD_384_WIDTH_WORDS],
+    modulus: &[u32; FIELD_384_WIDTH_WORDS],
+    result: &mut [u32; FIELD_384_WIDTH_WORDS],
+) {
+    modsub_384_unchecked(lhs, rhs, modulus, result);
+}
+
+#[stability::unstable]
+#[no_mangle]
+pub extern "C" fn risc0_bigint_extfield_deg2_add_256_unchecked(
+    lhs: &[[u32; FIELD_256_WIDTH_WORDS]; EXT_DEGREE_2],
+    rhs: &[[u32; FIELD_256_WIDTH_WORDS]; EXT_DEGREE_2],
+    modulus: &[u32; FIELD_256_WIDTH_WORDS],
+    result: &mut [[u32; FIELD_256_WIDTH_WORDS]; EXT_DEGREE_2],
+) {
+    extfield_deg2_add_256_unchecked(lhs, rhs, modulus, result);
+}
+
+#[stability::unstable]
+#[no_mangle]
+pub extern "C" fn risc0_bigint_extfield_deg2_add_384_unchecked(
+    lhs: &[[u32; FIELD_384_WIDTH_WORDS]; EXT_DEGREE_2],
+    rhs: &[[u32; FIELD_384_WIDTH_WORDS]; EXT_DEGREE_2],
+    modulus: &[u32; FIELD_384_WIDTH_WORDS],
+    result: &mut [[u32; FIELD_384_WIDTH_WORDS]; EXT_DEGREE_2],
+) {
+    extfield_deg2_add_384_unchecked(lhs, rhs, modulus, result);
+}
+
+/*
+#[stability::unstable]
+#[no_mangle]
+pub extern "C" fn risc0_bigint_extfield_deg2_mul_256_unchecked(
path: risc0/bigint2/src/field/syscall_field.rs
user: login: iddo-bentov
body: We did the deg4 function to demontrate the generic extfield mul, it's not actually needed for common use-cases.
The generic deg2 extfield mul is commented out because the custom xxone variant is more efficient and used in common use-cases.
So I just removed the extern "C" versions of these functions because they aren't interesting enough (and to avoid confusion), the rust versions should still be kept.
So I guess that for now I will just remove the commented-out code completely.
created_at: 2025-03-19T18:33:23Z
author_association: CONTRIBUTOR
path: risc0/bigint2/src/field/syscall_field.rs
user: login: iddo-bentov
body: Ok I will rename it to `extern_field`
created_at: 2025-03-19T18:33:25Z
author_association: CONTRIBUTOR
diff_hunk: @@ -15,6 +15,8 @@
 #[cfg(test)]
 mod tests;
 
+pub mod syscall_field;
path: risc0/bigint2/src/field/mod.rs
user: login: flaub
body: Could call it `ffi` (Foreign-function interface)


```suggestion
pub mod ffi;
```
created_at: 2025-03-19T18:37:23Z
author_association: CONTRIBUTOR
path: risc0/bigint2/src/field/syscall_field.rs
user: login: flaub
body: Maybe `ffi`?
created_at: 2025-03-19T18:38:22Z
author_association: CONTRIBUTOR
diff_hunk: @@ -15,6 +15,8 @@
 #[cfg(test)]
 mod tests;
 
+pub mod syscall_field;
path: risc0/bigint2/src/field/mod.rs
user: login: iddo-bentov
body: ok, for now I changed it to `ffi_field`, I'm not sure what convention is considered best because later we could also have `ffi_ec` in the other directory of this bigint2 crate.
created_at: 2025-03-19T18:46:37Z
author_association: CONTRIBUTOR
diff_hunk: @@ -15,6 +15,8 @@
 #[cfg(test)]
 mod tests;
 
+pub mod syscall_field;
path: risc0/bigint2/src/field/mod.rs
user: login: tzerrell
body: If we wanted this for EC we could put it in ~the same file~ a file with the same name in the EC directory, so I think ffi by itself would be fine
created_at: 2025-03-19T19:05:46Z
author_association: CONTRIBUTOR
diff_hunk: @@ -15,6 +15,8 @@
 #[cfg(test)]
 mod tests;
 
+pub mod syscall_field;
path: risc0/bigint2/src/field/mod.rs
user: login: flaub
body: Since this is already nested in a subdir, I think `ffi` is sufficient.
created_at: 2025-03-19T22:42:11Z
author_association: CONTRIBUTOR

PR comments:
user: login: vercel[bot]
created_at: 2025-03-14T20:40:06Z
body: [vc]: #kGtKRdi7mIQqs9sHxY/cVGzXhHxIpwL9ivC1MIHbhYM=:eyJpc01vbm9yZXBvIjp0cnVlLCJ0eXBlIjoiZ2l0aHViIiwicHJvamVjdHMiOlt7Im5hbWUiOiJkb2NzIiwicm9vdERpcmVjdG9yeSI6IndlYnNpdGUiLCJsaXZlRmVlZGJhY2siOnsicmVzb2x2ZWQiOjAsInVucmVzb2x2ZWQiOjAsInRvdGFsIjowLCJsaW5rIjoiIn0sImluc3BlY3RvclVybCI6Imh0dHBzOi8vdmVyY2VsLmNvbS9yaXNjemVyby9kb2NzLzVTMU16WFJvZUVScVFjYmpGTko0YXdQVkFmQjciLCJuZXh0Q29tbWl0U3RhdHVzIjoiSUdOT1JFRCIsInByZXZpZXdVcmwiOiJkb2NzLWdpdC1pZGRvLWV4dGVybnJ1c3RibG9iZm5zLXJpc2N6ZXJvLnZlcmNlbC5hcHAifSx7Im5hbWUiOiJyZXBvcnRzLWFuZC1iZW5jaG1hcmtzIiwicm9vdERpcmVjdG9yeSI6IndlYi9hcHBzL2JlbmNobWFya3MtYW5kLXJlcG9ydHMiLCJsaXZlRmVlZGJhY2siOnsicmVzb2x2ZWQiOjAsInVucmVzb2x2ZWQiOjAsInRvdGFsIjowLCJsaW5rIjoiIn0sImluc3BlY3RvclVybCI6Imh0dHBzOi8vdmVyY2VsLmNvbS9yaXNjemVyby9yZXBvcnRzLWFuZC1iZW5jaG1hcmtzL0ZyTnVzWVdTNGJyZmpCTlVQekRWYmpiSllwTDUiLCJuZXh0Q29tbWl0U3RhdHVzIjoiSUdOT1JFRCIsInByZXZpZXdVcmwiOiJyZXBvcnRzLWFuZC1iZW5jaG1hcmtzLWdpdC1pZGRvLWV4dGVybnJ1c3RibG9iZm5zLXJpc2N6ZXJvLnZlcmNlbC5hcHAifV19
**The latest updates on your projects**. Learn more about [Vercel for Git ↗︎](https://vercel.link/github-learn-more)


<details><summary>2 Skipped Deployments</summary>

| Name | Status | Preview | Comments | Updated (UTC) |
| :--- | :----- | :------ | :------- | :------ |
| **docs** | ⬜️ Ignored ([Inspect](https://vercel.com/risczero/docs/5S1MzXRoeERqQcbjFNJ4awPVAfB7)) | [Visit Preview](https://docs-git-iddo-externrustblobfns-risczero.vercel.app) |  | Mar 19, 2025 8:34pm |
| **reports-and-benchmarks** | ⬜️ Ignored ([Inspect](https://vercel.com/risczero/reports-and-benchmarks/FrNusYWS4brfjBNUPzDVbjbJYpL5)) | [Visit Preview](https://reports-and-benchmarks-git-iddo-externrustblobfns-risczero.vercel.app) |  | Mar 19, 2025 8:34pm |
</details>



author_association: NONE
user: login: iddo-bentov
created_at: 2025-03-14T20:56:28Z
body: > I think we should probably expose the "unchecked" versions externally as well, they are sometimes necessary for good performance and we may end up needing them if we end up patching precompiles into more C code.

No problem with me:)  For now I didn't even expose the generic deg2 mul functions, because the custom deg2 xxone functions are likely the only ones that'd be needed.
author_association: CONTRIBUTOR
user: login: flaub
created_at: 2025-03-14T21:11:33Z
body: Not a blocker, but we should also add CI that tests that C and rust can access these functions properly.
author_association: CONTRIBUTOR