1+ use std:: iter;
2+
3+ use cairo_lang_casm:: ap_change:: ApplyApChange ;
14use cairo_lang_casm:: builder:: CasmBuilder ;
2- use cairo_lang_casm:: cell_expression:: CellExpression ;
3- use cairo_lang_casm:: operand:: CellRef ;
5+ use cairo_lang_casm:: cell_expression:: { CellExpression , CellOperator } ;
6+ use cairo_lang_casm:: instructions:: Instruction ;
7+ use cairo_lang_casm:: operand:: { CellRef , DerefOrImmediate , Register } ;
48use cairo_lang_casm:: { casm, casm_build_extend, casm_extend} ;
59use cairo_lang_sierra:: extensions:: ConcreteLibfunc ;
6- use cairo_lang_sierra:: extensions:: enm:: { EnumConcreteLibfunc , EnumInitConcreteLibfunc } ;
10+ use cairo_lang_sierra:: extensions:: enm:: {
11+ EnumBoxedMatchConcreteLibfunc , EnumConcreteLibfunc , EnumInitConcreteLibfunc ,
12+ } ;
713use cairo_lang_sierra:: ids:: ConcreteTypeId ;
814use cairo_lang_sierra:: program:: { BranchInfo , BranchTarget } ;
915use cairo_lang_utils:: try_extract_matches;
@@ -33,6 +39,7 @@ pub fn build(
3339 EnumConcreteLibfunc :: Match ( _) | EnumConcreteLibfunc :: SnapshotMatch ( _) => {
3440 build_enum_match ( builder)
3541 }
42+ EnumConcreteLibfunc :: BoxedMatch ( libfunc) => build_enum_boxed_match ( libfunc, builder) ,
3643 }
3744}
3845
@@ -230,8 +237,20 @@ fn build_enum_match_short(
230237 Item = impl ExactSizeIterator < Item = ReferenceExpression > ,
231238 > ,
232239) -> Result < CompiledInvocation , InvocationError > {
233- let mut instructions = Vec :: new ( ) ;
240+ build_enum_match_short_ex ( builder, variant_selector, output_expressions, Vec :: with_capacity ( 1 ) )
241+ }
242+
243+ /// Extended version of `build_enum_match_short` that allows prepending instructions.
244+ fn build_enum_match_short_ex (
245+ builder : CompiledInvocationBuilder < ' _ > ,
246+ variant_selector : CellRef ,
247+ output_expressions : impl ExactSizeIterator <
248+ Item = impl ExactSizeIterator < Item = ReferenceExpression > ,
249+ > ,
250+ mut instructions : Vec < Instruction > ,
251+ ) -> Result < CompiledInvocation , InvocationError > {
234252 let mut relocations = Vec :: new ( ) ;
253+ let base_instruction_count = instructions. len ( ) ;
235254
236255 // First branch is fallthrough. If there is only one branch, this `match` statement is
237256 // translated to nothing in Casm.
@@ -245,7 +264,7 @@ fn build_enum_match_short(
245264
246265 instructions. extend ( casm ! { jmp rel 0 if variant_selector != 0 ; } . instructions ) ;
247266 relocations. push ( RelocationEntry {
248- instruction_idx : 0 ,
267+ instruction_idx : base_instruction_count ,
249268 relocation : Relocation :: RelativeStatementId ( statement_id) ,
250269 } ) ;
251270 }
@@ -284,12 +303,32 @@ fn build_enum_match_long(
284303 output_expressions : impl ExactSizeIterator <
285304 Item = impl ExactSizeIterator < Item = ReferenceExpression > ,
286305 > ,
306+ ) -> Result < CompiledInvocation , InvocationError > {
307+ let expected_instruction_count = builder. invocation . branches . len ( ) + 1 ;
308+ build_enum_match_long_ex (
309+ builder,
310+ variant_selector,
311+ output_expressions,
312+ Vec :: with_capacity ( expected_instruction_count) ,
313+ )
314+ }
315+
316+ /// Extended version of `build_enum_match_long` that allows prepending instructions.
317+ fn build_enum_match_long_ex (
318+ builder : CompiledInvocationBuilder < ' _ > ,
319+ variant_selector : CellRef ,
320+ output_expressions : impl ExactSizeIterator <
321+ Item = impl ExactSizeIterator < Item = ReferenceExpression > ,
322+ > ,
323+ mut instructions : Vec < Instruction > ,
287324) -> Result < CompiledInvocation , InvocationError > {
288325 let target_statement_ids = builder. invocation . branches [ 1 ..] . iter ( ) . map ( |b| match b {
289326 BranchInfo { target : BranchTarget :: Statement ( stmnt_id) , .. } => * stmnt_id,
290327 _ => panic ! ( "malformed invocation" ) ,
291328 } ) ;
292329
330+ let base_instruction_count = instructions. len ( ) ;
331+
293332 // The first instruction is the jmp to the relevant index in the jmp table.
294333 let mut ctx = casm ! { jmp rel variant_selector; } ;
295334 let mut relocations = Vec :: new ( ) ;
@@ -300,12 +339,13 @@ fn build_enum_match_long(
300339 // Add the jump instruction to the relevant target.
301340 casm_extend ! ( ctx, jmp rel 0 ; ) ;
302341 relocations. push ( RelocationEntry {
303- instruction_idx : i + 1 ,
342+ instruction_idx : base_instruction_count + i + 1 ,
304343 relocation : Relocation :: RelativeStatementId ( stmnt_id) ,
305344 } ) ;
306345 }
307346
308- Ok ( builder. build ( ctx. instructions , relocations, output_expressions) )
347+ instructions. extend ( ctx. instructions ) ;
348+ Ok ( builder. build ( instructions, relocations, output_expressions) )
309349}
310350
311351/// A struct representing an actual enum value in the Sierra program.
@@ -363,3 +403,89 @@ fn get_enum_size(
363403) -> Option < i16 > {
364404 Some ( program_info. type_sizes . get ( concrete_enum_type) ?. to_owned ( ) )
365405}
406+
407+ /// Generates CASM instructions for matching a boxed enum into individual boxed variants.
408+ ///
409+ /// This function takes a boxed enum (stored in a single memory cell containing the address)
410+ /// and branches based on the variant selector, returning a box pointing to the appropriate variant.
411+ fn build_enum_boxed_match (
412+ libfunc : & EnumBoxedMatchConcreteLibfunc ,
413+ builder : CompiledInvocationBuilder < ' _ > ,
414+ ) -> Result < CompiledInvocation , InvocationError > {
415+ let num_branches = builder. invocation . branches . len ( ) ;
416+
417+ // Handle zero variants case - no instructions needed for uninhabited type
418+ if num_branches == 0 {
419+ return Ok ( builder. build (
420+ vec ! [ ] ,
421+ vec ! [ ] ,
422+ iter:: empty :: < iter:: Empty < ReferenceExpression > > ( ) ,
423+ ) ) ;
424+ }
425+
426+ let [ cell] = builder. try_get_single_cells ( ) ?;
427+ let cell_ref = cell. to_deref ( ) . ok_or ( InvocationError :: InvalidReferenceExpressionForArgument ) ?;
428+
429+ // Calculate the size of each variant
430+ let mut variant_sizes: Vec < i16 > = Vec :: new ( ) ;
431+ for variant_ty in & libfunc. variants {
432+ let variant_size = * builder
433+ . program_info
434+ . type_sizes
435+ . get ( variant_ty)
436+ . ok_or ( InvocationError :: InvalidReferenceExpressionForArgument ) ?;
437+ variant_sizes. push ( variant_size) ;
438+ }
439+
440+ // The enum size is 1 (selector) + max(variant_sizes)
441+ let max_variant_size = variant_sizes. iter ( ) . max ( ) . copied ( ) . unwrap_or ( 0 ) ;
442+
443+ let output_cellref = if num_branches > 1 {
444+ let mut adjusted = cell_ref;
445+ assert ! ( adjusted. apply_known_ap_change( 1 ) ) ;
446+ adjusted
447+ } else {
448+ cell_ref
449+ } ;
450+
451+ // For each branch, we need to create a reference to Box<Variant>
452+ // The variant data starts at offset 1 + padding to skip to the variant
453+ let output_expressions_for_branches = variant_sizes. iter ( ) . map ( |& variant_size| {
454+ // Calculate padding: the variant is right-aligned in the enum's value space
455+ let padding = max_variant_size - variant_size;
456+ // The box should point to: base_addr + 1 (selector) + padding
457+ let variant_offset = 1 + padding;
458+ let variant_box = CellExpression :: BinOp {
459+ op : CellOperator :: Add ,
460+ a : output_cellref,
461+ b : DerefOrImmediate :: Immediate ( variant_offset. into ( ) ) ,
462+ } ;
463+
464+ vec ! [ ReferenceExpression :: from_cell( variant_box) ] . into_iter ( )
465+ } ) ;
466+
467+ // For single variant enums, no branching is needed - just return the variant box
468+ if num_branches == 1 {
469+ return Ok ( builder. build ( vec ! [ ] , vec ! [ ] , output_expressions_for_branches) ) ;
470+ }
471+
472+ // Load the variant selector into a temporary - this is a double deref
473+ let instructions = casm ! { [ ap] = [ [ & cell_ref] ] , ap++; } . instructions ;
474+ let variant_selector_cell = CellRef { register : Register :: AP , offset : -1 } ;
475+
476+ if num_branches == 2 {
477+ build_enum_match_short_ex (
478+ builder,
479+ variant_selector_cell,
480+ output_expressions_for_branches. into_iter ( ) . map ( |v| v. into_iter ( ) ) ,
481+ instructions,
482+ )
483+ } else {
484+ build_enum_match_long_ex (
485+ builder,
486+ variant_selector_cell,
487+ output_expressions_for_branches. into_iter ( ) . map ( |v| v. into_iter ( ) ) ,
488+ instructions,
489+ )
490+ }
491+ }
0 commit comments