@@ -391,6 +391,145 @@ impl FuncTranslator {
391391 Ok ( idx)
392392 }
393393
394+ /// Convert all branch params up to `depth` to [`Operand::Temp`].
395+ ///
396+ /// # Note
397+ ///
398+ /// - The top-most `depth` operands on the [`Stack`] will be [`Operand::Temp`] upon completion.
399+ /// - Does nothing if an [`Operand`] is already an [`Operand::Temp`].
400+ fn copy_branch_params_v2 (
401+ & mut self ,
402+ target : & impl ControlFrameBase ,
403+ consume_fuel_instr : Option < Instr > ,
404+ ) -> Result < ( ) , Error > {
405+ let len_branch_params = target. len_branch_params ( & self . engine ) ;
406+ let Some ( branch_results) = self . frame_results ( target) ? else {
407+ return Ok ( ( ) ) ;
408+ } ;
409+ self . encode_copies ( branch_results, len_branch_params, consume_fuel_instr) ?;
410+ Ok ( ( ) )
411+ }
412+
413+ /// Encodes a copy instruction for the top-most `len_values` on the stack to `results`.
414+ ///
415+ /// # Note
416+ ///
417+ /// - This does _not_ pop values from the stack or manipulate the stack otherwise.
418+ /// - This might allocate new function local constant values if necessary.
419+ /// - This does _not_ encode a copy if the copy is a no-op.
420+ fn encode_copies (
421+ & mut self ,
422+ results : RegSpan ,
423+ len_values : u16 ,
424+ consume_fuel_instr : Option < Instr > ,
425+ ) -> Result < ( ) , Error > {
426+ match len_values {
427+ 0 => Ok ( ( ) ) ,
428+ 1 => {
429+ let result = results. head ( ) ;
430+ let copy_instr = match self . stack . peek ( 0 ) {
431+ Operand :: Immediate ( operand) => match operand. ty ( ) {
432+ ValType :: I32 => {
433+ let value = i32:: from ( operand. val ( ) ) ;
434+ Instruction :: copy_imm32 ( result, value)
435+ }
436+ ValType :: I64 => {
437+ let value = i64:: from ( operand. val ( ) ) ;
438+ match Const32 :: try_from ( value) {
439+ Ok ( value) => Instruction :: copy_i64imm32 ( result, value) ,
440+ Err ( _) => {
441+ let value = self . layout . const_to_reg ( value) ?;
442+ Instruction :: copy ( result, value)
443+ }
444+ }
445+ }
446+ ValType :: F32 => {
447+ let value = f32:: from ( operand. val ( ) ) ;
448+ Instruction :: copy_imm32 ( result, value)
449+ }
450+ ValType :: F64 => {
451+ let value = f64:: from ( operand. val ( ) ) ;
452+ match Const32 :: try_from ( value) {
453+ Ok ( value) => Instruction :: copy_f64imm32 ( result, value) ,
454+ Err ( _) => {
455+ let value = self . layout . const_to_reg ( value) ?;
456+ Instruction :: copy ( result, value)
457+ }
458+ }
459+ }
460+ ValType :: V128 | ValType :: FuncRef | ValType :: ExternRef => {
461+ let value = self . layout . const_to_reg ( operand. val ( ) ) ?;
462+ Instruction :: copy ( result, value)
463+ }
464+ } ,
465+ operand => {
466+ let operand = self . layout . operand_to_reg ( operand) ?;
467+ Instruction :: copy ( result, operand)
468+ }
469+ } ;
470+ self . instrs
471+ . push_instr ( copy_instr, consume_fuel_instr, FuelCostsProvider :: base) ?;
472+ Ok ( ( ) )
473+ }
474+ 2 => {
475+ let ( fst, snd) = self . stack . peek2 ( ) ;
476+ let fst = self . layout . operand_to_reg ( fst) ?;
477+ let snd = self . layout . operand_to_reg ( snd) ?;
478+ self . instrs . push_instr (
479+ Instruction :: copy2_ext ( results, fst, snd) ,
480+ consume_fuel_instr,
481+ FuelCostsProvider :: base,
482+ ) ?;
483+ Ok ( ( ) )
484+ }
485+ _ => {
486+ self . instrs
487+ . bump_fuel_consumption ( consume_fuel_instr, |costs| {
488+ costs. fuel_for_copying_values ( u64:: from ( len_values) )
489+ } ) ?;
490+ if let Some ( values) = self . try_form_regspan ( usize:: from ( len_values) ) ? {
491+ // Case: can encode the copies as a more efficient `copy_span`
492+ if results == values {
493+ // Case: results and values are equal and therefore the copy is a no-op
494+ return Ok ( ( ) ) ;
495+ }
496+ debug_assert ! ( results. head( ) < values. head( ) ) ;
497+ self . instrs . push_instr (
498+ Instruction :: copy_span ( results, values, len_values) ,
499+ consume_fuel_instr,
500+ FuelCostsProvider :: base,
501+ ) ?;
502+ return Ok ( ( ) ) ;
503+ }
504+ self . stack
505+ . peek_n ( usize:: from ( len_values) , & mut self . operands ) ;
506+ let [ fst, snd, rest @ ..] = & self . operands [ ..] else {
507+ unreachable ! ( "asserted that operands.len() >= 3" )
508+ } ;
509+ let fst = self . layout . operand_to_reg ( * fst) ?;
510+ let snd = self . layout . operand_to_reg ( * snd) ?;
511+ self . instrs . push_instr (
512+ Instruction :: copy_many_ext ( results, fst, snd) ,
513+ consume_fuel_instr,
514+ FuelCostsProvider :: base,
515+ ) ?;
516+ self . instrs . encode_register_list ( rest, & mut self . layout ) ?;
517+ Ok ( ( ) )
518+ }
519+ }
520+ }
521+
522+ /// Returns the results [`RegSpan`] of the `frame` if any.
523+ fn frame_results ( & self , frame : & impl ControlFrameBase ) -> Result < Option < RegSpan > , Error > {
524+ if frame. len_branch_params ( & self . engine ) == 0 {
525+ return Ok ( None ) ;
526+ }
527+ let height = frame. height ( ) ;
528+ let start = self . layout . temp_to_reg ( OperandIdx :: from ( height) ) ?;
529+ let span = RegSpan :: new ( start) ;
530+ Ok ( Some ( span) )
531+ }
532+
394533 /// Returns `true` if the [`ControlFrame`] at `depth` requires copying for its branch parameters.
395534 ///
396535 /// # Note
0 commit comments