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