Skip to content

Conversation

@DiegoCivi
Copy link
Contributor

@DiegoCivi DiegoCivi commented Oct 20, 2025

Implement QM31 libfuncs

Implements all the QM31 related libfuncs:

Also added some tests and I was able to run the QM31 tests from the corelib:

% cargo run -p cairo-native-test corelib/src/test/qm31_test.cairo -s
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.27s
     Running `target/debug/cairo-native-test corelib/src/test/qm31_test.cairo -s`
running 6 tests
test qm31_test::qm31_test::test_qm31_add_and_sub ... ok (gas usage est.: 2100)
test qm31_test::qm31_test::test_qm31_mul_and_div ... ok (gas usage est.: 2500)
test qm31_test::qm31_test::test_qm31_inverse ... ok (gas usage est.: 2600)
test qm31_test::qm31_test::test_pack ... ok (gas usage est.: 5700)
test qm31_test::qm31_test::test_unpack ... ok (gas usage est.: 14800)
test qm31_test::qm31_test::test_m31_into_qm31 ... ok (gas usage est.: 13600)

test result: ok. 6 passed; 0 failed; 0 ignored; 0 filtered out;

Closes #1425

Introduces Breaking Changes?

Yes/No.

These PRs should be merged after this one right away, in that order.

Checklist

  • Linked to Github Issue.
  • Unit tests added.
  • Integration tests added.
  • This change requires new documentation.
    • Documentation has been added/updated.

@codecov-commenter
Copy link

codecov-commenter commented Oct 20, 2025

Codecov Report

❌ Patch coverage is 97.59358% with 18 lines in your changes missing coverage. Please review.
✅ Project coverage is 81.93%. Comparing base (1852943) to head (6b5e32e).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
src/libfuncs/qm31.rs 98.52% 9 Missing ⚠️
binaries/cairo-native-bin-utils/src/lib.rs 0.00% 5 Missing ⚠️
src/executor.rs 66.66% 2 Missing ⚠️
src/types.rs 83.33% 1 Missing ⚠️
src/values.rs 66.66% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1429      +/-   ##
==========================================
+ Coverage   81.45%   81.93%   +0.48%     
==========================================
  Files         105      107       +2     
  Lines       25862    26598     +736     
==========================================
+ Hits        21065    21794     +729     
- Misses       4797     4804       +7     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@github-actions
Copy link

github-actions bot commented Oct 20, 2025

Benchmark results Main vs HEAD.

Base

Command Mean [s] Min [s] Max [s] Relative
base dict_insert.cairo (JIT) 2.583 ± 0.023 2.560 2.629 1.02 ± 0.02
base dict_insert.cairo (AOT) 2.527 ± 0.030 2.487 2.581 1.00

Head

Command Mean [s] Min [s] Max [s] Relative
head dict_insert.cairo (JIT) 2.527 ± 0.016 2.504 2.557 1.02 ± 0.01
head dict_insert.cairo (AOT) 2.474 ± 0.020 2.448 2.503 1.00

Base

Command Mean [s] Min [s] Max [s] Relative
base dict_snapshot.cairo (JIT) 2.311 ± 0.020 2.282 2.336 1.09 ± 0.01
base dict_snapshot.cairo (AOT) 2.123 ± 0.017 2.101 2.150 1.00

Head

Command Mean [s] Min [s] Max [s] Relative
head dict_snapshot.cairo (JIT) 2.251 ± 0.022 2.218 2.303 1.06 ± 0.01
head dict_snapshot.cairo (AOT) 2.124 ± 0.012 2.103 2.146 1.00

Base

Command Mean [s] Min [s] Max [s] Relative
base factorial_2M.cairo (JIT) 2.624 ± 0.023 2.587 2.660 1.00
base factorial_2M.cairo (AOT) 2.630 ± 0.019 2.600 2.677 1.00 ± 0.01

Head

Command Mean [s] Min [s] Max [s] Relative
head factorial_2M.cairo (JIT) 2.598 ± 0.017 2.575 2.626 1.00
head factorial_2M.cairo (AOT) 2.628 ± 0.012 2.613 2.653 1.01 ± 0.01

Base

Command Mean [s] Min [s] Max [s] Relative
base fib_2M.cairo (JIT) 2.164 ± 0.020 2.139 2.187 1.03 ± 0.01
base fib_2M.cairo (AOT) 2.093 ± 0.011 2.081 2.111 1.00

