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        let mut builtin_offset = 3;
315        for builtin_name in helper.builtins.iter().rev() {
316            helper.builtin_vars.insert(
317                *builtin_name,
318                helper.ctx.add_var(CellExpression::Deref(cell_ref!([fp - builtin_offset]))),
319            );
320            builtin_offset += 1;
321        }
322    } else {
323        helper.process_builtins(param_types);
324    }
325    helper.process_params(param_types);
326    casm_build_extend!(helper.ctx, let () = call FUNCTION;);
327    helper.process_output(return_types);
328
329    if helper.has_post_calculation_loop {
330        helper.validate_segment_arena();
331    }
332
333    if !helper.config.testing {
334        helper.process_builtins_output();
335    }
336
337    casm_build_extend! (helper.ctx, ret;);
338    // Point `FUNCTION` to offset `code_offset` from this point which is the beginning of the Sierra
339    // based code.
340    helper.ctx.future_label("FUNCTION", code_offset);
341    Ok((helper.ctx.build([]).instructions, helper.builtins))
342}
343
344/// Helper struct for [create_entry_code_from_params].
345struct EntryCodeHelper {
346    ctx: CasmBuilder,
347    config: EntryCodeConfig,
348    builtin_ty_to_vm_name: OrderedHashMap<GenericTypeId, BuiltinName>,
349    builtins: Vec<BuiltinName>,
350    got_segment_arena: bool,
351    has_post_calculation_loop: bool,
352    /// Maps each builtin name to the variable that stores it.
353    builtin_vars: UnorderedHashMap<BuiltinName, Var>,
354    /// Maps each builtin name to the local cell expression that stores it.
355    /// This is needed because variables in `builtin_vars` might be invalidated.
356    local_exprs: OrderedHashMap<BuiltinName, CellExpression>,
357    emulated_builtins: UnorderedHashSet<GenericTypeId>,
358}
359
360impl EntryCodeHelper {
361    /// Creates a new `EntryCodeHelper` with the given configuration.
362    fn new(config: EntryCodeConfig) -> Self {
363        Self {
364            ctx: CasmBuilder::default(),
365            config,
366            builtin_ty_to_vm_name: OrderedHashMap::from_iter([
367                (PedersenType::ID, BuiltinName::pedersen),
368                (RangeCheckType::ID, BuiltinName::range_check),
369                (BitwiseType::ID, BuiltinName::bitwise),
370                (EcOpType::ID, BuiltinName::ec_op),
371                (PoseidonType::ID, BuiltinName::poseidon),
372                (RangeCheck96Type::ID, BuiltinName::range_check96),
373                (AddModType::ID, BuiltinName::add_mod),
374                (MulModType::ID, BuiltinName::mul_mod),
375                (SegmentArenaType::ID, BuiltinName::segment_arena),
376            ]),
377            builtins: vec![],
378            got_segment_arena: false,
379            has_post_calculation_loop: false,
380            builtin_vars: UnorderedHashMap::default(),
381            local_exprs: OrderedHashMap::default(),
382            emulated_builtins: UnorderedHashSet::<_>::from_iter([SystemType::ID]),
383        }
384    }
385
386    /// Processes the builtins required for the function parameters.
387    fn process_builtins(&mut self, param_types: &[(GenericTypeId, i16)]) {
388        let mut builtin_offset = 3;
389
390        // Process all builtins except the segment arena in reverse order.
391        for (builtin_ty, builtin_name) in self.builtin_ty_to_vm_name.iter().rev().skip(1) {
392            if param_types.iter().any(|(ty, _)| ty == builtin_ty) {
393                self.builtin_vars.insert(
394                    *builtin_name,
395                    self.ctx.add_var(CellExpression::Deref(cell_ref!([fp - builtin_offset]))),
396                );
397                builtin_offset += 1;
398                self.builtins.push(*builtin_name);
399            }
400        }
401        if !self.config.testing {
402            let output_builtin_var =
403                self.ctx.add_var(CellExpression::Deref(cell_ref!([fp - builtin_offset])));
404            self.builtin_vars.insert(BuiltinName::output, output_builtin_var);
405            self.builtins.push(BuiltinName::output);
406        }
407        self.builtins.reverse();
408    }
409
410    /// Processes the function parameters in preparation for the function call.
411    fn process_params(&mut self, param_types: &[(GenericTypeId, i16)]) {
412        self.got_segment_arena = param_types.iter().any(|(ty, _)| ty == &SegmentArenaType::ID);
413        self.has_post_calculation_loop = self.got_segment_arena && !self.config.testing;
414
415        if self.has_post_calculation_loop {
416            if !self.config.testing {
417                // Add a local variable for the output builtin
418                casm_build_extend!(self.ctx, localvar local;);
419                self.local_exprs
420                    .insert(BuiltinName::output, self.ctx.get_unadjusted(local).clone());
421            }
422
423            for (generic_ty, _ty_size) in param_types {
424                if let Some(name) = self.builtin_ty_to_vm_name.get(generic_ty)
425                    && name != &BuiltinName::segment_arena
426                {
427                    casm_build_extend!(self.ctx, localvar local;);
428                    self.local_exprs.insert(*name, self.ctx.get_unadjusted(local).clone());
429                }
430            }
431
432            if !self.local_exprs.is_empty() {
433                casm_build_extend!(self.ctx, ap += self.local_exprs.len(););
434            }
435        }
436
437        if self.got_segment_arena {
438            casm_build_extend! {self.ctx,
439                tempvar segment_arena;
440                tempvar infos;
441                hint AllocSegment into {dst: segment_arena};
442                hint AllocSegment into {dst: infos};
443                const czero = 0;
444                tempvar zero = czero;
445                // Write Infos segment, n_constructed (0), and n_destructed (0) to the segment.
446                assert infos = *(segment_arena++);
447                assert zero = *(segment_arena++);
448                assert zero = *(segment_arena++);
449            }
450            // Adding the segment arena to the builtins var map.
451            self.builtin_vars.insert(BuiltinName::segment_arena, segment_arena);
452        }
453        let mut unallocated_count = 0;
454        let mut param_index = 0;
455
456        // Not processing the user function params in the case of a proof, as these are processed
457        // after.
458        let non_proof_signature_params =
459            if self.config.testing { param_types } else { &param_types[..(param_types.len() - 2)] };
460        for (generic_ty, ty_size) in non_proof_signature_params {
461            if let Some(name) = self.builtin_ty_to_vm_name.get(generic_ty).cloned() {
462                let var = self.builtin_vars[&name];
463                casm_build_extend!(self.ctx, tempvar _builtin = var;);
464            } else if self.emulated_builtins.contains(generic_ty) {
465                assert!(
466                    self.config.allow_unsound,
467                    "Cannot support emulated builtins if not configured to `allow_unsound`."
468                );
469                casm_build_extend! {self.ctx,
470                    tempvar system;
471                    hint AllocSegment into {dst: system};
472                };
473                unallocated_count += ty_size;
474            } else if self.config.testing {
475                if *ty_size > 0 {
476                    casm_build_extend! {self.ctx,
477                        tempvar first;
478                        const param_index = param_index;
479                        hint ExternalHint::WriteRunParam { index: param_index } into { dst: first };
480                    };
481                    for _ in 1..*ty_size {
482                        casm_build_extend!(self.ctx, tempvar _cell;);
483                    }
484                }
485                param_index += 1;
486                unallocated_count += ty_size;
487            } else if generic_ty == &GasBuiltinType::ID {
488                casm_build_extend! {self.ctx,
489                    const max_gas = i64::MAX;
490                    tempvar gas = max_gas;
491                };
492            } else {
493                unreachable!("Unexpected argument type: {:?}", generic_ty);
494            }
495        }
496        if !self.config.testing {
497            let output_ptr = self.builtin_vars[&BuiltinName::output];
498            casm_build_extend! { self.ctx,
499                tempvar input_start;
500                tempvar _input_end;
501                const param_index = 0;
502                hint ExternalHint::WriteRunParam { index: param_index } into { dst: input_start };
503                tempvar output_start = output_ptr;
504                tempvar output_end = output_ptr;
505            };
506            unallocated_count += 2;
507        }
508        if unallocated_count > 0 {
509            casm_build_extend!(self.ctx, ap += unallocated_count.into_or_panic::<usize>(););
510        }
511    }
512
513    /// Processes the function return types.
514    fn process_output(&mut self, return_types: &[(GenericTypeId, i16)]) {
515        let mut unprocessed_return_size = return_types.iter().map(|(_, size)| size).sum::<i16>();
516        let mut next_unprocessed_deref = || {
517            let deref_cell = CellExpression::Deref(cell_ref!([ap - unprocessed_return_size]));
518            assert!(unprocessed_return_size > 0);
519            unprocessed_return_size -= 1;
520            deref_cell
521        };
522        // Not processing the user function return type in the case of a proof, as it is processed
523        // after.
524        let non_proof_return_types = if self.config.testing {
525            return_types
526        } else {
527            &return_types[..(return_types.len() - 1)]
528        };
529        let mut new_builtin_vars = UnorderedHashMap::<BuiltinName, Var>::default();
530        for (ret_ty, size) in non_proof_return_types {
531            if let Some(name) = self.builtin_ty_to_vm_name.get(ret_ty) {
532                new_builtin_vars.insert(*name, self.ctx.add_var(next_unprocessed_deref()));
533            } else if self.config.testing {
534                for _ in 0..*size {
535                    next_unprocessed_deref();
536                }
537            } else if self.emulated_builtins.contains(ret_ty) {
538                assert!(
539                    self.config.allow_unsound,
540                    "Cannot support emulated builtins if not configured to `allow_unsound`."
541                );
542                let _ = next_unprocessed_deref();
543            } else {
544                assert_eq!(ret_ty, &GasBuiltinType::ID);
545                let _ = next_unprocessed_deref();
546            }
547        }
548        if !self.config.testing {
549            let (ret_ty, size) = return_types.last().unwrap();
550            let opt_panic_indicator = match *size {
551                2 => None,
552                3 => Some(self.ctx.add_var(next_unprocessed_deref())),
553                _ => panic!("Unexpected output type: {:?}", ret_ty.0),
554            };
555            // The start ptr of the output in the case of successful run,
556            // or the panic data in case of a failure.
557            let ptr_start = self.ctx.add_var(next_unprocessed_deref());
558            // The end ptr of the output in the case of successful run,
559            // or the panic data in case of a failure.
560            let ptr_end = self.ctx.add_var(next_unprocessed_deref());
561            if let Some(panic_indicator) = opt_panic_indicator {
562                casm_build_extend! {self.ctx,
563                    const czero = 0;
564                    tempvar zero = czero;
565                    hint ExternalHint::AddMarker { start: ptr_start, end: ptr_end };
566                    assert zero = panic_indicator;
567                };
568            }
569            new_builtin_vars.insert(BuiltinName::output, ptr_end);
570        }
571        assert_eq!(unprocessed_return_size, 0);
572        if self.has_post_calculation_loop {
573            // Store the new builtin vars that were assigned local variables, as they are going to
574            // be invalidated by loop. Note that the builtin vars map is not updated
575            // with the new locals, since they will be invalidated by the rescoping
576            // performed in `validate_segment_arena`.
577            for (name, local_expr) in self.local_exprs.iter() {
578                let builtin_var = new_builtin_vars[name];
579                let local_expr = local_expr.clone();
580                let local_var = self.ctx.add_var(local_expr.clone());
581                casm_build_extend!(self.ctx, assert local_var = builtin_var;);
582            }
583        }
584
585        // All the builtins not in in return_types should be copied from self.builtin_vars to
586        // new_builtin_vars and local_exprs.
587        for name in &self.builtins {
588            new_builtin_vars.entry(*name).or_insert_with(|| {
589                assert!(
590                    self.config.builtin_list.is_some(),
591                    "if builtin_list is not some, output builtins should cover all input builtins"
592                );
593
594                let var = self.builtin_vars[name];
595                self.local_exprs.insert(*name, self.ctx.get_unadjusted(var).clone());
596                var
597            });
598        }
599        self.builtin_vars = new_builtin_vars;
600    }
601
602    /// Handles `SegmentArena` validation.
603    fn validate_segment_arena(&mut self) {
604        let segment_arena = self.builtin_vars.remove(&BuiltinName::segment_arena).unwrap();
605        casm_build_extend! {self.ctx,
606            tempvar n_segments = segment_arena[-2];
607            tempvar n_finalized = segment_arena[-1];
608            assert n_segments = n_finalized;
609            jump STILL_LEFT_PRE if n_segments != 0;
610            rescope{};
611            jump DONE_VALIDATION;
612            STILL_LEFT_PRE:
613            const one = 1;
614            tempvar infos = segment_arena[-3];
615            tempvar remaining_segments = n_segments - one;
616            rescope{infos = infos, remaining_segments = remaining_segments};
617            LOOP_START:
618            jump STILL_LEFT_LOOP if remaining_segments != 0;
619            rescope{};
620            jump DONE_VALIDATION;
621            STILL_LEFT_LOOP:
622            tempvar prev_end = infos[1];
623            tempvar curr_start = infos[3];
624            const one = 1;
625            let expected_curr_start = prev_end + one;
626            hint ExternalHint::AddRelocationRule { src: curr_start, dst: expected_curr_start };
627            assert curr_start = prev_end + one;
628            const three = 3;
629            tempvar next_infos = infos + three;
630            tempvar next_remaining_segments = remaining_segments - one;
631            rescope{infos = next_infos, remaining_segments = next_remaining_segments};
632            #{ steps = 0; }
633            jump LOOP_START;
634            DONE_VALIDATION:
635        };
636    }
637
638    /// Processes the output builtins for the end of a run.
639    fn process_builtins_output(&mut self) {
640        for name in &self.builtins {
641            if let Some(mut var) = self.builtin_vars.remove(name) {
642                if let Some(local_expr) = self.local_exprs.get(name) {
643                    // If the builtin was stored in a local variable, use it instead of the output
644                    // builtin var.
645                    var = self.ctx.add_var(local_expr.clone());
646                }
647                casm_build_extend!(self.ctx, tempvar cell = var;);
648            }
649        }
650        assert!(self.builtin_vars.is_empty());
651    }
652}
653
654/// Creates a list of instructions that will be appended to the program's bytecode.
655pub fn create_code_footer() -> Vec<Instruction> {
656    casm! {
657        // Add a `ret` instruction used in libfuncs that retrieve the current value of the `fp`
658        // and `pc` registers.
659        ret;
660    }
661    .instructions
662}
663
664/// Creates the metadata required for a Sierra program lowering to CASM.
665fn create_metadata(
666    sierra_program: &SierraProgram,
667    program_info: &ProgramRegistryInfo,
668    metadata_config: Option<MetadataComputationConfig>,
669) -> Result<Metadata, BuildError> {
670    if let Some(metadata_config) = metadata_config {
671        calc_metadata(sierra_program, program_info, metadata_config)
672    } else {
673        calc_metadata_ap_change_only(sierra_program, program_info)
674    }
675    .map_err(|err| match err {
676        MetadataError::ApChangeError(err) => BuildError::ApChangeError(err),
677        MetadataError::CostError(err) => BuildError::FailedGasCalculation(err),
678    })
679}