Skip to content

Commit e47089b

Browse files
chore: use new rust features
Signed-off-by: Henry <mail@henrygressmann.de>
1 parent c2061cf commit e47089b

10 files changed

Lines changed: 188 additions & 160 deletions

File tree

Cargo.lock

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

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ serde={version="1.0", features=["derive"]}
1919

2020
[workspace.package]
2121
version="0.9.0-alpha.0"
22-
rust-version="1.90"
22+
rust-version="1.95"
2323
edition="2024"
2424
license="MIT OR Apache-2.0"
2525
authors=["Henry Gressmann <crates@henrygressmann.de>"]

crates/tinywasm/src/interpreter/executor.rs

Lines changed: 60 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
use core::hint::cold_path;
2+
13
#[cfg(not(feature = "std"))]
24
#[allow(unused_imports)]
35
use super::no_std_floats::NoStdFloatExt;
@@ -17,9 +19,6 @@ use crate::instance::ModuleInstanceInner;
1719
use crate::interpreter::Value128;
1820
use crate::*;
1921

20-
#[cfg(feature = "std")]
21-
const TIME_BUDGET_CHECK_INTERVAL: usize = 2048;
22-
const FUEL_ACCOUNTING_INTERVAL: usize = 1024;
2322
const FUEL_COST_CALL_TOTAL: u32 = 5;
2423

