Skip to content

Commit 475c5eb

Browse files
committed
implement translate_br_if variants + fusion
1 parent 6e0c79f commit 475c5eb

File tree

2 files changed

+153
-4
lines changed

2 files changed

+153
-4
lines changed

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

Lines changed: 152 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ mod visit;
1111

1212
use self::{
1313
instrs::{InstrEncoder, InstrEncoderAllocations},
14-
layout::StackLayout,
14+
layout::{StackLayout, StackSpace},
1515
stack::{
1616
BlockControlFrame,
1717
ControlFrame,
@@ -23,19 +23,37 @@ use self::{
2323
OperandIdx,
2424
Stack,
2525
StackAllocations,
26+
TempOperand,
2627
UnreachableControlFrame,
2728
},
2829
utils::{Reset, ReusableAllocations},
2930
};
3031
use crate::{
3132
core::{FuelCostsProvider, Typed, TypedVal, ValType},
3233
engine::{
33-
translator::{Instr, LabelRef, LabelRegistry, WasmTranslator},
34+
translator::{
35+
comparator::{CompareResult as _, NegateCmpInstr as _, TryIntoCmpBranchInstr as _},
36+
Instr,
37+
LabelRef,
38+
LabelRegistry,
39+
WasmTranslator,
40+
},
3441
BlockType,
3542
CompiledFuncEntity,
3643
TranslationError,
3744
},
38-
ir::{BoundedRegSpan, Const16, Const32, Instruction, Reg, RegSpan},
45+
ir::{
46+
BoundedRegSpan,
47+
BranchOffset,
48+
BranchOffset16,
49+
Comparator,
50+
ComparatorAndOffset,
51+
Const16,
52+
Const32,
53+
Instruction,
54+
Reg,
55+
RegSpan,
56+
},
3957
module::{FuncIdx, ModuleHeader, WasmiValueType},
4058
Engine,
4159
Error,
@@ -341,6 +359,17 @@ impl FuncTranslator {
341359
Ok(())
342360
}
343361

362+
/// Pushes the `instr` to the function with the associated `fuel_costs`.
363+
fn push_instr(
364+
&mut self,
365+
instr: Instruction,
366+
fuel_costs: impl FnOnce(&FuelCostsProvider) -> u64,
367+
) -> Result<(), Error> {
368+
let consume_fuel = self.stack.consume_fuel_instr();
369+
self.instrs.push_instr(instr, consume_fuel, fuel_costs)?;
370+
Ok(())
371+
}
372+
344373
/// Translates a generic return instruction.
345374
fn translate_return(&mut self, consume_fuel: Option<Instr>) -> Result<Instr, Error> {
346375
let len_results = self.func_type_with(FuncType::len_results);
@@ -441,6 +470,126 @@ impl FuncTranslator {
441470
todo!()
442471
}
443472

473+
/// Translates a `i32.eqz`+`br_if` or `if` conditional branch instruction.
474+
fn translate_br_eqz(&mut self, condition: Operand, label: LabelRef) -> Result<(), Error> {
475+
self.translate_br_if(condition, label, true)
476+
}
477+
478+
/// Translates a `br_if` conditional branch instruction.
479+
fn translate_br_nez(&mut self, condition: Operand, label: LabelRef) -> Result<(), Error> {
480+
self.translate_br_if(condition, label, false)
481+
}
482+
483+
/// Translates a generic `br_if` fused conditional branch instruction.
484+
fn translate_br_if(
485+
&mut self,
486+
condition: Operand,
487+
label: LabelRef,
488+
branch_eqz: bool,
489+
) -> Result<(), Error> {
490+
if self.try_fuse_branch_cmp(condition, label, branch_eqz)? {
491+
return Ok(());
492+
}
493+
let condition = match condition {
494+
Operand::Local(condition) => self.layout.local_to_reg(condition.local_index())?,
495+
Operand::Temp(condition) => self.layout.temp_to_reg(condition.operand_index())?,
496+
Operand::Immediate(_condition) => {
497+
todo!() // TODO: translate as `br`
498+
}
499+
};
500+
let instr = self.instrs.next_instr();
501+
let offset = self.labels.try_resolve_label(label, instr)?;
502+
let instr = match BranchOffset16::try_from(offset) {
503+
Ok(offset) => match branch_eqz {
504+
true => Instruction::branch_i32_eq_imm16(condition, 0, offset),
505+
false => Instruction::branch_i32_ne_imm16(condition, 0, offset),
506+
},
507+
Err(_) => {
508+
let zero = self.layout.const_to_reg(0_i32)?;
509+
self.make_branch_cmp_fallback(Comparator::I32Eq, condition, zero, offset)?
510+
}
511+
};
512+
self.push_instr(instr, FuelCostsProvider::base)
513+
}
514+
515+
/// Create an [`Instruction::BranchCmpFallback`].
516+
fn make_branch_cmp_fallback(
517+
&mut self,
518+
cmp: Comparator,
519+
lhs: Reg,
520+
rhs: Reg,
521+
offset: BranchOffset,
522+
) -> Result<Instruction, Error> {
523+
let params = self
524+
.layout
525+
.const_to_reg(ComparatorAndOffset::new(cmp, offset))?;
526+
Ok(Instruction::branch_cmp_fallback(lhs, rhs, params))
527+
}
528+
529+
/// Try to fuse a cmp+branch [`Instruction`] with optional negation.
530+
fn try_fuse_branch_cmp(
531+
&mut self,
532+
condition: Operand,
533+
label: LabelRef,
534+
negate: bool,
535+
) -> Result<bool, Error> {
536+
let Operand::Temp(condition) = condition else {
537+
return Ok(false);
538+
};
539+
debug_assert_eq!(condition.ty(), ValType::I32);
540+
let Some(origin) = condition.instr() else {
541+
return Ok(false);
542+
};
543+
let fused_instr = self.try_make_fused_branch_cmp_instr(origin, condition, label, negate)?;
544+
let Some(fused_instr) = fused_instr else {
545+
return Ok(false);
546+
};
547+
self.instrs.try_replace_instr(origin, fused_instr)
548+
}
549+
550+
/// Try to return a fused cmp+branch [`Instruction`] from the given parameters.
551+
///
552+
///
553+
/// # Note
554+
///
555+
/// - The `instr` parameter refers to the to-be-fused cmp instruction.
556+
/// - Returns `Ok(Some)` if cmp+branch fusion was successful.
557+
/// - Returns `Ok(None)`, otherwise.
558+
fn try_make_fused_branch_cmp_instr(
559+
&mut self,
560+
instr: Instr,
561+
condition: TempOperand,
562+
label: LabelRef,
563+
negate: bool,
564+
) -> Result<Option<Instruction>, Error> {
565+
let cmp_instr = *self.instrs.get(instr);
566+
let Some(result) = cmp_instr.compare_result() else {
567+
// Note: cannot fuse non-cmp instructions or cmp-instructions without result.
568+
return Ok(None);
569+
};
570+
if matches!(self.layout.stack_space(result), StackSpace::Local) {
571+
// Note: cannot fuse cmp instructions with observable semantics.
572+
return Ok(None);
573+
}
574+
if result != self.layout.temp_to_reg(condition.operand_index())? {
575+
// Note: cannot fuse cmp instruction with a result that differs
576+
// from the condition operand.
577+
return Ok(None);
578+
}
579+
let cmp_instr = match negate {
580+
false => cmp_instr,
581+
true => match cmp_instr.negate_cmp_instr() {
582+
Some(negated) => negated,
583+
None => {
584+
// Note: cannot negate cmp instruction, thus not possible to fuse.
585+
return Ok(None);
586+
}
587+
},
588+
};
589+
let offset = self.labels.try_resolve_label(label, instr)?;
590+
cmp_instr.try_into_cmp_branch_instr(offset, &mut self.layout)
591+
}
592+
444593
/// Translates a commutative binary Wasm operator to Wasmi bytecode.
445594
fn translate_binary_commutative<T, R>(
446595
&mut self,

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ pub use self::{
2020
UnreachableControlFrame,
2121
},
2222
locals::LocalIdx,
23-
operand::Operand,
23+
operand::{Operand, TempOperand},
2424
operands::{OperandIdx, PreservedLocalsIter},
2525
};
2626
use super::{Reset, ReusableAllocations};

0 commit comments

Comments
 (0)