Skip to content

Commit e8f3d33

Browse files
chore: improve fused instructions
Signed-off-by: Henry <mail@henrygressmann.de>
1 parent 1cf50a2 commit e8f3d33

File tree

4 files changed

+135
-13
lines changed

4 files changed

+135
-13
lines changed

crates/parser/src/visit.rs

Lines changed: 127 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -388,7 +388,7 @@ impl<'a, R: WasmModuleResources> wasmparser::VisitOperator<'a> for FunctionBuild
388388

389389
define_operands! {
390390
// basic instructions
391-
visit_global_get(GlobalGet, u32), visit_i32_const(I32Const, i32), visit_i64_const(I64Const, i64), visit_call(Call, u32), visit_return_call(ReturnCall, u32), visit_memory_size(MemorySize, u32), visit_memory_grow(MemoryGrow, u32), visit_unreachable(Unreachable), visit_nop(Nop), visit_i32_eqz(I32Eqz), visit_i32_eq(I32Eq), visit_i32_ne(I32Ne), visit_i32_lt_s(I32LtS), visit_i32_lt_u(I32LtU), visit_i32_gt_s(I32GtS), visit_i32_gt_u(I32GtU), visit_i32_le_s(I32LeS), visit_i32_le_u(I32LeU), visit_i32_ge_s(I32GeS), visit_i32_ge_u(I32GeU), visit_i64_eqz(I64Eqz), visit_i64_eq(I64Eq), visit_i64_ne(I64Ne), visit_i64_lt_s(I64LtS), visit_i64_lt_u(I64LtU), visit_i64_gt_s(I64GtS), visit_i64_gt_u(I64GtU), visit_i64_le_s(I64LeS), visit_i64_le_u(I64LeU), visit_i64_ge_s(I64GeS), visit_i64_ge_u(I64GeU), visit_f32_eq(F32Eq), visit_f32_ne(F32Ne), visit_f32_lt(F32Lt), visit_f32_gt(F32Gt), visit_f32_le(F32Le), visit_f32_ge(F32Ge), visit_f64_eq(F64Eq), visit_f64_ne(F64Ne), visit_f64_lt(F64Lt), visit_f64_gt(F64Gt), visit_f64_le(F64Le), visit_f64_ge(F64Ge), visit_i32_clz(I32Clz), visit_i32_ctz(I32Ctz), visit_i32_popcnt(I32Popcnt), visit_i32_sub(I32Sub), visit_i32_mul(I32Mul), visit_i32_div_s(I32DivS), visit_i32_div_u(I32DivU), visit_i32_rem_s(I32RemS), visit_i32_rem_u(I32RemU), visit_i32_and(I32And), visit_i32_or(I32Or), visit_i32_xor(I32Xor), visit_i32_shl(I32Shl), visit_i32_shr_s(I32ShrS), visit_i32_shr_u(I32ShrU), visit_i32_rotl(I32Rotl), visit_i32_rotr(I32Rotr), visit_i64_clz(I64Clz), visit_i64_ctz(I64Ctz), visit_i64_popcnt(I64Popcnt), visit_i64_sub(I64Sub), visit_i64_mul(I64Mul), visit_i64_div_s(I64DivS), visit_i64_div_u(I64DivU), visit_i64_rem_s(I64RemS), visit_i64_rem_u(I64RemU), visit_i64_and(I64And), visit_i64_or(I64Or), visit_i64_xor(I64Xor), visit_i64_shl(I64Shl), visit_i64_shr_s(I64ShrS), visit_i64_shr_u(I64ShrU), visit_i64_rotr(I64Rotr), visit_f32_abs(F32Abs), visit_f32_neg(F32Neg), visit_f32_ceil(F32Ceil), visit_f32_floor(F32Floor), visit_f32_trunc(F32Trunc), visit_f32_nearest(F32Nearest), visit_f32_sqrt(F32Sqrt), visit_f32_add(F32Add), visit_f32_sub(F32Sub), visit_f32_mul(F32Mul), visit_f32_div(F32Div), visit_f32_min(F32Min), visit_f32_max(F32Max), visit_f32_copysign(F32Copysign), visit_f64_abs(F64Abs), visit_f64_neg(F64Neg), visit_f64_ceil(F64Ceil), visit_f64_floor(F64Floor), visit_f64_trunc(F64Trunc), visit_f64_nearest(F64Nearest), visit_f64_sqrt(F64Sqrt), visit_f64_add(F64Add), visit_f64_sub(F64Sub), visit_f64_mul(F64Mul), visit_f64_div(F64Div), visit_f64_min(F64Min), visit_f64_max(F64Max), visit_f64_copysign(F64Copysign), visit_i32_wrap_i64(I32WrapI64), visit_i32_trunc_f32_s(I32TruncF32S), visit_i32_trunc_f32_u(I32TruncF32U), visit_i32_trunc_f64_s(I32TruncF64S), visit_i32_trunc_f64_u(I32TruncF64U), visit_i64_extend_i32_s(I64ExtendI32S), visit_i64_extend_i32_u(I64ExtendI32U), visit_i64_trunc_f32_s(I64TruncF32S), visit_i64_trunc_f32_u(I64TruncF32U), visit_i64_trunc_f64_s(I64TruncF64S), visit_i64_trunc_f64_u(I64TruncF64U), visit_f32_convert_i32_s(F32ConvertI32S), visit_f32_convert_i32_u(F32ConvertI32U), visit_f32_convert_i64_s(F32ConvertI64S), visit_f32_convert_i64_u(F32ConvertI64U), visit_f32_demote_f64(F32DemoteF64), visit_f64_convert_i32_s(F64ConvertI32S), visit_f64_convert_i32_u(F64ConvertI32U), visit_f64_convert_i64_s(F64ConvertI64S), visit_f64_convert_i64_u(F64ConvertI64U), visit_f64_promote_f32(F64PromoteF32), visit_i32_reinterpret_f32(I32ReinterpretF32), visit_i64_reinterpret_f64(I64ReinterpretF64), visit_f32_reinterpret_i32(F32ReinterpretI32), visit_f64_reinterpret_i64(F64ReinterpretI64),
391+
visit_global_get(GlobalGet, u32), visit_i32_const(I32Const, i32), visit_i64_const(I64Const, i64), visit_call(Call, u32), visit_return_call(ReturnCall, u32), visit_memory_size(MemorySize, u32), visit_memory_grow(MemoryGrow, u32), visit_unreachable(Unreachable), visit_nop(Nop), visit_i32_eqz(I32Eqz), visit_i32_eq(I32Eq), visit_i32_ne(I32Ne), visit_i32_lt_s(I32LtS), visit_i32_lt_u(I32LtU), visit_i32_gt_s(I32GtS), visit_i32_gt_u(I32GtU), visit_i32_le_s(I32LeS), visit_i32_le_u(I32LeU), visit_i32_ge_s(I32GeS), visit_i32_ge_u(I32GeU), visit_i64_eqz(I64Eqz), visit_i64_eq(I64Eq), visit_i64_ne(I64Ne), visit_i64_lt_s(I64LtS), visit_i64_lt_u(I64LtU), visit_i64_gt_s(I64GtS), visit_i64_gt_u(I64GtU), visit_i64_le_s(I64LeS), visit_i64_le_u(I64LeU), visit_i64_ge_s(I64GeS), visit_i64_ge_u(I64GeU), visit_f32_eq(F32Eq), visit_f32_ne(F32Ne), visit_f32_lt(F32Lt), visit_f32_gt(F32Gt), visit_f32_le(F32Le), visit_f32_ge(F32Ge), visit_f64_eq(F64Eq), visit_f64_ne(F64Ne), visit_f64_lt(F64Lt), visit_f64_gt(F64Gt), visit_f64_le(F64Le), visit_f64_ge(F64Ge), visit_i32_clz(I32Clz), visit_i32_ctz(I32Ctz), visit_i32_popcnt(I32Popcnt), visit_i32_sub(I32Sub), visit_i32_mul(I32Mul), visit_i32_div_s(I32DivS), visit_i32_div_u(I32DivU), visit_i32_rem_s(I32RemS), visit_i32_rem_u(I32RemU), visit_i32_and(I32And), visit_i32_or(I32Or), visit_i32_xor(I32Xor), visit_i32_shl(I32Shl), visit_i32_shr_s(I32ShrS), visit_i32_shr_u(I32ShrU), visit_i32_rotl(I32Rotl), visit_i32_rotr(I32Rotr), visit_i64_clz(I64Clz), visit_i64_ctz(I64Ctz), visit_i64_popcnt(I64Popcnt), visit_i64_sub(I64Sub), visit_i64_mul(I64Mul), visit_i64_div_s(I64DivS), visit_i64_div_u(I64DivU), visit_i64_rem_s(I64RemS), visit_i64_rem_u(I64RemU), visit_i64_and(I64And), visit_i64_or(I64Or), visit_i64_xor(I64Xor), visit_i64_shl(I64Shl), visit_i64_shr_s(I64ShrS), visit_i64_shr_u(I64ShrU), visit_i64_rotr(I64Rotr), visit_f32_abs(F32Abs), visit_f32_neg(F32Neg), visit_f32_ceil(F32Ceil), visit_f32_floor(F32Floor), visit_f32_trunc(F32Trunc), visit_f32_nearest(F32Nearest), visit_f32_sqrt(F32Sqrt), visit_f32_add(F32Add), visit_f32_sub(F32Sub), visit_f32_mul(F32Mul), visit_f32_div(F32Div), visit_f32_min(F32Min), visit_f32_max(F32Max), visit_f32_copysign(F32Copysign), visit_f64_abs(F64Abs), visit_f64_neg(F64Neg), visit_f64_ceil(F64Ceil), visit_f64_floor(F64Floor), visit_f64_trunc(F64Trunc), visit_f64_nearest(F64Nearest), visit_f64_sqrt(F64Sqrt), visit_f64_add(F64Add), visit_f64_sub(F64Sub), visit_f64_mul(F64Mul), visit_f64_div(F64Div), visit_f64_min(F64Min), visit_f64_max(F64Max), visit_f64_copysign(F64Copysign), visit_i32_wrap_i64(I32WrapI64), visit_i32_trunc_f32_s(I32TruncF32S), visit_i32_trunc_f32_u(I32TruncF32U), visit_i32_trunc_f64_s(I32TruncF64S), visit_i32_trunc_f64_u(I32TruncF64U), visit_i64_extend_i32_s(I64ExtendI32S), visit_i64_extend_i32_u(I64ExtendI32U), visit_i64_trunc_f32_s(I64TruncF32S), visit_i64_trunc_f32_u(I64TruncF32U), visit_i64_trunc_f64_s(I64TruncF64S), visit_i64_trunc_f64_u(I64TruncF64U), visit_f32_convert_i32_s(F32ConvertI32S), visit_f32_convert_i32_u(F32ConvertI32U), visit_f32_convert_i64_s(F32ConvertI64S), visit_f32_convert_i64_u(F32ConvertI64U), visit_f32_demote_f64(F32DemoteF64), visit_f64_convert_i32_s(F64ConvertI32S), visit_f64_convert_i32_u(F64ConvertI32U), visit_f64_convert_i64_s(F64ConvertI64S), visit_f64_convert_i64_u(F64ConvertI64U), visit_f64_promote_f32(F64PromoteF32),
392392

393393
// sign_extension
394394
visit_i32_extend8_s(I32Extend8S), visit_i32_extend16_s(I32Extend16S), visit_i64_extend8_s(I64Extend8S), visit_i64_extend16_s(I64Extend16S), visit_i64_extend32_s(I64Extend32S),
@@ -446,6 +446,30 @@ impl<'a, R: WasmModuleResources> wasmparser::VisitOperator<'a> for FunctionBuild
446446
}
447447

