Skip to content

Commit 4a0df27

Browse files
return to compact side table, reduce runtime signature decoding, and misc optimizations
1 parent 32ef457 commit 4a0df27

4 files changed

Lines changed: 109 additions & 50 deletions

File tree

src/instance.rs

Lines changed: 31 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use crate::signature::{Signature, ValType};
55
use crate::wasm_memory::WasmMemory;
66
use crate::Module;
77
use paste::paste;
8-
use std::cell::RefCell;
8+
use std::cell::{RefCell, Cell};
99
use std::collections::HashMap;
1010
use std::rc::{Rc, Weak};
1111

@@ -244,7 +244,7 @@ impl WasmTable {
244244
pub struct WasmGlobal {
245245
pub ty: ValType,
246246
pub mutable: bool,
247-
pub value: WasmValue,
247+
pub value: Cell<WasmValue>,
248248
}
249249

250250
// --------------- Imports/Exports and Functions ---------------
@@ -419,7 +419,7 @@ impl Instance {
419419
// evaluate constant initializer
420420
let mut cpc = g.initializer_offset;
421421
let val = Instance::eval_const(&module, &mut cpc, &inst.globals)?;
422-
inst.globals.push(Rc::new(RefCell::new(WasmGlobal { ty: g.ty, mutable: g.is_mutable, value: val })));
422+
inst.globals.push(Rc::new(RefCell::new(WasmGlobal { ty: g.ty, mutable: g.is_mutable, value: Cell::new(val) })));
423423
}
424424
}
425425