2524
pub(crate) struct Executor<'store, const BUDGETED: bool> {
@@ -119,11 +118,14 @@ impl<'store, const BUDGETED: bool> Executor<'store, BUDGETED> {
119118

120119
let next = match self.func.instructions.0.get(self.cf.instr_ptr as usize) {
121120
Some(instr) => instr,
122-
None => unreachable!(
123-
"Instruction pointer out of bounds: {} ({} instructions)",
124-
self.cf.instr_ptr,
125-
self.func.instructions.0.len()
126-
),
121+
None => {
122+
cold_path();
123+
unreachable!(
124+
"Instruction pointer out of bounds: {} ({} instructions)",
125+
self.cf.instr_ptr,
126+
self.func.instructions.0.len()
127+
)
128+
}
127129
};
128130

129131
#[rustfmt::skip]
@@ -722,7 +724,11 @@ impl<'store, const BUDGETED: bool> Executor<'store, BUDGETED> {
722724
}
723725

724726
let res = self.store.stack.values.enter_locals(&wasm_func.func.params, &wasm_func.func.locals);
725-
let locals_base = res.map_err(|err| if IS_RETURN_CALL { err } else { Error::Trap(Trap::CallStackOverflow) })?;
727+
let locals_base = res.map_err(|err| {
728+
cold_path();
729+
if IS_RETURN_CALL { err } else { Error::Trap(Trap::CallStackOverflow) }
730+
})?;
731+
726732
let new_call_frame = CallFrame::new(func_addr, wasm_func.owner, locals_base, wasm_func.func.locals);
727733

728734
if !IS_RETURN_CALL {
@@ -763,7 +769,10 @@ impl<'store, const BUDGETED: bool> Executor<'store, BUDGETED> {
763769
}
764770

765771
let res = self.store.stack.values.enter_locals(&params, &locals);
766-
let locals_base = res.map_err(|err| if IS_RETURN_CALL { err } else { Error::Trap(Trap::CallStackOverflow) })?;
772+
let locals_base = res.map_err(|err| {
773+
cold_path();
774+
if IS_RETURN_CALL { err } else { Error::Trap(Trap::CallStackOverflow) }
775+
})?;
767776
let new_call_frame = CallFrame::new(self.cf.func_addr, self.cf.module_addr, locals_base, locals);
768777

769778
if !IS_RETURN_CALL {
@@ -781,15 +790,23 @@ impl<'store, const BUDGETED: bool> Executor<'store, BUDGETED> {
781790
let table_idx: u32 = self.store.stack.values.pop::<i32>() as u32;
782791
let table = self.store.state.get_table(self.module.resolve_table_addr(table_addr));
783792
assert!(table.kind.element_type == WasmType::RefFunc, "table is not of type funcref");
784-
let table =
785-
table.get(table_idx).map_err(|_| Error::from(Trap::UndefinedElement { index: table_idx as usize }))?;
786-
table.addr().ok_or_else(|| Error::from(Trap::UninitializedElement { index: table_idx as usize }))?
793+
794+
let table = table.get(table_idx).map_err(|_| {
795+
cold_path();
796+
Error::from(Trap::UndefinedElement { index: table_idx as usize })
797+
})?;
798+
799+
table.addr().ok_or_else(|| {
800+
cold_path();
801+
Error::from(Trap::UninitializedElement { index: table_idx as usize })
802+
})?
787803
};
788804

789805
let call_ty = self.module.func_ty(type_addr);
790806
match self.store.state.get_func(func_ref) {
791807
crate::FunctionInstance::Wasm(wasm_func) => {
792808
if wasm_func.ty() != call_ty {
809+
cold_path();
793810
return Err(Trap::IndirectCallTypeMismatch {
794811
actual: wasm_func.ty().clone(),
795812
expected: call_ty.clone(),
@@ -801,6 +818,7 @@ impl<'store, const BUDGETED: bool> Executor<'store, BUDGETED> {
801818
}
802819
crate::FunctionInstance::Host(host_func) => {
803820
if host_func.ty != *call_ty {
821+
cold_path();
804822
return Err(Trap::IndirectCallTypeMismatch {
805823
actual: host_func.ty.clone(),
806824
expected: call_ty.clone(),
@@ -833,6 +851,7 @@ impl<'store, const BUDGETED: bool> Executor<'store, BUDGETED> {
833851
fn local_mem_addr<const N: usize>(&self, memarg: MemoryArg, addr_local: u8) -> Result<usize> {
834852
let addr = u64::from(self.store.stack.values.local_get::<u32>(&self.cf, u16::from(addr_local)));
835853
let Some(Ok(addr)) = memarg.offset().checked_add(addr).map(|a| a.try_into()) else {
854+
cold_path();
836855
return Err(Error::Trap(Trap::MemoryOutOfBounds { offset: addr as usize, len: N, max: 0 }));
837856
};
838857
Ok(addr)
@@ -850,9 +869,11 @@ impl<'store, const BUDGETED: bool> Executor<'store, BUDGETED> {
850869
let addr = u64::from(self.store.stack.values.local_get::<u32>(&self.cf, u16::from(addr_local)));
851870
let value = cast(self.store.stack.values.local_get::<T>(&self.cf, u16::from(value_local))).to_mem_bytes();
852871
let Some(effective_addr) = memarg.offset().checked_add(addr) else {
872+
cold_path();
853873
return Err(Error::Trap(Trap::MemoryOutOfBounds { offset: addr as usize, len: N, max: 0 }));
854874
};
855875
let Ok(effective_addr) = usize::try_from(effective_addr) else {
876+
cold_path();
856877
return Err(Error::Trap(Trap::MemoryOutOfBounds { offset: addr as usize, len: N, max: 0 }));
857878
};
858879
mem.store(effective_addr, value.len(), &value)?;
@@ -970,14 +991,19 @@ impl<'store, const BUDGETED: bool> Executor<'store, BUDGETED> {
970991
let data_len = data.data.as_ref().map_or(0, |d| d.len());
971992

972993
if ((size + offset) as usize > data_len) || ((dst + size) as usize > mem.len()) {
994+
cold_path();
973995
return Err(Trap::MemoryOutOfBounds { offset: offset as usize, len: size as usize, max: data_len }.into());
974996
}
975997

976998
if size == 0 {
977999
return Ok(());
9781000
}
9791001

980-
let Some(data) = &data.data else { return Err(Trap::MemoryOutOfBounds { offset: 0, len: 0, max: 0 }.into()) };
1002+
let Some(data) = &data.data else {
1003+
cold_path();
1004+
return Err(Trap::MemoryOutOfBounds { offset: 0, len: 0, max: 0 }.into());
1005+
};
1006+
9811007
mem.store(dst as usize, size as usize, &data[offset as usize..((offset + size) as usize)])
9821008
}
9831009
fn exec_table_copy(&mut self, dst_table: u32, src_table: u32) -> Result<()> {
@@ -1012,6 +1038,7 @@ impl<'store, const BUDGETED: bool> Executor<'store, BUDGETED> {
10121038
let val = self.store.stack.values.pop::<i32>() as u64;
10131039
let mem = self.store.state.get_mem(self.module.resolve_mem_addr(mem_addr));
10141040
let Some(Ok(addr)) = offset.checked_add(val).map(TryInto::try_into) else {
1041+
cold_path();
10151042
return Err(Error::Trap(Trap::MemoryOutOfBounds { offset: val as usize, len: LOAD_SIZE, max: 0 }));
10161043
};
10171044
let val = mem.load_as::<LOAD_SIZE, LOAD>(addr)?.to_mem_bytes();
@@ -1038,11 +1065,8 @@ impl<'store, const BUDGETED: bool> Executor<'store, BUDGETED> {
10381065
self.store.stack.values.pop::<i32>() as u32 as u64
10391066
};
10401067

1041-
let Some(addr) = base.checked_add(offset) else {
1042-
return Err(Error::Trap(Trap::MemoryOutOfBounds { offset: base as usize, len: LOAD_SIZE, max: 0 }));
1043-
};
1044-
1045-
let Ok(addr) = usize::try_from(addr) else {
1068+
let Some(Ok(addr)) = base.checked_add(offset).map(usize::try_from) else {
1069+
cold_path();
10461070
return Err(Error::Trap(Trap::MemoryOutOfBounds { offset: base as usize, len: LOAD_SIZE, max: 0 }));
10471071
};
10481072

@@ -1068,10 +1092,8 @@ impl<'store, const BUDGETED: bool> Executor<'store, BUDGETED> {
10681092
false => self.store.stack.values.pop::<i32>() as u32 as u64,
10691093
};
10701094

1071-
let Some(effective_addr) = offset.checked_add(addr) else {
1072-
return Err(Error::Trap(Trap::MemoryOutOfBounds { offset: addr as usize, len: N, max: 0 }));
1073-
};
1074-
let Ok(effective_addr) = usize::try_from(effective_addr) else {
1095+
let Some(Ok(effective_addr)) = offset.checked_add(addr).map(usize::try_from) else {
1096+
cold_path();
10751097
return Err(Error::Trap(Trap::MemoryOutOfBounds { offset: addr as usize, len: N, max: 0 }));
10761098
};
10771099

@@ -1095,15 +1117,12 @@ impl<'store, const BUDGETED: bool> Executor<'store, BUDGETED> {
10951117
false => u64::from(self.store.stack.values.pop::<i32>() as u32),
10961118
};
10971119

1098-
let Some(effective_addr) = offset.checked_add(addr) else {
1099-
return Err(Error::Trap(Trap::MemoryOutOfBounds { offset: addr as usize, len: N, max: 0 }));
1100-
};
1101-
let Ok(effective_addr) = usize::try_from(effective_addr) else {
1120+
let Some(Ok(effective_addr)) = offset.checked_add(addr).map(usize::try_from) else {
1121+
cold_path();
11021122
return Err(Error::Trap(Trap::MemoryOutOfBounds { offset: addr as usize, len: N, max: 0 }));
11031123
};
11041124

11051125
mem.store(effective_addr, val.len(), &val)?;
1106-
11071126
Ok(())
11081127
}
11091128

@@ -1148,6 +1167,7 @@ impl<'store, const BUDGETED: bool> Executor<'store, BUDGETED> {
11481167
let table_len = table.size();
11491168

11501169
if size < 0 || ((size + offset) as usize > elem_len) || ((dst + size) > table_len) {
1170+
cold_path();
11511171
return Err(Trap::TableOutOfBounds { offset: offset as usize, len: size as usize, max: elem_len }.into());
11521172
}
11531173

@@ -1156,10 +1176,12 @@ impl<'store, const BUDGETED: bool> Executor<'store, BUDGETED> {
11561176
}
11571177

11581178
if let ElementKind::Active { .. } = elem.kind {
1179+
cold_path();
11591180
return Err(Error::Other("table.init with active element".to_string()));
11601181
}
11611182

11621183
let Some(items) = elem.items.as_ref() else {
1184+
cold_path();
11631185
return Err(Trap::TableOutOfBounds { offset: 0, len: 0, max: 0 }.into());
11641186
};
11651187

@@ -1187,6 +1209,7 @@ impl<'store, const BUDGETED: bool> Executor<'store, BUDGETED> {
11871209
let i = self.store.stack.values.pop::<i32>();
11881210

11891211
if i + n > table.size() {
1212+
cold_path();
11901213
return Err(Error::Trap(Trap::TableOutOfBounds {
11911214
offset: i as usize,
11921215
len: n as usize,
@@ -1205,10 +1228,13 @@ impl<'store, const BUDGETED: bool> Executor<'store, BUDGETED> {
12051228
impl<'store> Executor<'store, false> {
12061229
#[inline(always)]
12071230
pub(crate) fn run_to_completion(&mut self) -> Result<()> {
1208-
if self.exec::<{ usize::MAX }>()?.is_some() {
1209-
return Ok(());
1231+
// direct loop+match has worse codegen than a fixed for loop here for some reason (like ~5% worse)
1232+
// ideally we use `loop_match` / `become` once thats stabilized
1233+
loop {
1234+
if self.exec::<1024>()?.is_some() {
1235+
return Ok(());
1236+
}
12101237
}
1211-
unreachable!();
12121238
}
12131239

12141240
#[cfg(feature = "std")]
@@ -1221,7 +1247,7 @@ impl<'store> Executor<'store, false> {
12211247
}
12221248

12231249
loop {
1224-
if self.exec::<TIME_BUDGET_CHECK_INTERVAL>()?.is_some() {
1250+
if self.exec::<1024>()?.is_some() {
12251251
return Ok(ExecState::Completed);
12261252
}
12271253

@@ -1241,11 +1267,11 @@ impl<'store> Executor<'store, true> {
12411267
}
12421268

12431269
loop {
1244-
if self.exec::<FUEL_ACCOUNTING_INTERVAL>()?.is_some() {
1270+
if self.exec::<1024>()?.is_some() {
12451271
return Ok(ExecState::Completed);
12461272
}
12471273

1248-
self.store.execution_fuel = self.store.execution_fuel.saturating_sub(FUEL_ACCOUNTING_INTERVAL as u32);
1274+
self.store.execution_fuel = self.store.execution_fuel.saturating_sub(1024_u32);
12491275
if self.store.execution_fuel == 0 {
12501276
return Ok(ExecState::Suspended(self.cf));
12511277
}

crates/tinywasm/src/interpreter/simd/macros.rs

Lines changed: 13 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,12 @@
11
#![allow(unused_macros)]
22

33
macro_rules! simd_impl {
4-
($(wasm => $wasm:block)? $(x86 => $x86:block)? generic => $generic:block) => {{
5-
#[cfg(any(target_arch = "wasm32", target_arch = "wasm64"))]
6-
{
7-
simd_impl!(@pick_wasm $( $wasm )? ; $generic)
8-
}
9-
10-
#[cfg(all(
11-
not(any(target_arch = "wasm32", target_arch = "wasm64")),
12-
feature = "simd-x86",
13-
target_arch = "x86_64",
14-
target_feature = "sse4.2",
15-
target_feature = "avx",
16-
target_feature = "avx2",
17-
target_feature = "bmi1",
18-
target_feature = "bmi2",
19-
target_feature = "fma",
20-
target_feature = "lzcnt",
21-
target_feature = "movbe",
22-
target_feature = "popcnt"
23-
))]
24-
{
25-
simd_impl!(@pick_x86 $( $x86 )? ; $generic)
26-
}
4+
($(wasm => $wasm:block)? $(x86 => $x86:block)? generic => $generic:block) => {
5+
cfg_select! {
6+
any(target_arch = "wasm32", target_arch = "wasm64") => {
7+
simd_impl!(@pick_wasm $( $wasm )? ; $generic)
8+
},
279

28-
#[allow(unreachable_code)]
29-
#[cfg(not(any(
30-
any(target_arch = "wasm32", target_arch = "wasm64"),
3110
all(
3211
feature = "simd-x86",
3312
target_arch = "x86_64",
@@ -40,12 +19,15 @@ macro_rules! simd_impl {
4019
target_feature = "lzcnt",
4120
target_feature = "movbe",
4221
target_feature = "popcnt"
43-
)
44-
)))]
45-
{
46-
$generic
22+
) => {
23+
simd_impl!(@pick_x86 $( $x86 )? ; $generic)
24+
},
25+
26+
_ => {
27+
$generic
28+
}
4729
}
48-
}};
30+
};
4931

5032
(@pick_wasm $wasm:block ; $generic:block) => {
5133
$wasm

0 commit comments

Comments
 (0)