448448
fn visit_drop(&mut self) -> Self::Output {
449+
match self.instructions.last().copied() {
450+
Some(Instruction::LocalTee32(local)) => {
451+
self.instructions.pop();
452+
self.instructions.push(Instruction::LocalSet32(local));
453+
return;
454+
}
455+
Some(Instruction::LocalTee64(local)) => {
456+
self.instructions.pop();
457+
self.instructions.push(Instruction::LocalSet64(local));
458+
return;
459+
}
460+
Some(Instruction::LocalTee128(local)) => {
461+
self.instructions.pop();
462+
self.instructions.push(Instruction::LocalSet128(local));
463+
return;
464+
}
465+
Some(Instruction::LocalTeeRef(local)) => {
466+
self.instructions.pop();
467+
self.instructions.push(Instruction::LocalSetRef(local));
468+
return;
469+
}
470+
_ => {}
471+
}
472+
449473
match self.validator.get_operand_type(0) {
450474
Some(Some(t)) => self.instructions.push(match t {
451475
wasmparser::ValType::I32 => Instruction::Drop32,
@@ -491,6 +515,18 @@ impl<'a, R: WasmModuleResources> wasmparser::VisitOperator<'a> for FunctionBuild
491515
return;
492516
}
493517

518+
if len >= 2 {
519+
let lhs = self.instructions[len - 2];
520+
let rhs = self.instructions[len - 1];
521+
if let (Instruction::I32Const(c), Instruction::LocalGet32(local)) = (lhs, rhs) {
522+
self.instructions.pop();
523+
self.instructions.pop();
524+
self.instructions.push(Instruction::LocalGet32(local));
525+
self.instructions.push(Instruction::I32AddConst(c));
526+
return;
527+
}
528+
}
529+
494530
self.instructions.push(Instruction::I32Add);
495531
}
496532

@@ -513,6 +549,18 @@ impl<'a, R: WasmModuleResources> wasmparser::VisitOperator<'a> for FunctionBuild
513549
return;
514550
}
515551

552+
if len >= 2 {
553+
let lhs = self.instructions[len - 2];
554+
let rhs = self.instructions[len - 1];
555+
if let (Instruction::I64Const(c), Instruction::LocalGet64(local)) = (lhs, rhs) {
556+
self.instructions.pop();
557+
self.instructions.pop();
558+
self.instructions.push(Instruction::LocalGet64(local));
559+
self.instructions.push(Instruction::I64AddConst(c));
560+
return;
561+
}
562+
}
563+
516564
self.instructions.push(Instruction::I64Add);
517565
}
518566