Head

Command Mean [s] Min [s] Max [s] Relative
head fib_2M.cairo (JIT) 2.200 ± 0.018 2.174 2.231 1.02 ± 0.01
head fib_2M.cairo (AOT) 2.150 ± 0.014 2.121 2.167 1.00

Base

Command Mean [s] Min [s] Max [s] Relative
base heavy_circuit.cairo (JIT) 13.861 ± 0.074 13.753 13.954 1.02 ± 0.01
base heavy_circuit.cairo (AOT) 13.526 ± 0.090 13.415 13.707 1.00

Head

Command Mean [s] Min [s] Max [s] Relative
head heavy_circuit.cairo (JIT) 13.830 ± 0.041 13.742 13.876 1.02 ± 0.01
head heavy_circuit.cairo (AOT) 13.513 ± 0.087 13.409 13.700 1.00

Base

Command Mean [s] Min [s] Max [s] Relative
base linear_search.cairo (JIT) 2.312 ± 0.011 2.296 2.331 1.06 ± 0.01
base linear_search.cairo (AOT) 2.176 ± 0.006 2.169 2.188 1.00

Head

Command Mean [s] Min [s] Max [s] Relative
head linear_search.cairo (JIT) 2.322 ± 0.017 2.301 2.348 1.07 ± 0.01
head linear_search.cairo (AOT) 2.180 ± 0.016 2.155 2.200 1.00

Base

Command Mean [s] Min [s] Max [s] Relative
base logistic_map.cairo (JIT) 2.443 ± 0.029 2.400 2.489 1.07 ± 0.01
base logistic_map.cairo (AOT) 2.276 ± 0.013 2.248 2.290 1.00

Head

Command Mean [s] Min [s] Max [s] Relative
head logistic_map.cairo (JIT) 2.429 ± 0.023 2.394 2.470 1.08 ± 0.01
head logistic_map.cairo (AOT) 2.246 ± 0.013 2.232 2.267 1.00

@github-actions
Copy link

github-actions bot commented Oct 20, 2025

Benchmarking results

Benchmark for program dict_insert

