mirror of
https://github.com/fluencelabs/wasmer
synced 2025-06-25 06:31:32 +00:00
Get control flow (at least according to the llvm verifier) working.
Next up: - Importing vm intrinsics.
This commit is contained in:
@ -11,14 +11,17 @@ use libc::{
|
|||||||
};
|
};
|
||||||
use std::{
|
use std::{
|
||||||
ffi::CString,
|
ffi::CString,
|
||||||
|
mem,
|
||||||
ptr::{self, NonNull},
|
ptr::{self, NonNull},
|
||||||
};
|
};
|
||||||
use wasmer_runtime_core::{
|
use wasmer_runtime_core::{
|
||||||
backend::FuncResolver,
|
backend::{FuncResolver, ProtectedCaller, Token, UserTrapper},
|
||||||
|
error::RuntimeResult,
|
||||||
|
export::Context,
|
||||||
module::{ModuleInfo, ModuleInner},
|
module::{ModuleInfo, ModuleInner},
|
||||||
structures::TypedIndex,
|
structures::TypedIndex,
|
||||||
types::LocalFuncIndex,
|
types::{FuncIndex, FuncSig, LocalFuncIndex, LocalOrImport, SigIndex, Type, Value},
|
||||||
vm,
|
vm::{self, ImportBacking},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
@ -155,7 +158,7 @@ pub struct LLVMBackend {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl LLVMBackend {
|
impl LLVMBackend {
|
||||||
pub fn new(module: Module, intrinsics: Intrinsics) -> Self {
|
pub fn new(module: Module, intrinsics: Intrinsics) -> (Self, LLVMProtectedCaller) {
|
||||||
Target::initialize_x86(&InitializationConfig {
|
Target::initialize_x86(&InitializationConfig {
|
||||||
asm_parser: true,
|
asm_parser: true,
|
||||||
asm_printer: true,
|
asm_printer: true,
|
||||||
@ -198,10 +201,13 @@ impl LLVMBackend {
|
|||||||
panic!("failed to load object")
|
panic!("failed to load object")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
(
|
||||||
Self {
|
Self {
|
||||||
module,
|
module,
|
||||||
memory_buffer,
|
memory_buffer,
|
||||||
}
|
},
|
||||||
|
LLVMProtectedCaller { module },
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_func(
|
pub fn get_func(
|
||||||
@ -239,6 +245,137 @@ impl FuncResolver for LLVMBackend {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct Placeholder;
|
||||||
|
|
||||||
|
unsafe impl Send for LLVMProtectedCaller {}
|
||||||
|
unsafe impl Sync for LLVMProtectedCaller {}
|
||||||
|
|
||||||
|
pub struct LLVMProtectedCaller {
|
||||||
|
module: *mut LLVMModule,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ProtectedCaller for LLVMProtectedCaller {
|
||||||
|
fn call(
|
||||||
|
&self,
|
||||||
|
module: &ModuleInner,
|
||||||
|
func_index: FuncIndex,
|
||||||
|
params: &[Value],
|
||||||
|
import_backing: &ImportBacking,
|
||||||
|
vmctx: *mut vm::Ctx,
|
||||||
|
_: Token,
|
||||||
|
) -> RuntimeResult<Vec<Value>> {
|
||||||
|
let (func_ptr, ctx, signature, sig_index) =
|
||||||
|
get_func_from_index(&module, import_backing, func_index);
|
||||||
|
|
||||||
|
let vmctx_ptr = match ctx {
|
||||||
|
Context::External(external_vmctx) => external_vmctx,
|
||||||
|
Context::Internal => vmctx,
|
||||||
|
};
|
||||||
|
|
||||||
|
assert!(
|
||||||
|
signature.returns().len() <= 1,
|
||||||
|
"multi-value returns not yet supported"
|
||||||
|
);
|
||||||
|
|
||||||
|
assert!(
|
||||||
|
signature.check_param_value_types(params),
|
||||||
|
"incorrect signature"
|
||||||
|
);
|
||||||
|
|
||||||
|
let param_vec: Vec<u64> = params
|
||||||
|
.iter()
|
||||||
|
.map(|val| match val {
|
||||||
|
Value::I32(x) => *x as u64,
|
||||||
|
Value::I64(x) => *x as u64,
|
||||||
|
Value::F32(x) => x.to_bits() as u64,
|
||||||
|
Value::F64(x) => x.to_bits(),
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let mut return_vec = vec![0; signature.returns().len()];
|
||||||
|
|
||||||
|
let trampoline: unsafe extern "C" fn(*mut vm::Ctx, *const vm::Func, *const u64, *mut u64) = unsafe {
|
||||||
|
let name = if cfg!(target_os = "macos") {
|
||||||
|
format!("_trmp{}", sig_index.index())
|
||||||
|
} else {
|
||||||
|
format!("trmp{}", sig_index.index())
|
||||||
|
};
|
||||||
|
|
||||||
|
let c_str = CString::new(name).unwrap();
|
||||||
|
let symbol = get_func_symbol(self.module, c_str.as_ptr());
|
||||||
|
assert!(!symbol.is_null());
|
||||||
|
|
||||||
|
mem::transmute(symbol)
|
||||||
|
};
|
||||||
|
|
||||||
|
// Here we go.
|
||||||
|
unsafe {
|
||||||
|
trampoline(
|
||||||
|
vmctx_ptr,
|
||||||
|
func_ptr,
|
||||||
|
param_vec.as_ptr(),
|
||||||
|
return_vec.as_mut_ptr(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(return_vec
|
||||||
|
.iter()
|
||||||
|
.zip(signature.returns().iter())
|
||||||
|
.map(|(&x, ty)| match ty {
|
||||||
|
Type::I32 => Value::I32(x as i32),
|
||||||
|
Type::I64 => Value::I64(x as i64),
|
||||||
|
Type::F32 => Value::F32(f32::from_bits(x as u32)),
|
||||||
|
Type::F64 => Value::F64(f64::from_bits(x as u64)),
|
||||||
|
})
|
||||||
|
.collect())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_early_trapper(&self) -> Box<dyn UserTrapper> {
|
||||||
|
Box::new(Placeholder)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl UserTrapper for Placeholder {
|
||||||
|
unsafe fn do_early_trap(&self, msg: String) -> ! {
|
||||||
|
unimplemented!("do early trap: {}", msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_func_from_index<'a>(
|
||||||
|
module: &'a ModuleInner,
|
||||||
|
import_backing: &ImportBacking,
|
||||||
|
func_index: FuncIndex,
|
||||||
|
) -> (*const vm::Func, Context, &'a FuncSig, SigIndex) {
|
||||||
|
let sig_index = *module
|
||||||
|
.info
|
||||||
|
.func_assoc
|
||||||
|
.get(func_index)
|
||||||
|
.expect("broken invariant, incorrect func index");
|
||||||
|
|
||||||
|
let (func_ptr, ctx) = match func_index.local_or_import(&module.info) {
|
||||||
|
LocalOrImport::Local(local_func_index) => (
|
||||||
|
module
|
||||||
|
.func_resolver
|
||||||
|
.get(&module, local_func_index)
|
||||||
|
.expect("broken invariant, func resolver not synced with module.exports")
|
||||||
|
.cast()
|
||||||
|
.as_ptr() as *const _,
|
||||||
|
Context::Internal,
|
||||||
|
),
|
||||||
|
LocalOrImport::Import(imported_func_index) => {
|
||||||
|
let imported_func = import_backing.imported_func(imported_func_index);
|
||||||
|
(
|
||||||
|
imported_func.func as *const _,
|
||||||
|
Context::External(imported_func.vmctx),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let signature = &module.info.signatures[sig_index];
|
||||||
|
|
||||||
|
(func_ptr, ctx, signature, sig_index)
|
||||||
|
}
|
||||||
|
|
||||||
unsafe fn disass_ptr(ptr: *const u8, size: usize, inst_count: usize) {
|
unsafe fn disass_ptr(ptr: *const u8, size: usize, inst_count: usize) {
|
||||||
use capstone::arch::BuildsCapstone;
|
use capstone::arch::BuildsCapstone;
|
||||||
let mut cs = capstone::Capstone::new() // Call builder-pattern
|
let mut cs = capstone::Capstone::new() // Call builder-pattern
|
||||||
|
@ -24,6 +24,7 @@ use wasmparser::{
|
|||||||
use crate::intrinsics::{CtxType, GlobalCache, Intrinsics};
|
use crate::intrinsics::{CtxType, GlobalCache, Intrinsics};
|
||||||
use crate::read_info::type_to_type;
|
use crate::read_info::type_to_type;
|
||||||
use crate::state::{ControlFrame, IfElseState, State};
|
use crate::state::{ControlFrame, IfElseState, State};
|
||||||
|
use crate::trampolines::generate_trampolines;
|
||||||
|
|
||||||
fn func_sig_to_llvm(context: &Context, intrinsics: &Intrinsics, sig: &FuncSig) -> FunctionType {
|
fn func_sig_to_llvm(context: &Context, intrinsics: &Intrinsics, sig: &FuncSig) -> FunctionType {
|
||||||
let user_param_types = sig.params().iter().map(|&ty| type_to_llvm(intrinsics, ty));
|
let user_param_types = sig.params().iter().map(|&ty| type_to_llvm(intrinsics, ty));
|
||||||
@ -102,10 +103,19 @@ pub fn parse_function_bodies(
|
|||||||
LocalFuncIndex::new(local_func_index),
|
LocalFuncIndex::new(local_func_index),
|
||||||
locals_reader,
|
locals_reader,
|
||||||
op_reader,
|
op_reader,
|
||||||
)?;
|
)
|
||||||
|
.map_err(|e| BinaryReaderError {
|
||||||
|
message: e.message,
|
||||||
|
offset: local_func_index,
|
||||||
|
})?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// module.print_to_stderr();
|
||||||
|
|
||||||
|
generate_trampolines(info, &signatures, &module, &context, &builder, &intrinsics);
|
||||||
|
|
||||||
let pass_manager = PassManager::create_for_module();
|
let pass_manager = PassManager::create_for_module();
|
||||||
|
pass_manager.add_verifier_pass();
|
||||||
pass_manager.add_function_inlining_pass();
|
pass_manager.add_function_inlining_pass();
|
||||||
pass_manager.add_promote_memory_to_register_pass();
|
pass_manager.add_promote_memory_to_register_pass();
|
||||||
pass_manager.add_cfg_simplification_pass();
|
pass_manager.add_cfg_simplification_pass();
|
||||||
@ -116,7 +126,6 @@ pub fn parse_function_bodies(
|
|||||||
pass_manager.add_gvn_pass();
|
pass_manager.add_gvn_pass();
|
||||||
// pass_manager.add_new_gvn_pass();
|
// pass_manager.add_new_gvn_pass();
|
||||||
pass_manager.add_aggressive_dce_pass();
|
pass_manager.add_aggressive_dce_pass();
|
||||||
// pass_manager.add_verifier_pass();
|
|
||||||
pass_manager.run_on_module(&module);
|
pass_manager.run_on_module(&module);
|
||||||
|
|
||||||
Ok((module, intrinsics))
|
Ok((module, intrinsics))
|
||||||
@ -209,8 +218,34 @@ fn parse_function(
|
|||||||
locals.push(alloca);
|
locals.push(alloca);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let mut unreachable_depth = 0;
|
||||||
|
|
||||||
for op in op_reader {
|
for op in op_reader {
|
||||||
match op? {
|
let op = op?;
|
||||||
|
if !state.reachable {
|
||||||
|
match op {
|
||||||
|
Operator::Block { ty: _ } | Operator::Loop { ty: _ } | Operator::If { ty: _ } => {
|
||||||
|
unreachable_depth += 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
Operator::Else => {
|
||||||
|
if unreachable_depth != 0 {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Operator::End => {
|
||||||
|
if unreachable_depth != 0 {
|
||||||
|
unreachable_depth -= 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
match op {
|
||||||
/***************************
|
/***************************
|
||||||
* Control Flow instructions.
|
* Control Flow instructions.
|
||||||
* https://github.com/sunfishcode/wasm-reference-manual/blob/master/WebAssembly.md#control-flow-instructions
|
* https://github.com/sunfishcode/wasm-reference-manual/blob/master/WebAssembly.md#control-flow-instructions
|
||||||
@ -356,7 +391,7 @@ fn parse_function(
|
|||||||
builder.build_switch(index.into_int_value(), default_frame.br_dest(), &cases[..]);
|
builder.build_switch(index.into_int_value(), default_frame.br_dest(), &cases[..]);
|
||||||
|
|
||||||
state.popn(res_len)?;
|
state.popn(res_len)?;
|
||||||
builder.build_unreachable();
|
state.reachable = false;
|
||||||
}
|
}
|
||||||
Operator::If { ty } => {
|
Operator::If { ty } => {
|
||||||
let current_block = builder.get_insert_block().ok_or(BinaryReaderError {
|
let current_block = builder.get_insert_block().ok_or(BinaryReaderError {
|
||||||
@ -440,8 +475,22 @@ fn parse_function(
|
|||||||
builder.build_unconditional_branch(frame.code_after());
|
builder.build_unconditional_branch(frame.code_after());
|
||||||
|
|
||||||
for phi in frame.phis().iter().rev() {
|
for phi in frame.phis().iter().rev() {
|
||||||
|
if phi.count_incoming() != 0 {
|
||||||
let value = state.pop1()?;
|
let value = state.pop1()?;
|
||||||
phi.add_incoming(&[(&value, ¤t_block)])
|
phi.add_incoming(&[(&value, ¤t_block)])
|
||||||
|
} else {
|
||||||
|
let basic_ty = phi.as_basic_value().get_type();
|
||||||
|
let placeholder_value = match basic_ty {
|
||||||
|
BasicTypeEnum::IntType(int_ty) => {
|
||||||
|
int_ty.const_int(0, false).as_basic_value_enum()
|
||||||
|
}
|
||||||
|
BasicTypeEnum::FloatType(float_ty) => {
|
||||||
|
float_ty.const_float(0.0).as_basic_value_enum()
|
||||||
|
}
|
||||||
|
_ => unimplemented!(),
|
||||||
|
};
|
||||||
|
phi.add_incoming(&[(&placeholder_value, ¤t_block)]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -466,13 +515,40 @@ fn parse_function(
|
|||||||
|
|
||||||
// Push each phi value to the value stack.
|
// Push each phi value to the value stack.
|
||||||
for phi in frame.phis() {
|
for phi in frame.phis() {
|
||||||
|
if phi.count_incoming() != 0 {
|
||||||
state.push1(phi.as_basic_value());
|
state.push1(phi.as_basic_value());
|
||||||
|
} else {
|
||||||
|
let basic_ty = phi.as_basic_value().get_type();
|
||||||
|
let placeholder_value = match basic_ty {
|
||||||
|
BasicTypeEnum::IntType(int_ty) => {
|
||||||
|
int_ty.const_int(0, false).as_basic_value_enum()
|
||||||
|
}
|
||||||
|
BasicTypeEnum::FloatType(float_ty) => {
|
||||||
|
float_ty.const_float(0.0).as_basic_value_enum()
|
||||||
|
}
|
||||||
|
_ => unimplemented!(),
|
||||||
|
};
|
||||||
|
state.push1(placeholder_value);
|
||||||
|
phi.as_instruction().erase_from_basic_block();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Operator::Return => {
|
Operator::Return => {
|
||||||
let frame = state.outermost_frame()?;
|
let frame = state.outermost_frame()?;
|
||||||
|
let current_block = builder.get_insert_block().ok_or(BinaryReaderError {
|
||||||
|
message: "not currently in a block",
|
||||||
|
offset: -1isize as usize,
|
||||||
|
})?;
|
||||||
|
|
||||||
builder.build_unconditional_branch(frame.br_dest());
|
builder.build_unconditional_branch(frame.br_dest());
|
||||||
|
|
||||||
|
let phis = frame.phis().to_vec();
|
||||||
|
|
||||||
|
for phi in phis.iter() {
|
||||||
|
let arg = state.pop1()?;
|
||||||
|
phi.add_incoming(&[(&arg, ¤t_block)]);
|
||||||
|
}
|
||||||
|
|
||||||
state.reachable = false;
|
state.reachable = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -508,7 +584,7 @@ fn parse_function(
|
|||||||
Operator::F32Const { value } => {
|
Operator::F32Const { value } => {
|
||||||
let f = intrinsics
|
let f = intrinsics
|
||||||
.f32_ty
|
.f32_ty
|
||||||
.const_float(f64::from_bits(value.bits() as u64));
|
.const_float(f32::from_bits(value.bits()) as f64);
|
||||||
state.push1(f);
|
state.push1(f);
|
||||||
}
|
}
|
||||||
Operator::F64Const { value } => {
|
Operator::F64Const { value } => {
|
||||||
@ -562,15 +638,20 @@ fn parse_function(
|
|||||||
|
|
||||||
Operator::Select => {
|
Operator::Select => {
|
||||||
let (v1, v2, cond) = state.pop3()?;
|
let (v1, v2, cond) = state.pop3()?;
|
||||||
let cond = cond.into_int_value();
|
let cond_value = builder.build_int_compare(
|
||||||
let res = builder.build_select(cond, v1, v2, &state.var_name());
|
IntPredicate::NE,
|
||||||
|
cond.into_int_value(),
|
||||||
|
intrinsics.i32_zero,
|
||||||
|
&state.var_name(),
|
||||||
|
);
|
||||||
|
let res = builder.build_select(cond_value, v1, v2, &state.var_name());
|
||||||
state.push1(res);
|
state.push1(res);
|
||||||
}
|
}
|
||||||
Operator::Call { function_index } => {
|
Operator::Call { function_index } => {
|
||||||
let func_index = FuncIndex::new(function_index as usize);
|
let func_index = FuncIndex::new(function_index as usize);
|
||||||
let sigindex = info.func_assoc[func_index];
|
let sigindex = info.func_assoc[func_index];
|
||||||
let llvm_sig = signatures[sigindex];
|
let llvm_sig = signatures[sigindex];
|
||||||
let func_sig = &info.signatures[sig_index];
|
let func_sig = &info.signatures[sigindex];
|
||||||
|
|
||||||
let call_site = match func_index.local_or_import(info) {
|
let call_site = match func_index.local_or_import(info) {
|
||||||
LocalOrImport::Local(local_func_index) => {
|
LocalOrImport::Local(local_func_index) => {
|
||||||
@ -710,7 +791,6 @@ fn parse_function(
|
|||||||
let args: Vec<_> = std::iter::once(ctx_ptr)
|
let args: Vec<_> = std::iter::once(ctx_ptr)
|
||||||
.chain(pushed_args.into_iter())
|
.chain(pushed_args.into_iter())
|
||||||
.collect();
|
.collect();
|
||||||
println!("args: {:?}", args);
|
|
||||||
|
|
||||||
let typed_func_ptr = builder.build_pointer_cast(
|
let typed_func_ptr = builder.build_pointer_cast(
|
||||||
func_ptr,
|
func_ptr,
|
||||||
@ -948,22 +1028,24 @@ fn parse_function(
|
|||||||
}
|
}
|
||||||
Operator::I32Eqz => {
|
Operator::I32Eqz => {
|
||||||
let input = state.pop1()?.into_int_value();
|
let input = state.pop1()?.into_int_value();
|
||||||
let res = builder.build_int_compare(
|
let cond = builder.build_int_compare(
|
||||||
IntPredicate::EQ,
|
IntPredicate::EQ,
|
||||||
input,
|
input,
|
||||||
intrinsics.i32_zero,
|
intrinsics.i32_zero,
|
||||||
&state.var_name(),
|
&state.var_name(),
|
||||||
);
|
);
|
||||||
|
let res = builder.build_int_z_extend(cond, intrinsics.i32_ty, &state.var_name());
|
||||||
state.push1(res);
|
state.push1(res);
|
||||||
}
|
}
|
||||||
Operator::I64Eqz => {
|
Operator::I64Eqz => {
|
||||||
let input = state.pop1()?.into_int_value();
|
let input = state.pop1()?.into_int_value();
|
||||||
let res = builder.build_int_compare(
|
let cond = builder.build_int_compare(
|
||||||
IntPredicate::EQ,
|
IntPredicate::EQ,
|
||||||
input,
|
input,
|
||||||
intrinsics.i64_zero,
|
intrinsics.i64_zero,
|
||||||
&state.var_name(),
|
&state.var_name(),
|
||||||
);
|
);
|
||||||
|
let res = builder.build_int_z_extend(cond, intrinsics.i32_ty, &state.var_name());
|
||||||
state.push1(res);
|
state.push1(res);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1170,61 +1252,71 @@ fn parse_function(
|
|||||||
Operator::I32Eq | Operator::I64Eq => {
|
Operator::I32Eq | Operator::I64Eq => {
|
||||||
let (v1, v2) = state.pop2()?;
|
let (v1, v2) = state.pop2()?;
|
||||||
let (v1, v2) = (v1.into_int_value(), v2.into_int_value());
|
let (v1, v2) = (v1.into_int_value(), v2.into_int_value());
|
||||||
let res = builder.build_int_compare(IntPredicate::EQ, v1, v2, &state.var_name());
|
let cond = builder.build_int_compare(IntPredicate::EQ, v1, v2, &state.var_name());
|
||||||
|
let res = builder.build_int_z_extend(cond, intrinsics.i32_ty, &state.var_name());
|
||||||
state.push1(res);
|
state.push1(res);
|
||||||
}
|
}
|
||||||
Operator::I32Ne | Operator::I64Ne => {
|
Operator::I32Ne | Operator::I64Ne => {
|
||||||
let (v1, v2) = state.pop2()?;
|
let (v1, v2) = state.pop2()?;
|
||||||
let (v1, v2) = (v1.into_int_value(), v2.into_int_value());
|
let (v1, v2) = (v1.into_int_value(), v2.into_int_value());
|
||||||
let res = builder.build_int_compare(IntPredicate::NE, v1, v2, &state.var_name());
|
let cond = builder.build_int_compare(IntPredicate::NE, v1, v2, &state.var_name());
|
||||||
|
let res = builder.build_int_z_extend(cond, intrinsics.i32_ty, &state.var_name());
|
||||||
state.push1(res);
|
state.push1(res);
|
||||||
}
|
}
|
||||||
Operator::I32LtS | Operator::I64LtS => {
|
Operator::I32LtS | Operator::I64LtS => {
|
||||||
let (v1, v2) = state.pop2()?;
|
let (v1, v2) = state.pop2()?;
|
||||||
let (v1, v2) = (v1.into_int_value(), v2.into_int_value());
|
let (v1, v2) = (v1.into_int_value(), v2.into_int_value());
|
||||||
let res = builder.build_int_compare(IntPredicate::SLT, v1, v2, &state.var_name());
|
let cond = builder.build_int_compare(IntPredicate::SLT, v1, v2, &state.var_name());
|
||||||
|
let res = builder.build_int_z_extend(cond, intrinsics.i32_ty, &state.var_name());
|
||||||
state.push1(res);
|
state.push1(res);
|
||||||
}
|
}
|
||||||
Operator::I32LtU | Operator::I64LtU => {
|
Operator::I32LtU | Operator::I64LtU => {
|
||||||
let (v1, v2) = state.pop2()?;
|
let (v1, v2) = state.pop2()?;
|
||||||
let (v1, v2) = (v1.into_int_value(), v2.into_int_value());
|
let (v1, v2) = (v1.into_int_value(), v2.into_int_value());
|
||||||
let res = builder.build_int_compare(IntPredicate::ULT, v1, v2, &state.var_name());
|
let cond = builder.build_int_compare(IntPredicate::ULT, v1, v2, &state.var_name());
|
||||||
|
let res = builder.build_int_z_extend(cond, intrinsics.i32_ty, &state.var_name());
|
||||||
state.push1(res);
|
state.push1(res);
|
||||||
}
|
}
|
||||||
Operator::I32LeS | Operator::I64LeS => {
|
Operator::I32LeS | Operator::I64LeS => {
|
||||||
let (v1, v2) = state.pop2()?;
|
let (v1, v2) = state.pop2()?;
|
||||||
let (v1, v2) = (v1.into_int_value(), v2.into_int_value());
|
let (v1, v2) = (v1.into_int_value(), v2.into_int_value());
|
||||||
let res = builder.build_int_compare(IntPredicate::SLE, v1, v2, &state.var_name());
|
let cond = builder.build_int_compare(IntPredicate::SLE, v1, v2, &state.var_name());
|
||||||
|
let res = builder.build_int_z_extend(cond, intrinsics.i32_ty, &state.var_name());
|
||||||
state.push1(res);
|
state.push1(res);
|
||||||
}
|
}
|
||||||
Operator::I32LeU | Operator::I64LeU => {
|
Operator::I32LeU | Operator::I64LeU => {
|
||||||
let (v1, v2) = state.pop2()?;
|
let (v1, v2) = state.pop2()?;
|
||||||
let (v1, v2) = (v1.into_int_value(), v2.into_int_value());
|
let (v1, v2) = (v1.into_int_value(), v2.into_int_value());
|
||||||
let res = builder.build_int_compare(IntPredicate::ULE, v1, v2, &state.var_name());
|
let cond = builder.build_int_compare(IntPredicate::ULE, v1, v2, &state.var_name());
|
||||||
|
let res = builder.build_int_z_extend(cond, intrinsics.i32_ty, &state.var_name());
|
||||||
state.push1(res);
|
state.push1(res);
|
||||||
}
|
}
|
||||||
Operator::I32GtS | Operator::I64GtS => {
|
Operator::I32GtS | Operator::I64GtS => {
|
||||||
let (v1, v2) = state.pop2()?;
|
let (v1, v2) = state.pop2()?;
|
||||||
let (v1, v2) = (v1.into_int_value(), v2.into_int_value());
|
let (v1, v2) = (v1.into_int_value(), v2.into_int_value());
|
||||||
let res = builder.build_int_compare(IntPredicate::SGT, v1, v2, &state.var_name());
|
let cond = builder.build_int_compare(IntPredicate::SGT, v1, v2, &state.var_name());
|
||||||
|
let res = builder.build_int_z_extend(cond, intrinsics.i32_ty, &state.var_name());
|
||||||
state.push1(res);
|
state.push1(res);
|
||||||
}
|
}
|
||||||
Operator::I32GtU | Operator::I64GtU => {
|
Operator::I32GtU | Operator::I64GtU => {
|
||||||
let (v1, v2) = state.pop2()?;
|
let (v1, v2) = state.pop2()?;
|
||||||
let (v1, v2) = (v1.into_int_value(), v2.into_int_value());
|
let (v1, v2) = (v1.into_int_value(), v2.into_int_value());
|
||||||
let res = builder.build_int_compare(IntPredicate::UGT, v1, v2, &state.var_name());
|
let cond = builder.build_int_compare(IntPredicate::UGT, v1, v2, &state.var_name());
|
||||||
|
let res = builder.build_int_z_extend(cond, intrinsics.i32_ty, &state.var_name());
|
||||||
state.push1(res);
|
state.push1(res);
|
||||||
}
|
}
|
||||||
Operator::I32GeS | Operator::I64GeS => {
|
Operator::I32GeS | Operator::I64GeS => {
|
||||||
let (v1, v2) = state.pop2()?;
|
let (v1, v2) = state.pop2()?;
|
||||||
let (v1, v2) = (v1.into_int_value(), v2.into_int_value());
|
let (v1, v2) = (v1.into_int_value(), v2.into_int_value());
|
||||||
let res = builder.build_int_compare(IntPredicate::SGE, v1, v2, &state.var_name());
|
let cond = builder.build_int_compare(IntPredicate::SGE, v1, v2, &state.var_name());
|
||||||
|
let res = builder.build_int_z_extend(cond, intrinsics.i32_ty, &state.var_name());
|
||||||
state.push1(res);
|
state.push1(res);
|
||||||
}
|
}
|
||||||
Operator::I32GeU | Operator::I64GeU => {
|
Operator::I32GeU | Operator::I64GeU => {
|
||||||
let (v1, v2) = state.pop2()?;
|
let (v1, v2) = state.pop2()?;
|
||||||
let (v1, v2) = (v1.into_int_value(), v2.into_int_value());
|
let (v1, v2) = (v1.into_int_value(), v2.into_int_value());
|
||||||
let res = builder.build_int_compare(IntPredicate::UGE, v1, v2, &state.var_name());
|
let cond = builder.build_int_compare(IntPredicate::UGE, v1, v2, &state.var_name());
|
||||||
|
let res = builder.build_int_z_extend(cond, intrinsics.i32_ty, &state.var_name());
|
||||||
state.push1(res);
|
state.push1(res);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1235,43 +1327,49 @@ fn parse_function(
|
|||||||
Operator::F32Eq | Operator::F64Eq => {
|
Operator::F32Eq | Operator::F64Eq => {
|
||||||
let (v1, v2) = state.pop2()?;
|
let (v1, v2) = state.pop2()?;
|
||||||
let (v1, v2) = (v1.into_float_value(), v2.into_float_value());
|
let (v1, v2) = (v1.into_float_value(), v2.into_float_value());
|
||||||
let res =
|
let cond =
|
||||||
builder.build_float_compare(FloatPredicate::OEQ, v1, v2, &state.var_name());
|
builder.build_float_compare(FloatPredicate::OEQ, v1, v2, &state.var_name());
|
||||||
|
let res = builder.build_int_z_extend(cond, intrinsics.i32_ty, &state.var_name());
|
||||||
state.push1(res);
|
state.push1(res);
|
||||||
}
|
}
|
||||||
Operator::F32Ne | Operator::F64Ne => {
|
Operator::F32Ne | Operator::F64Ne => {
|
||||||
let (v1, v2) = state.pop2()?;
|
let (v1, v2) = state.pop2()?;
|
||||||
let (v1, v2) = (v1.into_float_value(), v2.into_float_value());
|
let (v1, v2) = (v1.into_float_value(), v2.into_float_value());
|
||||||
let res =
|
let cond =
|
||||||
builder.build_float_compare(FloatPredicate::UNE, v1, v2, &state.var_name());
|
builder.build_float_compare(FloatPredicate::UNE, v1, v2, &state.var_name());
|
||||||
|
let res = builder.build_int_z_extend(cond, intrinsics.i32_ty, &state.var_name());
|
||||||
state.push1(res);
|
state.push1(res);
|
||||||
}
|
}
|
||||||
Operator::F32Lt | Operator::F64Lt => {
|
Operator::F32Lt | Operator::F64Lt => {
|
||||||
let (v1, v2) = state.pop2()?;
|
let (v1, v2) = state.pop2()?;
|
||||||
let (v1, v2) = (v1.into_float_value(), v2.into_float_value());
|
let (v1, v2) = (v1.into_float_value(), v2.into_float_value());
|
||||||
let res =
|
let cond =
|
||||||
builder.build_float_compare(FloatPredicate::OLT, v1, v2, &state.var_name());
|
builder.build_float_compare(FloatPredicate::OLT, v1, v2, &state.var_name());
|
||||||
|
let res = builder.build_int_z_extend(cond, intrinsics.i32_ty, &state.var_name());
|
||||||
state.push1(res);
|
state.push1(res);
|
||||||
}
|
}
|
||||||
Operator::F32Le | Operator::F64Le => {
|
Operator::F32Le | Operator::F64Le => {
|
||||||
let (v1, v2) = state.pop2()?;
|
let (v1, v2) = state.pop2()?;
|
||||||
let (v1, v2) = (v1.into_float_value(), v2.into_float_value());
|
let (v1, v2) = (v1.into_float_value(), v2.into_float_value());
|
||||||
let res =
|
let cond =
|
||||||
builder.build_float_compare(FloatPredicate::OLE, v1, v2, &state.var_name());
|
builder.build_float_compare(FloatPredicate::OLE, v1, v2, &state.var_name());
|
||||||
|
let res = builder.build_int_z_extend(cond, intrinsics.i32_ty, &state.var_name());
|
||||||
state.push1(res);
|
state.push1(res);
|
||||||
}
|
}
|
||||||
Operator::F32Gt | Operator::F64Gt => {
|
Operator::F32Gt | Operator::F64Gt => {
|
||||||
let (v1, v2) = state.pop2()?;
|
let (v1, v2) = state.pop2()?;
|
||||||
let (v1, v2) = (v1.into_float_value(), v2.into_float_value());
|
let (v1, v2) = (v1.into_float_value(), v2.into_float_value());
|
||||||
let res =
|
let cond =
|
||||||
builder.build_float_compare(FloatPredicate::OGT, v1, v2, &state.var_name());
|
builder.build_float_compare(FloatPredicate::OGT, v1, v2, &state.var_name());
|
||||||
|
let res = builder.build_int_z_extend(cond, intrinsics.i32_ty, &state.var_name());
|
||||||
state.push1(res);
|
state.push1(res);
|
||||||
}
|
}
|
||||||
Operator::F32Ge | Operator::F64Ge => {
|
Operator::F32Ge | Operator::F64Ge => {
|
||||||
let (v1, v2) = state.pop2()?;
|
let (v1, v2) = state.pop2()?;
|
||||||
let (v1, v2) = (v1.into_float_value(), v2.into_float_value());
|
let (v1, v2) = (v1.into_float_value(), v2.into_float_value());
|
||||||
let res =
|
let cond =
|
||||||
builder.build_float_compare(FloatPredicate::OGE, v1, v2, &state.var_name());
|
builder.build_float_compare(FloatPredicate::OGE, v1, v2, &state.var_name());
|
||||||
|
let res = builder.build_int_z_extend(cond, intrinsics.i32_ty, &state.var_name());
|
||||||
state.push1(res);
|
state.push1(res);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1816,9 +1914,5 @@ fn resolve_memory_ptr(
|
|||||||
let mem_base_int = builder.build_ptr_to_int(mem_base, intrinsics.i64_ty, &state.var_name());
|
let mem_base_int = builder.build_ptr_to_int(mem_base, intrinsics.i64_ty, &state.var_name());
|
||||||
let effective_address_int =
|
let effective_address_int =
|
||||||
builder.build_int_add(mem_base_int, effective_offset, &state.var_name());
|
builder.build_int_add(mem_base_int, effective_offset, &state.var_name());
|
||||||
Ok(builder.build_int_to_ptr(
|
Ok(builder.build_int_to_ptr(effective_address_int, ptr_ty, &state.var_name()))
|
||||||
effective_address_int,
|
|
||||||
intrinsics.i32_ptr_ty,
|
|
||||||
&state.var_name(),
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
|
@ -639,7 +639,6 @@ impl<'a> CtxType<'a> {
|
|||||||
let int = builder.build_ptr_to_int(global_ptr, intrinsics.i64_ty, "global_ptr_int");
|
let int = builder.build_ptr_to_int(global_ptr, intrinsics.i64_ty, "global_ptr_int");
|
||||||
builder.build_int_to_ptr(int, llvm_ptr_ty, "global_ptr_typed")
|
builder.build_int_to_ptr(int, llvm_ptr_ty, "global_ptr_typed")
|
||||||
};
|
};
|
||||||
println!("global_ptr: {:?}", global_ptr_typed);
|
|
||||||
|
|
||||||
if mutable {
|
if mutable {
|
||||||
GlobalCache::Mut {
|
GlobalCache::Mut {
|
||||||
|
@ -9,12 +9,14 @@ use wasmer_runtime_core::{
|
|||||||
error::CompileError,
|
error::CompileError,
|
||||||
module::ModuleInner,
|
module::ModuleInner,
|
||||||
};
|
};
|
||||||
|
use wasmparser::{self, WasmDecoder};
|
||||||
|
|
||||||
mod backend;
|
mod backend;
|
||||||
mod code;
|
mod code;
|
||||||
mod intrinsics;
|
mod intrinsics;
|
||||||
mod read_info;
|
mod read_info;
|
||||||
mod state;
|
mod state;
|
||||||
|
mod trampolines;
|
||||||
|
|
||||||
pub struct LLVMCompiler {
|
pub struct LLVMCompiler {
|
||||||
_private: (),
|
_private: (),
|
||||||
@ -28,13 +30,15 @@ impl LLVMCompiler {
|
|||||||
|
|
||||||
impl Compiler for LLVMCompiler {
|
impl Compiler for LLVMCompiler {
|
||||||
fn compile(&self, wasm: &[u8], _: Token) -> Result<ModuleInner, CompileError> {
|
fn compile(&self, wasm: &[u8], _: Token) -> Result<ModuleInner, CompileError> {
|
||||||
|
validate(wasm)?;
|
||||||
|
|
||||||
let (info, code_reader) = read_info::read_module(wasm).unwrap();
|
let (info, code_reader) = read_info::read_module(wasm).unwrap();
|
||||||
let (module, intrinsics) = code::parse_function_bodies(&info, code_reader).unwrap();
|
let (module, intrinsics) = code::parse_function_bodies(&info, code_reader).unwrap();
|
||||||
|
|
||||||
let backend = backend::LLVMBackend::new(module, intrinsics);
|
let (backend, protected_caller) = backend::LLVMBackend::new(module, intrinsics);
|
||||||
|
|
||||||
// Create placeholder values here.
|
// Create placeholder values here.
|
||||||
let (protected_caller, cache_gen) = {
|
let cache_gen = {
|
||||||
use wasmer_runtime_core::backend::{
|
use wasmer_runtime_core::backend::{
|
||||||
sys::Memory, CacheGen, ProtectedCaller, UserTrapper,
|
sys::Memory, CacheGen, ProtectedCaller, UserTrapper,
|
||||||
};
|
};
|
||||||
@ -44,22 +48,6 @@ impl Compiler for LLVMCompiler {
|
|||||||
use wasmer_runtime_core::types::{FuncIndex, Value};
|
use wasmer_runtime_core::types::{FuncIndex, Value};
|
||||||
use wasmer_runtime_core::vm;
|
use wasmer_runtime_core::vm;
|
||||||
struct Placeholder;
|
struct Placeholder;
|
||||||
impl ProtectedCaller for Placeholder {
|
|
||||||
fn call(
|
|
||||||
&self,
|
|
||||||
_module: &ModuleInner,
|
|
||||||
_func_index: FuncIndex,
|
|
||||||
_params: &[Value],
|
|
||||||
_import_backing: &vm::ImportBacking,
|
|
||||||
_vmctx: *mut vm::Ctx,
|
|
||||||
_: Token,
|
|
||||||
) -> RuntimeResult<Vec<Value>> {
|
|
||||||
unimplemented!("the llvm-based backend does not yet implement ProtectedCaller")
|
|
||||||
}
|
|
||||||
fn get_early_trapper(&self) -> Box<dyn UserTrapper> {
|
|
||||||
Box::new(Placeholder)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl CacheGen for Placeholder {
|
impl CacheGen for Placeholder {
|
||||||
fn generate_cache(
|
fn generate_cache(
|
||||||
&self,
|
&self,
|
||||||
@ -68,18 +56,13 @@ impl Compiler for LLVMCompiler {
|
|||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl UserTrapper for Placeholder {
|
|
||||||
unsafe fn do_early_trap(&self, msg: String) -> ! {
|
|
||||||
unimplemented!("do early trap: {}", msg)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
(Box::new(Placeholder), Box::new(Placeholder))
|
Box::new(Placeholder)
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(ModuleInner {
|
Ok(ModuleInner {
|
||||||
func_resolver: Box::new(backend),
|
func_resolver: Box::new(backend),
|
||||||
protected_caller,
|
protected_caller: Box::new(protected_caller),
|
||||||
cache_gen,
|
cache_gen,
|
||||||
|
|
||||||
info,
|
info,
|
||||||
@ -91,6 +74,20 @@ impl Compiler for LLVMCompiler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn validate(bytes: &[u8]) -> Result<(), CompileError> {
|
||||||
|
let mut parser = wasmparser::ValidatingParser::new(bytes, None);
|
||||||
|
loop {
|
||||||
|
let state = parser.read();
|
||||||
|
match *state {
|
||||||
|
wasmparser::ParserState::EndWasm => break Ok(()),
|
||||||
|
wasmparser::ParserState::Error(err) => Err(CompileError::ValidationError {
|
||||||
|
msg: err.message.to_string(),
|
||||||
|
})?,
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_read_module() {
|
fn test_read_module() {
|
||||||
use std::mem::transmute;
|
use std::mem::transmute;
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
use crate::intrinsics::Intrinsics;
|
||||||
use inkwell::{
|
use inkwell::{
|
||||||
builder::Builder,
|
builder::Builder,
|
||||||
context::Context,
|
context::Context,
|
||||||
@ -9,30 +10,111 @@ use inkwell::{
|
|||||||
};
|
};
|
||||||
use wasmer_runtime_core::{
|
use wasmer_runtime_core::{
|
||||||
module::ModuleInfo,
|
module::ModuleInfo,
|
||||||
types::{SigIndex, FuncSig},
|
structures::{SliceMap, TypedIndex},
|
||||||
structures::{TypedIndex, SliceMap},
|
types::{FuncSig, SigIndex, Type},
|
||||||
};
|
};
|
||||||
use crate::intrinsics::Intrinsics;
|
|
||||||
|
|
||||||
pub fn generate_trampolines(info: &ModuleInfo, signatures: &SliceMap<SigIndex, FunctionType>, module: &Module, builder: &Builder, intrinsics: &Intrinsics) -> Result<(), String> {
|
|
||||||
let trampoline_sig = intrinsics.void_ty.fn_type(&[
|
|
||||||
intrinsics.ctx_ptr_ty, // vmctx ptr
|
|
||||||
intrinsics.i64_ptr_ty, // func ptr
|
|
||||||
intrinsics.i64_ptr_ty,
|
|
||||||
intrinsics.i64_ptr_ty,
|
|
||||||
], false);
|
|
||||||
|
|
||||||
|
pub fn generate_trampolines(
|
||||||
|
info: &ModuleInfo,
|
||||||
|
signatures: &SliceMap<SigIndex, FunctionType>,
|
||||||
|
module: &Module,
|
||||||
|
context: &Context,
|
||||||
|
builder: &Builder,
|
||||||
|
intrinsics: &Intrinsics,
|
||||||
|
) {
|
||||||
for (sig_index, sig) in info.signatures.iter() {
|
for (sig_index, sig) in info.signatures.iter() {
|
||||||
|
let func_type = signatures[sig_index];
|
||||||
|
|
||||||
}
|
let trampoline_sig = intrinsics.void_ty.fn_type(
|
||||||
}
|
&[
|
||||||
|
intrinsics.ctx_ptr_ty.as_basic_type_enum(), // vmctx ptr
|
||||||
|
func_type
|
||||||
|
.ptr_type(AddressSpace::Generic)
|
||||||
|
.as_basic_type_enum(), // func ptr
|
||||||
|
intrinsics.i64_ptr_ty.as_basic_type_enum(), // args ptr
|
||||||
|
intrinsics.i64_ptr_ty.as_basic_type_enum(), // returns ptr
|
||||||
|
],
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
|
||||||
pub fn generate_trampoline(sig_index: usize, trampoline_sig: FunctionType, sig: &FuncSig, builder: &Builder, intrinsics: &Intrinsics) {
|
let trampoline_func = module.add_function(
|
||||||
let function = module.add_function(
|
&format!("trmp{}", sig_index.index()),
|
||||||
&format!("tramp{}", sig_index.index()),
|
trampoline_sig,
|
||||||
signatures[sig_index],
|
|
||||||
Some(Linkage::External),
|
Some(Linkage::External),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
generate_trampoline(
|
||||||
|
trampoline_func,
|
||||||
|
func_type,
|
||||||
|
sig,
|
||||||
|
context,
|
||||||
|
builder,
|
||||||
|
intrinsics,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generate_trampoline(
|
||||||
|
trampoline_func: FunctionValue,
|
||||||
|
sig_type: FunctionType,
|
||||||
|
func_sig: &FuncSig,
|
||||||
|
context: &Context,
|
||||||
|
builder: &Builder,
|
||||||
|
intrinsics: &Intrinsics,
|
||||||
|
) {
|
||||||
|
let entry_block = context.append_basic_block(&trampoline_func, "entry");
|
||||||
|
builder.position_at_end(&entry_block);
|
||||||
|
|
||||||
|
let (vmctx_ptr, func_ptr, args_ptr, returns_ptr) = match trampoline_func.get_params().as_slice()
|
||||||
|
{
|
||||||
|
&[vmctx_ptr, func_ptr, args_ptr, returns_ptr] => (
|
||||||
|
vmctx_ptr,
|
||||||
|
func_ptr.into_pointer_value(),
|
||||||
|
args_ptr.into_pointer_value(),
|
||||||
|
returns_ptr.into_pointer_value(),
|
||||||
|
),
|
||||||
|
_ => unimplemented!(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let cast_ptr_ty = |wasmer_ty| match wasmer_ty {
|
||||||
|
Type::I32 => intrinsics.i32_ptr_ty,
|
||||||
|
Type::I64 => intrinsics.i64_ptr_ty,
|
||||||
|
Type::F32 => intrinsics.f32_ptr_ty,
|
||||||
|
Type::F64 => intrinsics.f64_ptr_ty,
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut args_vec = Vec::with_capacity(func_sig.params().len() + 1);
|
||||||
|
args_vec.push(vmctx_ptr);
|
||||||
|
|
||||||
|
for (i, param_ty) in func_sig.params().iter().enumerate() {
|
||||||
|
let index = intrinsics.i32_ty.const_int(i as _, false);
|
||||||
|
let item_pointer = unsafe { builder.build_in_bounds_gep(args_ptr, &[index], "arg_ptr") };
|
||||||
|
|
||||||
|
let casted_pointer_type = cast_ptr_ty(*param_ty);
|
||||||
|
|
||||||
|
let typed_item_pointer =
|
||||||
|
builder.build_pointer_cast(item_pointer, casted_pointer_type, "typed_arg_pointer");
|
||||||
|
|
||||||
|
let arg = builder.build_load(typed_item_pointer, "arg");
|
||||||
|
args_vec.push(arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
let call_site = builder.build_call(func_ptr, &args_vec, "call");
|
||||||
|
|
||||||
|
match func_sig.returns() {
|
||||||
|
&[] => {}
|
||||||
|
&[one_ret] => {
|
||||||
|
let ret_ptr_type = cast_ptr_ty(one_ret);
|
||||||
|
|
||||||
|
let typed_ret_ptr =
|
||||||
|
builder.build_pointer_cast(returns_ptr, ret_ptr_type, "typed_ret_ptr");
|
||||||
|
builder.build_store(
|
||||||
|
typed_ret_ptr,
|
||||||
|
call_site.try_as_basic_value().left().unwrap(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
_ => unimplemented!("multi-value returns"),
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.build_return(None);
|
||||||
}
|
}
|
@ -454,7 +454,7 @@ impl<'a> DynFunc<'a> {
|
|||||||
/// # Ok(())
|
/// # Ok(())
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
pub fn call(&mut self, params: &[Value]) -> CallResult<Vec<Value>> {
|
pub fn call(&self, params: &[Value]) -> CallResult<Vec<Value>> {
|
||||||
if !self.signature.check_param_value_types(params) {
|
if !self.signature.check_param_value_types(params) {
|
||||||
Err(ResolveError::Signature {
|
Err(ResolveError::Signature {
|
||||||
expected: (*self.signature).clone(),
|
expected: (*self.signature).clone(),
|
||||||
|
@ -1,23 +1,16 @@
|
|||||||
use wasmer_runtime::{compile, error, imports, Func};
|
use wasmer_runtime::{compile, error, imports, Func, Value};
|
||||||
|
|
||||||
use wabt::wat2wasm;
|
use wabt::wat2wasm;
|
||||||
|
|
||||||
static WAT: &'static str = r#"
|
static WAT: &'static str = r#"
|
||||||
(module
|
(module
|
||||||
(type $t0 (func (param i32) (result i32)))
|
(type (;0;) (func (result i32)))
|
||||||
(type $t1 (func (result i32)))
|
(func (;0;) (type 0) (result i32)
|
||||||
(memory 1)
|
block (result i32) ;; label = @1
|
||||||
(global $g0 (mut i32) (i32.const 0))
|
i32.const 1
|
||||||
(export "foo" (func $foo))
|
end
|
||||||
(func $foo (type $t0) (param i32) (result i32)
|
return)
|
||||||
get_local 0
|
(export "as-return-value" (func 0))
|
||||||
call $bar
|
|
||||||
)
|
|
||||||
(func $bar (type $t0) (param i32) (result i32)
|
|
||||||
get_local 0
|
|
||||||
i32.const 10
|
|
||||||
i32.add
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
"#;
|
"#;
|
||||||
|
|
||||||
@ -32,11 +25,12 @@ fn main() -> Result<(), error::Error> {
|
|||||||
|
|
||||||
let imports = imports! {};
|
let imports = imports! {};
|
||||||
|
|
||||||
|
println!("instantiating");
|
||||||
let instance = module.instantiate(&imports)?;
|
let instance = module.instantiate(&imports)?;
|
||||||
|
|
||||||
let foo: Func<i32, i32> = instance.func("foo")?;
|
let foo = instance.dyn_func("as-call-value")?;
|
||||||
|
|
||||||
let result = foo.call(42);
|
let result = foo.call(&[]);
|
||||||
|
|
||||||
println!("result: {:?}", result);
|
println!("result: {:?}", result);
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user