@@ -541,14 +589,44 @@ impl<'a, R: WasmModuleResources> wasmparser::VisitOperator<'a> for FunctionBuild
541589
};
542590

543591
match self.validator.get_local_type(idx) {
544-
Some(t) => self.instructions.push(match t {
545-
wasmparser::ValType::I32 => Instruction::LocalGet32(resolved_idx),
546-
wasmparser::ValType::F32 => Instruction::LocalGet32(resolved_idx),
547-
wasmparser::ValType::I64 => Instruction::LocalGet64(resolved_idx),
548-
wasmparser::ValType::F64 => Instruction::LocalGet64(resolved_idx),
549-
wasmparser::ValType::V128 => Instruction::LocalGet128(resolved_idx),
550-
wasmparser::ValType::Ref(_) => Instruction::LocalGetRef(resolved_idx),
551-
}),
592+
Some(t) => match t {
593+
wasmparser::ValType::I32 | wasmparser::ValType::F32 => {
594+
if matches!(self.instructions.last(), Some(Instruction::LocalSet32(local)) if *local == resolved_idx)
595+
{
596+
self.instructions.pop();
597+
self.instructions.push(Instruction::LocalTee32(resolved_idx));
598+
return;
599+
}
600+
self.instructions.push(Instruction::LocalGet32(resolved_idx));
601+
}
602+
wasmparser::ValType::I64 | wasmparser::ValType::F64 => {
603+
if matches!(self.instructions.last(), Some(Instruction::LocalSet64(local)) if *local == resolved_idx)
604+
{
605+
self.instructions.pop();
606+
self.instructions.push(Instruction::LocalTee64(resolved_idx));
607+
return;
608+
}
609+
self.instructions.push(Instruction::LocalGet64(resolved_idx));
610+
}
611+
wasmparser::ValType::V128 => {
612+
if matches!(self.instructions.last(), Some(Instruction::LocalSet128(local)) if *local == resolved_idx)
613+
{
614+
self.instructions.pop();
615+
self.instructions.push(Instruction::LocalTee128(resolved_idx));
616+
return;
617+
}
618+
self.instructions.push(Instruction::LocalGet128(resolved_idx));
619+
}
620+
wasmparser::ValType::Ref(_) => {
621+
if matches!(self.instructions.last(), Some(Instruction::LocalSetRef(local)) if *local == resolved_idx)
622+
{
623+
self.instructions.pop();
624+
self.instructions.push(Instruction::LocalTeeRef(resolved_idx));
625+
return;
626+
}
627+
self.instructions.push(Instruction::LocalGetRef(resolved_idx));
628+
}
629+
},
552630
_ => {
553631
self.visit_unreachable();
554632
}
@@ -572,6 +650,12 @@ impl<'a, R: WasmModuleResources> wasmparser::VisitOperator<'a> for FunctionBuild
572650
{
573651
let from = *from;
574652
self.instructions.pop();
653+
654+
if from == resolved_idx {
655+
// local.set x (local.get x) is a no-op; drop it.
656+
return;
657+
}
658+
575659
// validation will ensure that the last instruction is the correct local.get
576660
match self.validator.get_operand_type(0) {
577661
Some(Some(t)) => self.instructions.push(match t {
@@ -612,6 +696,35 @@ impl<'a, R: WasmModuleResources> wasmparser::VisitOperator<'a> for FunctionBuild
612696
return;
613697
};
614698

699+
if let Some(Some(t)) = self.validator.get_operand_type(0) {
700+
match t {
701+
wasmparser::ValType::I32 | wasmparser::ValType::F32 => {
702+
if matches!(self.instructions.last(), Some(Instruction::LocalGet32(local)) if *local == resolved_idx)
703+
{
704+
return;
705+
}
706+
}
707+
wasmparser::ValType::I64 | wasmparser::ValType::F64 => {
708+
if matches!(self.instructions.last(), Some(Instruction::LocalGet64(local)) if *local == resolved_idx)
709+
{
710+
return;
711+
}
712+
}
713+
wasmparser::ValType::V128 => {
714+
if matches!(self.instructions.last(), Some(Instruction::LocalGet128(local)) if *local == resolved_idx)
715+
{
716+
return;
717+
}
718+
}
719+
wasmparser::ValType::Ref(_) => {
720+
if matches!(self.instructions.last(), Some(Instruction::LocalGetRef(local)) if *local == resolved_idx)
721+
{
722+
return;
723+
}
724+
}
725+
}
726+
}
727+
615728
if let Some(Instruction::I64XorRotlConst(c)) = self.instructions.last().copied() {
616729
match self.validator.get_operand_type(0) {
617730
Some(Some(wasmparser::ValType::I64)) | Some(Some(wasmparser::ValType::F64)) => {
@@ -836,6 +949,11 @@ impl<'a, R: WasmModuleResources> wasmparser::VisitOperator<'a> for FunctionBuild
836949
wasmparser::ValType::Ref(_) => Instruction::SelectRef,
837950
});
838951
}
952+
953+
fn visit_f32_reinterpret_i32(&mut self) -> Self::Output {}
954+
fn visit_f64_reinterpret_i64(&mut self) -> Self::Output {}
955+
fn visit_i32_reinterpret_f32(&mut self) -> Self::Output {}
956+
fn visit_i64_reinterpret_f64(&mut self) -> Self::Output {}
839957
}
840958

841959
macro_rules! impl_visit_simd_operator {

crates/tinywasm/src/interpreter/executor.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ impl<'store, const BUDGETED: bool> Executor<'store, BUDGETED> {
100100

101101
#[rustfmt::skip]
102102
match next {
103-
Nop | I32ReinterpretF32 | I64ReinterpretF64 | F32ReinterpretI32 | F64ReinterpretI64 => {}
103+
Nop => {}
104104
Unreachable => return Err(Trap::Unreachable.into()),
105105
Drop32 => self.store.stack.values.drop::<Value32>(),
106106
Drop64 => self.store.stack.values.drop::<Value64>(),

crates/types/src/instructions.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -139,23 +139,27 @@ pub enum Instruction {
139139
// See <https://webassembly.github.io/spec/core/binary/instructions.html#numeric-instructions>
140140
I32Eqz, I32Eq, I32Ne, I32LtS, I32LtU, I32GtS, I32GtU, I32LeS, I32LeU, I32GeS, I32GeU,
141141
I64Eqz, I64Eq, I64Ne, I64LtS, I64LtU, I64GtS, I64GtU, I64LeS, I64LeU, I64GeS, I64GeU,
142+
142143
// Comparisons
143144
F32Eq, F32Ne, F32Lt, F32Gt, F32Le, F32Ge,
144145
F64Eq, F64Ne, F64Lt, F64Gt, F64Le, F64Ge,
145146
I32Clz, I32Ctz, I32Popcnt, I32Add, I32Sub, I32Mul, I32DivS, I32DivU, I32RemS, I32RemU,
146147
I64Clz, I64Ctz, I64Popcnt, I64Add, I64Sub, I64Mul, I64DivS, I64DivU, I64RemS, I64RemU,
148+
147149
// Bitwise
148150
I32And, I32Or, I32Xor, I32Shl, I32ShrS, I32ShrU, I32Rotl, I32Rotr,
149151
I64And, I64Or, I64Xor, I64Shl, I64ShrS, I64ShrU, I64Rotl, I64Rotr,
152+
150153
// Floating Point
151154
F32Abs, F32Neg, F32Ceil, F32Floor, F32Trunc, F32Nearest, F32Sqrt, F32Add, F32Sub, F32Mul, F32Div, F32Min, F32Max, F32Copysign,
152155
F64Abs, F64Neg, F64Ceil, F64Floor, F64Trunc, F64Nearest, F64Sqrt, F64Add, F64Sub, F64Mul, F64Div, F64Min, F64Max, F64Copysign,
153156
I32WrapI64, I32TruncF32S, I32TruncF32U, I32TruncF64S, I32TruncF64U, I32Extend8S, I32Extend16S,
154157
I64Extend8S, I64Extend16S, I64Extend32S, I64ExtendI32S, I64ExtendI32U, I64TruncF32S, I64TruncF32U, I64TruncF64S, I64TruncF64U,
155158
F32ConvertI32S, F32ConvertI32U, F32ConvertI64S, F32ConvertI64U, F32DemoteF64,
156159
F64ConvertI32S, F64ConvertI32U, F64ConvertI64S, F64ConvertI64U, F64PromoteF32,
157-
// Reinterpretations (noops at runtime)
158-
I32ReinterpretF32, I64ReinterpretF64, F32ReinterpretI32, F64ReinterpretI64,
160+
161+
// Reinterpretations are parser no-ops and intentionally omitted.
162+
159163
// Saturating Float-to-Int Conversions
160164
I32TruncSatF32S, I32TruncSatF32U, I32TruncSatF64S, I32TruncSatF64U,
161165
I64TruncSatF32S, I64TruncSatF32U, I64TruncSatF64S, I64TruncSatF64U,

crates/types/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ pub mod archive;
5050

5151
#[cfg(not(feature = "archive"))]
5252
pub mod archive {
53-
#[cfg_attr(feature = "debug", derive(Debug))]
53+
#[derive(Debug)]
5454
pub enum TwasmError {}
5555
impl core::fmt::Display for TwasmError {
5656
fn fmt(&self, _: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {

0 commit comments

Comments
 (0)