Skip to content

Commit

Permalink
Implement tables and call_indirect (#12)
Browse files Browse the repository at this point in the history
* Implement tables and call_indirect

* Restore comment about sig checking.

* Widen callee index on 64bit platforms.
  • Loading branch information
pepyakin authored and sunfishcode committed Aug 15, 2018
1 parent e7c8d23 commit 7b22219
Show file tree
Hide file tree
Showing 5 changed files with 138 additions and 17 deletions.
50 changes: 50 additions & 0 deletions filetests/call_indirect.wat
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
(module
(type $indirect_sig (func (param i64) (result i64)))

(func $assert (param i32)
(block $ok
(br_if $ok
(get_local 0)
)
(unreachable)
)
)

(func $plus_1 (param i64) (result i64)
get_local 0
i64.const 1
i64.add
)
(func $minus_1 (param i64) (result i64)
get_local 0
i64.const 1
i64.sub
)

(func $main
(call $call_indirect
(i32.const 0)
(i64.const 2)
)
(call $call_indirect
(i32.const 1)
(i64.const 0)
)
)

(func $call_indirect (param $func i32) (param $expected i64)
(call $assert
(i64.eq
(call_indirect (type $indirect_sig)
(i64.const 1)
(get_local $func)
)
(get_local $expected)
)
)
)
(start $main)

(table 2 2 anyfunc)
(elem (i32.const 0) $plus_1 $minus_1)
)
56 changes: 49 additions & 7 deletions lib/environ/src/environ.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use cranelift_codegen::cursor::FuncCursor;
use cranelift_codegen::ir;
use cranelift_codegen::ir::immediates::Offset32;
use cranelift_codegen::ir::immediates::{Imm64, Offset32};
use cranelift_codegen::ir::types::*;
use cranelift_codegen::ir::{
AbiParam, ArgumentPurpose, ExtFuncData, ExternalName, FuncRef, Function, InstBuilder, Signature,
Expand Down Expand Up @@ -312,8 +312,29 @@ impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'m
})
}

