tasm_lib/verifier/master_table/
air_constraint_evaluation.rs

1use ndarray::Array1;
2use triton_vm::challenges::Challenges;
3use triton_vm::constraints::dynamic_air_constraint_evaluation_tasm;
4use triton_vm::constraints::static_air_constraint_evaluation_tasm;
5use triton_vm::memory_layout::DynamicTasmConstraintEvaluationMemoryLayout;
6use triton_vm::memory_layout::IntegralMemoryLayout;
7use triton_vm::memory_layout::StaticTasmConstraintEvaluationMemoryLayout;
8use triton_vm::prelude::*;
9use triton_vm::table::auxiliary_table::Evaluable;
10use triton_vm::table::master_table::MasterAuxTable;
11use triton_vm::table::master_table::MasterMainTable;
12use twenty_first::math::x_field_element::EXTENSION_DEGREE;
13
14use crate::data_type::ArrayType;
15use crate::memory::dyn_malloc::DYN_MALLOC_ADDRESS;
16use crate::prelude::*;
17use crate::verifier::challenges::shared::conventional_challenges_pointer;
18
19#[derive(Debug, Clone, Copy)]
20pub enum MemoryLayout {
21    Dynamic(DynamicTasmConstraintEvaluationMemoryLayout),
22    Static(StaticTasmConstraintEvaluationMemoryLayout),
23}
24
25impl MemoryLayout {
26    pub fn conventional_static() -> Self {
27        const CURRENT_MAIN_ROW_PTR: u64 = 30u64;
28        const MAIN_ROW_SIZE: u64 = (MasterMainTable::NUM_COLUMNS * EXTENSION_DEGREE) as u64;
29        const AUX_ROW_SIZE: u64 = (MasterAuxTable::NUM_COLUMNS * EXTENSION_DEGREE) as u64;
30        const METADATA_SIZE_PER_PROOF_ITEM_ELEMENT: u64 = 2; // 1 for discriminant, 1 for elem size
31        let mem_layout = StaticTasmConstraintEvaluationMemoryLayout {
32            free_mem_page_ptr: BFieldElement::new(((1u64 << 32) - 3) * (1u64 << 32)),
33            curr_main_row_ptr: BFieldElement::new(CURRENT_MAIN_ROW_PTR),
34            curr_aux_row_ptr: BFieldElement::new(
35                CURRENT_MAIN_ROW_PTR + MAIN_ROW_SIZE + METADATA_SIZE_PER_PROOF_ITEM_ELEMENT,
36            ),
37            next_main_row_ptr: BFieldElement::new(
38                CURRENT_MAIN_ROW_PTR
39                    + MAIN_ROW_SIZE
40                    + AUX_ROW_SIZE
41                    + 2 * METADATA_SIZE_PER_PROOF_ITEM_ELEMENT,
42            ),
43            next_aux_row_ptr: BFieldElement::new(
44                CURRENT_MAIN_ROW_PTR
45                    + 2 * MAIN_ROW_SIZE
46                    + AUX_ROW_SIZE
47                    + 3 * METADATA_SIZE_PER_PROOF_ITEM_ELEMENT,
48            ),
49            challenges_ptr: conventional_challenges_pointer(),
50        };
51        assert!(mem_layout.is_integral());
52
53        Self::Static(mem_layout)
54    }
55
56    /// Generate a memory layout that allows you to store the proof anywhere in
57    /// memory.
58    pub fn conventional_dynamic() -> Self {
59        let mem_layout = DynamicTasmConstraintEvaluationMemoryLayout {
60            free_mem_page_ptr: BFieldElement::new(((1u64 << 32) - 3) * (1u64 << 32)),
61            challenges_ptr: conventional_challenges_pointer(),
62        };
63        assert!(mem_layout.is_integral());
64
65        Self::Dynamic(mem_layout)
66    }
67
68    pub fn challenges_pointer(&self) -> BFieldElement {
69        match self {
70            MemoryLayout::Dynamic(dl) => dl.challenges_ptr,
71            MemoryLayout::Static(sl) => sl.challenges_ptr,
72        }
73    }
74
75    /// Check that the memory layout matches the convention of this standard-library
76    pub fn is_integral(&self) -> bool {
77        // A: cannot collide with `kmalloc`
78        // B: cannot collide with `dyn_malloc` (must only use static memory (address.value() >= 2^{63}))
79        // C: cannot collide with state of `dyn_malloc`
80        // D: cannot have overlapping regions
81
82        // Notice that it's allowed to point to ND memory, $[0, 2^{32})$.
83        let memory_regions = match self {
84            MemoryLayout::Dynamic(dl) => dl.memory_regions(),
85            MemoryLayout::Static(sl) => sl.memory_regions(),
86        };
87
88        let kmalloc_region = Library::kmalloc_memory_region();
89        let kmalloc_disjoint = memory_regions
90            .iter()
91            .all(|mr| mr.disjoint_from(&kmalloc_region));
92
93        let dyn_malloc_region = DynMalloc::memory_region();
94        let dyn_malloc_disjoint = memory_regions
95            .iter()
96            .all(|mr| mr.disjoint_from(&dyn_malloc_region));
97
98        let dyn_malloc_state_disjoint = memory_regions
99            .iter()
100            .all(|mr| !mr.contains_address(DYN_MALLOC_ADDRESS));
101
102        let internally_consistent = match self {
103            MemoryLayout::Dynamic(dl) => dl.is_integral(),
104            MemoryLayout::Static(sl) => sl.is_integral(),
105        };
106
107        kmalloc_disjoint
108            && dyn_malloc_disjoint
109            && dyn_malloc_state_disjoint
110            && internally_consistent
111    }
112
113    pub fn label_friendly_name(&self) -> &str {
114        match self {
115            MemoryLayout::Dynamic(_) => "dynamic",
116            MemoryLayout::Static(_) => "static",
117        }
118    }
119}
120
121#[derive(Debug, Clone, Copy)]
122pub struct AirConstraintEvaluation {
123    pub memory_layout: MemoryLayout,
124}
125
126impl AirConstraintEvaluation {
127    pub fn new_static(static_layout: StaticTasmConstraintEvaluationMemoryLayout) -> Self {
128        Self {
129            memory_layout: MemoryLayout::Static(static_layout),
130        }
131    }
132
133    pub fn new_dynamic(static_layout: DynamicTasmConstraintEvaluationMemoryLayout) -> Self {
134        Self {
135            memory_layout: MemoryLayout::Dynamic(static_layout),
136        }
137    }
138
139    pub fn with_conventional_static_memory_layout() -> Self {
140        Self {
141            memory_layout: MemoryLayout::conventional_static(),
142        }
143    }
144
145    pub fn with_conventional_dynamic_memory_layout() -> Self {
146        Self {
147            memory_layout: MemoryLayout::conventional_dynamic(),
148        }
149    }
150
151    pub fn output_type() -> DataType {
152        DataType::Array(Box::new(ArrayType {
153            element_type: DataType::Xfe,
154            length: MasterAuxTable::NUM_CONSTRAINTS,
155        }))
156    }
157
158    /// Return the concatenated AIR-constraint evaluation
159    pub fn host_machine_air_constraint_evaluation(
160        input_values: AirConstraintSnippetInputs,
161    ) -> Vec<XFieldElement> {
162        let current_main_row = Array1::from(input_values.current_main_row);
163        let current_aux_row = Array1::from(input_values.current_aux_row);
164        let next_main_row = Array1::from(input_values.next_main_row);
165        let next_aux_row = Array1::from(input_values.next_aux_row);
166        let evaluated_initial_constraints = MasterAuxTable::evaluate_initial_constraints(
167            current_main_row.view(),
168            current_aux_row.view(),
169            &input_values.challenges,
170        );
171        let evaluated_consistency_constraints = MasterAuxTable::evaluate_consistency_constraints(
172            current_main_row.view(),
173            current_aux_row.view(),
174            &input_values.challenges,
175        );
176        let evaluated_transition_constraints = MasterAuxTable::evaluate_transition_constraints(
177            current_main_row.view(),
178            current_aux_row.view(),
179            next_main_row.view(),
180            next_aux_row.view(),
181            &input_values.challenges,
182        );
183        let evaluated_terminal_constraints = MasterAuxTable::evaluate_terminal_constraints(
184            current_main_row.view(),
185            current_aux_row.view(),
186            &input_values.challenges,
187        );
188
189        [
190            evaluated_initial_constraints,
191            evaluated_consistency_constraints,
192            evaluated_transition_constraints,
193            evaluated_terminal_constraints,
194        ]
195        .concat()
196    }
197}
198
199impl BasicSnippet for AirConstraintEvaluation {
200    fn inputs(&self) -> Vec<(DataType, String)> {
201        match self.memory_layout {
202            MemoryLayout::Dynamic(_) => vec![
203                (DataType::VoidPointer, "*curr_main_row".to_string()),
204                (DataType::VoidPointer, "*curr_extrow".to_string()),
205                (DataType::VoidPointer, "*next_main_row".to_string()),
206                (DataType::VoidPointer, "*next_aux_row".to_string()),
207            ],
208            MemoryLayout::Static(_) => vec![],
209        }
210    }
211
212    fn outputs(&self) -> Vec<(DataType, String)> {
213        vec![(Self::output_type(), "evaluated_constraints".to_owned())]
214    }
215
216    fn entrypoint(&self) -> String {
217        assert!(
218            self.memory_layout.is_integral(),
219            "Memory layout for input values for constraint evaluation must be integral"
220        );
221
222        // Consider parameterizing this entrypoint name if you need more than one instance.
223        "tasmlib_verifier_master_table_air_constraint_evaluation".to_owned()
224    }
225
226    fn code(&self, _library: &mut Library) -> Vec<LabelledInstruction> {
227        assert!(
228            self.memory_layout.is_integral(),
229            "Memory layout for input values for constraint evaluation must be integral"
230        );
231
232        let snippet_body = match self.memory_layout {
233            MemoryLayout::Dynamic(dynamic_layout) => {
234                dynamic_air_constraint_evaluation_tasm(dynamic_layout)
235            }
236            MemoryLayout::Static(static_layout) => {
237                static_air_constraint_evaluation_tasm(static_layout)
238            }
239        };
240
241        let entrypoint = self.entrypoint();
242        let mut code = triton_asm!(
243            {entrypoint}:
244        );
245        code.extend(snippet_body);
246        code.extend(triton_asm!(return));
247
248        code
249    }
250}
251
252/// Please notice that putting the proof into ND memory will *not* result in
253/// memory that's compatible with this layout. So this layout will fail to
254/// yield a functional STARK verifier program.
255#[cfg(test)]
256pub fn an_integral_but_profane_static_memory_layout() -> StaticTasmConstraintEvaluationMemoryLayout
257{
258    let mem_layout = StaticTasmConstraintEvaluationMemoryLayout {
259        free_mem_page_ptr: BFieldElement::new((u32::MAX as u64 - 200) * (1u64 << 32)),
260        curr_main_row_ptr: BFieldElement::new(1u64),
261        curr_aux_row_ptr: BFieldElement::new(1u64 << 20),
262        next_main_row_ptr: BFieldElement::new(1u64 << 21),
263        next_aux_row_ptr: BFieldElement::new(1u64 << 22),
264        challenges_ptr: BFieldElement::new(1u64 << 23),
265    };
266    assert!(mem_layout.is_integral());
267
268    mem_layout
269}
270
271#[cfg(test)]
272pub fn an_integral_but_profane_dynamic_memory_layout() -> DynamicTasmConstraintEvaluationMemoryLayout
273{
274    let mem_layout = DynamicTasmConstraintEvaluationMemoryLayout {
275        free_mem_page_ptr: BFieldElement::new((u32::MAX as u64 - 100) * (1u64 << 32)),
276        challenges_ptr: BFieldElement::new(1u64 << 30),
277    };
278    assert!(mem_layout.is_integral());
279
280    mem_layout
281}
282
283#[derive(Debug, Clone)]
284pub struct AirConstraintSnippetInputs {
285    pub current_main_row: Vec<XFieldElement>,
286    pub current_aux_row: Vec<XFieldElement>,
287    pub next_main_row: Vec<XFieldElement>,
288    pub next_aux_row: Vec<XFieldElement>,
289    pub challenges: Challenges,
290}
291
292#[cfg(test)]
293mod tests {
294    use arbitrary::Arbitrary;
295    use arbitrary::Unstructured;
296    use num_traits::ConstZero;
297    use rand::distr::StandardUniform;
298    use triton_vm::proof_stream::ProofStream;
299    use triton_vm::table::master_table::MasterMainTable;
300    use twenty_first::math::x_field_element::EXTENSION_DEGREE;
301
302    use super::*;
303    use crate::execute_test;
304    use crate::library::STATIC_MEMORY_LAST_ADDRESS;
305    use crate::rust_shadowing_helper_functions::array::array_get;
306    use crate::rust_shadowing_helper_functions::array::insert_as_array;
307    use crate::structure::tasm_object::decode_from_memory_with_size;
308    use crate::test_prelude::*;
309
310    #[test]
311    fn conventional_air_constraint_memory_layouts_are_integral() {
312        assert!(MemoryLayout::conventional_static().is_integral());
313        assert!(MemoryLayout::conventional_dynamic().is_integral());
314    }
315
316    #[test]
317    fn disallow_using_kmalloc_region() {
318        let mem_layout = MemoryLayout::Dynamic(DynamicTasmConstraintEvaluationMemoryLayout {
319            free_mem_page_ptr: STATIC_MEMORY_LAST_ADDRESS,
320            challenges_ptr: BFieldElement::new(1u64 << 30),
321        });
322        assert!(!mem_layout.is_integral());
323    }
324
325    #[test]
326    fn disallow_using_dynmalloc_region() {
327        let mem_layout = MemoryLayout::Dynamic(DynamicTasmConstraintEvaluationMemoryLayout {
328            free_mem_page_ptr: BFieldElement::new(42 * (1u64 << 32)),
329            challenges_ptr: BFieldElement::new(1u64 << 30),
330        });
331        assert!(!mem_layout.is_integral());
332    }
333
334    #[test]
335    fn disallow_using_dynmalloc_state() {
336        let mem_layout = MemoryLayout::Dynamic(DynamicTasmConstraintEvaluationMemoryLayout {
337            free_mem_page_ptr: BFieldElement::new(42 * (1u64 << 32)),
338            challenges_ptr: BFieldElement::new(BFieldElement::MAX),
339        });
340        assert!(!mem_layout.is_integral());
341    }
342
343    #[test]
344    fn disallow_overlapping_regions() {
345        let mut dyn_memory_layout = DynamicTasmConstraintEvaluationMemoryLayout {
346            free_mem_page_ptr: BFieldElement::new((u32::MAX as u64 - 100) * (1u64 << 32)),
347            challenges_ptr: conventional_challenges_pointer(),
348        };
349        assert!(MemoryLayout::Dynamic(dyn_memory_layout).is_integral());
350
351        dyn_memory_layout.challenges_ptr = dyn_memory_layout.free_mem_page_ptr;
352        assert!(!MemoryLayout::Dynamic(dyn_memory_layout).is_integral());
353    }
354
355    #[test]
356    fn conventional_memory_layout_agrees_with_tvm_proof_stored_at_address_zero() {
357        let program = triton_program!(halt);
358        let claim = Claim::about_program(&program);
359
360        let proof =
361            triton_vm::prove(Stark::default(), &claim, program, NonDeterminism::default()).unwrap();
362
363        const PROOF_ADDRESS: BFieldElement = BFieldElement::ZERO;
364        let mut memory = HashMap::<BFieldElement, BFieldElement>::new();
365        let proof_stream = ProofStream::try_from(&proof).unwrap();
366        encode_to_memory(&mut memory, PROOF_ADDRESS, &proof);
367
368        let assumed_memory_layout = MemoryLayout::conventional_static();
369        let MemoryLayout::Static(assumed_memory_layout) = assumed_memory_layout else {
370            panic!()
371        };
372        const MAIN_ROW_SIZE: usize = MasterMainTable::NUM_COLUMNS * EXTENSION_DEGREE;
373        const AUX_ROW_SIZE: usize = MasterAuxTable::NUM_COLUMNS * EXTENSION_DEGREE;
374
375        let assumed_curr_main_row: [XFieldElement; MasterMainTable::NUM_COLUMNS] =
376            *decode_from_memory_with_size(
377                &memory,
378                assumed_memory_layout.curr_main_row_ptr,
379                MAIN_ROW_SIZE,
380            )
381            .unwrap();
382        let actual_curr_main_row_from_proof = proof_stream.items[4]
383            .clone()
384            .try_into_out_of_domain_main_row()
385            .unwrap();
386        assert_eq!(*actual_curr_main_row_from_proof, assumed_curr_main_row);
387
388        let assumed_curr_aux_row: [XFieldElement; MasterAuxTable::NUM_COLUMNS] =
389            *decode_from_memory_with_size(
390                &memory,
391                assumed_memory_layout.curr_aux_row_ptr,
392                AUX_ROW_SIZE,
393            )
394            .unwrap();
395        let actual_curr_aux_row_from_proof = proof_stream.items[5]
396            .clone()
397            .try_into_out_of_domain_aux_row()
398            .unwrap();
399        assert_eq!(*actual_curr_aux_row_from_proof, assumed_curr_aux_row);
400
401        let assumed_next_main_row: [XFieldElement; MasterMainTable::NUM_COLUMNS] =
402            *decode_from_memory_with_size(
403                &memory,
404                assumed_memory_layout.next_main_row_ptr,
405                MAIN_ROW_SIZE,
406            )
407            .unwrap();
408        let actual_next_main_row_from_proof = proof_stream.items[6]
409            .clone()
410            .try_into_out_of_domain_main_row()
411            .unwrap();
412        assert_eq!(*actual_next_main_row_from_proof, assumed_next_main_row);
413
414        let assumed_next_aux_row: [XFieldElement; MasterAuxTable::NUM_COLUMNS] =
415            *decode_from_memory_with_size(
416                &memory,
417                assumed_memory_layout.next_aux_row_ptr,
418                AUX_ROW_SIZE,
419            )
420            .unwrap();
421        let actual_next_aux_row_from_proof = proof_stream.items[7]
422            .clone()
423            .try_into_out_of_domain_aux_row()
424            .unwrap();
425        assert_eq!(*actual_next_aux_row_from_proof, assumed_next_aux_row);
426    }
427
428    impl Function for AirConstraintEvaluation {
429        fn rust_shadow(
430            &self,
431            _: &mut Vec<BFieldElement>,
432            _: &mut HashMap<BFieldElement, BFieldElement>,
433        ) {
434            // Never called as we do a more manual test.
435            // The more manual test is done bc we don't want to
436            // have to simulate all the intermediate calculations
437            // that are stored to memory.
438            unimplemented!()
439        }
440
441        fn pseudorandom_initial_state(
442            &self,
443            seed: [u8; 32],
444            _: Option<BenchmarkCase>,
445        ) -> FunctionInitialState {
446            // Used for benchmarking
447            let mut rng = StdRng::from_seed(seed);
448            let input_values = Self::random_input_values(&mut rng);
449            let (memory, stack) = self.prepare_tvm_memory_and_stack(input_values);
450
451            FunctionInitialState { stack, memory }
452        }
453    }
454
455    #[test]
456    fn constraint_evaluation_test() {
457        let static_snippet = AirConstraintEvaluation {
458            memory_layout: MemoryLayout::Static(an_integral_but_profane_static_memory_layout()),
459        };
460
461        let dynamic_snippet = AirConstraintEvaluation {
462            memory_layout: MemoryLayout::Dynamic(an_integral_but_profane_dynamic_memory_layout()),
463        };
464
465        let mut seed: [u8; 32] = [0u8; 32];
466        rand::rng().fill_bytes(&mut seed);
467        static_snippet.test_equivalence_with_host_machine_evaluation(seed);
468
469        rand::rng().fill_bytes(&mut seed);
470        dynamic_snippet.test_equivalence_with_host_machine_evaluation(seed);
471    }
472
473    impl AirConstraintEvaluation {
474        fn test_equivalence_with_host_machine_evaluation(&self, seed: [u8; 32]) {
475            let mut rng = StdRng::from_seed(seed);
476            let input_values = Self::random_input_values(&mut rng);
477
478            let (tasm_result, _) = self.tasm_result(input_values.clone());
479            let host_machine_result = Self::host_machine_air_constraint_evaluation(input_values);
480
481            assert_eq!(tasm_result.len(), host_machine_result.len());
482            assert_eq!(
483                tasm_result.iter().copied().sum::<XFieldElement>(),
484                host_machine_result.iter().copied().sum::<XFieldElement>()
485            );
486            assert_eq!(tasm_result, host_machine_result);
487        }
488
489        pub(crate) fn random_input_values(rng: &mut StdRng) -> AirConstraintSnippetInputs {
490            let current_main_row: Vec<XFieldElement> = rng
491                .sample_iter(StandardUniform)
492                .take(MasterMainTable::NUM_COLUMNS)
493                .collect();
494            let current_aux_row: Vec<XFieldElement> = rng
495                .sample_iter(StandardUniform)
496                .take(MasterAuxTable::NUM_COLUMNS)
497                .collect();
498            let next_main_row: Vec<XFieldElement> = rng
499                .sample_iter(StandardUniform)
500                .take(MasterMainTable::NUM_COLUMNS)
501                .collect();
502            let next_aux_row: Vec<XFieldElement> = rng
503                .sample_iter(StandardUniform)
504                .take(MasterAuxTable::NUM_COLUMNS)
505                .collect();
506
507            let mut ch_seed = [0u8; 12000];
508            rng.fill_bytes(&mut ch_seed);
509            let mut unstructured = Unstructured::new(&ch_seed);
510            let challenges: Challenges = Challenges::arbitrary(&mut unstructured).unwrap();
511
512            AirConstraintSnippetInputs {
513                current_main_row,
514                current_aux_row,
515                next_main_row,
516                next_aux_row,
517                challenges,
518            }
519        }
520
521        pub(crate) fn prepare_tvm_memory_and_stack(
522            &self,
523            input_values: AirConstraintSnippetInputs,
524        ) -> (HashMap<BFieldElement, BFieldElement>, Vec<BFieldElement>) {
525            match self.memory_layout {
526                MemoryLayout::Static(static_layout) => {
527                    let mut memory: HashMap<BFieldElement, BFieldElement> = HashMap::default();
528                    insert_as_array(
529                        static_layout.curr_main_row_ptr,
530                        &mut memory,
531                        input_values.current_main_row,
532                    );
533                    insert_as_array(
534                        static_layout.curr_aux_row_ptr,
535                        &mut memory,
536                        input_values.current_aux_row,
537                    );
538                    insert_as_array(
539                        static_layout.next_main_row_ptr,
540                        &mut memory,
541                        input_values.next_main_row,
542                    );
543                    insert_as_array(
544                        static_layout.next_aux_row_ptr,
545                        &mut memory,
546                        input_values.next_aux_row,
547                    );
548                    insert_as_array(
549                        static_layout.challenges_ptr,
550                        &mut memory,
551                        input_values.challenges.challenges.to_vec(),
552                    );
553
554                    (memory, self.init_stack_for_isolated_run())
555                }
556                MemoryLayout::Dynamic(dynamic_layout) => {
557                    let mut memory: HashMap<BFieldElement, BFieldElement> = HashMap::default();
558                    let curr_main_row_ptr = dynamic_layout.challenges_ptr + bfe!(10000);
559                    let curr_aux_row_ptr = curr_main_row_ptr
560                        + bfe!((input_values.current_main_row.len() * EXTENSION_DEGREE + 1) as u64);
561                    let next_main_row_ptr = curr_aux_row_ptr
562                        + bfe!((input_values.current_aux_row.len() * EXTENSION_DEGREE + 2) as u64);
563                    let next_aux_row_ptr = next_main_row_ptr
564                        + bfe!((input_values.next_main_row.len() * EXTENSION_DEGREE + 3) as u64);
565
566                    insert_as_array(
567                        curr_main_row_ptr,
568                        &mut memory,
569                        input_values.current_main_row,
570                    );
571                    insert_as_array(curr_aux_row_ptr, &mut memory, input_values.current_aux_row);
572                    insert_as_array(next_main_row_ptr, &mut memory, input_values.next_main_row);
573                    insert_as_array(next_aux_row_ptr, &mut memory, input_values.next_aux_row);
574                    insert_as_array(
575                        dynamic_layout.challenges_ptr,
576                        &mut memory,
577                        input_values.challenges.challenges.to_vec(),
578                    );
579
580                    let mut stack = self.init_stack_for_isolated_run();
581                    stack.push(curr_main_row_ptr);
582                    stack.push(curr_aux_row_ptr);
583                    stack.push(next_main_row_ptr);
584                    stack.push(next_aux_row_ptr);
585
586                    (memory, stack)
587                }
588            }
589        }
590
591        /// Return the pointed-to array and its address.
592        /// Note that the result lives as an array in TVM memory but is represented as a list here
593        /// since its length is not known at `tasm-lib`'s compile time.
594        pub(crate) fn read_result_from_memory(
595            mut final_state: VMState,
596        ) -> (Vec<XFieldElement>, BFieldElement) {
597            let result_pointer = final_state.op_stack.stack.pop().unwrap();
598            let mut tasm_result: Vec<XFieldElement> = vec![];
599            for i in 0..MasterAuxTable::NUM_CONSTRAINTS {
600                tasm_result.push(XFieldElement::new(
601                    array_get(result_pointer, i, &final_state.ram, EXTENSION_DEGREE)
602                        .try_into()
603                        .unwrap(),
604                ));
605            }
606
607            (tasm_result, result_pointer)
608        }
609
610        /// Return evaluated constraints and their location in memory
611        pub(crate) fn tasm_result(
612            &self,
613            input_values: AirConstraintSnippetInputs,
614        ) -> (Vec<XFieldElement>, BFieldElement) {
615            let (init_memory, stack) = self.prepare_tvm_memory_and_stack(input_values);
616
617            let final_state = execute_test(
618                &self.link_for_isolated_run(),
619                &mut stack.clone(),
620                self.stack_diff(),
621                vec![],
622                NonDeterminism::default().with_ram(init_memory),
623                None,
624            );
625
626            Self::read_result_from_memory(final_state)
627        }
628    }
629}
630
631#[cfg(test)]
632mod bench {
633    use super::*;
634    use crate::test_prelude::*;
635
636    #[test]
637    fn bench_air_constraint_evaluation() {
638        ShadowedFunction::new(AirConstraintEvaluation {
639            memory_layout: MemoryLayout::Static(an_integral_but_profane_static_memory_layout()),
640        })
641        .bench();
642    }
643}