Skip to main content

cairo_lang_runnable_utils/
builder.rs

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