Skip to content

Commit 281d222

Browse files
committed
adjust Wasmi translation for conditional return removal
1 parent 5afa798 commit 281d222

File tree

3 files changed

+39
-81
lines changed

3 files changed

+39
-81
lines changed

crates/wasmi/src/engine/translator/instr_encoder.rs

-61
Original file line numberDiff line numberDiff line change
@@ -641,67 +641,6 @@ impl InstrEncoder {
641641
Ok(())
642642
}
643643

644-
/// Encodes an conditional `return` instruction.
645-
pub fn encode_return_nez(
646-
&mut self,
647-
stack: &mut ValueStack,
648-
condition: Reg,
649-
values: &[TypedProvider],
650-
fuel_info: FuelInfo,
651-
) -> Result<(), Error> {
652-
// Note: We bump fuel unconditionally even if the conditional return is not taken.
653-
// This is very conservative and may lead to more fuel costs than
654-
// actually needed for the computation. We might revisit this decision
655-
// later. An alternative solution would consume fuel during execution
656-
// time only when the return is taken.
657-
let instr = match values {
658-
[] => Instruction::return_nez(condition),
659-
[TypedProvider::Register(reg)] => Instruction::return_nez_reg(condition, *reg),
660-
[TypedProvider::Const(value)] => match value.ty() {
661-
ValType::I32 => Instruction::return_nez_imm32(condition, i32::from(*value)),
662-
ValType::I64 => match <Const32<i64>>::try_from(i64::from(*value)).ok() {
663-
Some(value) => Instruction::return_nez_i64imm32(condition, value),
664-
None => Instruction::return_nez_reg(condition, stack.alloc_const(*value)?),
665-
},
666-
ValType::F32 => Instruction::return_nez_imm32(condition, f32::from(*value)),
667-
ValType::F64 => match <Const32<f64>>::try_from(f64::from(*value)).ok() {
668-
Some(value) => Instruction::return_nez_f64imm32(condition, value),
669-
None => Instruction::return_nez_reg(condition, stack.alloc_const(*value)?),
670-
},
671-
ValType::V128 | ValType::FuncRef | ValType::ExternRef => {
672-
Instruction::return_nez_reg(condition, stack.alloc_const(*value)?)
673-
}
674-
},
675-
[v0, v1] => {
676-
let reg0 = stack.provider2reg(v0)?;
677-
let reg1 = stack.provider2reg(v1)?;
678-
Instruction::return_nez_reg2_ext(condition, reg0, reg1)
679-
}
680-
[v0, v1, rest @ ..] => {
681-
debug_assert!(!rest.is_empty());
682-
// Note: The fuel for return values might result in 0 charges if there aren't
683-
// enough return values to account for at least 1 fuel. Therefore we need
684-
// to also bump by `FuelCosts::base` to charge at least 1 fuel.
685-
self.bump_fuel_consumption(&fuel_info, FuelCostsProvider::base)?;
686-
self.bump_fuel_consumption(&fuel_info, |costs| {
687-
costs.fuel_for_copying_values(rest.len() as u64 + 3)
688-
})?;
689-
if let Some(span) = BoundedRegSpan::from_providers(values) {
690-
self.push_instr(Instruction::return_nez_span(condition, span))?;
691-
return Ok(());
692-
}
693-
let reg0 = stack.provider2reg(v0)?;
694-
let reg1 = stack.provider2reg(v1)?;
695-
self.push_instr(Instruction::return_nez_many_ext(condition, reg0, reg1))?;
696-
self.encode_register_list(stack, rest)?;
697-
return Ok(());
698-
}
699-
};
700-
self.bump_fuel_consumption(&fuel_info, FuelCostsProvider::base)?;
701-
self.push_instr(instr)?;
702-
Ok(())
703-
}
704-
705644
/// Encode the given slice of [`TypedProvider`] as a list of [`Reg`].
706645
///
707646
/// # Note

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