@@ -578,7 +578,7 @@ impl Instance {
578578
0x42 => { let v: i64 = read_sleb128(bytes, pc)?; stack.push(WasmValue::from_i64(v)); }
579579
0x43 => { let bits = u32::from_le_bytes(bytes[*pc..*pc+4].try_into().unwrap()); *pc += 4; stack.push(WasmValue::from_f32_bits(bits)); }
580580
0x44 => { let bits = u64::from_le_bytes(bytes[*pc..*pc+8].try_into().unwrap()); *pc += 8; stack.push(WasmValue::from_f64_bits(bits)); }
581-
0x23 => { let gi: u32 = read_leb128(bytes, pc)?; let g = gi as usize; if g >= globals.len() { return Err(Error::Validation(UNKNOWN_GLOBAL)); } stack.push(globals[g].borrow().value); }
581+
0x23 => { let gi: u32 = read_leb128(bytes, pc)?; let g = gi as usize; if g >= globals.len() { return Err(Error::Validation(UNKNOWN_GLOBAL)); } stack.push(globals[g].borrow().value.get()); }
582582
0x6a => { let b = stack.pop().unwrap().as_u32(); let a = stack.pop().unwrap().as_u32(); stack.push(WasmValue::from_u32(a.wrapping_add(b))); }
583583
0x6b => { let b = stack.pop().unwrap().as_u32(); let a = stack.pop().unwrap().as_u32(); stack.push(WasmValue::from_u32(a.wrapping_sub(b))); }
584584
0x6c => { let b = stack.pop().unwrap().as_u32(); let a = stack.pop().unwrap().as_u32(); stack.push(WasmValue::from_u32(a.wrapping_mul(b))); }
@@ -685,6 +685,7 @@ impl Instance {
685685
ctrl_bases: &mut Vec<usize>
686686
) -> Result<(), Error> {
687687
let bytes = &self.module.bytes;
688+
let mem_opt = self.memory.as_ref();
688689

689690
macro_rules! next_op { () => {{ let byte = bytes[pc]; pc += 1; byte }} }
690691
macro_rules! pop_val { () => {{
@@ -891,7 +892,7 @@ impl Instance {
891892
let _align: u32 = read_leb128(bytes, &mut pc)?;
892893
let offset: u32 = read_leb128(bytes, &mut pc)?;
893894
let addr = pop_val!().as_u32();
894-
let mem = self.memory.as_ref().ok_or_else(|| Error::Validation(UNKNOWN_MEMORY))?;
895+
let mem = mem_opt.ok_or_else(|| Error::Validation(UNKNOWN_MEMORY))?;
895896
let v = mem.borrow().$method(addr, offset).map_err(|e| Error::Trap(e))?;
896897
let val = ($push)(v);
897898
stack.push(val);
@@ -902,7 +903,7 @@ impl Instance {
902903
let raw = pop_val!();
903904
let addr = pop_val!().as_u32();
904905
let val = ($from)(raw);
905-
let mem = self.memory.as_ref().ok_or_else(|| Error::Validation(UNKNOWN_MEMORY))?;
906+
let mem = mem_opt.ok_or_else(|| Error::Validation(UNKNOWN_MEMORY))?;
906907
mem.borrow_mut().$method(addr, offset, val).map_err(|e| Error::Trap(e))?;
907908
}}}
908909

@@ -912,36 +913,36 @@ impl Instance {
912913
0x00 => return Err(Error::Trap(UNREACHABLE)),
913914
0x01 | 0xbc | 0xbd | 0xbe | 0xbf => {} // nop and reinterprets (no-op on raw bits)
914915
0x02 => { // block
915-
let sig = Signature::read(&self.module.types, bytes, &mut pc)?;
916-
let block_end = *self.module.block_ends.get(&pc).unwrap();
916+
let entry = *self.module.side_table.get(&(pc as u32)).unwrap();
917+
pc = entry.body_pc as usize;
917918
control.push(ControlFrame {
918-
stack_len: stack.len() - sig.params.len(),
919-
dest_pc: block_end,
920-
arity: sig.result.is_some() as u32,
921-
has_result: sig.result.is_some()
919+
stack_len: stack.len() - (entry.params_len as usize),
920+
dest_pc: entry.end_pc as usize,
921+
arity: entry.has_result as u32,
922+
has_result: entry.has_result
922923
});
923924
}
924925
0x03 => { // loop
925926
let loop_op_pc = pc - 1;
926-
let sig = Signature::read(&self.module.types, bytes, &mut pc)?;
927+
let entry = *self.module.side_table.get(&(pc as u32)).unwrap();
928+
pc = entry.body_pc as usize;
927929
control.push(ControlFrame {
928-
stack_len: stack.len() - sig.params.len(),
930+
stack_len: stack.len() - (entry.params_len as usize),
929931
dest_pc: loop_op_pc,
930-
arity: sig.params.len() as u32,
931-
has_result: sig.result.is_some()
932+
arity: entry.params_len as u32,
933+
has_result: entry.has_result
932934
});
933935
}
934936
0x04 => { // if
935-
let sig = Signature::read(&self.module.types, bytes, &mut pc)?;
937+
let entry = *self.module.side_table.get(&(pc as u32)).unwrap();
936938
let cond = pop_val!().as_u32();
937-
let if_jump = self.module.if_jumps.get(&pc).unwrap();
938939
control.push(ControlFrame {
939-
stack_len: stack.len() - sig.params.len(),
940-
dest_pc: if_jump.end_offset,
941-
arity: sig.result.is_some() as u32,
942-
has_result: sig.result.is_some()
940+
stack_len: stack.len() - (entry.params_len as usize),
941+
dest_pc: entry.end_pc as usize,
942+
arity: entry.has_result as u32,
943+
has_result: entry.has_result
943944
});
944-
if cond == 0 { pc = if_jump.else_offset; }
945+
pc = if cond == 0 { entry.else_pc as usize } else { entry.body_pc as usize };
945946
}
946947
0x05 => { // else
947948
let _ = Instance::branch(&mut pc, stack, control, 0);
@@ -1009,6 +1010,9 @@ impl Instance {
10091010
}
10101011
// Call instructions
10111012
0x10 => { // call
1013+
// direct calls are fully type-checked at validation time; no
1014+
// structural type check is required here. we only use the
1015+
// runtime signature for fast param/result counts to set up frames.
10121016
let fi: u32 = read_leb128(bytes, &mut pc)?;
10131017
let f = &self.functions[fi as usize];
10141018

@@ -1047,6 +1051,8 @@ impl Instance {
10471051
}
10481052
}
10491053
0x11 => { // call_indirect
1054+
// Indirect calls must enforce params at runtime
1055+
// Here we must parse the indices
10501056
let type_idx: u32 = read_leb128(bytes, &mut pc)?;
10511057
pc += 1; // Skip the zero flag
10521058
let elem_idx = match stack.pop() {
@@ -1206,7 +1212,7 @@ impl Instance {
12061212
if gi as usize >= self.globals.len() {
12071213
return Err(Error::Trap(UNKNOWN_GLOBAL));
12081214
}
1209-
stack.push(self.globals[gi as usize].borrow().value);
1215+
stack.push(self.globals[gi as usize].borrow().value.get());
12101216
}
12111217
0x24 => { // global.set
12121218
let gi: u32 = read_leb128(bytes, &mut pc)?;
@@ -1217,7 +1223,7 @@ impl Instance {
12171223
Some(v) => v,
12181224
None => return Err(Error::Trap(STACK_UNDERFLOW))
12191225
};
1220-
self.globals[gi as usize].borrow_mut().value = val;
1226+
self.globals[gi as usize].borrow().value.set(val);
12211227
}
12221228
// Memory instructions - loads
12231229
0x28 => { load!(load_u32, |v: u32| WasmValue::from_u32(v)); }

src/module.rs

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use std::collections::HashMap;
2+
use nohash_hasher::BuildNoHashHasher;
23
use std::rc::Rc;
34

45
use crate::byte_iter::*;
@@ -67,8 +68,14 @@ pub struct Export { pub extern_type: ExternType, pub idx: u32 }
6768
#[derive(Clone)]
6869
pub struct DataSegment { pub data_range: std::ops::Range<usize>, pub initializer_offset: usize }
6970

70-
#[derive(Clone, Copy)]
71-
pub struct IfJump { pub else_offset: usize, pub end_offset: usize }
71+
#[derive(Clone, Copy, Default)]
72+
pub struct SideTableEntry {
73+
pub params_len: u16,
74+
pub has_result: bool,
75+
pub body_pc: u32, // start pc of block body (after the sig)
76+
pub end_pc: u32, // end pc of the construct
77+
pub else_pc: u32, // else start pc or end_pc if no else
78+
}
7279

7380
// ---------------- Module Structure ----------------
7481
#[derive(Default)]
@@ -86,8 +93,7 @@ pub struct Module {
8693
pub functions: Vec<Function>,
8794
pub n_data: u32,
8895
pub data_segments: Vec<DataSegment>,
89-
pub if_jumps: HashMap<usize, IfJump>,
90-
pub block_ends: HashMap<usize, usize>,
96+
pub side_table: HashMap<u32, SideTableEntry, BuildNoHashHasher<u32>>,
9197
}
9298

9399
impl Module {

src/validator.rs

Lines changed: 62 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ pub struct ControlFrame {
2424
pub height: usize,
2525
pub unreachable: bool,
2626
pub control_type: ControlType,
27+
pub sig_pc: usize,
2728
}
2829

2930
// ---------------- ValidatorStack for Type Checking ----------------
@@ -78,12 +79,13 @@ impl ValidatorStack {
7879
Ok(popped)
7980
}
8081

81-
pub fn push_ctrl(&mut self, sig: Signature, control_type: ControlType) -> Result<(), Error> {
82+
pub fn push_ctrl(&mut self, sig: Signature, control_type: ControlType, sig_pc: usize) -> Result<(), Error> {
8283
let frame = ControlFrame {
8384
sig: sig.clone(),
8485
height: self.val_stack.len(),
8586
unreachable: false,
8687
control_type,
88+
sig_pc,
8789
};
8890
self.ctrl_stack.push(frame);
8991
self.push_vals(&sig.params);
@@ -188,7 +190,8 @@ impl<'a> Validator<'a> {
188190
sig: func.ty.clone(),
189191
height: func.ty.params.len(), // Stack height after params
190192
unreachable: false,
191-
control_type: ControlType::Function
193+
control_type: ControlType::Function,
194+
sig_pc: func.body.start.saturating_sub(1),
192195
});
193196

194197
// Validation loop
@@ -229,27 +232,58 @@ fn validate_nop(_: &mut Module, _: &mut ByteIter, _: &Function, _: &mut Validato
229232
Ok(Action::Continue)
230233
}
231234

232-
fn validate_block(m: &mut Module, it: &mut ByteIter, _: &Function, vs: &mut ValidatorStack) -> Result<Action, Error> {
235+
fn validate_block(m: &mut Module, it: &mut ByteIter, _: &Function, vs: &mut ValidatorStack) -> Result<Action, Error> {
236+
let sig_pc = it.cur();
233237
let sig = Signature::read(&m.types, &m.bytes, &mut it.idx)?;
234238
let block_start = it.cur();
235239
vs.pop_vals(&sig.params)?;
236-
vs.push_ctrl(sig, ControlType::Block { start: block_start })?;
240+
let params_len = sig.params.len() as u16;
241+
let has_result = sig.result.is_some();
242+
vs.push_ctrl(sig, ControlType::Block { start: block_start }, sig_pc)?;
243+
m.side_table.entry(sig_pc as u32).or_insert(SideTableEntry {
244+
params_len,
245+
has_result,
246+
body_pc: block_start as u32,
247+
end_pc: 0,
248+
else_pc: 0,
249+
});
237250
Ok(Action::Continue)
238251
}
239252

240-
fn validate_loop(m: &mut Module, it: &mut ByteIter, _: &Function, vs: &mut ValidatorStack) -> Result<Action, Error> {
253+
fn validate_loop(m: &mut Module, it: &mut ByteIter, _: &Function, vs: &mut ValidatorStack) -> Result<Action, Error> {
254+
let sig_pc = it.cur();
241255
let sig = Signature::read(&m.types, &m.bytes, &mut it.idx)?;
256+
let loop_body_pc = it.cur(); // body starts here
242257
vs.pop_vals(&sig.params)?;
243-
vs.push_ctrl(sig, ControlType::Loop)?;
258+
let params_len = sig.params.len() as u16;
259+
let has_result = sig.result.is_some();
260+
vs.push_ctrl(sig, ControlType::Loop, sig_pc)?;
261+
m.side_table.entry(sig_pc as u32).or_insert(SideTableEntry {
262+
params_len,
263+
has_result,
264+
body_pc: loop_body_pc as u32,
265+
end_pc: 0,
266+
else_pc: loop_body_pc as u32, // for loop back-edge
267+
});
244268
Ok(Action::Continue)
245269
}
246270

247-
fn validate_if(m: &mut Module, it: &mut ByteIter, _: &Function, vs: &mut ValidatorStack) -> Result<Action, Error> {
271+
fn validate_if(m: &mut Module, it: &mut ByteIter, _: &Function, vs: &mut ValidatorStack) -> Result<Action, Error> {
272+
let sig_pc = it.cur();
248273
let sig = Signature::read(&m.types, &m.bytes, &mut it.idx)?;
249274
vs.pop_val_expect(ValType::I32)?;
250275
vs.pop_vals(&sig.params)?;
251-
let if_start = it.cur();
252-
vs.push_ctrl(sig, ControlType::If { start: if_start })?;
276+
let if_body_pc = it.cur();
277+
let params_len = sig.params.len() as u16;
278+
let has_result = sig.result.is_some();
279+
vs.push_ctrl(sig, ControlType::If { start: if_body_pc }, sig_pc)?;
280+
m.side_table.entry(sig_pc as u32).or_insert(SideTableEntry {
281+
params_len,
282+
has_result,
283+
body_pc: if_body_pc as u32,
284+
end_pc: 0,
285+
else_pc: 0, // filled at else/end
286+
});
253287
Ok(Action::Continue)
254288
}
255289

@@ -288,6 +322,7 @@ fn validate_else(_: &mut Module, it: &mut ByteIter, _: &Function, vs: &mut Valid
288322
height: frame.height,
289323
unreachable: false,
290324
control_type: new_control_type,
325+
sig_pc: frame.sig_pc,
291326
});
292327
vs.push_vals(&params);
293328
Ok(Action::Continue)
@@ -317,22 +352,34 @@ fn validate_end(m: &mut Module, it: &mut ByteIter, f: &Function, vs: &mut Valida
317352

318353
// Handle jump offset tracking
319354
match frame.control_type {
320-
ControlType::Block { start } => {
321-
m.block_ends.insert(start, it.cur());
355+
ControlType::Block { .. } => {
356+
let k = frame.sig_pc as u32;
357+
let mut updated = m.side_table.get(&k).copied().unwrap_or_default();
358+
updated.end_pc = it.cur() as u32;
359+
updated.else_pc = updated.end_pc;
360+
m.side_table.insert(k, updated);
322361
}
323362
ControlType::Loop => {}
324-
ControlType::If { start } => {
363+
ControlType::If { .. } => {
325364
// For if without else, params must equal results
326365
let results_as_vec: Vec<ValType> = frame.sig.result.into_iter().collect();
327366
if frame.sig.params != results_as_vec {
328367
return Err(Validation(TYPE_MISMATCH));
329368
}
330369
let else_off = it.cur() - 1;
331370
let end_off = it.cur();
332-
m.if_jumps.insert(start, IfJump { else_offset: else_off, end_offset: end_off });
371+
let k = frame.sig_pc as u32;
372+
let mut updated = m.side_table.get(&k).copied().unwrap_or_default();
373+
updated.end_pc = end_off as u32;
374+
updated.else_pc = else_off as u32;
375+
m.side_table.insert(k, updated);
333376
}
334-
ControlType::IfElse { if_start, else_start } => {
335-
m.if_jumps.insert(if_start, IfJump { else_offset: else_start, end_offset: it.cur() });
377+
ControlType::IfElse { else_start, .. } => {
378+
let k = frame.sig_pc as u32;
379+
let mut updated = m.side_table.get(&k).copied().unwrap_or_default();
380+
updated.end_pc = it.cur() as u32;
381+
updated.else_pc = else_start as u32;
382+
m.side_table.insert(k, updated);
336383
}
337384
ControlType::Function => {}
338385
}

tests/spec_tests.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use std::{collections::HashMap, fs, path::Path, process::Command, rc::{Rc}, cell::RefCell, env};
1+
use std::{collections::HashMap, fs, path::Path, process::Command, rc::{Rc}, cell::{RefCell, Cell}, env};
22
use serde::Deserialize;
33
use wagmi::{Module, Instance, Imports, ExportValue, WasmValue, WasmGlobal, WasmTable, WasmMemory, RuntimeFunction, RuntimeSignature, ValType, Error, Signature};
44

@@ -107,16 +107,16 @@ fn spectest_exports() -> HashMap<String, ExportValue> {
107107
let mut exports = HashMap::new();
108108

109109
exports.insert("global_i32".into(), ExportValue::Global(Rc::new(RefCell::new(
110-
WasmGlobal { ty: ValType::I32, mutable: false, value: WasmValue::from_u32(666) }
110+
WasmGlobal { ty: ValType::I32, mutable: false, value: Cell::new(WasmValue::from_u32(666)) }
111111
))));
112112
exports.insert("global_i64".into(), ExportValue::Global(Rc::new(RefCell::new(
113-
WasmGlobal { ty: ValType::I64, mutable: false, value: WasmValue::from_u64(666) }
113+
WasmGlobal { ty: ValType::I64, mutable: false, value: Cell::new(WasmValue::from_u64(666)) }
114114
))));
115115
exports.insert("global_f32".into(), ExportValue::Global(Rc::new(RefCell::new(
116-
WasmGlobal { ty: ValType::F32, mutable: false, value: WasmValue::from_f32(666.6) }
116+
WasmGlobal { ty: ValType::F32, mutable: false, value: Cell::new(WasmValue::from_f32(666.6)) }
117117
))));
118118
exports.insert("global_f64".into(), ExportValue::Global(Rc::new(RefCell::new(
119-
WasmGlobal { ty: ValType::F64, mutable: false, value: WasmValue::from_f64(666.6) }
119+
WasmGlobal { ty: ValType::F64, mutable: false, value: Cell::new(WasmValue::from_f64(666.6)) }
120120
))));
121121

122122
exports.insert("table".into(), ExportValue::Table(Rc::new(RefCell::new(
@@ -157,7 +157,7 @@ fn exec_action(instances: &HashMap<String, Rc<Instance>>, action: &Act) -> Resul
157157

158158
match action {
159159
Act::Get { .. } => match export {
160-
ExportValue::Global(g) => Ok(vec![g.borrow().value]),
160+
ExportValue::Global(g) => Ok(vec![g.borrow().value.get()]),
161161
_ => Err(Error::Trap("not a global"))
162162
},
163163
Act::Invoke { .. } => match export {

0 commit comments

Comments
 (0)