Skip to content

Commit de6b9f1

Browse files
committed
Implement tables and call_indirect
1 parent d84012f commit de6b9f1

File tree

5 files changed

+131
-17
lines changed

5 files changed

+131
-17
lines changed

filetests/call_indirect.wat

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
(module
2+
(type $indirect_sig (func (param i64) (result i64)))
3+
4+
(func $assert (param i32)
5+
(block $ok
6+
(br_if $ok
7+
(get_local 0)
8+
)
9+
(unreachable)
10+
)
11+
)
12+
13+
(func $plus_1 (param i64) (result i64)
14+
get_local 0
15+
i64.const 1
16+
i64.add
17+
)
18+
(func $minus_1 (param i64) (result i64)
19+
get_local 0
20+
i64.const 1
21+
i64.sub
22+
)
23+
24+
(func $main
25+
(call $call_indirect
26+
(i32.const 0)
27+
(i64.const 2)
28+
)
29+
(call $call_indirect
30+
(i32.const 1)
31+
(i64.const 0)
32+
)
33+
)
34+
35+
(func $call_indirect (param $func i32) (param $expected i64)
36+
(call $assert
37+
(i64.eq
38+
(call_indirect (type $indirect_sig)
39+
(i64.const 1)
40+
(get_local $func)
41+
)
42+
(get_local $expected)
43+
)
44+
)
45+
)
46+
(start $main)
47+
48+
(table 2 2 anyfunc)
49+
(elem (i32.const 0) $plus_1 $minus_1)
50+
)

lib/environ/src/environ.rs

Lines changed: 42 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use cranelift_codegen::cursor::FuncCursor;
22
use cranelift_codegen::ir;
3-
use cranelift_codegen::ir::immediates::Offset32;
3+
use cranelift_codegen::ir::immediates::{Imm64, Offset32};
44
use cranelift_codegen::ir::types::*;
55
use cranelift_codegen::ir::{
66
AbiParam, ArgumentPurpose, ExtFuncData, ExternalName, FuncRef, Function, InstBuilder, Signature,
@@ -312,8 +312,29 @@ impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'m
312312
})
313313
}
314314

