Skip to main content

cairo_lang_runnable_utils/
builder.rs

1use cairo_lang_casm::assembler::AssembledCairoProgram;
2use cairo_lang_casm::builder::{CasmBuilder, Var};
3use cairo_lang_casm::cell_expression::CellExpression;
4use cairo_lang_casm::hints::ExternalHint;
5use cairo_lang_casm::instructions::Instruction;
6use cairo_lang_casm::{ap_change, casm, casm_build_extend, cell_ref};
7use cairo_lang_sierra::extensions::bitwise::BitwiseType;
8use cairo_lang_sierra::extensions::circuit::{AddModType, MulModType};
9use cairo_lang_sierra::extensions::core::{CoreLibfunc, CoreType};
10use cairo_lang_sierra::extensions::ec::EcOpType;
11use cairo_lang_sierra::extensions::gas::GasBuiltinType;
12use cairo_lang_sierra::extensions::pedersen::PedersenType;
13use cairo_lang_sierra::extensions::poseidon::PoseidonType;
14use cairo_lang_sierra::extensions::range_check::{RangeCheck96Type, RangeCheckType};
15use cairo_lang_sierra::extensions::segment_arena::SegmentArenaType;
16use cairo_lang_sierra::extensions::starknet::syscalls::SystemType;
17use cairo_lang_sierra::extensions::{ConcreteType, NamedType};
18use cairo_lang_sierra::ids::{ConcreteTypeId, GenericTypeId};
19use cairo_lang_sierra::program::{
20    ConcreteTypeLongId, Function, Program as SierraProgram, StatementIdx,
21};
22use cairo_lang_sierra::program_registry::{ProgramRegistry, ProgramRegistryError};
23use cairo_lang_sierra_ap_change::ApChangeError;
24use cairo_lang_sierra_gas::CostError;
25use cairo_lang_sierra_to_casm::annotations::AnnotationError;
26use cairo_lang_sierra_to_casm::compiler::{CairoProgram, CompilationError, SierraToCasmConfig};
27use cairo_lang_sierra_to_casm::metadata::{
28    Metadata, MetadataComputationConfig, MetadataError, calc_metadata, calc_metadata_ap_change_only,
29};
30use cairo_lang_sierra_type_size::ProgramRegistryInfo;
31use cairo_lang_utils::casts::IntoOrPanic;
32use cairo_lang_utils::ordered_hash_map::OrderedHashMap;
33use cairo_lang_utils::unordered_hash_map::UnorderedHashMap;
34use cairo_lang_utils::unordered_hash_set::UnorderedHashSet;
35use cairo_vm::types::builtin_name::BuiltinName;
36use thiserror::Error;
37
38#[derive(Debug, Error)]
39pub enum BuildError {
40    #[error(
41        "Failed calculating gas usage, it is likely a call for `gas::withdraw_gas` is missing. \
42         Inner error: {0}"
43    )]
44    FailedGasCalculation(#[from] CostError),
45    #[error("Function with suffix `{suffix}` to run not found.")]
46    MissingFunction { suffix: String },
47    #[error(transparent)]
48    ProgramRegistryError(#[from] Box<ProgramRegistryError>),
49    #[error(transparent)]
50    SierraCompilationError(#[from] Box<CompilationError>),
51    #[error(transparent)]
52    ApChangeError(#[from] ApChangeError),
53}
54
55impl BuildError {
56    pub fn stmt_indices(&self) -> Vec<StatementIdx> {
57        match self {
58            BuildError::SierraCompilationError(err) => err.stmt_indices(),
59            _ => vec![],
60        }
61    }
62
63    pub fn is_ap_overflow_error(&self) -> bool {
64        let BuildError::SierraCompilationError(err) = self else {
65            return false;
66        };
67
68        let CompilationError::AnnotationError(AnnotationError::ApChangeError { ref error, .. }) =
69            **err
70        else {
71            return false;
72        };
73
74        *error == ap_change::ApChangeError::OffsetOverflow
75    }
76}
77
78/// Builder for creating a runnable CASM program from Sierra code.
79pub struct RunnableBuilder {
80    /// The Sierra program.
81    sierra_program: SierraProgram,
82    /// Metadata for the Sierra program.
83    metadata: Metadata,
84    /// Program registry and type sizes for the Sierra program.
85    program_info: ProgramRegistryInfo,
86    /// The CASM program matching the Sierra code.
87    casm_program: CairoProgram,
88    /// The types of the non-user argument variables.
89    non_args_types: UnorderedHashSet<GenericTypeId>,
90}
91
92impl RunnableBuilder {
93    /// Creates a new `RunnableBuilder` for a Sierra program.
94    pub fn new(
95        sierra_program: SierraProgram,
96        metadata_config: Option<MetadataComputationConfig>,
97    ) -> Result<Self, BuildError> {
98        let program_info = ProgramRegistryInfo::new(&sierra_program)?;
99        let gas_usage_check = metadata_config.is_some();
100        let metadata = create_metadata(&sierra_program, &program_info, metadata_config)?;
101        let casm_program = cairo_lang_sierra_to_casm::compiler::compile(
102            &sierra_program,
103            &program_info,
104            &metadata,
105            SierraToCasmConfig { gas_usage_check, max_bytecode_size: usize::MAX },
106        )?;
107
108        Ok(Self {
109            sierra_program,
110            metadata,
111            program_info,
112            casm_program,
113            non_args_types: UnorderedHashSet::from_iter([
114                AddModType::ID,
115                BitwiseType::ID,
116                GasBuiltinType::ID,
117                EcOpType::ID,
118                MulModType::ID,
119                PedersenType::ID,
120                PoseidonType::ID,
121                RangeCheck96Type::ID,
122                RangeCheckType::ID,
123                SegmentArenaType::ID,
124                SystemType::ID,
125            ]),
126        })
127    }
128
129    /// Returns the Sierra program.
130    pub fn sierra_program(&self) -> &SierraProgram {
131        &self.sierra_program
132    }
133
134    /// Returns the CASM program of the Sierra program, without any modifications.
135    pub fn casm_program(&self) -> &CairoProgram {
136        &self.casm_program
137    }
138
139    /// Returns the metadata of the Sierra program.
140    pub fn metadata(&self) -> &Metadata {
141        &self.metadata
142    }
143
144    /// Returns the program registry of the Sierra program.
145    pub fn registry(&self) -> &ProgramRegistry<CoreType, CoreLibfunc> {
146        &self.program_info.registry
147    }
148
149    /// Finds the first function ending with `name_suffix`.
150    pub fn find_function(&self, name_suffix: &str) -> Result<&Function, BuildError> {
151        self.sierra_program
152            .funcs
153            .iter()
154            .find(|f| {
155                if let Some(name) = &f.id.debug_name { name.ends_with(name_suffix) } else { false }
156            })
157            .ok_or_else(|| BuildError::MissingFunction { suffix: name_suffix.to_owned() })
158    }
159
160    /// Returns the type info of a given `ConcreteTypeId`.
161    fn type_info(&self, ty: &ConcreteTypeId) -> &cairo_lang_sierra::extensions::types::TypeInfo {
162        self.program_info.registry.get_type(ty).unwrap().info()
163    }
164
165    /// Returns the long id of a given `ConcreteTypeId`.
166    pub fn type_long_id(&self, ty: &ConcreteTypeId) -> &ConcreteTypeLongId {
167        &self.type_info(ty).long_id
168    }
169
170    /// Returns the size of a given `ConcreteTypeId`.
171    pub fn type_size(&self, ty: &ConcreteTypeId) -> i16 {
172        self.program_info.type_sizes[ty]
173    }
174
175    /// Returns whether `ty` is a user argument type (e.g., not a builtin).
176    pub fn is_user_arg_type(&self, ty: &GenericTypeId) -> bool {
177        !self.non_args_types.contains(ty)
178    }
179
180    /// Creates the wrapper info for a given function.
181    pub fn create_wrapper_info(
182        &self,
183        func: &Function,
184        config: EntryCodeConfig,
185    ) -> Result<CasmProgramWrapperInfo, BuildError> {
186        let (header, builtins) = self.create_entry_code(func, config)?;
187        Ok(CasmProgramWrapperInfo { header, builtins, footer: create_code_footer() })
188    }
189
190    /// Assembles a function program for a given function.
191    pub fn assemble_function_program(
192        &self,
193        func: &Function,
194        config: EntryCodeConfig,
195    ) -> Result<(AssembledCairoProgram, Vec<BuiltinName>), BuildError> {
196        let info = self.create_wrapper_info(func, config)?;
197        let assembled_cairo_program = self.casm_program.assemble_ex(&info.header, &info.footer);
198        Ok((assembled_cairo_program, info.builtins))
199    }
200
201    /// Returns the instructions to add to the beginning of the code to successfully call the main
202    /// function, as well as the builtins required to execute the program.
203    ///
204    /// # Arguments
205    ///
206    /// * `func` - The function to create the entry code for.
207    /// * `config` - The entry code configuration.
208    ///
209    /// # Returns
210    ///
211    /// A `Result` containing a tuple with a vector of `Instruction`s and a vector of
212    /// `BuiltinName`s, or a `BuildError`.
213    fn create_entry_code(
214        &self,
215        func: &Function,
216        config: EntryCodeConfig,
217    ) -> Result<(Vec<Instruction>, Vec<BuiltinName>), BuildError> {
218        let param_types = self.generic_id_and_size_from_concrete(&func.signature.param_types);
219        let return_types = self.generic_id_and_size_from_concrete(&func.signature.ret_types);
220
221        let entry_point = func.entry_point.0;
222        let code_offset =
223            self.casm_program.debug_info.sierra_statement_info[entry_point].start_offset;
224
225        create_entry_code_from_params(&param_types, &return_types, code_offset, config)
226    }
227
228    /// Converts array of `ConcreteTypeId`s into corresponding `GenericTypeId`s and their sizes.
229    pub fn generic_id_and_size_from_concrete(
230        &self,
231        types: &[ConcreteTypeId],
232    ) -> Vec<(GenericTypeId, i16)> {
233        types
234            .iter()
235            .map(|pt| {
236                let info = self.type_info(pt);
237                let generic_id = &info.long_id.generic_id;
238                let size = self.type_size(pt);
239                (generic_id.clone(), size)
240            })
241            .collect()
242    }
243}
244
245/// Configuration for the entry code creation.
246#[derive(Clone, Debug)]
247pub struct EntryCodeConfig {
248    /// Whether this is a testing configuration.
249    ///
250    /// In the case of testing, the function signature can be anything.
251    /// In the case of execution, the function signature is expected to be
252    /// `(Span<felt252>, Array<felt252>) -> Array<felt252>`.
253    /// Additionally, the output builtin will be injected to be the supplied array input,
254    /// and to be the result of the output.
255    ///
256    /// Currently, if exists in params, the segment arena will also be finalized in execution mode.
257    pub testing: bool,
258    /// Whether to allow unsound operations in the program.
259    /// Currently relevant for supporting emulated builtins.
260    pub allow_unsound: bool,
261
262    /// An optional list of builtins to use in the entry code (if its none the builtins will be
263    /// inferred from the param_types).
264    pub builtin_list: Option<Vec<BuiltinName>>,
265}
266impl EntryCodeConfig {
267    /// Returns a configuration for testing purposes.
268    ///
269    /// This configuration will not finalize the segment arena after calling the function, to
270    /// prevent failure in case of functions returning values.
271    pub fn testing() -> Self {
272        Self { testing: true, allow_unsound: true, builtin_list: None }
273    }
274
275    /// Returns a configuration for execution purposes.
276    pub fn executable(allow_unsound: bool, builtin_list: Option<Vec<BuiltinName>>) -> Self {
277        Self { testing: false, allow_unsound, builtin_list }
278    }
279}
280
281/// Information about a CASM program.
282pub struct CasmProgramWrapperInfo {
283    /// The builtins used in the program.
284    pub builtins: Vec<BuiltinName>,
285    /// The instructions before the program.
286    pub header: Vec<Instruction>,
287    /// The instructions after the program.
288    pub footer: Vec<Instruction>,
289}
290
291/// Creates the entry code to call the function.
292///
293/// # Arguments
294///
295/// * `param_types` - The types and sizes of the parameters of the function.
296/// * `return_types` - The types and sizes of the return values of the function.
297/// * `code_offset` - The offset of the entry_point in the CASM program (before adding the
298///   executable header).
299/// * `config` - The configuration for the entry code creation.
300///
301/// # Returns
302///
303/// A `Result` containing a tuple with a vector of `Instruction`s and a vector of `BuiltinName`s, or
304/// a `BuildError`.
305pub fn create_entry_code_from_params(
306    param_types: &[(GenericTypeId, i16)],
307    return_types: &[(GenericTypeId, i16)],
308    code_offset: usize,
309    config: EntryCodeConfig,
310) -> Result<(Vec<Instruction>, Vec<BuiltinName>), BuildError> {
311    let mut helper = EntryCodeHelper::new(config);
312    if let Some(builtin_list) = &helper.config.builtin_list {
313        helper.builtins = builtin_list.clone();
314        for (builtin_offset, builtin_name) in (3..).zip(helper.builtins.iter().rev()) {
315            helper.builtin_vars.insert(
316                *builtin_name,
317                helper.ctx.add_var(CellExpression::Deref(cell_ref!([fp - builtin_offset]))),
318            );
319        }
320    } else {
321        helper.process_builtins(param_types);
322    }
323    helper.process_params(param_types);
324    casm_build_extend!(helper.ctx, let () = call FUNCTION;);
325    helper.process_output(return_types);
326
327    if helper.has_post_calculation_loop {
328        helper.validate_segment_arena();
329    }
330
331    if !helper.config.testing {
332        helper.process_builtins_output();
333    }
334
335    casm_build_extend! (helper.ctx, ret;);
336    // Point `FUNCTION` to offset `code_offset` from this point which is the beginning of the Sierra
337    // based code.
338    helper.ctx.future_label("FUNCTION", code_offset);
339    Ok((helper.ctx.build([]).instructions, helper.builtins))
340}
341
342/// Helper struct for [create_entry_code_from_params].
343struct EntryCodeHelper {
344    ctx: CasmBuilder,
345    config: EntryCodeConfig,
346    builtin_ty_to_vm_name: OrderedHashMap<GenericTypeId, BuiltinName>,
347    builtins: Vec<BuiltinName>,
348    got_segment_arena: bool,
349    has_post_calculation_loop: bool,
350    /// Maps each builtin name to the variable that stores it.
351    builtin_vars: UnorderedHashMap<BuiltinName, Var>,
352    /// Maps each builtin name to the local cell expression that stores it.
353    /// This is needed because variables in `builtin_vars` might be invalidated.
354    local_exprs: OrderedHashMap<BuiltinName, CellExpression>,
355    emulated_builtins: UnorderedHashSet<GenericTypeId>,
356}
357
358impl EntryCodeHelper {
359    /// Creates a new `EntryCodeHelper` with the given configuration.
360    fn new(config: EntryCodeConfig) -> Self {
361        Self {
362            ctx: CasmBuilder::default(),
363            config,
364            builtin_ty_to_vm_name: OrderedHashMap::from_iter([
365                (PedersenType::ID, BuiltinName::pedersen),
366                (RangeCheckType::ID, BuiltinName::range_check),
367                (BitwiseType::ID, BuiltinName::bitwise),
368                (EcOpType::ID, BuiltinName::ec_op),
369                (PoseidonType::ID, BuiltinName::poseidon),
370                (RangeCheck96Type::ID, BuiltinName::range_check96),
371                (AddModType::ID, BuiltinName::add_mod),
372                (MulModType::ID, BuiltinName::mul_mod),
373                (SegmentArenaType::ID, BuiltinName::segment_arena),
374            ]),
375            builtins: vec![],
376            got_segment_arena: false,
377            has_post_calculation_loop: false,
378            builtin_vars: UnorderedHashMap::default(),
379            local_exprs: OrderedHashMap::default(),
380            emulated_builtins: UnorderedHashSet::<_>::from_iter([SystemType::ID]),
381        }
382    }
383
384    /// Processes the builtins required for the function parameters.
385    fn process_builtins(&mut self, param_types: &[(GenericTypeId, i16)]) {
386        let mut builtin_offset = 3;
387
388        // Process all builtins except the segment arena in reverse order.
389        for (builtin_ty, builtin_name) in self.builtin_ty_to_vm_name.iter().rev().skip(1) {
390            if param_types.iter().any(|(ty, _)| ty == builtin_ty) {
391                self.builtin_vars.insert(
392                    *builtin_name,
393                    self.ctx.add_var(CellExpression::Deref(cell_ref!([fp - builtin_offset]))),
394                );
395                builtin_offset += 1;
396                self.builtins.push(*builtin_name);
397            }
398        }
399        if !self.config.testing {
400            let output_builtin_var =
401                self.ctx.add_var(CellExpression::Deref(cell_ref!([fp - builtin_offset])));
402            self.builtin_vars.insert(BuiltinName::output, output_builtin_var);
403            self.builtins.push(BuiltinName::output);
404        }
405        self.builtins.reverse();
406    }
407
408    /// Processes the function parameters in preparation for the function call.
409    fn process_params(&mut self, param_types: &[(GenericTypeId, i16)]) {
410        self.got_segment_arena = param_types.iter().any(|(ty, _)| ty == &SegmentArenaType::ID);
411        self.has_post_calculation_loop = self.got_segment_arena && !self.config.testing;
412
413        if self.has_post_calculation_loop {
414            if !self.config.testing {
415                // Add a local variable for the output builtin
416                casm_build_extend!(self.ctx, localvar local;);
417                self.local_exprs
418                    .insert(BuiltinName::output, self.ctx.get_unadjusted(local).clone());
419            }
420
421            for (generic_ty, _ty_size) in param_types {
422                if let Some(name) = self.builtin_ty_to_vm_name.get(generic_ty)
423                    && name != &BuiltinName::segment_arena
424                {
425                    casm_build_extend!(self.ctx, localvar local;);
426                    self.local_exprs.insert(*name, self.ctx.get_unadjusted(local).clone());
427                }
428            }
429
430            if !self.local_exprs.is_empty() {
431                casm_build_extend!(self.ctx, ap += self.local_exprs.len(););
432            }
433        }
434
435        if self.got_segment_arena {
436            casm_build_extend! {self.ctx,
437                tempvar segment_arena;
438                tempvar infos;
439                hint AllocSegment into {dst: segment_arena};
440                hint AllocSegment into {dst: infos};
441                const czero = 0;
442                tempvar zero = czero;
443                // Write Infos segment, n_constructed (0), and n_destructed (0) to the segment.
444                assert infos = *(segment_arena++);
445                assert zero = *(segment_arena++);
446                assert zero = *(segment_arena++);
447            }
448            // Adding the segment arena to the builtins var map.
449            self.builtin_vars.insert(BuiltinName::segment_arena, segment_arena);
450        }
451        let mut unallocated_count = 0;
452        let mut param_index = 0;
453
454        // Not processing the user function params in the case of a proof, as these are processed
455        // after.
456        let non_proof_signature_params =
457            if self.config.testing { param_types } else { &param_types[..(param_types.len() - 2)] };
458        for (generic_ty, ty_size) in non_proof_signature_params {
459            if let Some(name) = self.builtin_ty_to_vm_name.get(generic_ty).cloned() {
460                let var = self.builtin_vars[&name];
461                casm_build_extend!(self.ctx, tempvar _builtin = var;);
462            } else if self.emulated_builtins.contains(generic_ty) {
463                assert!(
464                    self.config.allow_unsound,
465                    "Cannot support emulated builtins if not configured to `allow_unsound`."
466                );
467                casm_build_extend! {self.ctx,
468                    tempvar system;
469                    hint AllocSegment into {dst: system};
470                };
471                unallocated_count += ty_size;
472            } else if self.config.testing {
473                if *ty_size > 0 {
474                    casm_build_extend! {self.ctx,
475                        tempvar first;
476                        const param_index = param_index;
477                        hint ExternalHint::WriteRunParam { index: param_index } into { dst: first };
478                    };
479                    for _ in 1..*ty_size {
480                        casm_build_extend!(self.ctx, tempvar _cell;);
481                    }
482                }
483                param_index += 1;
484                unallocated_count += ty_size;
485            } else if generic_ty == &GasBuiltinType::ID {
486                casm_build_extend! {self.ctx,
487                    const max_gas = i64::MAX;
488                    tempvar gas = max_gas;
489                };
490            } else {
491                unreachable!("Unexpected argument type: {:?}", generic_ty);
492            }
493        }
494        if !self.config.testing {
495            let output_ptr = self.builtin_vars[&BuiltinName::output];
496            casm_build_extend! { self.ctx,
497                tempvar input_start;
498                tempvar _input_end;
499                const param_index = 0;
500                hint ExternalHint::WriteRunParam { index: param_index } into { dst: input_start };
501                tempvar output_start = output_ptr;
502                tempvar output_end = output_ptr;
503            };
504            unallocated_count += 2;
505        }
506        if unallocated_count > 0 {
507            casm_build_extend!(self.ctx, ap += unallocated_count.into_or_panic::<usize>(););
508        }
509    }
510
511    /// Processes the function return types.
512    fn process_output(&mut self, return_types: &[(GenericTypeId, i16)]) {
513        let mut unprocessed_return_size = return_types.iter().map(|(_, size)| size).sum::<i16>();
514        let mut next_unprocessed_deref = || {
515            let deref_cell = CellExpression::Deref(cell_ref!([ap - unprocessed_return_size]));
516            assert!(unprocessed_return_size > 0);
517            unprocessed_return_size -= 1;
518            deref_cell
519        };
520        // Not processing the user function return type in the case of a proof, as it is processed
521        // after.
522        let non_proof_return_types = if self.config.testing {
523            return_types
524        } else {
525            &return_types[..(return_types.len() - 1)]
526        };
527        let mut new_builtin_vars = UnorderedHashMap::<BuiltinName, Var>::default();
528        for (ret_ty, size) in non_proof_return_types {
529            if let Some(name) = self.builtin_ty_to_vm_name.get(ret_ty) {
530                new_builtin_vars.insert(*name, self.ctx.add_var(next_unprocessed_deref()));
531            } else if self.config.testing {
532                for _ in 0..*size {
533                    next_unprocessed_deref();
534                }
535            } else if self.emulated_builtins.contains(ret_ty) {
536                assert!(
537                    self.config.allow_unsound,
538                    "Cannot support emulated builtins if not configured to `allow_unsound`."
539                );
540                let _ = next_unprocessed_deref();
541            } else {
542                assert_eq!(ret_ty, &GasBuiltinType::ID);
543                let _ = next_unprocessed_deref();
544            }
545        }
546        if !self.config.testing {
547            let (ret_ty, size) = return_types.last().unwrap();
548            let opt_panic_indicator = match *size {
549                2 => None,
550                3 => Some(self.ctx.add_var(next_unprocessed_deref())),
551                _ => panic!("Unexpected output type: {:?}", ret_ty.0),
552            };
553            // The start ptr of the output in the case of successful run,
554            // or the panic data in case of a failure.
555            let ptr_start = self.ctx.add_var(next_unprocessed_deref());
556            // The end ptr of the output in the case of successful run,
557            // or the panic data in case of a failure.
558            let ptr_end = self.ctx.add_var(next_unprocessed_deref());
559            if let Some(panic_indicator) = opt_panic_indicator {
560                casm_build_extend! {self.ctx,
561                    const czero = 0;
562                    tempvar zero = czero;
563                    hint ExternalHint::AddMarker { start: ptr_start, end: ptr_end };
564                    assert zero = panic_indicator;
565                };
566            }
567            new_builtin_vars.insert(BuiltinName::output, ptr_end);
568        }
569        assert_eq!(unprocessed_return_size, 0);
570        if self.has_post_calculation_loop {
571            // Store the new builtin vars that were assigned local variables, as they are going to
572            // be invalidated by loop. Note that the builtin vars map is not updated
573            // with the new locals, since they will be invalidated by the rescoping
574            // performed in `validate_segment_arena`.
575            for (name, local_expr) in self.local_exprs.iter() {
576                let builtin_var = new_builtin_vars[name];
577                let local_expr = local_expr.clone();
578                let local_var = self.ctx.add_var(local_expr.clone());
579                casm_build_extend!(self.ctx, assert local_var = builtin_var;);
580            }
581        }
582
583        // All the builtins not in return_types should be copied from self.builtin_vars to
584        // new_builtin_vars and local_exprs.
585        for name in &self.builtins {
586            new_builtin_vars.entry(*name).or_insert_with(|| {
587                assert!(
588                    self.config.builtin_list.is_some(),
589                    "if builtin_list is not some, output builtins should cover all input builtins"
590                );
591
592                let var = self.builtin_vars[name];
593                self.local_exprs.insert(*name, self.ctx.get_unadjusted(var).clone());
594                var
595            });
596        }
597        self.builtin_vars = new_builtin_vars;
598    }
599
600    /// Handles `SegmentArena` validation.
601    fn validate_segment_arena(&mut self) {
602        let segment_arena = self.builtin_vars.remove(&BuiltinName::segment_arena).unwrap();
603        casm_build_extend! {self.ctx,
604            tempvar n_segments = segment_arena[-2];
605            tempvar n_finalized = segment_arena[-1];
606            assert n_segments = n_finalized;
607            jump STILL_LEFT_PRE if n_segments != 0;
608            rescope{};
609            jump DONE_VALIDATION;
610            STILL_LEFT_PRE:
611            const one = 1;
612            tempvar infos = segment_arena[-3];
613            tempvar remaining_segments = n_segments - one;
614            rescope{infos = infos, remaining_segments = remaining_segments};
615            LOOP_START:
616            jump STILL_LEFT_LOOP if remaining_segments != 0;
617            rescope{};
618            jump DONE_VALIDATION;
619            STILL_LEFT_LOOP:
620            tempvar prev_end = infos[1];
621            tempvar curr_start = infos[3];
622            const one = 1;
623            let expected_curr_start = prev_end + one;
624            hint ExternalHint::AddRelocationRule { src: curr_start, dst: expected_curr_start };
625            assert curr_start = prev_end + one;
626            const three = 3;
627            tempvar next_infos = infos + three;
628            tempvar next_remaining_segments = remaining_segments - one;
629            rescope{infos = next_infos, remaining_segments = next_remaining_segments};
630            #{ steps = 0; }
631            jump LOOP_START;
632            DONE_VALIDATION:
633        };
634    }
635
636    /// Processes the output builtins for the end of a run.
637    fn process_builtins_output(&mut self) {
638        for name in &self.builtins {
639            if let Some(mut var) = self.builtin_vars.remove(name) {
640                if let Some(local_expr) = self.local_exprs.get(name) {
641                    // If the builtin was stored in a local variable, use it instead of the output
642                    // builtin var.
643                    var = self.ctx.add_var(local_expr.clone());
644                }
645                casm_build_extend!(self.ctx, tempvar cell = var;);
646            }
647        }
648        assert!(self.builtin_vars.is_empty());
649    }
650}
651
652/// Creates a list of instructions that will be appended to the program's bytecode.
653pub fn create_code_footer() -> Vec<Instruction> {
654    casm! {
655        // Add a `ret` instruction used in libfuncs that retrieve the current value of the `fp`
656        // and `pc` registers.
657        ret;
658    }
659    .instructions
660}
661
662/// Creates the metadata required for a Sierra program lowering to CASM.
663fn create_metadata(
664    sierra_program: &SierraProgram,
665    program_info: &ProgramRegistryInfo,
666    metadata_config: Option<MetadataComputationConfig>,
667) -> Result<Metadata, BuildError> {
668    if let Some(metadata_config) = metadata_config {
669        calc_metadata(sierra_program, program_info, metadata_config)
670    } else {
671        calc_metadata_ap_change_only(sierra_program, program_info)
672    }
673    .map_err(|err| match err {
674        MetadataError::ApChangeError(err) => BuildError::ApChangeError(err),
675        MetadataError::CostError(err) => BuildError::FailedGasCalculation(err),
676    })
677}