1use std::fmt::Display;
2
3use cairo_lang_casm::assembler::AssembledCairoProgram;
4use cairo_lang_casm::instructions::{Instruction, InstructionBody, RetInstruction};
5use cairo_lang_sierra::extensions::ConcreteLibfunc;
6use cairo_lang_sierra::extensions::circuit::{CircuitConcreteLibfunc, CircuitInfo, VALUE_SIZE};
7use cairo_lang_sierra::extensions::const_type::ConstConcreteLibfunc;
8use cairo_lang_sierra::extensions::core::{
9 CoreConcreteLibfunc, CoreLibfunc, CoreType, CoreTypeConcrete,
10};
11use cairo_lang_sierra::extensions::coupon::CouponConcreteLibfunc;
12use cairo_lang_sierra::extensions::gas::GasConcreteLibfunc;
13use cairo_lang_sierra::extensions::lib_func::SierraApChange;
14use cairo_lang_sierra::ids::{ConcreteLibfuncId, ConcreteTypeId, VarId};
15use cairo_lang_sierra::program::{
16 BranchTarget, GenericArg, Invocation, Program, Statement, StatementIdx,
17};
18use cairo_lang_sierra::program_registry::{ProgramRegistry, ProgramRegistryError};
19use cairo_lang_sierra_type_size::{TypeSizeMap, get_type_size_map};
20use cairo_lang_utils::casts::IntoOrPanic;
21use cairo_lang_utils::ordered_hash_map::OrderedHashMap;
22use cairo_lang_utils::unordered_hash_map::UnorderedHashMap;
23use cairo_lang_utils::unordered_hash_set::UnorderedHashSet;
24use itertools::{chain, zip_eq};
25use num_bigint::BigInt;
26use num_traits::{ToPrimitive, Zero};
27use thiserror::Error;
28
29use crate::annotations::{AnnotationError, ProgramAnnotations, StatementAnnotations};
30use crate::circuit::CircuitsInfo;
31use crate::invocations::enm::get_variant_selector;
32use crate::invocations::{
33 BranchChanges, InvocationError, ProgramInfo, check_references_on_stack, compile_invocation,
34};
35use crate::metadata::Metadata;
36use crate::references::{ReferenceValue, ReferencesError, check_types_match};
37use crate::relocations::{RelocationEntry, relocate_instructions};
38
39#[cfg(test)]
40#[path = "compiler_test.rs"]
41mod test;
42
43#[derive(Error, Debug, Eq, PartialEq)]
44pub enum CompilationError {
45 #[error("Failed building type information")]
46 FailedBuildingTypeInformation,
47 #[error("Error from program registry: {0}")]
48 ProgramRegistryError(Box<ProgramRegistryError>),
49 #[error(transparent)]
50 AnnotationError(#[from] AnnotationError),
51 #[error("#{statement_idx}: {error}")]
52 InvocationError { statement_idx: StatementIdx, error: InvocationError },
53 #[error("#{statement_idx}: Return arguments are not on the stack.")]
54 ReturnArgumentsNotOnStack { statement_idx: StatementIdx },
55 #[error("#{statement_idx}: {error}")]
56 ReferencesError { statement_idx: StatementIdx, error: ReferencesError },
57 #[error("#{statement_idx}: Invocation mismatched to libfunc")]
58 LibfuncInvocationMismatch { statement_idx: StatementIdx },
59 #[error("{var_id} is dangling at #{statement_idx}.")]
60 DanglingReferences { statement_idx: StatementIdx, var_id: VarId },
61 #[error("#{source_statement_idx}->#{destination_statement_idx}: Expected branch align")]
62 ExpectedBranchAlign {
63 source_statement_idx: StatementIdx,
64 destination_statement_idx: StatementIdx,
65 },
66 #[error("Const data does not match the declared const type.")]
67 ConstDataMismatch,
68 #[error("Unsupported const type.")]
69 UnsupportedConstType,
70 #[error("Unsupported circuit type.")]
71 UnsupportedCircuitType,
72 #[error("Const segments must appear in ascending order without holes.")]
73 ConstSegmentsOutOfOrder,
74 #[error("Code size limit exceeded.")]
75 CodeSizeLimitExceeded,
76 #[error("Unknown function id in metadata.")]
77 MetadataUnknownFunctionId,
78 #[error("Statement #{0} out of bounds in metadata.")]
79 MetadataStatementOutOfBound(StatementIdx),
80 #[error("Statement #{0} should not have gas variables.")]
81 StatementNotSupportingGasVariables(StatementIdx),
82 #[error("Statement #{0} should not have ap-change variables.")]
83 StatementNotSupportingApChangeVariables(StatementIdx),
84 #[error("Expected all gas variables to be positive.")]
85 MetadataNegativeGasVariable,
86}
87
88#[derive(Debug, Eq, PartialEq, Clone, Copy)]
90pub struct SierraToCasmConfig {
91 pub gas_usage_check: bool,
93 pub max_bytecode_size: usize,
95}
96
97#[derive(Debug, Eq, PartialEq, Clone)]
99pub struct CairoProgram {
100 pub instructions: Vec<Instruction>,
101 pub debug_info: CairoProgramDebugInfo,
102 pub consts_info: ConstsInfo,
103}
104impl Display for CairoProgram {
105 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
106 if std::env::var("PRINT_CASM_BYTECODE_OFFSETS").is_ok() {
107 let mut bytecode_offset = 0;
108 for instruction in &self.instructions {
109 writeln!(f, "{instruction}; // {bytecode_offset}")?;
110 bytecode_offset += instruction.body.op_size();
111 }
112 for segment in self.consts_info.segments.values() {
113 writeln!(f, "ret; // {bytecode_offset}")?;
114 bytecode_offset += 1;
115 for value in &segment.values {
116 writeln!(f, "dw {value}; // {bytecode_offset}")?;
117 bytecode_offset += 1;
118 }
119 }
120 } else {
121 for instruction in &self.instructions {
122 writeln!(f, "{instruction};")?;
123 }
124 for segment in self.consts_info.segments.values() {
125 writeln!(f, "ret;")?;
126 for value in &segment.values {
127 writeln!(f, "dw {value};")?;
128 }
129 }
130 }
131 Ok(())
132 }
133}
134
135impl CairoProgram {
136 pub fn assemble(&self) -> AssembledCairoProgram {
138 self.assemble_ex(&[], &[])
139 }
140
141 pub fn assemble_ex<'a>(
144 &'a self,
145 header: impl IntoIterator<Item = &'a Instruction>,
146 footer: &[Instruction],
147 ) -> AssembledCairoProgram {
148 let mut bytecode = vec![];
149 let mut hints = vec![];
150 for instruction in chain!(header, &self.instructions) {
151 if !instruction.hints.is_empty() {
152 hints.push((bytecode.len(), instruction.hints.clone()))
153 }
154 bytecode.extend(instruction.assemble().encode().into_iter())
155 }
156 let [ref ret_bytecode] = Instruction::new(InstructionBody::Ret(RetInstruction {}), false)
157 .assemble()
158 .encode()[..]
159 else {
160 panic!("`ret` instruction should be a single word.")
161 };
162 for segment in self.consts_info.segments.values() {
163 bytecode.push(ret_bytecode.clone());
164 bytecode.extend(segment.values.clone());
165 }
166 for instruction in footer {
167 assert!(
168 instruction.hints.is_empty(),
169 "All footer instructions must have no hints since these cannot be added to the \
170 hints dict."
171 );
172 bytecode.extend(instruction.assemble().encode().into_iter())
173 }
174 AssembledCairoProgram { bytecode, hints }
175 }
176}
177
178#[derive(Debug, Eq, PartialEq, Clone)]
180pub struct SierraStatementDebugInfo {
181 pub start_offset: usize,
183 pub end_offset: usize,
185 pub instruction_idx: usize,
187 pub additional_kind_info: StatementKindDebugInfo,
189}
190
191#[derive(Debug, Eq, PartialEq, Clone)]
194pub enum StatementKindDebugInfo {
195 Return(ReturnStatementDebugInfo),
196 Invoke(InvokeStatementDebugInfo),
197}
198
199#[derive(Debug, Eq, PartialEq, Clone)]
201pub struct ReturnStatementDebugInfo {
202 pub ref_values: Vec<ReferenceValue>,
204}
205
206#[derive(Debug, Eq, PartialEq, Clone)]
208pub struct InvokeStatementDebugInfo {
209 pub result_branch_changes: Vec<BranchChanges>,
211 pub ref_values: Vec<ReferenceValue>,
213}
214
215#[derive(Debug, Eq, PartialEq, Clone)]
217pub struct CairoProgramDebugInfo {
218 pub sierra_statement_info: Vec<SierraStatementDebugInfo>,
220}
221
222#[derive(Debug, Eq, PartialEq, Default, Clone)]
224pub struct ConstsInfo {
225 pub segments: OrderedHashMap<u32, ConstSegment>,
226 pub total_segments_size: usize,
227
228 pub circuit_segments: OrderedHashMap<ConcreteTypeId, u32>,
230}
231impl ConstsInfo {
232 pub fn new<'a>(
234 registry: &ProgramRegistry<CoreType, CoreLibfunc>,
235 type_sizes: &TypeSizeMap,
236 libfunc_ids: impl Iterator<Item = &'a ConcreteLibfuncId> + Clone,
237 circuit_infos: &OrderedHashMap<ConcreteTypeId, CircuitInfo>,
238 const_segments_max_size: usize,
239 ) -> Result<Self, CompilationError> {
240 let mut segments_data_size = 0;
241
242 let mut add_const = |segments: &mut OrderedHashMap<u32, ConstSegment>,
245 segment_id,
246 ty,
247 const_data: Vec<BigInt>| {
248 let segment: &mut ConstSegment = segments.entry(segment_id).or_default();
249
250 segments_data_size += const_data.len();
251 segment.const_offset.insert(ty, segment.values.len());
252 segment.values.extend(const_data);
253 if segments_data_size + segments.len() > const_segments_max_size {
254 return Err(CompilationError::CodeSizeLimitExceeded);
255 }
256 Ok(())
257 };
258
259 let mut segments = OrderedHashMap::default();
260
261 for id in libfunc_ids.clone() {
262 if let CoreConcreteLibfunc::Const(ConstConcreteLibfunc::AsBox(as_box)) =
263 registry.get_libfunc(id).unwrap()
264 {
265 add_const(
266 &mut segments,
267 as_box.segment_id,
268 as_box.const_type.clone(),
269 extract_const_value(registry, type_sizes, &as_box.const_type).unwrap(),
270 )?;
271 }
272 }
273
274 if segments
276 .keys()
277 .enumerate()
278 .any(|(i, segment_id)| i != segment_id.into_or_panic::<usize>())
279 {
280 return Err(CompilationError::ConstSegmentsOutOfOrder);
281 }
282
283 let mut next_segment = segments.len() as u32;
284 let mut circuit_segments = OrderedHashMap::default();
285
286 for id in libfunc_ids {
287 if let CoreConcreteLibfunc::Circuit(CircuitConcreteLibfunc::GetDescriptor(libfunc)) =
288 registry.get_libfunc(id).unwrap()
289 {
290 let circ_ty = &libfunc.ty;
291 let info = circuit_infos.get(circ_ty).unwrap();
292 let mut const_value: Vec<BigInt> = vec![];
293 let mut push_offset =
294 |offset: usize| const_value.push((offset * VALUE_SIZE).into());
295 for gate_offsets in chain!(info.add_offsets.iter(), info.mul_offsets.iter()) {
296 push_offset(gate_offsets.lhs);
297 push_offset(gate_offsets.rhs);
298 push_offset(gate_offsets.output);
299 }
300
301 add_const(&mut segments, next_segment, circ_ty.clone(), const_value)?;
302 circuit_segments.insert(circ_ty.clone(), next_segment);
303 next_segment += 1;
304 }
305 }
306
307 let mut total_segments_size = 0;
308 for (_, segment) in segments.iter_mut() {
309 segment.segment_offset = total_segments_size;
310 total_segments_size += 1 + segment.values.len();
312 }
313 Ok(Self { segments, total_segments_size, circuit_segments })
314 }
315}
316
317#[derive(Debug, Eq, PartialEq, Default, Clone)]
319pub struct ConstSegment {
320 pub values: Vec<BigInt>,
322 pub const_offset: UnorderedHashMap<ConcreteTypeId, usize>,
324 pub segment_offset: usize,
326}
327
328fn extract_const_value(
331 registry: &ProgramRegistry<CoreType, CoreLibfunc>,
332 type_sizes: &TypeSizeMap,
333 ty: &ConcreteTypeId,
334) -> Result<Vec<BigInt>, CompilationError> {
335 let mut values = Vec::new();
336 let mut types_stack = vec![ty.clone()];
337 while let Some(ty) = types_stack.pop() {
338 let CoreTypeConcrete::Const(const_type) = registry.get_type(&ty).unwrap() else {
339 return Err(CompilationError::UnsupportedConstType);
340 };
341 let inner_type = registry.get_type(&const_type.inner_ty).unwrap();
342 match inner_type {
343 CoreTypeConcrete::Struct(_) => {
344 for arg in const_type.inner_data.iter().rev() {
346 match arg {
347 GenericArg::Type(arg_ty) => types_stack.push(arg_ty.clone()),
348 _ => return Err(CompilationError::ConstDataMismatch),
349 }
350 }
351 }
352 CoreTypeConcrete::Enum(enm) => {
353 match &const_type.inner_data[..] {
355 [GenericArg::Value(variant_index), GenericArg::Type(ty)] => {
356 let variant_index = variant_index.to_usize().unwrap();
357 values.push(
358 get_variant_selector(enm.variants.len(), variant_index).unwrap().into(),
359 );
360 let full_enum_size: usize =
361 type_sizes[&const_type.inner_ty].into_or_panic();
362 let variant_size: usize =
363 type_sizes[&enm.variants[variant_index]].into_or_panic();
364 values.extend(itertools::repeat_n(
366 BigInt::zero(),
367 full_enum_size - variant_size - 1,
369 ));
370 types_stack.push(ty.clone());
371 }
372 _ => return Err(CompilationError::ConstDataMismatch),
373 }
374 }
375 CoreTypeConcrete::NonZero(_) => match &const_type.inner_data[..] {
376 [GenericArg::Type(inner)] => {
377 types_stack.push(inner.clone());
378 }
379 _ => return Err(CompilationError::ConstDataMismatch),
380 },
381 _ => match &const_type.inner_data[..] {
382 [GenericArg::Value(value)] => {
383 values.push(value.clone());
384 }
385 _ => return Err(CompilationError::ConstDataMismatch),
386 },
387 };
388 }
389 Ok(values)
390}
391
392pub fn check_basic_structure(
394 statement_idx: StatementIdx,
395 invocation: &Invocation,
396 libfunc: &CoreConcreteLibfunc,
397) -> Result<(), CompilationError> {
398 if invocation.args.len() != libfunc.param_signatures().len()
399 || !itertools::equal(
400 invocation.branches.iter().map(|branch| branch.results.len()),
401 libfunc.output_types().iter().map(|types| types.len()),
402 )
403 || match libfunc.fallthrough() {
404 Some(expected_fallthrough) => {
405 invocation.branches[expected_fallthrough].target != BranchTarget::Fallthrough
406 }
407 None => false,
408 }
409 {
410 Err(CompilationError::LibfuncInvocationMismatch { statement_idx })
411 } else {
412 Ok(())
413 }
414}
415
416pub fn compile(
419 program: &Program,
420 metadata: &Metadata,
421 config: SierraToCasmConfig,
422) -> Result<CairoProgram, Box<CompilationError>> {
423 let mut instructions = Vec::new();
424 let mut relocations: Vec<RelocationEntry> = Vec::new();
425
426 let mut sierra_statement_info: Vec<SierraStatementDebugInfo> =
430 Vec::with_capacity(program.statements.len());
431
432 let registry = ProgramRegistry::<CoreType, CoreLibfunc>::new_with_ap_change(
433 program,
434 metadata.ap_change_info.function_ap_change.clone(),
435 )
436 .map_err(CompilationError::ProgramRegistryError)?;
437 validate_metadata(program, ®istry, metadata)?;
438 let type_sizes = get_type_size_map(program, ®istry)
439 .ok_or(CompilationError::FailedBuildingTypeInformation)?;
440 let mut backwards_jump_indices = UnorderedHashSet::<_>::default();
441 for (statement_id, statement) in program.statements.iter().enumerate() {
442 if let Statement::Invocation(invocation) = statement {
443 for branch in &invocation.branches {
444 if let BranchTarget::Statement(target) = branch.target {
445 if target.0 < statement_id {
446 backwards_jump_indices.insert(target);
447 }
448 }
449 }
450 }
451 }
452 let mut program_annotations = ProgramAnnotations::create(
453 program.statements.len(),
454 backwards_jump_indices,
455 &program.funcs,
456 metadata,
457 config.gas_usage_check,
458 &type_sizes,
459 )
460 .map_err(|err| Box::new(err.into()))?;
461
462 let circuits_info =
463 CircuitsInfo::new(®istry, program.type_declarations.iter().map(|td| &td.id))?;
464
465 let mut program_offset: usize = 0;
466 for (statement_id, statement) in program.statements.iter().enumerate() {
467 let statement_idx = StatementIdx(statement_id);
468
469 if program_offset > config.max_bytecode_size {
470 return Err(Box::new(CompilationError::CodeSizeLimitExceeded));
471 }
472 match statement {
473 Statement::Return(ref_ids) => {
474 let (annotations, return_refs) = program_annotations
475 .get_annotations_after_take_args(statement_idx, ref_ids.iter())
476 .map_err(|err| Box::new(err.into()))?;
477 return_refs.iter().for_each(|r| r.validate(&type_sizes));
478
479 if let Some(var_id) = annotations.refs.keys().next() {
480 return Err(Box::new(CompilationError::DanglingReferences {
481 statement_idx,
482 var_id: var_id.clone(),
483 }));
484 };
485
486 program_annotations
487 .validate_final_annotations(
488 statement_idx,
489 &annotations,
490 &program.funcs,
491 metadata,
492 &return_refs,
493 )
494 .map_err(|err| Box::new(err.into()))?;
495 check_references_on_stack(&return_refs).map_err(|error| match error {
496 InvocationError::InvalidReferenceExpressionForArgument => {
497 CompilationError::ReturnArgumentsNotOnStack { statement_idx }
498 }
499 _ => CompilationError::InvocationError { statement_idx, error },
500 })?;
501
502 let start_offset = program_offset;
503
504 let ret_instruction = RetInstruction {};
505 program_offset += ret_instruction.op_size();
506
507 sierra_statement_info.push(SierraStatementDebugInfo {
508 start_offset,
509 end_offset: program_offset,
510 instruction_idx: instructions.len(),
511 additional_kind_info: StatementKindDebugInfo::Return(
512 ReturnStatementDebugInfo { ref_values: return_refs },
513 ),
514 });
515
516 instructions.push(Instruction::new(InstructionBody::Ret(ret_instruction), false));
517 }
518 Statement::Invocation(invocation) => {
519 let (annotations, invoke_refs) = program_annotations
520 .get_annotations_after_take_args(statement_idx, invocation.args.iter())
521 .map_err(|err| Box::new(err.into()))?;
522
523 let libfunc = registry
524 .get_libfunc(&invocation.libfunc_id)
525 .map_err(CompilationError::ProgramRegistryError)?;
526 check_basic_structure(statement_idx, invocation, libfunc)?;
527
528 let param_types: Vec<_> = libfunc
529 .param_signatures()
530 .iter()
531 .map(|param_signature| param_signature.ty.clone())
532 .collect();
533 check_types_match(&invoke_refs, ¶m_types).map_err(|error| {
534 Box::new(AnnotationError::ReferencesError { statement_idx, error }.into())
535 })?;
536 invoke_refs.iter().for_each(|r| r.validate(&type_sizes));
537 let compiled_invocation = compile_invocation(
538 ProgramInfo {
539 metadata,
540 type_sizes: &type_sizes,
541 circuits_info: &circuits_info,
542 const_data_values: &|ty| {
543 extract_const_value(®istry, &type_sizes, ty).unwrap()
544 },
545 },
546 invocation,
547 libfunc,
548 statement_idx,
549 &invoke_refs,
550 annotations.environment,
551 )
552 .map_err(|error| CompilationError::InvocationError { statement_idx, error })?;
553
554 let start_offset = program_offset;
555
556 for instruction in &compiled_invocation.instructions {
557 program_offset += instruction.body.op_size();
558 }
559
560 sierra_statement_info.push(SierraStatementDebugInfo {
561 start_offset,
562 end_offset: program_offset,
563 instruction_idx: instructions.len(),
564 additional_kind_info: StatementKindDebugInfo::Invoke(
565 InvokeStatementDebugInfo {
566 result_branch_changes: compiled_invocation.results.clone(),
567 ref_values: invoke_refs,
568 },
569 ),
570 });
571
572 for entry in compiled_invocation.relocations {
573 relocations.push(RelocationEntry {
574 instruction_idx: instructions.len() + entry.instruction_idx,
575 relocation: entry.relocation,
576 });
577 }
578 instructions.extend(compiled_invocation.instructions);
579
580 let branching_libfunc = compiled_invocation.results.len() > 1;
581 let mut all_updated_annotations = vec![StatementAnnotations {
584 environment: compiled_invocation.environment,
585 ..annotations
586 }];
587 while all_updated_annotations.len() < compiled_invocation.results.len() {
588 all_updated_annotations.push(all_updated_annotations[0].clone());
589 }
590
591 for ((branch_info, branch_changes), updated_annotations) in
592 zip_eq(&invocation.branches, compiled_invocation.results)
593 .zip(all_updated_annotations)
594 {
595 let destination_statement_idx = statement_idx.next(&branch_info.target);
596 if branching_libfunc
597 && !is_branch_align(
598 ®istry,
599 &program.statements[destination_statement_idx.0],
600 )?
601 {
602 return Err(Box::new(CompilationError::ExpectedBranchAlign {
603 source_statement_idx: statement_idx,
604 destination_statement_idx,
605 }));
606 }
607
608 program_annotations
609 .propagate_annotations(
610 statement_idx,
611 destination_statement_idx,
612 updated_annotations,
613 branch_info,
614 branch_changes,
615 branching_libfunc,
616 )
617 .map_err(|err| Box::new(err.into()))?;
618 }
619 }
620 }
621 }
622
623 let statement_offsets: Vec<usize> = std::iter::once(0)
624 .chain(sierra_statement_info.iter().map(|s: &SierraStatementDebugInfo| s.end_offset))
625 .collect();
626
627 let const_segments_max_size = config
628 .max_bytecode_size
629 .checked_sub(program_offset)
630 .ok_or_else(|| Box::new(CompilationError::CodeSizeLimitExceeded))?;
631 let consts_info = ConstsInfo::new(
632 ®istry,
633 &type_sizes,
634 program.libfunc_declarations.iter().map(|ld| &ld.id),
635 &circuits_info.circuits,
636 const_segments_max_size,
637 )?;
638 relocate_instructions(&relocations, &statement_offsets, &consts_info, &mut instructions);
639
640 Ok(CairoProgram {
641 instructions,
642 consts_info,
643 debug_info: CairoProgramDebugInfo { sierra_statement_info },
644 })
645}
646
647pub fn validate_metadata(
649 program: &Program,
650 registry: &ProgramRegistry<CoreType, CoreLibfunc>,
651 metadata: &Metadata,
652) -> Result<(), CompilationError> {
653 for function_id in metadata.ap_change_info.function_ap_change.keys() {
655 registry
656 .get_function(function_id)
657 .map_err(|_| CompilationError::MetadataUnknownFunctionId)?;
658 }
659 for (function_id, costs) in metadata.gas_info.function_costs.iter() {
660 registry
661 .get_function(function_id)
662 .map_err(|_| CompilationError::MetadataUnknownFunctionId)?;
663 for (_token_type, value) in costs.iter() {
664 if *value < 0 {
665 return Err(CompilationError::MetadataNegativeGasVariable);
666 }
667 }
668 }
669
670 let get_libfunc = |idx: &StatementIdx| -> Result<&CoreConcreteLibfunc, CompilationError> {
672 if let Statement::Invocation(invocation) =
673 program.get_statement(idx).ok_or(CompilationError::MetadataStatementOutOfBound(*idx))?
674 {
675 registry
676 .get_libfunc(&invocation.libfunc_id)
677 .map_err(CompilationError::ProgramRegistryError)
678 } else {
679 Err(CompilationError::StatementNotSupportingApChangeVariables(*idx))
680 }
681 };
682
683 for idx in metadata.ap_change_info.variable_values.keys() {
685 if !matches!(get_libfunc(idx)?, CoreConcreteLibfunc::BranchAlign(_)) {
686 return Err(CompilationError::StatementNotSupportingApChangeVariables(*idx));
687 }
688 }
689 for ((idx, _token), value) in metadata.gas_info.variable_values.iter() {
690 if *value < 0 {
691 return Err(CompilationError::MetadataNegativeGasVariable);
692 }
693 if !matches!(
694 get_libfunc(idx)?,
695 CoreConcreteLibfunc::BranchAlign(_)
696 | CoreConcreteLibfunc::Coupon(CouponConcreteLibfunc::Refund(_))
697 | CoreConcreteLibfunc::Gas(
698 GasConcreteLibfunc::WithdrawGas(_)
699 | GasConcreteLibfunc::BuiltinWithdrawGas(_)
700 | GasConcreteLibfunc::RedepositGas(_)
701 )
702 ) {
703 return Err(CompilationError::StatementNotSupportingGasVariables(*idx));
704 }
705 }
706 Ok(())
707}
708
709fn is_branch_align(
711 registry: &ProgramRegistry<CoreType, CoreLibfunc>,
712 statement: &Statement,
713) -> Result<bool, CompilationError> {
714 if let Statement::Invocation(invocation) = statement {
715 let libfunc = registry
716 .get_libfunc(&invocation.libfunc_id)
717 .map_err(CompilationError::ProgramRegistryError)?;
718 if let [branch_signature] = libfunc.branch_signatures() {
719 if branch_signature.ap_change == SierraApChange::BranchAlign {
720 return Ok(true);
721 }
722 }
723 }
724
725 Ok(false)
726}