mirror of
https://github.com/fluencelabs/wasmer
synced 2025-06-13 00:51:20 +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::{
|
||||
ffi::CString,
|
||||
mem,
|
||||
ptr::{self, NonNull},
|
||||
};
|
||||
use wasmer_runtime_core::{
|
||||
backend::FuncResolver,
|
||||
backend::{FuncResolver, ProtectedCaller, Token, UserTrapper},
|
||||
error::RuntimeResult,
|
||||
export::Context,
|
||||
module::{ModuleInfo, ModuleInner},
|
||||
structures::TypedIndex,
|
||||
types::LocalFuncIndex,
|
||||
vm,
|
||||
types::{FuncIndex, FuncSig, LocalFuncIndex, LocalOrImport, SigIndex, Type, Value},
|
||||
vm::{self, ImportBacking},
|
||||
};
|
||||
|
||||
#[repr(C)]
|
||||
@ -155,7 +158,7 @@ pub struct LLVMBackend {
|
||||
}
|
||||
|
||||
impl LLVMBackend {
|
||||
pub fn new(module: Module, intrinsics: Intrinsics) -> Self {
|
||||
pub fn new(module: Module, intrinsics: Intrinsics) -> (Self, LLVMProtectedCaller) {
|
||||
Target::initialize_x86(&InitializationConfig {
|
||||
asm_parser: true,
|
||||
asm_printer: true,
|
||||
@ -198,10 +201,13 @@ impl LLVMBackend {
|
||||
panic!("failed to load object")
|
||||
}
|
||||
|
||||
Self {
|
||||
module,
|
||||
memory_buffer,
|
||||
}
|
||||
(
|
||||
Self {
|
||||
module,
|
||||
memory_buffer,
|
||||
},
|
||||
LLVMProtectedCaller { module },
|
||||
)
|
||||
}
|
||||
|
||||
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) {
|
||||
use capstone::arch::BuildsCapstone;
|
||||
let mut cs = capstone::Capstone::new() // Call builder-pattern
|
||||
|
@ -24,6 +24,7 @@ use wasmparser::{
|
||||
use crate::intrinsics::{CtxType, GlobalCache, Intrinsics};
|
||||
use crate::read_info::type_to_type;
|
||||
use crate::state::{ControlFrame, IfElseState, State};
|
||||
use crate::trampolines::generate_trampolines;
|
||||
|
||||
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));
|
||||
@ -102,10 +103,19 @@ pub fn parse_function_bodies(
|
||||
LocalFuncIndex::new(local_func_index),
|
||||
locals_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();
|
||||
pass_manager.add_verifier_pass();
|
||||
pass_manager.add_function_inlining_pass();
|
||||
pass_manager.add_promote_memory_to_register_pass();
|
||||
pass_manager.add_cfg_simplification_pass();
|
||||
@ -116,7 +126,6 @@ pub fn parse_function_bodies(
|
||||
pass_manager.add_gvn_pass();
|
||||
// pass_manager.add_new_gvn_pass();
|
||||
pass_manager.add_aggressive_dce_pass();
|
||||
// pass_manager.add_verifier_pass();
|
||||
pass_manager.run_on_module(&module);
|
||||
|
||||
Ok((module, intrinsics))
|
||||
@ -209,8 +218,34 @@ fn parse_function(
|
||||
locals.push(alloca);
|
||||
}
|
||||
|
||||
let mut unreachable_depth = 0;
|
||||
|
||||
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.
|
||||
* 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[..]);
|
||||
|
||||
state.popn(res_len)?;
|
||||
builder.build_unreachable();
|
||||
state.reachable = false;
|
||||
}
|
||||
Operator::If { ty } => {
|
||||
let current_block = builder.get_insert_block().ok_or(BinaryReaderError {
|
||||
@ -440,8 +475,22 @@ fn parse_function(
|
||||
builder.build_unconditional_branch(frame.code_after());
|
||||
|
||||
for phi in frame.phis().iter().rev() {
|
||||
let value = state.pop1()?;
|
||||
phi.add_incoming(&[(&value, ¤t_block)])
|
||||
if phi.count_incoming() != 0 {
|
||||
let value = state.pop1()?;
|
||||
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.
|
||||
for phi in frame.phis() {
|
||||
state.push1(phi.as_basic_value());
|
||||
if phi.count_incoming() != 0 {
|
||||
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 => {
|
||||
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());
|
||||
|
||||
let phis = frame.phis().to_vec();
|
||||
|
||||
for phi in phis.iter() {
|
||||
let arg = state.pop1()?;
|
||||
phi.add_incoming(&[(&arg, ¤t_block)]);
|
||||
}
|
||||
|
||||
state.reachable = false;
|
||||
}
|
||||
|
||||
@ -508,7 +584,7 @@ fn parse_function(
|
||||
Operator::F32Const { value } => {
|
||||
let f = intrinsics
|
||||
.f32_ty
|
||||
.const_float(f64::from_bits(value.bits() as u64));
|
||||
.const_float(f32::from_bits(value.bits()) as f64);
|
||||
state.push1(f);
|
||||
}
|
||||
Operator::F64Const { value } => {
|
||||
@ -562,15 +638,20 @@ fn parse_function(
|
||||
|
||||
Operator::Select => {
|
||||
let (v1, v2, cond) = state.pop3()?;
|
||||
let cond = cond.into_int_value();
|
||||
let res = builder.build_select(cond, v1, v2, &state.var_name());
|
||||
let cond_value = builder.build_int_compare(
|
||||
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);
|
||||
}
|
||||
Operator::Call { function_index } => {
|
||||
let func_index = FuncIndex::new(function_index as usize);
|
||||
let sigindex = info.func_assoc[func_index];
|
||||
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) {
|
||||
LocalOrImport::Local(local_func_index) => {
|
||||
@ -710,7 +791,6 @@ fn parse_function(
|
||||
let args: Vec<_> = std::iter::once(ctx_ptr)
|
||||
.chain(pushed_args.into_iter())
|
||||
.collect();
|
||||
println!("args: {:?}", args);
|
||||
|
||||
let typed_func_ptr = builder.build_pointer_cast(
|
||||
func_ptr,
|
||||
@ -948,22 +1028,24 @@ fn parse_function(
|
||||
}
|
||||
Operator::I32Eqz => {
|
||||
let input = state.pop1()?.into_int_value();
|
||||
let res = builder.build_int_compare(
|
||||
let cond = builder.build_int_compare(
|
||||
IntPredicate::EQ,
|
||||
input,
|
||||
intrinsics.i32_zero,
|
||||
&state.var_name(),
|
||||
);
|
||||
let res = builder.build_int_z_extend(cond, intrinsics.i32_ty, &state.var_name());
|
||||
state.push1(res);
|
||||
}
|
||||
Operator::I64Eqz => {
|
||||
let input = state.pop1()?.into_int_value();
|
||||
let res = builder.build_int_compare(
|
||||
let cond = builder.build_int_compare(
|
||||
IntPredicate::EQ,
|
||||
input,
|
||||
intrinsics.i64_zero,
|
||||
&state.var_name(),
|
||||
);
|
||||
let res = builder.build_int_z_extend(cond, intrinsics.i32_ty, &state.var_name());
|
||||
state.push1(res);
|
||||
}
|
||||
|
||||
@ -1170,61 +1252,71 @@ fn parse_function(
|
||||
Operator::I32Eq | Operator::I64Eq => {
|
||||
let (v1, v2) = state.pop2()?;
|
||||
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);
|
||||
}
|
||||
Operator::I32Ne | Operator::I64Ne => {
|
||||
let (v1, v2) = state.pop2()?;
|
||||
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);
|
||||
}
|
||||
Operator::I32LtS | Operator::I64LtS => {
|
||||
let (v1, v2) = state.pop2()?;
|
||||
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);
|
||||
}
|
||||
Operator::I32LtU | Operator::I64LtU => {
|
||||
let (v1, v2) = state.pop2()?;
|
||||
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);
|
||||
}
|
||||
Operator::I32LeS | Operator::I64LeS => {
|
||||
let (v1, v2) = state.pop2()?;
|
||||
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);
|
||||
}
|
||||
Operator::I32LeU | Operator::I64LeU => {
|
||||
let (v1, v2) = state.pop2()?;
|
||||
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);
|
||||
}
|
||||
Operator::I32GtS | Operator::I64GtS => {
|
||||
let (v1, v2) = state.pop2()?;
|
||||
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);
|
||||
}
|
||||
Operator::I32GtU | Operator::I64GtU => {
|
||||
let (v1, v2) = state.pop2()?;
|
||||
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);
|
||||
}
|
||||
Operator::I32GeS | Operator::I64GeS => {
|
||||
let (v1, v2) = state.pop2()?;
|
||||
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);
|
||||
}
|
||||
Operator::I32GeU | Operator::I64GeU => {
|
||||
let (v1, v2) = state.pop2()?;
|
||||
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);
|
||||
}
|
||||
|
||||
@ -1235,43 +1327,49 @@ fn parse_function(
|
||||
Operator::F32Eq | Operator::F64Eq => {
|
||||
let (v1, v2) = state.pop2()?;
|
||||
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());
|
||||
let res = builder.build_int_z_extend(cond, intrinsics.i32_ty, &state.var_name());
|
||||
state.push1(res);
|
||||
}
|
||||
Operator::F32Ne | Operator::F64Ne => {
|
||||
let (v1, v2) = state.pop2()?;
|
||||
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());
|
||||
let res = builder.build_int_z_extend(cond, intrinsics.i32_ty, &state.var_name());
|
||||
state.push1(res);
|
||||
}
|
||||
Operator::F32Lt | Operator::F64Lt => {
|
||||
let (v1, v2) = state.pop2()?;
|
||||
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());
|
||||
let res = builder.build_int_z_extend(cond, intrinsics.i32_ty, &state.var_name());
|
||||
state.push1(res);
|
||||
}
|
||||
Operator::F32Le | Operator::F64Le => {
|
||||
let (v1, v2) = state.pop2()?;
|
||||
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());
|
||||
let res = builder.build_int_z_extend(cond, intrinsics.i32_ty, &state.var_name());
|
||||
state.push1(res);
|
||||
}
|
||||
Operator::F32Gt | Operator::F64Gt => {
|
||||
let (v1, v2) = state.pop2()?;
|
||||
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());
|
||||
let res = builder.build_int_z_extend(cond, intrinsics.i32_ty, &state.var_name());
|
||||
state.push1(res);
|
||||
}
|
||||
Operator::F32Ge | Operator::F64Ge => {
|
||||
let (v1, v2) = state.pop2()?;
|
||||
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());
|
||||
let res = builder.build_int_z_extend(cond, intrinsics.i32_ty, &state.var_name());
|
||||
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 effective_address_int =
|
||||
builder.build_int_add(mem_base_int, effective_offset, &state.var_name());
|
||||
Ok(builder.build_int_to_ptr(
|
||||
effective_address_int,
|
||||
intrinsics.i32_ptr_ty,
|
||||
&state.var_name(),
|
||||
))
|
||||
Ok(builder.build_int_to_ptr(effective_address_int, 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");
|
||||
builder.build_int_to_ptr(int, llvm_ptr_ty, "global_ptr_typed")
|
||||
};
|
||||
println!("global_ptr: {:?}", global_ptr_typed);
|
||||
|
||||
if mutable {
|
||||
GlobalCache::Mut {
|
||||
|
@ -9,12 +9,14 @@ use wasmer_runtime_core::{
|
||||
error::CompileError,
|
||||
module::ModuleInner,
|
||||
};
|
||||
use wasmparser::{self, WasmDecoder};
|
||||
|
||||
mod backend;
|
||||
mod code;
|
||||
mod intrinsics;
|
||||
mod read_info;
|
||||
mod state;
|
||||
mod trampolines;
|
||||
|
||||
pub struct LLVMCompiler {
|
||||
_private: (),
|
||||
@ -28,13 +30,15 @@ impl LLVMCompiler {
|
||||
|
||||
impl Compiler for LLVMCompiler {
|
||||
fn compile(&self, wasm: &[u8], _: Token) -> Result<ModuleInner, CompileError> {
|
||||
validate(wasm)?;
|
||||
|
||||
let (info, code_reader) = read_info::read_module(wasm).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.
|
||||
let (protected_caller, cache_gen) = {
|
||||
let cache_gen = {
|
||||
use wasmer_runtime_core::backend::{
|
||||
sys::Memory, CacheGen, ProtectedCaller, UserTrapper,
|
||||
};
|
||||
@ -44,22 +48,6 @@ impl Compiler for LLVMCompiler {
|
||||
use wasmer_runtime_core::types::{FuncIndex, Value};
|
||||
use wasmer_runtime_core::vm;
|
||||
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 {
|
||||
fn generate_cache(
|
||||
&self,
|
||||
@ -68,18 +56,13 @@ impl Compiler for LLVMCompiler {
|
||||
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 {
|
||||
func_resolver: Box::new(backend),
|
||||
protected_caller,
|
||||
protected_caller: Box::new(protected_caller),
|
||||
cache_gen,
|
||||
|
||||
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]
|
||||
fn test_read_module() {
|
||||
use std::mem::transmute;
|
||||
|
@ -1,3 +1,4 @@
|
||||
use crate::intrinsics::Intrinsics;
|
||||
use inkwell::{
|
||||
builder::Builder,
|
||||
context::Context,
|
||||
@ -9,30 +10,111 @@ use inkwell::{
|
||||
};
|
||||
use wasmer_runtime_core::{
|
||||
module::ModuleInfo,
|
||||
types::{SigIndex, FuncSig},
|
||||
structures::{TypedIndex, SliceMap},
|
||||
structures::{SliceMap, TypedIndex},
|
||||
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() {
|
||||
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,
|
||||
);
|
||||
|
||||
let trampoline_func = module.add_function(
|
||||
&format!("trmp{}", sig_index.index()),
|
||||
trampoline_sig,
|
||||
Some(Linkage::External),
|
||||
);
|
||||
|
||||
generate_trampoline(
|
||||
trampoline_func,
|
||||
func_type,
|
||||
sig,
|
||||
context,
|
||||
builder,
|
||||
intrinsics,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn generate_trampoline(sig_index: usize, trampoline_sig: FunctionType, sig: &FuncSig, builder: &Builder, intrinsics: &Intrinsics) {
|
||||
let function = module.add_function(
|
||||
&format!("tramp{}", sig_index.index()),
|
||||
signatures[sig_index],
|
||||
Some(Linkage::External),
|
||||
);
|
||||
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);
|
||||
}
|
||||
|
Reference in New Issue
Block a user