Skip to content

Commit 2da2020

Browse files
feat: shared tables across modules
Signed-off-by: Henry Gressmann <[email protected]>
1 parent baf9942 commit 2da2020

File tree

8 files changed

+65
-44
lines changed

8 files changed

+65
-44
lines changed

ARCHITECTURE.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
# TinyWasm's Architecture

Cargo.lock

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

crates/parser/src/conversion.rs

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -184,11 +184,6 @@ pub(crate) fn convert_blocktype(blocktype: wasmparser::BlockType) -> BlockArgs {
184184
use wasmparser::BlockType::*;
185185
match blocktype {
186186
Empty => BlockArgs::Empty,
187-
188-
// We should maybe have all this in a single variant for our custom bytecode
189-
190-
// TODO: maybe solve this differently so we can support 128-bit values
191-
// without having to increase the size of the WasmValue enum
192187
Type(ty) => BlockArgs::Type(convert_valtype(&ty)),
193188
FuncType(ty) => BlockArgs::FuncType(ty),
194189
}
@@ -228,8 +223,8 @@ pub fn process_const_operator(op: wasmparser::Operator) -> Result<ConstInstructi
228223
wasmparser::Operator::RefFunc { function_index } => Ok(ConstInstruction::RefFunc(function_index)),
229224
wasmparser::Operator::I32Const { value } => Ok(ConstInstruction::I32Const(value)),
230225
wasmparser::Operator::I64Const { value } => Ok(ConstInstruction::I64Const(value)),
231-
wasmparser::Operator::F32Const { value } => Ok(ConstInstruction::F32Const(f32::from_bits(value.bits()))), // TODO: check if this is correct
232-
wasmparser::Operator::F64Const { value } => Ok(ConstInstruction::F64Const(f64::from_bits(value.bits()))), // TODO: check if this is correct
226+
wasmparser::Operator::F32Const { value } => Ok(ConstInstruction::F32Const(f32::from_bits(value.bits()))),
227+
wasmparser::Operator::F64Const { value } => Ok(ConstInstruction::F64Const(f64::from_bits(value.bits()))),
233228
wasmparser::Operator::GlobalGet { global_index } => Ok(ConstInstruction::GlobalGet(global_index)),
234229
op => Err(crate::ParseError::UnsupportedOperator(format!("Unsupported const instruction: {:?}", op))),
235230
}