Open benchmarks
Command Mean [s] Min [s] Max [s] Relative
Cairo-vm (Rust, Cairo 1) 11.014 ± 0.039 10.956 11.083 4.29 ± 0.05
cairo-native (embedded AOT) 2.569 ± 0.032 2.534 2.625 1.00
cairo-native (embedded JIT using LLVM's ORC Engine) 2.662 ± 0.020 2.630 2.702 1.04 ± 0.02

Benchmark for program dict_snapshot

Open benchmarks
Command Mean [ms] Min [ms] Max [ms] Relative
Cairo-vm (Rust, Cairo 1) 550.4 ± 4.4 545.9 558.1 1.00
cairo-native (embedded AOT) 2310.0 ± 26.3 2280.5 2365.2 4.20 ± 0.06
cairo-native (embedded JIT using LLVM's ORC Engine) 2424.7 ± 21.9 2389.8 2454.6 4.41 ± 0.05

Benchmark for program factorial_2M

Open benchmarks
Command Mean [s] Min [s] Max [s] Relative
Cairo-vm (Rust, Cairo 1) 4.822 ± 0.020 4.804 4.869 1.79 ± 0.02
cairo-native (embedded AOT) 2.700 ± 0.025 2.674 2.764 1.00
cairo-native (embedded JIT using LLVM's ORC Engine) 2.718 ± 0.015 2.692 2.738 1.01 ± 0.01

Benchmark for program fib_2M

Open benchmarks
Command Mean [s] Min [s] Max [s] Relative
Cairo-vm (Rust, Cairo 1) 4.727 ± 0.028 4.682 4.768 2.08 ± 0.02
cairo-native (embedded AOT) 2.275 ± 0.021 2.232 2.306 1.00
cairo-native (embedded JIT using LLVM's ORC Engine) 2.312 ± 0.026 2.260 2.359 1.02 ± 0.01

Benchmark for program heavy_circuit

Open benchmarks
Command Mean [s] Min [s] Max [s] Relative
Cairo-vm (Rust, Cairo 1) 11.014 ± 0.124 10.807 11.261 1.01 ± 0.02
cairo-native (embedded AOT) 10.926 ± 0.198 10.624 11.181 1.00
cairo-native (embedded JIT using LLVM's ORC Engine) 11.131 ± 0.161 10.921 11.401 1.02 ± 0.02

Benchmark for program linear_search

Open benchmarks
Command Mean [ms] Min [ms] Max [ms] Relative
Cairo-vm (Rust, Cairo 1) 575.1 ± 4.9 568.7 581.8 1.00
cairo-native (embedded AOT) 2345.9 ± 15.4 2324.7 2382.1 4.08 ± 0.04
cairo-native (embedded JIT using LLVM's ORC Engine) 2482.3 ± 18.1 2459.1 2516.2 4.32 ± 0.05

Benchmark for program logistic_map

Open benchmarks
Command Mean [ms] Min [ms] Max [ms] Relative
Cairo-vm (Rust, Cairo 1) 395.0 ± 5.6 387.4 407.8 1.00
cairo-native (embedded AOT) 2449.6 ± 27.2 2409.6 2493.1 6.20 ± 0.11
cairo-native (embedded JIT using LLVM's ORC Engine) 2652.4 ± 24.0 2605.7 2678.9 6.72 ± 0.11

Comment on lines 579 to 580
/// Operation is done between `lhs_ptr` and `rhs_ptr` while the result is stored
/// in `res_ptr`.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment is outdated, right?

Also, I would mention that lhs_ptr and rhs_ptr are pointers to a value of type QM31

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mmm I wouldn't say outdated, the operation is still using those pointers and the result is being stored in the res_ptr which is now created in the function and is not a parameter. I added the mention about the pointers here b7b0bd0

Comment on lines +439 to +440
#[cfg(target_arch = "x86_64")]
return Err(Error::ParseAttributeError);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would add a comment with something like: "on x86_64, a QM31 value is always returned by memory, therefore making this branch is unreachable on that architecture".

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in b7b0bd0

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A QM31 is not bigger than an u128, but its returned as a pointer regardless. I would remove that part of the comment as its probably more complex than that.

src/runtime.rs Outdated

// SAFETY: The only possible error is if rhs is zero. However, in the QM31 division libfunc, the divisor
// is of type NonZero<qm31> which ensures that we are not falling into the error case.
*res = to_representative_coefficients((lhs / rhs).unwrap());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you use expect instead of unwrap?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in da3ed95

Copy link
Contributor

@JulianGCalderon JulianGCalderon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment on lines +929 to +930
// TODO: Refactor cairo functions to receive m31 as parameters so we don't need different ones
// to test different cases and we can unify them into one. This can be done when issue #1217 gets closed.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

BoundedInt case is not implemented in to_ptr() method so we can pass it as an argument from Rust to Cairo. I didn´t implement it here because it is out of the scope of this PR

Copy link
Contributor

@JulianGCalderon JulianGCalderon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Left some nits, but looks good.

Given that this PR requires the EGCD algorithm, perhaps its better to join the implementations first, and then merge this PR reusing the existing implementations (so that we don't duplicate the implementation again)

Comment on lines 579 to 580
/// Executes the operation on the `QM31` values referenced by `lhs_ptr` and `rhs_ptr`,
/// and stores the resulting `QM31` in `res_ptr`.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wouldn't mention res_ptr, its an implementation detail. The user should only care that this function takes two pointers to an QM31, and returns a QM31 value.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in 6b5e32e

/// Executes the operation on the `QM31` values referenced by `lhs_ptr` and `rhs_ptr`,
/// and stores the resulting `QM31` in `res_ptr`.
///
/// Returns a opaque pointer as the result.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It doesn't return an opaque pointer, but a QM31 value, because of the load at line 622

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in 6b5e32e

Comment on lines +439 to +440
#[cfg(target_arch = "x86_64")]
return Err(Error::ParseAttributeError);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A QM31 is not bigger than an u128, but its returned as a pointer regardless. I would remove that part of the comment as its probably more complex than that.

@gabrielbosio
Copy link
Collaborator

Given that this PR requires the EGCD algorithm, perhaps its better to join the implementations first, and then merge this PR reusing the existing implementations (so that we don't duplicate the implementation again)

Agree

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[2.12.0] Implement QM31 related Libfuncs

6 participants