315-
fn make_table(&mut self, _func: &mut ir::Function, _index: TableIndex) -> ir::Table {
316-
unimplemented!("make_table");
315+
fn make_table(&mut self, func: &mut ir::Function, _index: TableIndex) -> ir::Table {
316+
let pointer_bytes = self.pointer_bytes();
317+
let base_gv_addr = func.create_global_value(ir::GlobalValueData::VMContext {
318+
offset: Offset32::new(pointer_bytes as i32 * 2),
319+
});
320+
let base_gv = func.create_global_value(ir::GlobalValueData::Deref {
321+
base: base_gv_addr,
322+
offset: 0.into(),
323+
});
324+
let bound_gv_addr = func.create_global_value(ir::GlobalValueData::VMContext {
325+
offset: Offset32::new(pointer_bytes as i32 * 3),
326+
});
327+
let bound_gv = func.create_global_value(ir::GlobalValueData::Deref {
328+
base: bound_gv_addr,
329+
offset: 0.into(),
330+
});
331+
332+
func.create_table(ir::TableData {
333+
base_gv,
334+
min_size: Imm64::new(0),
335+
bound_gv,
336+
element_size: Imm64::new(i64::from(self.pointer_bytes() as i64)),
337+
})
317338
}
318339

319340
fn make_indirect_sig(&mut self, func: &mut ir::Function, index: SignatureIndex) -> ir::SigRef {
@@ -338,17 +359,31 @@ impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'m
338359
&mut self,
339360
mut pos: FuncCursor,
340361
table_index: TableIndex,
341-
_table: ir::Table,
362+
table: ir::Table,
342363
_sig_index: SignatureIndex,
343364
sig_ref: ir::SigRef,
344365
callee: ir::Value,
345366
call_args: &[ir::Value],
346367
) -> WasmResult<ir::Inst> {
347-
// TODO: Cranelift's call_indirect doesn't implement bounds checking
348-
// or signature checking, so we need to implement it ourselves.
349368
debug_assert_eq!(table_index, 0, "non-default tables not supported yet");
369+
370+
let callee_ty = pos.func.dfg.value_type(callee);
371+
debug_assert_eq!(callee_ty, I32);
372+
373+
// For some reason table_addr only works with I64.
374+
let callee_i64 = pos.ins().uextend(I64, callee);
375+
let table_entry_addr = pos.ins().table_addr(I64, table, callee_i64, 0);
376+
377+
// Dereference table_entry_addr to get the function address.
378+
let mut mem_flags = ir::MemFlags::new();
379+
mem_flags.set_notrap();
380+
mem_flags.set_aligned();
381+
let func_addr = pos
382+
.ins()
383+
.load(self.pointer_type(), mem_flags, table_entry_addr, 0);
384+
350385
let real_call_args = FuncEnvironment::get_real_call_args(pos.func, call_args);
351-
Ok(pos.ins().call_indirect(sig_ref, callee, &real_call_args))
386+
Ok(pos.ins().call_indirect(sig_ref, func_addr, &real_call_args))
352387
}
353388

354389
fn translate_call(

lib/execute/src/execute.rs

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use memory::LinearMemory;
55
use region::protect;
66
use region::Protection;
77
use std::mem::transmute;
8-
use std::ptr::write_unaligned;
8+
use std::ptr::{self, write_unaligned};
99
use wasmtime_environ::{
1010
compile_module, Compilation, Module, ModuleTranslation, Relocation, RelocationTarget,
1111
};
@@ -66,7 +66,7 @@ fn relocate(compilation: &mut Compilation, relocations: &[Vec<Relocation>]) {
6666

6767
extern "C" fn grow_memory(size: u32, vmctx: *mut *mut u8) -> u32 {
6868
unsafe {
69-
let instance = (*vmctx.offset(2)) as *mut Instance;
69+
let instance = (*vmctx.offset(4)) as *mut Instance;
7070
(*instance)
7171
.memory_mut(0)
7272
.grow(size)
@@ -76,18 +76,32 @@ extern "C" fn grow_memory(size: u32, vmctx: *mut *mut u8) -> u32 {
7676

7777
extern "C" fn current_memory(vmctx: *mut *mut u8) -> u32 {
7878
unsafe {
79-
let instance = (*vmctx.offset(2)) as *mut Instance;
79+
let instance = (*vmctx.offset(4)) as *mut Instance;
8080
(*instance).memory_mut(0).current_size()
8181
}
8282
}
8383

8484
/// Create the VmCtx data structure for the JIT'd code to use. This must
8585
/// match the VmCtx layout in the environment.
8686
fn make_vmctx(instance: &mut Instance, mem_base_addrs: &mut [*mut u8]) -> Vec<*mut u8> {
87+
debug_assert!(
88+
instance.tables.len() <= 1,
89+
"non-default tables is not supported"
90+
);
91+
92+
let (default_table_ptr, default_table_len) = instance
93+
.tables
94+
.get_mut(0)
95+
.map(|table| (table.as_mut_ptr() as *mut u8, table.len()))
96+
.unwrap_or((ptr::null_mut(), 0));
97+
8798
let mut vmctx = Vec::new();
8899
vmctx.push(instance.globals.as_mut_ptr());
89100
vmctx.push(mem_base_addrs.as_mut_ptr() as *mut u8);
101+
vmctx.push(default_table_ptr);
102+
vmctx.push(default_table_len as *mut u8);
90103
vmctx.push(instance as *mut Instance as *mut u8);
104+
91105
vmctx
92106
}
93107

lib/execute/src/instance.rs

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
use cranelift_codegen::ir;
55
use cranelift_wasm::GlobalIndex;
66
use memory::LinearMemory;
7-
use wasmtime_environ::{DataInitializer, Module, TableElements};
7+
use wasmtime_environ::{Compilation, DataInitializer, Module, TableElements};
88

99
/// An Instance of a WebAssemby module.
1010
#[derive(Debug)]
@@ -21,20 +21,29 @@ pub struct Instance {
2121

2222
impl Instance {
2323
/// Create a new `Instance`.
24-
pub fn new(module: &Module, data_initializers: &[DataInitializer]) -> Self {
24+
pub fn new(
25+
module: &Module,
26+
compilation: &Compilation,
27+
data_initializers: &[DataInitializer],
28+
) -> Self {
2529
let mut result = Self {
2630
tables: Vec::new(),
2731
memories: Vec::new(),
2832
globals: Vec::new(),
2933
};
30-
result.instantiate_tables(module, &module.table_elements);
34+
result.instantiate_tables(module, compilation, &module.table_elements);
3135
result.instantiate_memories(module, data_initializers);
3236
result.instantiate_globals(module);
3337
result
3438
}
3539

3640
/// Allocate memory in `self` for just the tables of the current module.
37-
fn instantiate_tables(&mut self, module: &Module, table_initializers: &[TableElements]) {
41+
fn instantiate_tables(
42+
&mut self,
43+
module: &Module,
44+
compilation: &Compilation,
45+
table_initializers: &[TableElements],
46+
) {
3847
debug_assert!(self.tables.is_empty());
3948
self.tables.reserve_exact(module.tables.len());
4049
for table in &module.tables {
@@ -47,7 +56,10 @@ impl Instance {
4756
debug_assert!(init.base.is_none(), "globalvar base not supported yet");
4857
let to_init =
4958
&mut self.tables[init.table_index][init.offset..init.offset + init.elements.len()];
50-
to_init.copy_from_slice(&init.elements);
59+
for (i, func_idx) in init.elements.iter().enumerate() {
60+
let code_buf = &compilation.functions[*func_idx];
61+
to_init[i] = code_buf.as_ptr() as usize;
62+
}
5163
}
5264
}
5365

src/main.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -133,8 +133,11 @@ fn handle_module(args: &Args, path: PathBuf, isa: &TargetIsa) -> Result<(), Stri
133133
let translation = environ.translate(&data).map_err(|e| e.to_string())?;
134134
let instance = match compile_and_link_module(isa, &translation) {
135135
Ok(compilation) => {
136-
let mut instance =
137-
Instance::new(translation.module, &translation.lazy.data_initializers);
136+
let mut instance = Instance::new(
137+
translation.module,
138+
&compilation,
139+
&translation.lazy.data_initializers,
140+
);
138141
execute(&translation.module, &compilation, &mut instance)?;
139142
instance
140143
}

0 commit comments

Comments
 (0)