fn make_table(&mut self, _func: &mut ir::Function, _index: TableIndex) -> ir::Table {
unimplemented!("make_table");
fn make_table(&mut self, func: &mut ir::Function, _index: TableIndex) -> ir::Table {
let pointer_bytes = self.pointer_bytes();
let base_gv_addr = func.create_global_value(ir::GlobalValueData::VMContext {
offset: Offset32::new(pointer_bytes as i32 * 2),
});
let base_gv = func.create_global_value(ir::GlobalValueData::Deref {
base: base_gv_addr,
offset: 0.into(),
});
let bound_gv_addr = func.create_global_value(ir::GlobalValueData::VMContext {
offset: Offset32::new(pointer_bytes as i32 * 3),
});
let bound_gv = func.create_global_value(ir::GlobalValueData::Deref {
base: bound_gv_addr,
offset: 0.into(),
});

func.create_table(ir::TableData {
base_gv,
min_size: Imm64::new(0),
bound_gv,
element_size: Imm64::new(i64::from(self.pointer_bytes() as i64)),
})
}

fn make_indirect_sig(&mut self, func: &mut ir::Function, index: SignatureIndex) -> ir::SigRef {
Expand All @@ -338,17 +359,38 @@ impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'m
&mut self,
mut pos: FuncCursor,
table_index: TableIndex,
_table: ir::Table,
table: ir::Table,
_sig_index: SignatureIndex,
sig_ref: ir::SigRef,
callee: ir::Value,
call_args: &[ir::Value],
) -> WasmResult<ir::Inst> {
// TODO: Cranelift's call_indirect doesn't implement bounds checking
// or signature checking, so we need to implement it ourselves.
// TODO: Cranelift's call_indirect doesn't implement signature checking,
// so we need to implement it ourselves.
debug_assert_eq!(table_index, 0, "non-default tables not supported yet");

let callee_ty = pos.func.dfg.value_type(callee);
debug_assert_eq!(callee_ty, I32, "wasm call indirect index should be I32");
let callee = if self.pointer_type() == I64 {
// The current limitation of `table_addr` is that the index should be
// the same type as `self.pointer_type()`. So we just extend the given
// index to 64-bit here.
pos.ins().uextend(I64, callee)
} else {
callee
};
let table_entry_addr = pos.ins().table_addr(I64, table, callee, 0);

// Dereference table_entry_addr to get the function address.
let mut mem_flags = ir::MemFlags::new();
mem_flags.set_notrap();
mem_flags.set_aligned();
let func_addr = pos
.ins()
.load(self.pointer_type(), mem_flags, table_entry_addr, 0);

let real_call_args = FuncEnvironment::get_real_call_args(pos.func, call_args);
Ok(pos.ins().call_indirect(sig_ref, callee, &real_call_args))
Ok(pos.ins().call_indirect(sig_ref, func_addr, &real_call_args))
}

fn translate_call(
Expand Down
20 changes: 17 additions & 3 deletions lib/execute/src/execute.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use memory::LinearMemory;
use region::protect;
use region::Protection;
use std::mem::transmute;
use std::ptr::write_unaligned;
use std::ptr::{self, write_unaligned};
use wasmtime_environ::{
compile_module, Compilation, Module, ModuleTranslation, Relocation, RelocationTarget,
};
Expand Down Expand Up @@ -66,7 +66,7 @@ fn relocate(compilation: &mut Compilation, relocations: &[Vec<Relocation>]) {

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

extern "C" fn current_memory(vmctx: *mut *mut u8) -> u32 {
unsafe {
let instance = (*vmctx.offset(2)) as *mut Instance;
let instance = (*vmctx.offset(4)) as *mut Instance;
(*instance).memory_mut(0).current_size()
}
}

/// Create the VmCtx data structure for the JIT'd code to use. This must
/// match the VmCtx layout in the environment.
fn make_vmctx(instance: &mut Instance, mem_base_addrs: &mut [*mut u8]) -> Vec<*mut u8> {
debug_assert!(
instance.tables.len() <= 1,
"non-default tables is not supported"
);

let (default_table_ptr, default_table_len) = instance
.tables
.get_mut(0)
.map(|table| (table.as_mut_ptr() as *mut u8, table.len()))
.unwrap_or((ptr::null_mut(), 0));

let mut vmctx = Vec::new();
vmctx.push(instance.globals.as_mut_ptr());
vmctx.push(mem_base_addrs.as_mut_ptr() as *mut u8);
vmctx.push(default_table_ptr);
vmctx.push(default_table_len as *mut u8);
vmctx.push(instance as *mut Instance as *mut u8);

vmctx
}

Expand Down
22 changes: 17 additions & 5 deletions lib/execute/src/instance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
use cranelift_codegen::ir;
use cranelift_wasm::GlobalIndex;
use memory::LinearMemory;
use wasmtime_environ::{DataInitializer, Module, TableElements};
use wasmtime_environ::{Compilation, DataInitializer, Module, TableElements};

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

impl Instance {
/// Create a new `Instance`.
pub fn new(module: &Module, data_initializers: &[DataInitializer]) -> Self {
pub fn new(
module: &Module,
compilation: &Compilation,
data_initializers: &[DataInitializer],
) -> Self {
let mut result = Self {
tables: Vec::new(),
memories: Vec::new(),
globals: Vec::new(),
};
result.instantiate_tables(module, &module.table_elements);
result.instantiate_tables(module, compilation, &module.table_elements);
result.instantiate_memories(module, data_initializers);
result.instantiate_globals(module);
result
}

/// Allocate memory in `self` for just the tables of the current module.
fn instantiate_tables(&mut self, module: &Module, table_initializers: &[TableElements]) {
fn instantiate_tables(
&mut self,
module: &Module,
compilation: &Compilation,
table_initializers: &[TableElements],
) {
debug_assert!(self.tables.is_empty());
self.tables.reserve_exact(module.tables.len());
for table in &module.tables {
Expand All @@ -47,7 +56,10 @@ impl Instance {
debug_assert!(init.base.is_none(), "globalvar base not supported yet");
let to_init =
&mut self.tables[init.table_index][init.offset..init.offset + init.elements.len()];
to_init.copy_from_slice(&init.elements);
for (i, func_idx) in init.elements.iter().enumerate() {
let code_buf = &compilation.functions[*func_idx];
to_init[i] = code_buf.as_ptr() as usize;
}
}
}

Expand Down
7 changes: 5 additions & 2 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -133,8 +133,11 @@ fn handle_module(args: &Args, path: PathBuf, isa: &TargetIsa) -> Result<(), Stri
let translation = environ.translate(&data).map_err(|e| e.to_string())?;
let instance = match compile_and_link_module(isa, &translation) {
Ok(compilation) => {
let mut instance =
Instance::new(translation.module, &translation.lazy.data_initializers);
let mut instance = Instance::new(
translation.module,
&compilation,
&translation.lazy.data_initializers,
);
execute(&translation.module, &compilation, &mut instance)?;
instance
}
Expand Down

0 comments on commit 7b22219

Please sign in to comment.