mirror of
https://github.com/fluencelabs/wasmer
synced 2025-06-12 08:31:21 +00:00
Implement table, partial call_indirect
This commit is contained in:
@ -13,7 +13,8 @@ use wasmer_runtime_core::{
|
||||
module::ModuleInfo,
|
||||
structures::{Map, SliceMap, TypedIndex},
|
||||
types::{
|
||||
FuncIndex, FuncSig, GlobalIndex, LocalFuncIndex, LocalOrImport, MemoryIndex, SigIndex, Type,
|
||||
FuncIndex, FuncSig, GlobalIndex, LocalFuncIndex, LocalOrImport, MemoryIndex, SigIndex,
|
||||
TableIndex, Type,
|
||||
},
|
||||
};
|
||||
use wasmparser::{
|
||||
@ -622,6 +623,82 @@ fn parse_function(
|
||||
}
|
||||
}
|
||||
Operator::CallIndirect { index, table_index } => {
|
||||
let expected_dynamic_sigindex = ctx.dynamic_sigindex(SigIndex::new(index as usize));
|
||||
let (table_base, table_bound) = ctx.table(TableIndex::new(table_index as usize));
|
||||
let func_index = state.pop1()?.into_int_value();
|
||||
|
||||
// We assume the table has the `anyfunc` element type.
|
||||
let casted_table_base = builder.build_pointer_cast(
|
||||
table_base,
|
||||
intrinsics.anyfunc_ty.ptr_type(AddressSpace::Generic),
|
||||
"casted_table_base",
|
||||
);
|
||||
|
||||
let anyfunc_struct_ptr = unsafe {
|
||||
builder.build_in_bounds_gep(
|
||||
casted_table_base,
|
||||
&[func_index],
|
||||
"anyfunc_struct_ptr",
|
||||
)
|
||||
};
|
||||
|
||||
// Load things from the anyfunc data structure.
|
||||
let (func_ptr, ctx_ptr, found_dynamic_sigindex) = unsafe {
|
||||
(
|
||||
builder
|
||||
.build_load(
|
||||
builder.build_struct_gep(anyfunc_struct_ptr, 0, "func_ptr_ptr"),
|
||||
"func_ptr",
|
||||
)
|
||||
.into_pointer_value(),
|
||||
builder
|
||||
.build_load(
|
||||
builder.build_struct_gep(anyfunc_struct_ptr, 1, "ctx_ptr_ptr"),
|
||||
"ctx_ptr",
|
||||
)
|
||||
.into_pointer_value(),
|
||||
builder
|
||||
.build_load(
|
||||
builder.build_struct_gep(anyfunc_struct_ptr, 2, "sigindex_ptr"),
|
||||
"sigindex",
|
||||
)
|
||||
.into_int_value(),
|
||||
)
|
||||
};
|
||||
|
||||
let sigindices_equal = builder.build_int_compare(
|
||||
IntPredicate::EQ,
|
||||
expected_dynamic_sigindex,
|
||||
found_dynamic_sigindex,
|
||||
"sigindices_equal",
|
||||
);
|
||||
|
||||
// Tell llvm that `expected_dynamic_sigindex` should equal `found_dynamic_sigindex`.
|
||||
let sigindices_equal = builder
|
||||
.build_call(
|
||||
intrinsics.expect_i1,
|
||||
&[
|
||||
sigindices_equal.as_basic_value_enum(),
|
||||
intrinsics.i1_ty.const_int(1, false).as_basic_value_enum(),
|
||||
],
|
||||
"sigindices_equal_expect",
|
||||
)
|
||||
.try_as_basic_value()
|
||||
.left()
|
||||
.unwrap();
|
||||
|
||||
let continue_block = context.append_basic_block(&function, "continue_block");
|
||||
let sigindices_notequal_block =
|
||||
context.append_basic_block(&function, "sigindices_notequal_block");
|
||||
|
||||
builder.position_at_end(&sigindices_notequal_block);
|
||||
builder.build_unreachable();
|
||||
builder.position_at_end(&continue_block);
|
||||
|
||||
println!("func ptr: {:#?}", func_ptr);
|
||||
println!("ctx ptr: {:#?}", ctx_ptr);
|
||||
println!("found dynamic sigindex: {:#?}", found_dynamic_sigindex);
|
||||
|
||||
unimplemented!("{}, {}", index, table_index);
|
||||
}
|
||||
|
||||
|
@ -12,7 +12,9 @@ use wasmer_runtime_core::{
|
||||
memory::MemoryType,
|
||||
module::ModuleInfo,
|
||||
structures::TypedIndex,
|
||||
types::{GlobalIndex, ImportedFuncIndex, LocalOrImport, MemoryIndex, TableIndex, Type},
|
||||
types::{
|
||||
GlobalIndex, ImportedFuncIndex, LocalOrImport, MemoryIndex, SigIndex, TableIndex, Type,
|
||||
},
|
||||
};
|
||||
|
||||
fn type_to_llvm_ptr(intrinsics: &Intrinsics, ty: Type) -> PointerType {
|
||||
@ -61,6 +63,8 @@ pub struct Intrinsics {
|
||||
pub copysign_f32: FunctionValue,
|
||||
pub copysign_f64: FunctionValue,
|
||||
|
||||
pub expect_i1: FunctionValue,
|
||||
|
||||
pub void_ty: VoidType,
|
||||
pub i1_ty: IntType,
|
||||
pub i8_ty: IntType,
|
||||
@ -77,6 +81,8 @@ pub struct Intrinsics {
|
||||
pub f32_ptr_ty: PointerType,
|
||||
pub f64_ptr_ty: PointerType,
|
||||
|
||||
pub anyfunc_ty: StructType,
|
||||
|
||||
pub i1_zero: IntValue,
|
||||
pub i32_zero: IntValue,
|
||||
pub i64_zero: IntValue,
|
||||
@ -142,6 +148,17 @@ impl Intrinsics {
|
||||
let local_global_ty = i64_ty;
|
||||
let imported_func_ty =
|
||||
context.struct_type(&[i8_ptr_ty_basic, ctx_ptr_ty.as_basic_type_enum()], false);
|
||||
let sigindex_ty = i32_ty;
|
||||
|
||||
let anyfunc_ty = context.struct_type(
|
||||
&[
|
||||
i8_ptr_ty_basic,
|
||||
ctx_ptr_ty.as_basic_type_enum(),
|
||||
sigindex_ty.as_basic_type_enum(),
|
||||
],
|
||||
false,
|
||||
);
|
||||
|
||||
ctx_ty.set_body(
|
||||
&[
|
||||
local_memory_ty
|
||||
@ -172,6 +189,9 @@ impl Intrinsics {
|
||||
.ptr_type(AddressSpace::Generic)
|
||||
.ptr_type(AddressSpace::Generic)
|
||||
.as_basic_type_enum(),
|
||||
sigindex_ty
|
||||
.ptr_type(AddressSpace::Generic)
|
||||
.as_basic_type_enum(),
|
||||
],
|
||||
false,
|
||||
);
|
||||
@ -195,6 +215,8 @@ impl Intrinsics {
|
||||
let ret_i32_take_ctx_i32 =
|
||||
i32_ty.fn_type(&[ctx_ptr_ty.as_basic_type_enum(), i32_ty_basic], false);
|
||||
|
||||
let ret_i1_take_i1_i1 = i1_ty.fn_type(&[i1_ty_basic, i1_ty_basic], false);
|
||||
|
||||
Self {
|
||||
ctlz_i32: module.add_function("llvm.ctlz.i32", ret_i32_take_i32_i1, None),
|
||||
ctlz_i64: module.add_function("llvm.ctlz.i64", ret_i64_take_i64_i1, None),
|
||||
@ -232,6 +254,8 @@ impl Intrinsics {
|
||||
copysign_f32: module.add_function("llvm.copysign.f32", ret_f32_take_f32_f32, None),
|
||||
copysign_f64: module.add_function("llvm.copysign.f64", ret_f64_take_f64_f64, None),
|
||||
|
||||
expect_i1: module.add_function("llvm.expect.i1", ret_i1_take_i1_i1, None),
|
||||
|
||||
void_ty,
|
||||
i1_ty,
|
||||
i8_ty,
|
||||
@ -248,6 +272,8 @@ impl Intrinsics {
|
||||
f32_ptr_ty,
|
||||
f64_ptr_ty,
|
||||
|
||||
anyfunc_ty,
|
||||
|
||||
i1_zero,
|
||||
i32_zero,
|
||||
i64_zero,
|
||||
@ -340,6 +366,7 @@ impl Intrinsics {
|
||||
|
||||
cached_memories: HashMap::new(),
|
||||
cached_tables: HashMap::new(),
|
||||
cached_sigindices: HashMap::new(),
|
||||
cached_globals: HashMap::new(),
|
||||
cached_imported_functions: HashMap::new(),
|
||||
|
||||
@ -389,6 +416,7 @@ pub struct CtxType<'a> {
|
||||
|
||||
cached_memories: HashMap<MemoryIndex, MemoryCache>,
|
||||
cached_tables: HashMap<TableIndex, TableCache>,
|
||||
cached_sigindices: HashMap<SigIndex, IntValue>,
|
||||
cached_globals: HashMap<GlobalIndex, GlobalCache>,
|
||||
cached_imported_functions: HashMap<ImportedFuncIndex, ImportedFuncCache>,
|
||||
|
||||
@ -473,6 +501,89 @@ impl<'a> CtxType<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn table(&mut self, index: TableIndex) -> (PointerValue, IntValue) {
|
||||
let (cached_tables, builder, info, ctx_ptr_value, intrinsics) = (
|
||||
&mut self.cached_tables,
|
||||
self.builder,
|
||||
self.info,
|
||||
self.ctx_ptr_value,
|
||||
self.intrinsics,
|
||||
);
|
||||
|
||||
let TableCache {
|
||||
ptr_to_base_ptr,
|
||||
ptr_to_bounds,
|
||||
} = *cached_tables.entry(index).or_insert_with(|| {
|
||||
let (table_array_ptr_ptr, index) = match index.local_or_import(info) {
|
||||
LocalOrImport::Local(local_table_index) => (
|
||||
unsafe { builder.build_struct_gep(ctx_ptr_value, 1, "table_array_ptr_ptr") },
|
||||
local_table_index.index() as u64,
|
||||
),
|
||||
LocalOrImport::Import(import_table_index) => (
|
||||
unsafe { builder.build_struct_gep(ctx_ptr_value, 4, "table_array_ptr_ptr") },
|
||||
import_table_index.index() as u64,
|
||||
),
|
||||
};
|
||||
|
||||
let table_array_ptr = builder
|
||||
.build_load(table_array_ptr_ptr, "table_array_ptr")
|
||||
.into_pointer_value();
|
||||
let const_index = intrinsics.i32_ty.const_int(index, false);
|
||||
let table_ptr_ptr = unsafe {
|
||||
builder.build_in_bounds_gep(table_array_ptr, &[const_index], "table_ptr_ptr")
|
||||
};
|
||||
let table_ptr = builder
|
||||
.build_load(table_ptr_ptr, "table_ptr")
|
||||
.into_pointer_value();
|
||||
|
||||
let (ptr_to_base_ptr, ptr_to_bounds) = unsafe {
|
||||
(
|
||||
builder.build_struct_gep(table_ptr, 0, "base_ptr"),
|
||||
builder.build_struct_gep(table_ptr, 1, "bounds_ptr"),
|
||||
)
|
||||
};
|
||||
|
||||
TableCache {
|
||||
ptr_to_base_ptr,
|
||||
ptr_to_bounds,
|
||||
}
|
||||
});
|
||||
|
||||
(
|
||||
builder
|
||||
.build_load(ptr_to_base_ptr, "base_ptr")
|
||||
.into_pointer_value(),
|
||||
builder.build_load(ptr_to_bounds, "bounds").into_int_value(),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn dynamic_sigindex(&mut self, index: SigIndex) -> IntValue {
|
||||
let (cached_sigindices, builder, info, ctx_ptr_value, intrinsics) = (
|
||||
&mut self.cached_sigindices,
|
||||
self.builder,
|
||||
self.info,
|
||||
self.ctx_ptr_value,
|
||||
self.intrinsics,
|
||||
);
|
||||
|
||||
*cached_sigindices.entry(index).or_insert_with(|| {
|
||||
let sigindex_array_ptr_ptr =
|
||||
unsafe { builder.build_struct_gep(ctx_ptr_value, 7, "sigindex_array_ptr_ptr") };
|
||||
let sigindex_array_ptr = builder
|
||||
.build_load(sigindex_array_ptr_ptr, "sigindex_array_ptr")
|
||||
.into_pointer_value();
|
||||
let const_index = intrinsics.i32_ty.const_int(index.index() as u64, false);
|
||||
|
||||
let sigindex_ptr = unsafe {
|
||||
builder.build_in_bounds_gep(sigindex_array_ptr, &[const_index], "sigindex_ptr")
|
||||
};
|
||||
|
||||
builder
|
||||
.build_load(sigindex_ptr, "sigindex")
|
||||
.into_int_value()
|
||||
})
|
||||
}
|
||||
|
||||
pub fn global_cache(&mut self, index: GlobalIndex) -> GlobalCache {
|
||||
let (cached_globals, builder, ctx_ptr_value, info, intrinsics) = (
|
||||
&mut self.cached_globals,
|
||||
|
@ -1,13 +1,14 @@
|
||||
use wasmer_runtime_core::{
|
||||
backend::{Compiler, Token},
|
||||
error::CompileError,
|
||||
module::ModuleInner,
|
||||
};
|
||||
use inkwell::{
|
||||
execution_engine::JitFunction,
|
||||
targets::{TargetMachine, Target, RelocMode, CodeModel, InitializationConfig, FileType},
|
||||
targets::{CodeModel, FileType, InitializationConfig, RelocMode, Target, TargetMachine},
|
||||
OptimizationLevel,
|
||||
};
|
||||
use wasmer_runtime_core::{
|
||||
backend::{Compiler, Token},
|
||||
cache::{Artifact, Error as CacheError},
|
||||
error::CompileError,
|
||||
module::ModuleInner,
|
||||
};
|
||||
|
||||
mod code;
|
||||
mod intrinsics;
|
||||
@ -31,22 +32,29 @@ impl Compiler for LLVMCompiler {
|
||||
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
unsafe fn from_cache(&self, _artifact: Artifact, _: Token) -> Result<ModuleInner, CacheError> {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_read_module() {
|
||||
use wasmer_runtime_core::vmcalls;
|
||||
use wabt::wat2wasm;
|
||||
use wasmer_runtime_core::vmcalls;
|
||||
// let wasm = include_bytes!("../../spectests/examples/simple/simple.wasm") as &[u8];
|
||||
let wat = r#"
|
||||
(module
|
||||
(type $t0 (func (param i32) (result i32)))
|
||||
(type $t1 (func (result i32)))
|
||||
(memory 1)
|
||||
(table 10 anyfunc)
|
||||
(elem (i32.const 0) $foobar)
|
||||
(global $g0 (mut i32) (i32.const 0))
|
||||
(func $foo (type $t0) (param i32) (result i32)
|
||||
get_local 0
|
||||
call $foobar
|
||||
i32.const 0
|
||||
call_indirect (type $t0)
|
||||
memory.grow
|
||||
)
|
||||
(func $foobar (type $t0)
|
||||
@ -74,19 +82,23 @@ fn test_read_module() {
|
||||
});
|
||||
let triple = TargetMachine::get_default_triple().to_string();
|
||||
let target = Target::from_triple(&triple).unwrap();
|
||||
let target_machine = target.create_target_machine(
|
||||
&triple,
|
||||
&TargetMachine::get_host_cpu_name().to_string(),
|
||||
&TargetMachine::get_host_cpu_features().to_string(),
|
||||
OptimizationLevel::Default,
|
||||
RelocMode::PIC,
|
||||
CodeModel::Default,
|
||||
).unwrap();
|
||||
let target_machine = target
|
||||
.create_target_machine(
|
||||
&triple,
|
||||
&TargetMachine::get_host_cpu_name().to_string(),
|
||||
&TargetMachine::get_host_cpu_features().to_string(),
|
||||
OptimizationLevel::Default,
|
||||
RelocMode::PIC,
|
||||
CodeModel::Default,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let memory_buffer = target_machine.write_to_memory_buffer(&module, FileType::Object).unwrap();
|
||||
let memory_buffer = target_machine
|
||||
.write_to_memory_buffer(&module, FileType::Object)
|
||||
.unwrap();
|
||||
// std::fs::write("memory_buffer", memory_buffer.as_slice()).unwrap();
|
||||
let mem_buf_slice = memory_buffer.as_slice();
|
||||
|
||||
|
||||
let macho = goblin::mach::MachO::parse(mem_buf_slice, 0).unwrap();
|
||||
let symbols = macho.symbols.as_ref().unwrap();
|
||||
let relocations = macho.relocations().unwrap();
|
||||
@ -95,22 +107,50 @@ fn test_read_module() {
|
||||
for reloc_info in reloc_iter {
|
||||
let reloc_info = reloc_info.unwrap();
|
||||
println!("\treloc_info: {:#?}", reloc_info);
|
||||
println!("\tsymbol: {:#?}", symbols.get(reloc_info.r_symbolnum()).unwrap());
|
||||
println!(
|
||||
"\tsymbol: {:#?}",
|
||||
symbols.get(reloc_info.r_symbolnum()).unwrap()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
let exec_engine = module.create_jit_execution_engine(OptimizationLevel::Default).unwrap();
|
||||
let exec_engine = module
|
||||
.create_jit_execution_engine(OptimizationLevel::Default)
|
||||
.unwrap();
|
||||
|
||||
exec_engine.add_global_mapping(&intrinsics.memory_grow_dynamic_local, vmcalls::local_dynamic_memory_grow as usize);
|
||||
exec_engine.add_global_mapping(&intrinsics.memory_grow_static_local, vmcalls::local_static_memory_grow as usize);
|
||||
exec_engine.add_global_mapping(&intrinsics.memory_grow_dynamic_import, vmcalls::imported_dynamic_memory_grow as usize);
|
||||
exec_engine.add_global_mapping(&intrinsics.memory_grow_static_import, vmcalls::imported_static_memory_grow as usize);
|
||||
exec_engine.add_global_mapping(&intrinsics.memory_size_dynamic_local, vmcalls::local_dynamic_memory_size as usize);
|
||||
exec_engine.add_global_mapping(&intrinsics.memory_size_static_local, vmcalls::local_static_memory_size as usize);
|
||||
exec_engine.add_global_mapping(&intrinsics.memory_size_dynamic_import, vmcalls::imported_dynamic_memory_size as usize);
|
||||
exec_engine.add_global_mapping(&intrinsics.memory_size_static_import, vmcalls::imported_static_memory_size as usize);
|
||||
exec_engine.add_global_mapping(
|
||||
&intrinsics.memory_grow_dynamic_local,
|
||||
vmcalls::local_dynamic_memory_grow as usize,
|
||||
);
|
||||
exec_engine.add_global_mapping(
|
||||
&intrinsics.memory_grow_static_local,
|
||||
vmcalls::local_static_memory_grow as usize,
|
||||
);
|
||||
exec_engine.add_global_mapping(
|
||||
&intrinsics.memory_grow_dynamic_import,
|
||||
vmcalls::imported_dynamic_memory_grow as usize,
|
||||
);
|
||||
exec_engine.add_global_mapping(
|
||||
&intrinsics.memory_grow_static_import,
|
||||
vmcalls::imported_static_memory_grow as usize,
|
||||
);
|
||||
exec_engine.add_global_mapping(
|
||||
&intrinsics.memory_size_dynamic_local,
|
||||
vmcalls::local_dynamic_memory_size as usize,
|
||||
);
|
||||
exec_engine.add_global_mapping(
|
||||
&intrinsics.memory_size_static_local,
|
||||
vmcalls::local_static_memory_size as usize,
|
||||
);
|
||||
exec_engine.add_global_mapping(
|
||||
&intrinsics.memory_size_dynamic_import,
|
||||
vmcalls::imported_dynamic_memory_size as usize,
|
||||
);
|
||||
exec_engine.add_global_mapping(
|
||||
&intrinsics.memory_size_static_import,
|
||||
vmcalls::imported_static_memory_size as usize,
|
||||
);
|
||||
|
||||
// unsafe {
|
||||
// let func: JitFunction<unsafe extern fn(*mut u8, i32) -> i32> = exec_engine.get_function("fn0").unwrap();
|
||||
|
Reference in New Issue
Block a user