fix(values): make Value::to_ptr always return the inline representation - #1655
Merged
Conversation
|
✅ Code is now correctly formatted. |
This was referenced Aug 18, 2026
Benchmark results Main vs HEAD.Base
Head
Base
Head
Base
Head
Base
Head
Base
Head
Base
Head
|
Benchmarking resultsBenchmark for program
|
| Command | Mean [s] | Min [s] | Max [s] | Relative |
|---|---|---|---|---|
Cairo-vm (Rust, Cairo 1) |
8.393 ± 0.044 | 8.347 | 8.493 | 6.16 ± 0.05 |
cairo-native (embedded AOT) |
1.363 ± 0.007 | 1.358 | 1.375 | 1.00 |
cairo-native (embedded JIT using LLVM's ORC Engine) |
1.372 ± 0.006 | 1.363 | 1.379 | 1.01 ± 0.01 |
Benchmark for program dict_snapshot
Open benchmarks
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
Cairo-vm (Rust, Cairo 1) |
387.5 ± 8.6 | 379.5 | 402.2 | 1.00 |
cairo-native (embedded AOT) |
1213.6 ± 3.3 | 1209.2 | 1218.1 | 3.13 ± 0.07 |
cairo-native (embedded JIT using LLVM's ORC Engine) |
1245.5 ± 10.5 | 1225.2 | 1257.6 | 3.21 ± 0.08 |
Benchmark for program factorial_2M
Open benchmarks
| Command | Mean [s] | Min [s] | Max [s] | Relative |
|---|---|---|---|---|
Cairo-vm (Rust, Cairo 1) |
3.805 ± 0.015 | 3.788 | 3.834 | 3.00 ± 0.02 |
cairo-native (embedded AOT) |
1.269 ± 0.009 | 1.257 | 1.287 | 1.00 |
cairo-native (embedded JIT using LLVM's ORC Engine) |
1.288 ± 0.007 | 1.278 | 1.297 | 1.01 ± 0.01 |
Benchmark for program fib_2M
Open benchmarks
| Command | Mean [s] | Min [s] | Max [s] | Relative |
|---|---|---|---|---|
Cairo-vm (Rust, Cairo 1) |
3.746 ± 0.010 | 3.731 | 3.765 | 3.07 ± 0.02 |
cairo-native (embedded AOT) |
1.222 ± 0.006 | 1.213 | 1.230 | 1.00 |
cairo-native (embedded JIT using LLVM's ORC Engine) |
1.238 ± 0.009 | 1.228 | 1.253 | 1.01 ± 0.01 |
Benchmark for program linear_search
Open benchmarks
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
Cairo-vm (Rust, Cairo 1) |
426.6 ± 7.7 | 414.2 | 437.0 | 1.00 |
cairo-native (embedded AOT) |
1235.2 ± 5.9 | 1225.6 | 1246.1 | 2.90 ± 0.05 |
cairo-native (embedded JIT using LLVM's ORC Engine) |
1264.3 ± 7.7 | 1253.7 | 1279.5 | 2.96 ± 0.06 |
Benchmark for program logistic_map
Open benchmarks
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
Cairo-vm (Rust, Cairo 1) |
360.6 ± 9.3 | 350.5 | 378.6 | 1.00 |
cairo-native (embedded AOT) |
1216.9 ± 7.4 | 1206.8 | 1227.6 | 3.37 ± 0.09 |
cairo-native (embedded JIT using LLVM's ORC Engine) |
1248.6 ± 8.1 | 1235.0 | 1261.3 | 3.46 ± 0.09 |
TomerStarkware
force-pushed
the
tomer/values_inline_to_ptr
branch
2 times, most recently
from
August 19, 2026 16:07
ab01fbe to
c534003
Compare
orizi
approved these changes
Aug 20, 2026
orizi
left a comment
Collaborator
There was a problem hiding this comment.
@orizi reviewed 4 files and all commit messages, and made 1 comment.
Reviewable status:complete! all files reviewed, all discussions resolved (waiting on TomerStarkware).
For memory-allocated types (>=2-variant enums and aggregates containing one), `Value::to_ptr` returned a *wrapper* pointer (a slot holding the data pointer) while `Value::from_ptr` always reads the inline representation. Every recursive consumer had to un-box the wrapper, and four of six forgot: - the Array arm copied `elem_layout.size()` bytes out of the 8-byte wrapper, so even `Array<bool>` was corrupted (the 16-byte-aligned enum body keeps the tag bit clear, decoding every element as variant 0); - the Felt252Dict arm had the same omission (`Felt252Dict<bool>`); - the `AbiArgument` Box and Nullable arms memcpy'd the wrapper bytes into the heap block (`Box<SomeEnum>`). Enumerating all callers shows nothing consumes the wrapper — every call site either stripped it immediately or was one of these bugs — so invert the contract instead of patching each site: `to_ptr` now always returns a pointer to the inline representation per `TypeBuilder::layout()`, exactly what `from_ptr` reads, and the by-pointer ABI decision lives only in `crate::arch`'s `AbiArgument` impl. Also make the Felt252Dict arm follow the same convention: it returned the `FeltDict*` itself instead of a slot holding it, so a dict nested in an aggregate copied 8 bytes of HashMap internals instead of the dict pointer. The arch.rs dict arm now dereferences the slot once. The Nullable arm also now passes the payload type id like the Box arm does. Only reachable via the `invoke_dynamic(&[Value])` API; the Starknet contract path marshals felts directly and is unaffected. Adds a VM-vs-native regression test for `Array<bool>` and `to_ptr`->`from_ptr` round-trip unit tests (bool array, struct with bool, nested enum, bool dict) that the new symmetric contract enables. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
TomerStarkware
force-pushed
the
tomer/values_inline_to_ptr
branch
from
August 23, 2026 08:31
c534003 to
5d7a5d3
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
For memory-allocated types (>=2-variant enums and aggregates transitively containing one),
Value::to_ptrreturned a wrapper pointer (a slot holding the data pointer), whileValue::from_ptralways reads the inline representation. Every recursive consumer ofto_ptrhad to un-box the wrapper — and four of six forgot:values.rs): copiedelem_layout.size()bytes out of the 8-byte wrapper. EvenArray<bool>was corrupted — the 16-byte-aligned enum body keeps the tag bit clear, so every element decoded as variant 0 (and gas diverged from the VM).Felt252Dict<bool>is legal Cairo viaBoolFelt252DictValue).AbiArgumentBox and Nullable arms (arch.rs): memcpy'd the wrapper bytes into the heap payload (Box<SomeEnum>).Fix
Enumerating every
.to_ptr(call site shows nothing consumes the wrapper — each caller either stripped it immediately (Struct/Enumarms, arch.rs enum/struct arms) or was one of the bugs. So instead of patching each site, invert the contract:This deletes the wrapping in the Struct/Enum arms and the un-wrapping in arch.rs, and fixes the four forgetting sites by construction. The Felt252Dict arm is also brought under the same convention: it returned the
FeltDict*itself instead of a slot holding it, so a dict nested inside an aggregate copied 8 bytes of HashMap internals.Only reachable via
invoke_dynamic(&[Value]); the Starknet contract path marshals felts directly and is unaffected. Codegen-sideis_memory_allocateduses are the compiled ABI and are untouched — the wrapper never crossed the FFI boundary.Tests
Array<bool>argument.to_ptr→from_ptrround-trip unit tests, now valid under the symmetric contract: bool array, struct with bool, nested enum, bool dict.Follow-up of #1650, which fixed this same bug class for enum payloads.
🤖 Generated with Claude Code
This change is