Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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