+38-19
Original file line numberDiff line numberDiff line change
@@ -796,10 +796,8 @@ impl FuncTranslator {
796796
let block_type = BlockType::func_type(func_type);
797797
let end_label = self.alloc.instr_encoder.new_label();
798798
let consume_fuel = self.make_fuel_instr()?;
799-
// Note: we use a dummy `RegSpan` as placeholder.
800-
//
801-
// We can do this since the branch parameters of the function enclosing block
802-
// are never used due to optimizations to directly return to the caller instead.
799+
// Note: we use a dummy `RegSpan` as placeholder since the function enclosing
800+
// control block never has branch parameters.
803801
let branch_params = RegSpan::new(Reg::from(0));
804802
let block_frame = BlockControlFrame::new(
805803
block_type,
@@ -1043,6 +1041,42 @@ impl FuncTranslator {
10431041
FuelInfo::some(fuel_costs, fuel_instr)
10441042
}
10451043
};
1044+
if self.reachable && frame.is_branched_to() {
1045+
// If the end of the `block` is reachable AND
1046+
// there are branches to the end of the `block`
1047+
// prior, we need to copy the results to the
1048+
// block result registers.
1049+
//
1050+
// # Note
1051+
//
1052+
// We can skip this step if the above condition is
1053+
// not met since the code at this point is either
1054+
// unreachable OR there is only one source of results
1055+
// and thus there is no need to copy the results around.
1056+
// self.translate_copy_branch_params(frame.branch_params(self.engine()))?;
1057+
let branch_params = frame.branch_params(self.engine());
1058+
let params = &mut self.alloc.buffer.providers;
1059+
self.alloc
1060+
.stack
1061+
.pop_n(usize::from(branch_params.len()), params);
1062+
self.alloc.instr_encoder.encode_copies(
1063+
&mut self.alloc.stack,
1064+
branch_params,
1065+
&self.alloc.buffer.providers[..],
1066+
&fuel_info,
1067+
)?;
1068+
// Case: branches to this block exist so we cannot treat the
1069+
// basic block as a no-op and instead have to put its
1070+
// block results on top of the stack.
1071+
self.alloc
1072+
.stack
1073+
.trunc(frame.block_height().into_u16() as usize);
1074+
for result in frame.branch_params(self.engine()) {
1075+
self.alloc.stack.push_register(result)?;
1076+
}
1077+
}
1078+
// Since the `block` is now sealed we can pin its end label.
1079+
self.alloc.instr_encoder.pin_label(frame.end_label());
10461080
// We dropped the Wasm `block` that encloses the function itself so we can return.
10471081
return self.translate_return_with(&fuel_info);
10481082
}
@@ -2732,21 +2766,6 @@ impl FuncTranslator {
27322766
Ok(())
27332767
}
27342768

2735-
/// Translates a conditional `br_if` that targets the function enclosing `block`.
2736-
fn translate_return_if(&mut self, condition: Reg) -> Result<(), Error> {
2737-
bail_unreachable!(self);
2738-
let len_results = self.func_type().results().len();
2739-
let fuel_info = self.fuel_info();
2740-
let values = &mut self.alloc.buffer.providers;
2741-
self.alloc.stack.peek_n(len_results, values);
2742-
self.alloc.instr_encoder.encode_return_nez(
2743-
&mut self.alloc.stack,
2744-
condition,
2745-
values,
2746-
fuel_info,
2747-
)
2748-
}
2749-
27502769
/// Create either [`Instruction::CallIndirectParams`] or [`Instruction::CallIndirectParamsImm16`] depending on the inputs.
27512770
fn call_indirect_params(
27522771
&mut self,

crates/wasmi/src/engine/translator/visit.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -403,7 +403,7 @@ impl<'a> VisitOperator<'a> for FuncTranslator {
403403
};
404404
let fuel_info = self.fuel_info();
405405
let frame = match self.alloc.control_stack.acquire_target(relative_depth) {
406-
AcquiredTarget::Return(_frame) => return self.translate_return_if(condition),
406+
AcquiredTarget::Return(frame) => frame,
407407
AcquiredTarget::Branch(frame) => frame,
408408
};
409409
frame.bump_branches();

0 commit comments

Comments
 (0)