crates/tinywasm/src/instance.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ impl ModuleInstance {
5757
// don't need to create a auxiliary frame etc.
5858

5959
let idx = store.next_module_instance_idx();
60+
log::error!("Instantiating module at index {}", idx);
6061
let imports = imports.unwrap_or_default();
6162

6263
let mut addrs = imports.link(store, &module, idx)?;
@@ -68,13 +69,12 @@ impl ModuleInstance {
6869
addrs.tables.extend(store.init_tables(data.table_types.into(), idx)?);
6970
addrs.memories.extend(store.init_memories(data.memory_types.into(), idx)?);
7071

71-
let elem_addrs = store.init_elements(&addrs.tables, data.elements.into(), idx)?;
72+
let elem_addrs = store.init_elements(&addrs.tables, &addrs.funcs, data.elements.into(), idx)?;
7273
let data_addrs = store.init_datas(&addrs.memories, data.data.into(), idx)?;
7374

7475
let instance = ModuleInstanceInner {
7576
store_id: store.id(),
7677
idx,
77-
7878
types: data.func_types,
7979
func_addrs: addrs.funcs.into_boxed_slice(),
8080
table_addrs: addrs.tables.into_boxed_slice(),

crates/tinywasm/src/runtime/executor/mod.rs

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use crate::{
77
CallFrame, Error, FuncContext, LabelArgs, ModuleInstance, Result, Store, Trap,
88
};
99
use alloc::{string::ToString, vec::Vec};
10-
use tinywasm_types::{ElementKind, Instruction};
10+
use tinywasm_types::{ElementKind, Instruction, ValType};
1111

1212
mod macros;
1313
mod traits;
@@ -171,15 +171,17 @@ fn exec_one(
171171
let call_ty = module.func_ty(*type_addr);
172172

173173
let func_idx = stack.values.pop_t::<u32>()?;
174-
let actual_func_addr = table
174+
175+
// verify that the table is of the right type, this should be validated by the parser already
176+
assert!(table.borrow().kind.element_type == ValType::RefFunc, "table is not of type funcref");
177+
178+
let func_ref = table
175179
.borrow()
176180
.get(func_idx as usize)?
181+
.addr()
177182
.ok_or_else(|| Trap::UninitializedElement { index: func_idx as usize })?;
178183

179-
let resolved_func_addr = module.resolve_func_addr(actual_func_addr);
180-
181-
// prepare the call frame
182-
let func_inst = store.get_func(resolved_func_addr as usize)?;
184+
let func_inst = store.get_func(func_ref as usize)?;
183185
let func_ty = func_inst.func.ty();
184186

185187
let func = match &func_inst.func {
@@ -200,8 +202,7 @@ fn exec_one(
200202
}
201203

202204
let params = stack.values.pop_n_rev(func_ty.params.len())?;
203-
let call_frame =
204-
CallFrame::new_raw(resolved_func_addr as usize, &params, func.locals.to_vec(), func_inst._owner);
205+
let call_frame = CallFrame::new_raw(func_ref as usize, &params, func.locals.to_vec(), func_inst._owner);
205206

206207
// push the call frame
207208
cf.instr_ptr += 1; // skip the call instruction
@@ -606,7 +607,7 @@ fn exec_one(
606607
return Err(Trap::TableOutOfBounds { offset: 0, len: 0, max: 0 }.into());
607608
};
608609

609-
table.borrow_mut().init(0, items)?;
610+
table.borrow_mut().init(module.func_addrs(), 0, items)?;
610611
}
611612

612613
I32TruncSatF32S => arithmetic_single!(trunc, f32, i32, stack),

crates/tinywasm/src/store.rs

Lines changed: 46 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,6 @@ impl Default for Store {
8282
///
8383
/// Data should only be addressable by the module that owns it
8484
/// See <https://webassembly.github.io/spec/core/exec/runtime.html#store>
85-
// TODO: Arena allocate these?
8685
pub(crate) struct StoreData {
8786
pub(crate) funcs: Vec<Rc<FunctionInstance>>,
8887
pub(crate) tables: Vec<Rc<RefCell<TableInstance>>>,
@@ -178,6 +177,7 @@ impl Store {
178177
pub(crate) fn init_elements(
179178
&mut self,
180179
table_addrs: &[TableAddr],
180+
func_addrs: &[FuncAddr],
181181
elements: Vec<Element>,
182182
idx: ModuleInstanceAddr,
183183
) -> Result<Box<[Addr]>> {
@@ -194,6 +194,8 @@ impl Store {
194194
})
195195
.collect::<Result<Vec<_>>>()?;
196196

197+
log::error!("element kind: {:?}", element.kind);
198+
197199
let items = match element.kind {
198200
// doesn't need to be initialized, can be initialized lazily using the `table.init` instruction
199201
ElementKind::Passive => Some(init),
@@ -218,7 +220,7 @@ impl Store {
218220
// d. Execute the instruction i32.const n
219221
// e. Execute the instruction table.init tableidx i
220222
if let Some(table) = self.data.tables.get_mut(table_addr as usize) {
221-
table.borrow_mut().init(offset, &init)?;
223+
table.borrow_mut().init(func_addrs, offset, &init)?;
222224
} else {
223225
log::error!("table {} not found", table);
224226
}
@@ -385,8 +387,6 @@ impl Store {
385387
/// See <https://webassembly.github.io/spec/core/exec/runtime.html#function-instances>
386388
pub struct FunctionInstance {
387389
pub(crate) func: Function,
388-
389-
// TODO: this is important for call_indirect
390390
pub(crate) _type_idx: TypeAddr,
391391
pub(crate) _owner: ModuleInstanceAddr, // index into store.module_instances, none for host functions
392392
}
@@ -408,52 +408,77 @@ impl FunctionInstance {
408408
}
409409
}
410410

411+
#[derive(Debug, Clone, Copy)]
412+
pub(crate) enum TableElement {
413+
Uninitialized,
414+
Initialized(Addr),
415+
}
416+
417+
impl TableElement {
418+
pub(crate) fn addr(&self) -> Option<Addr> {
419+
match self {
420+
TableElement::Uninitialized => None,
421+
TableElement::Initialized(addr) => Some(*addr),
422+
}
423+
}
424+
}
425+
411426
/// A WebAssembly Table Instance
412427
///
413428
/// See <https://webassembly.github.io/spec/core/exec/runtime.html#table-instances>
414429
#[derive(Debug)]
415430
pub(crate) struct TableInstance {
416-
pub(crate) elements: Vec<Option<Addr>>,
431+
pub(crate) elements: Vec<TableElement>,
417432
pub(crate) kind: TableType,
418433
pub(crate) _owner: ModuleInstanceAddr, // index into store.module_instances
419434
}
420435

421436
impl TableInstance {
422437
pub(crate) fn new(kind: TableType, owner: ModuleInstanceAddr) -> Self {
423-
Self { elements: vec![None; kind.size_initial as usize], kind, _owner: owner }
438+
Self { elements: vec![TableElement::Uninitialized; kind.size_initial as usize], kind, _owner: owner }
424439
}
425440

426441
pub(crate) fn get_wasm_val(&self, addr: usize) -> Result<WasmValue> {
442+
let val = self.get(addr)?.addr();
443+
427444
Ok(match self.kind.element_type {
428-
ValType::RefFunc => {
429-
self.get(addr)?.map(|v| WasmValue::RefFunc(v)).unwrap_or(WasmValue::RefNull(ValType::RefFunc))
430-
}
445+
ValType::RefFunc => val.map(|v| WasmValue::RefFunc(v)).unwrap_or(WasmValue::RefNull(ValType::RefFunc)),
431446
ValType::RefExtern => {
432-
self.get(addr)?.map(|v| WasmValue::RefExtern(v)).unwrap_or(WasmValue::RefNull(ValType::RefExtern))
447+
val.map(|v| WasmValue::RefExtern(v)).unwrap_or(WasmValue::RefNull(ValType::RefExtern))
433448
}
434449
_ => unimplemented!("unsupported table type: {:?}", self.kind.element_type),
435450
})
436451
}
437452

438-
pub(crate) fn get(&self, addr: usize) -> Result<Option<Addr>> {
439-
self.elements.get(addr).copied().ok_or_else(|| Error::Trap(Trap::UndefinedElement { index: addr }))
453+
pub(crate) fn get(&self, addr: usize) -> Result<&TableElement> {
454+
self.elements.get(addr).ok_or_else(|| Error::Trap(Trap::UndefinedElement { index: addr }))
440455
}
441456

442-
pub(crate) fn set(&mut self, addr: usize, value: Addr) -> Result<()> {
443-
if addr >= self.elements.len() {
444-
return Err(Error::Other(format!("table element {} not found", addr)));
445-
}
446-
447-
self.elements[addr] = Some(value);
457+
pub(crate) fn set(&mut self, table_idx: usize, value: Addr) -> Result<()> {
458+
let el = self
459+
.elements
460+
.get_mut(table_idx)
461+
.ok_or_else(|| Error::Other(format!("table element {} not found", table_idx)))?;
462+
*el = TableElement::Initialized(value);
448463
Ok(())
449464
}
450465

451466
pub(crate) fn size(&self) -> i32 {
452467
self.elements.len() as i32
453468
}
454469

455-
pub(crate) fn init(&mut self, offset: i32, init: &[Addr]) -> Result<()> {
456-
let init = init.iter().map(|item| Some(*item)).collect::<Vec<_>>();
470+
pub(crate) fn init(&mut self, func_addrs: &[u32], offset: i32, init: &[Addr]) -> Result<()> {
471+
let init = init
472+
.iter()
473+
.map(|item| {
474+
TableElement::Initialized(match self.kind.element_type == ValType::RefFunc {
475+
true => *func_addrs.get(*item as usize).expect(
476+
"error initializing table: function not found. This should have been caught by the validator",
477+
),
478+
false => *item,
479+
})
480+
})
481+
.collect::<Vec<_>>();
457482

458483
let offset = offset as usize;
459484
let end = offset.checked_add(init.len()).ok_or_else(|| {
@@ -465,6 +490,7 @@ impl TableInstance {
465490
}
466491

467492
self.elements[offset..end].copy_from_slice(&init);
493+
log::debug!("table: {:?}", self.elements);
468494
Ok(())
469495
}
470496
}

0 commit comments

Comments
 (0)