@@ -2,6 +2,8 @@ use std::ops::{Deref, DerefMut};
22
33use p3_field:: PrimeField64 ;
44
5+ use super :: { address:: MemoryAddress , val:: MemoryValue } ;
6+
57/// A memory cell used by the VM for storing 64-bit field elements with metadata.
68///
79/// Internally, the cell holds a single `u64` value.
@@ -34,26 +36,13 @@ impl MemoryCell {
3436 pub ( crate ) const NONE_MASK : u64 = 1 << 63 ;
3537 /// Flag bit indicating the cell was accessed (bit 62 set).
3638 pub ( crate ) const ACCESS_MASK : u64 = 1 << 62 ;
39+ /// Flag bit indicating the cell contains a MemoryAddress (bit 61 set).
40+ pub ( crate ) const ADDRESS_MASK : u64 = 1 << 61 ;
41+ /// A mask to extract only the value bits, ignoring all flags.
42+ pub ( crate ) const VALUE_MASK : u64 = 0x1FFF_FFFF_FFFF_FFFF ;
3743 /// Constant representing an empty cell.
3844 pub ( crate ) const NONE : Self = Self ( Self :: NONE_MASK ) ;
3945
40- /// Creates a `MemoryCell` from a field element, using its canonical `u64` representation.
41- ///
42- /// This clears any flag bits and assumes the value is valid.
43- pub ( crate ) fn from_f < F > ( value : F ) -> Self
44- where
45- F : PrimeField64 ,
46- {
47- Self ( value. as_canonical_u64 ( ) )
48- }
49-
50- /// Creates a raw `MemoryCell` from a `u64` value.
51- ///
52- /// Caller is responsible for ensuring no flag bits are set unless intentional.
53- pub ( crate ) const fn from_u64 ( value : u64 ) -> Self {
54- Self ( value)
55- }
56-
5746 /// Returns true if the cell is marked as empty (`NONE`).
5847 pub ( crate ) const fn is_none ( self ) -> bool {
5948 self . 0 & Self :: NONE_MASK == Self :: NONE_MASK
@@ -73,67 +62,223 @@ impl MemoryCell {
7362 pub ( crate ) const fn is_accessed ( self ) -> bool {
7463 self . 0 & Self :: ACCESS_MASK == Self :: ACCESS_MASK
7564 }
65+
66+ pub ( crate ) fn value < F > ( self ) -> Option < MemoryValue < F > >
67+ where
68+ MemoryValue < F > : From < Self > ,
69+ {
70+ self . is_some ( ) . then ( || self . into ( ) )
71+ }
72+ }
73+
74+ impl < F > From < MemoryValue < F > > for MemoryCell
75+ where
76+ F : PrimeField64 ,
77+ {
78+ fn from ( value : MemoryValue < F > ) -> Self {
79+ match value {
80+ // If it's an integer, store its u64 representation.
81+ // The ADDRESS_MASK bit will be 0 by default.
82+ MemoryValue :: Int ( f) => Self ( f. as_canonical_u64 ( ) ) ,
83+
84+ // If it's an address, pack it into the u64.
85+ MemoryValue :: Address ( addr) => {
86+ // Ensure the address components fit within their allocated bit-space.
87+ // 29 bits for segment allows for 536+ million segments.
88+ // 32 bits for offset allows for 4+ billion items per segment.
89+ debug_assert ! (
90+ addr. segment_index < ( 1 << 29 ) ,
91+ "Segment index out of bounds"
92+ ) ;
93+ debug_assert ! ( addr. offset < ( 1 << 32 ) , "Offset out of bounds" ) ;
94+
95+ // Pack segment and offset into a single u64, and set the address flag.
96+ let segment = ( addr. segment_index as u64 ) << 32 ;
97+ let offset = addr. offset as u64 ;
98+ Self ( segment | offset | Self :: ADDRESS_MASK )
99+ }
100+ }
101+ }
102+ }
103+
104+ impl < F > From < MemoryCell > for MemoryValue < F >
105+ where
106+ F : PrimeField64 ,
107+ {
108+ fn from ( cell : MemoryCell ) -> Self {
109+ // Check the address flag to determine the type of value.
110+ if ( cell. 0 & MemoryCell :: ADDRESS_MASK ) == MemoryCell :: ADDRESS_MASK {
111+ // It's an address, so we unpack it.
112+ let segment_index = ( ( cell. 0 & MemoryCell :: VALUE_MASK ) >> 32 ) as usize ;
113+ // Mask for lower 32 bits
114+ let offset = ( cell. 0 & 0xFFFF_FFFF ) as usize ;
115+
116+ Self :: Address ( MemoryAddress {
117+ segment_index,
118+ offset,
119+ } )
120+ } else {
121+ // It's an integer. We extract the value bits and convert to a field element.
122+ let value_bits = cell. 0 & MemoryCell :: VALUE_MASK ;
123+ Self :: Int ( F :: from_u64 ( value_bits) )
124+ }
125+ }
76126}
77127
78128#[ cfg( test) ]
79129mod tests {
80130 use p3_baby_bear:: BabyBear ;
81131 use p3_field:: PrimeCharacteristicRing ;
132+ use proptest:: prelude:: * ;
82133
83134 use super :: * ;
84135
85136 type F = BabyBear ;
86137
87- #[ test]
88- fn test_from_f_and_accessors ( ) {
89- let f = F :: from_u64 ( 123 ) ;
90- let cell = MemoryCell :: from_f ( f) ;
91- assert_eq ! ( * cell, 123 ) ;
92- assert ! ( cell. is_some( ) ) ;
93- assert ! ( !cell. is_none( ) ) ;
94- assert ! ( !cell. is_accessed( ) ) ;
95- }
96-
97- #[ test]
98- fn test_from_u64_and_flags ( ) {
99- let raw = 0xFFFF_FFFF ;
100- let cell = MemoryCell :: from_u64 ( raw) ;
101- assert_eq ! ( * cell, raw) ;
102- assert ! ( cell. is_some( ) ) ;
103- assert ! ( !cell. is_none( ) ) ;
104- }
105-
106138 #[ test]
107139 fn test_is_none_and_is_some ( ) {
140+ // A cell explicitly created as NONE should be none.
108141 let none_cell = MemoryCell :: NONE ;
109142 assert ! ( none_cell. is_none( ) ) ;
110143 assert ! ( !none_cell. is_some( ) ) ;
111144
112- let some_cell = MemoryCell :: from_u64 ( 42 ) ;
145+ // A cell with a value (even zero) should be some.
146+ let some_cell = MemoryCell :: from ( MemoryValue :: < F > :: Int ( F :: from_u64 ( 42 ) ) ) ;
113147 assert ! ( !some_cell. is_none( ) ) ;
114148 assert ! ( some_cell. is_some( ) ) ;
149+
150+ let zero_cell = MemoryCell :: from ( MemoryValue :: < F > :: Int ( F :: ZERO ) ) ;
151+ assert ! ( !zero_cell. is_none( ) ) ;
152+ assert ! ( zero_cell. is_some( ) ) ;
115153 }
116154
117155 #[ test]
118- fn test_mark_accessed_and_is_accessed ( ) {
119- let mut cell = MemoryCell :: from_u64 ( 7 ) ;
156+ fn test_mark_and_check_accessed ( ) {
157+ let mut cell = MemoryCell :: from ( MemoryValue :: < F > :: Int ( F :: from_u64 ( 99 ) ) ) ;
158+
159+ // Initially not accessed.
120160 assert ! ( !cell. is_accessed( ) ) ;
161+
162+ // Mark it as accessed.
121163 cell. mark_accessed ( ) ;
164+
165+ // Now it should be accessed.
122166 assert ! ( cell. is_accessed( ) ) ;
167+ // Should not affect the NONE flag.
168+ assert ! ( cell. is_some( ) ) ;
123169
124- // Ensure value bits are still preserved
125- assert_eq ! ( * cell & 0x3FFF_FFFF_FFFF_FFFF , 7 ) ;
170+ // The original value should be preserved alongside the flag.
171+ let value_without_flags = cell. 0 & MemoryCell :: VALUE_MASK ;
172+ assert_eq ! ( value_without_flags, 99 ) ;
126173 }
127174
128175 #[ test]
129- fn test_none_and_access_bits_do_not_conflict ( ) {
130- let mut cell = MemoryCell :: NONE ;
131- assert ! ( cell. is_none( ) ) ;
132- assert ! ( !cell. is_accessed( ) ) ;
176+ fn test_flag_interactions ( ) {
177+ // Mark a NONE cell as accessed
178+ let mut none_cell = MemoryCell :: NONE ;
179+ none_cell. mark_accessed ( ) ;
180+ assert ! ( none_cell. is_none( ) , "is_none should be true after access" ) ;
181+ assert ! ( none_cell. is_accessed( ) , "is_accessed should be true" ) ;
182+ assert_eq ! ( none_cell. 0 , MemoryCell :: NONE_MASK | MemoryCell :: ACCESS_MASK ) ;
133183
134- // Mark accessed should not clear the NONE flag
135- cell. mark_accessed ( ) ;
136- assert ! ( cell. is_none( ) ) ;
137- assert ! ( cell. is_accessed( ) ) ;
184+ // Mark an ADDRESS cell as accessed
185+ let mut addr_cell = MemoryCell :: from ( MemoryValue :: < F > :: Address ( MemoryAddress {
186+ segment_index : 1 ,
187+ offset : 2 ,
188+ } ) ) ;
189+ addr_cell. mark_accessed ( ) ;
190+ assert ! ( addr_cell. is_some( ) , "Address cell should be 'some'" ) ;
191+ assert ! (
192+ ( addr_cell. 0 & MemoryCell :: ADDRESS_MASK ) != 0 ,
193+ "Address flag should be set"
194+ ) ;
195+ assert ! (
196+ addr_cell. is_accessed( ) ,
197+ "Address cell should be marked accessed"
198+ ) ;
199+ }
200+
201+ #[ test]
202+ fn test_value_method ( ) {
203+ // Test on a NONE cell.
204+ let none_cell = MemoryCell :: NONE ;
205+ assert_eq ! ( none_cell. value:: <F >( ) , None ) ;
206+
207+ // Test on a valid integer cell.
208+ let int_val = MemoryValue :: Int ( F :: from_u64 ( 123 ) ) ;
209+ let int_cell = MemoryCell :: from ( int_val. clone ( ) ) ;
210+ assert_eq ! ( int_cell. value( ) , Some ( int_val) ) ;
211+
212+ // Test on a valid address cell.
213+ let addr_val = MemoryValue :: < F > :: Address ( MemoryAddress {
214+ segment_index : 5 ,
215+ offset : 10 ,
216+ } ) ;
217+ let addr_cell = MemoryCell :: from ( addr_val. clone ( ) ) ;
218+ assert_eq ! ( addr_cell. value( ) , Some ( addr_val) ) ;
219+ }
220+
221+ #[ test]
222+ fn test_conversion_from_int_value ( ) {
223+ let val = MemoryValue :: Int ( F :: from_u64 ( 500 ) ) ;
224+ let cell = MemoryCell :: from ( val) ;
225+ // Should just be the raw value, no flags set.
226+ assert_eq ! ( cell. 0 , 500 ) ;
227+ }
228+
229+ #[ test]
230+ fn test_conversion_from_address_value ( ) {
231+ let val = MemoryValue :: < F > :: Address ( MemoryAddress {
232+ segment_index : 10 ,
233+ offset : 20 ,
234+ } ) ;
235+ let cell = MemoryCell :: from ( val) ;
236+
237+ // Expected packed value: 0x2000000A00000014
238+ // Bit 61 (ADDRESS_MASK) + segment 10 shifted by 32 + offset 20
239+ let expected = ( 10u64 << 32 ) | 20u64 | MemoryCell :: ADDRESS_MASK ;
240+ assert_eq ! ( cell. 0 , expected) ;
241+ }
242+
243+ #[ test]
244+ fn test_conversion_to_int_value ( ) {
245+ // Raw u64 for an integer.
246+ let int_cell = MemoryCell ( 42 ) ;
247+ let val = MemoryValue :: < F > :: from ( int_cell) ;
248+ assert_eq ! ( val, MemoryValue :: Int ( F :: from_u64( 42 ) ) ) ;
249+
250+ // An integer cell can also be marked accessed; the flag should be ignored.
251+ let accessed_int_cell = MemoryCell ( 42 | MemoryCell :: ACCESS_MASK ) ;
252+ let accessed_val = MemoryValue :: < F > :: from ( accessed_int_cell) ;
253+ assert_eq ! ( accessed_val, MemoryValue :: Int ( F :: from_u64( 42 ) ) ) ;
254+ }
255+
256+ #[ test]
257+ fn test_conversion_to_address_value ( ) {
258+ let raw_addr = ( 50u64 << 32 ) | 100u64 | MemoryCell :: ADDRESS_MASK ;
259+ let addr_cell = MemoryCell ( raw_addr) ;
260+ let val = MemoryValue :: < F > :: from ( addr_cell) ;
261+
262+ let expected = MemoryValue :: Address ( MemoryAddress {
263+ segment_index : 50 ,
264+ offset : 100 ,
265+ } ) ;
266+ assert_eq ! ( val, expected) ;
267+ }
268+
269+ proptest ! {
270+ #[ test]
271+ fn proptest_roundtrip_conversion(
272+ val in any:: <MemoryValue <F >>( )
273+ ) {
274+ // Convert the generated MemoryValue to a MemoryCell.
275+ let cell = MemoryCell :: from( val. clone( ) ) ;
276+
277+ // Convert the MemoryCell back to a MemoryValue.
278+ let roundtrip_val = MemoryValue :: <F >:: from( cell) ;
279+
280+ // Assert that the original and round-tripped values are identical.
281+ prop_assert_eq!( val, roundtrip_val) ;
282+ }
138283 }
139284}
0 commit comments