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