Skip to content

Commit 19538ac

Browse files
committed
add new copy_branch_params implementation (v2)
1 parent 7d86d9a commit 19538ac

1 file changed

Lines changed: 139 additions & 0 deletions

File tree

  • crates/wasmi/src/engine/translator/func2

crates/wasmi/src/engine/translator/func2/mod.rs

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)