Skip to content

Commit e862a9e

Browse files
chore: improve interpreter performance, add cold path macros
Signed-off-by: Henry <mail@henrygressmann.de>
1 parent 64cf33c commit e862a9e

25 files changed

Lines changed: 343 additions & 472 deletions

‎Cargo.lock‎

Lines changed: 10 additions & 10 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎Cargo.toml‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,9 @@ pretty_env_logger = "0.5"
3131
serde = { version = "1.0", features = ["derive"] }
3232
serde_json = { version = "1.0" }
3333
wasm-testsuite = { version = "0.7" }
34-
wasmparser = { version = "0.255", default-features = false }
35-
wast = "255"
36-
wat = "1.255"
34+
wasmparser = { version = "0.256", default-features = false }
35+
wast = "256"
36+
wat = "1.256"
3737

3838
criterion = { version = "0.8", default-features = false, features = [
3939
"cargo_bench_support",

‎crates/parser/src/optimize.rs‎

Lines changed: 38 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -96,9 +96,14 @@ fn rewrite(
9696
rewrite!(instrs, i, [Const32(c), LocalGet32(local)] => BinOpLocalConst32(op, local, c));
9797
rewrite!(instrs, i, [Const32(c), GlobalGet32(global)] => BinOpGlobalConst32(op, global, c));
9898
rewrite!(instrs, i, [GlobalGet32(global)] => BinOpStackGlobal32(op, global));
99+
rewrite!(instrs, i, [LocalGet32(local)] => BinOpStackLocal32(op, local));
99100
if matches!(op, BinOp::IAdd) {
100101
rewrite!(instrs, i, [Const32(c)] => AddConst32(c));
101102
rewrite!(instrs, i, [I32Add] => I32Add3);
103+
rewrite!(instrs, i,
104+
[BinOpStackLocal32(BinOp::IAdd, local)] =>
105+
[LocalGet32(local), I32Add3]
106+
);
102107
}
103108
}
104109
instr @ (I32Sub | I32Shl | I32ShrS | I32ShrU | I32Rotl | I32Rotr) => {
@@ -107,6 +112,7 @@ fn rewrite(
107112
rewrite!(instrs, i, [LocalGet32(local), Const32(c)] => BinOpLocalConst32(op, local, c));
108113
rewrite!(instrs, i, [GlobalGet32(global), Const32(c)] => BinOpGlobalConst32(op, global, c));
109114
rewrite!(instrs, i, [GlobalGet32(global)] => BinOpStackGlobal32(op, global));
115+
rewrite!(instrs, i, [LocalGet32(local)] => BinOpStackLocal32(op, local));
110116
if matches!(op, BinOp::IShrS) {
111117
rewrite!(instrs, i, [BinOpLocalConst32(BinOp::IShl, local, 8), Const32(8)] => [LocalGet32(local), I32Extend8S]);
112118
rewrite!(instrs, i, [BinOpLocalConst32(BinOp::IShl, local, 16), Const32(16)] => [LocalGet32(local), I32Extend16S]);
@@ -152,12 +158,14 @@ fn rewrite(
152158
rewrite!(instrs, i, [GlobalGet32(global), Const32(c)] => BinOpGlobalConst32(op, global, c));
153159
rewrite!(instrs, i, [Const32(c), LocalGet32(local)] => BinOpLocalConst32(op, local, c));
154160
rewrite!(instrs, i, [Const32(c), GlobalGet32(global)] => BinOpGlobalConst32(op, global, c));
161+
rewrite!(instrs, i, [LocalGet32(local)] => BinOpStackLocal32(op, local));
155162
}
156163
instr @ (F32Sub | F32Div | F32Copysign) => {
157164
let Some(op) = float_bin_op(instr) else { unreachable!() };
158165
rewrite!(instrs, i, [LocalGet32(a), LocalGet32(b)] => BinOpLocalLocal32(op, a, b));
159166
rewrite!(instrs, i, [LocalGet32(local), Const32(c)] => BinOpLocalConst32(op, local, c));
160167
rewrite!(instrs, i, [GlobalGet32(global), Const32(c)] => BinOpGlobalConst32(op, global, c));
168+
rewrite!(instrs, i, [LocalGet32(local)] => BinOpStackLocal32(op, local));
161169
}
162170
instr @ (F64Add | F64Mul | F64Min | F64Max) => {
163171
let Some(op) = float_bin_op(instr) else { unreachable!() };
@@ -198,6 +206,10 @@ fn rewrite(
198206
IncMemoryLocal32(memarg, load_addr)
199207
);
200208
rewrite!(instrs, i, [F32Mul, F32Add] => FMaStoreF32(memarg));
209+
rewrite!(instrs, i,
210+
[BinOpStackLocal32(BinOp::FMul, local), F32Add] =>
211+
[LocalGet32(local), FMaStoreF32(memarg)]
212+
);
201213
rewrite!(instrs, i,
202214
[LocalGet32(addr_local), LocalGet32(value_local)] if
203215
(let (Ok(addr_local), Ok(value_local)) = (u8::try_from(addr_local), u8::try_from(value_local))) =>
@@ -313,6 +325,15 @@ fn rewrite(
313325
);
314326
rewrite!(instrs, i, [I32Mul, LocalGet32(acc), I32Add] if (acc == dst) => MulAccLocal32(dst));
315327
rewrite!(instrs, i, [F32Mul, LocalGet32(acc), F32Add] if (acc == dst) => FMulAccLocal32(dst));
328+
rewrite!(instrs, i,
329+
[I32Mul, BinOpStackLocal32(BinOp::IAdd, acc)] if (acc == dst) =>
330+
MulAccLocal32(dst)
331+
);
332+
rewrite!(instrs, i,
333+
[F32Mul, BinOpStackLocal32(BinOp::FAdd, acc)] if (acc == dst) =>
334+
FMulAccLocal32(dst)
335+
);
336+
rewrite!(instrs, i, [BinOpStackLocal32(op, local)] => BinOpStackLocalSet32(op, local, dst));
316337
rewrite_local_set_direct!(
317338
instrs,
318339
i,
@@ -403,6 +424,7 @@ fn rewrite(
403424
);
404425
}
405426
LocalTee32(dst) => {
427+
rewrite!(instrs, i, [BinOpStackLocal32(op, local)] => BinOpStackLocalTee32(op, local, dst));
406428
fold_local_binop!(
407429
instrs, i, dst,
408430
source = resolve_local_source_32,
@@ -501,16 +523,22 @@ fn rewrite(
501523
LoadLocalTee128(memarg, addr, dst)
502524
);
503525
}
504-
Drop32 => rewrite_drop_tee_direct!(
505-
instrs,
506-
i,
507-
tee = LocalTee32,
508-
set = LocalSet32,
509-
binop_local_local_tee = BinOpLocalLocalTee32,
510-
binop_local_local_set = BinOpLocalLocalSet32,
511-
binop_local_const_tee = BinOpLocalConstTee32,
512-
binop_local_const_set = BinOpLocalConstSet32
513-
),
526+
Drop32 => {
527+
rewrite!(instrs, i,
528+
[BinOpStackLocalTee32(op, local, dst)] =>
529+
BinOpStackLocalSet32(op, local, dst)
530+
);
531+
rewrite_drop_tee_direct!(
532+
instrs,
533+
i,
534+
tee = LocalTee32,
535+
set = LocalSet32,
536+
binop_local_local_tee = BinOpLocalLocalTee32,
537+
binop_local_local_set = BinOpLocalLocalSet32,
538+
binop_local_const_tee = BinOpLocalConstTee32,
539+
binop_local_const_set = BinOpLocalConstSet32
540+
);
541+
}
514542
Drop64 => rewrite_drop_tee_direct!(
515543
instrs,
516544
i,

‎crates/tinywasm/src/error.rs‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -250,18 +250,21 @@ impl LinkingError {
250250
}
251251

252252
impl From<LinkingError> for Error {
253+
#[cold]
253254
fn from(value: LinkingError) -> Self {
254255
Self::Linker(value)
255256
}
256257
}
257258

258259
impl From<TwasmError> for Error {
260+
#[cold]
259261
fn from(value: TwasmError) -> Self {
260262
Self::Twasm(value)
261263
}
262264
}
263265

264266
impl From<Trap> for Error {
267+
#[cold]
265268
fn from(value: Trap) -> Self {
266269
Self::Trap(value)
267270
}
@@ -364,6 +367,7 @@ impl From<Error> for crate::std::io::Error {
364367

365368
#[cfg(feature = "parser")]
366369
impl From<tinywasm_parser::ParseError> for Error {
370+
#[cold]
367371
fn from(value: tinywasm_parser::ParseError) -> Self {
368372
Self::Parser(value)
369373
}

‎crates/tinywasm/src/imports.rs‎

Lines changed: 10 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
use alloc::collections::BTreeMap;
22
use alloc::string::{String, ToString};
33
use alloc::vec::Vec;
4-
use core::hint::cold_path;
54

65
use crate::{Function, Global, HostFunction, LinkingError, Memory, Result, Table, Tag};
76
use tinywasm_types::*;
@@ -175,8 +174,7 @@ impl Imports {
175174

176175
fn compare_types<T: PartialEq>(import: &Import, actual: &T, expected: &T) -> Result<()> {
177176
if expected != actual {
178-
cold_path();
179-
return Err(LinkingError::incompatible_import_type(import).into());
177+
return cold!(Err(LinkingError::incompatible_import_type(import).into()));
180178
}
181179
Ok(())
182180
}
@@ -187,14 +185,12 @@ impl Imports {
187185
return Err(LinkingError::incompatible_import_type(import).into());
188186
}
189187
if actual.size_initial < expected.size_initial {
190-
cold_path();
191-
return Err(LinkingError::incompatible_import_type(import).into());
188+
return cold!(Err(LinkingError::incompatible_import_type(import).into()));
192189
}
193190

194191
match expected.size_max {
195192
Some(expected_max) if actual.size_max.is_none_or(|actual_max| actual_max > expected_max) => {
196-
cold_path();
197-
Err(LinkingError::incompatible_import_type(import).into())
193+
cold!(Err(LinkingError::incompatible_import_type(import).into()))
198194
}
199195
_ => Ok(()),
200196
}
@@ -268,17 +264,15 @@ impl Imports {
268264
}
269265
Extern::HostFunction(func) => {
270266
let ImportKind::Function(type_idx) = import.kind else {
271-
cold_path();
272-
return Err(LinkingError::incompatible_import_type(import).into());
267+
return cold!(Err(LinkingError::incompatible_import_type(import).into()));
273268
};
274269
let expected_type_addr = type_addrs
275270
.get(type_idx as usize)
276271
.ok_or_else(|| LinkingError::incompatible_import_type(import))?;
277272
let actual_ty = func.resolve_import_type(type_addrs)?;
278273
let actual_type_addr = store.register_host_type(&actual_ty);
279274
if !store.state.type_addr_is_subtype(actual_type_addr, *expected_type_addr) {
280-
cold_path();
281-
return Err(LinkingError::incompatible_import_type(import).into());
275+
return cold!(Err(LinkingError::incompatible_import_type(import).into()));
282276
}
283277
ExternVal::Func(func.instantiate_registered(store, actual_type_addr).addr())
284278
}
@@ -289,16 +283,14 @@ impl Imports {
289283
}
290284
} else {
291285
let Some(instance) = self.modules.get(import.module.as_ref()) else {
292-
cold_path();
293-
return Err(LinkingError::unknown_import(import).into());
286+
return cold!(Err(LinkingError::unknown_import(import).into()));
294287
};
295288
instance.validate_store(store)?;
296289
instance.export_addr(&import.name).ok_or_else(|| LinkingError::unknown_import(import))?
297290
};
298291

299292
if val.kind() != (&import.kind).into() {
300-
cold_path();
301-
return Err(LinkingError::incompatible_import_type(import).into());
293+
return cold!(Err(LinkingError::incompatible_import_type(import).into()));
302294
}
303295

304296
match (val, &import.kind) {
@@ -309,8 +301,7 @@ impl Imports {
309301
&& store.state.value_type_is_subtype(global_ty.ty, expected.ty)
310302
&& (!ty.mutable || store.state.value_type_is_subtype(expected.ty, global_ty.ty));
311303
if !compatible {
312-
cold_path();
313-
return Err(LinkingError::incompatible_import_type(import).into());
304+
return cold!(Err(LinkingError::incompatible_import_type(import).into()));
314305
}
315306
imports.globals.push(global_addr);
316307
}
@@ -336,8 +327,7 @@ impl Imports {
336327
type_addrs.get(*ty as usize).ok_or_else(|| LinkingError::incompatible_import_type(import))?;
337328
if !store.state.type_addr_is_subtype(store.state.get_func(func_addr).type_addr, *expected_type_addr)
338329
{
339-
cold_path();
340-
return Err(LinkingError::incompatible_import_type(import).into());
330+
return cold!(Err(LinkingError::incompatible_import_type(import).into()));
341331
}
342332
imports.funcs.push(func_addr);
343333
}
@@ -346,8 +336,7 @@ impl Imports {
346336
.get(ty.type_idx as usize)
347337
.ok_or_else(|| LinkingError::incompatible_import_type(import))?;
348338
if store.state.get_tag(tag_addr).type_addr != *expected_type_addr {
349-
cold_path();
350-
return Err(LinkingError::incompatible_import_type(import).into());
339+
return cold!(Err(LinkingError::incompatible_import_type(import).into()));
351340
}
352341
imports.tags.push(tag_addr);
353342
}

‎crates/tinywasm/src/instance.rs‎

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -111,8 +111,7 @@ impl ModuleInstance {
111111
#[inline]
112112
pub(crate) fn validate_store(&self, store: &Store) -> Result<()> {
113113
if self.0.store_id != store.id() {
114-
cold_path();
115-
return Err(Trap::InvalidStore.into());
114+
return cold!(Err(Trap::InvalidStore.into()));
116115
}
117116
Ok(())
118117
}
@@ -205,8 +204,7 @@ impl ModuleInstance {
205204
store.add_instance(instance.clone());
206205

207206
if let Some(trap) = elem_trapped.or(data_trapped) {
208-
cold_path();
209-
return Err(trap.into());
207+
return cold!(Err(trap.into()));
210208
}
211209
Ok(instance)
212210
}
@@ -356,8 +354,7 @@ impl ModuleInstance {
356354
self.validate_store(store)?;
357355

358356
let ExternVal::Func(func_addr) = self.require_export(name)? else {
359-
cold_path();
360-
return Err(Error::Other(format!("Export is not a function: {name}")));
357+
return cold!(Err(Error::Other(format!("Export is not a function: {name}"))));
361358
};
362359

363360
Ok(Function { item: StoreItem::new(self.0.store_id, func_addr), module_id: self.id() })
@@ -410,8 +407,7 @@ impl ModuleInstance {
410407
self.validate_store(store)?;
411408

412409
let ExternVal::Func(func_addr) = self.require_export(name)? else {
413-
cold_path();
414-
return Err(Error::Other(format!("Export is not a function: {name}")));
410+
return cold!(Err(Error::Other(format!("Export is not a function: {name}"))));
415411
};
416412

417413
let func = Function { item: StoreItem::new(self.0.store_id, func_addr), module_id: self.id() };
@@ -464,8 +460,7 @@ impl ModuleInstance {
464460
match self.require_export(name)? {
465461
ExternVal::Memory(mem_addr) => Ok(Memory(StoreItem::new(self.0.store_id, mem_addr))),
466462
_ => {
467-
cold_path();
468-
Err(Error::Other(format!("Export is not a memory: {name}")))
463+
cold!(Err(Error::Other(format!("Export is not a memory: {name}"))))
469464
}
470465
}
471466
}

0 commit comments

Comments
 (0)