mirror of
https://github.com/fluencelabs/wasmer
synced 2025-06-13 00:51:20 +00:00
Implement many wasm instructions
This commit is contained in:
1
lib/llvm-backend/.llvmenv
Normal file
1
lib/llvm-backend/.llvmenv
Normal file
@ -0,0 +1 @@
|
||||
/usr/local/opt/llvm/bin
|
17
lib/llvm-backend/Cargo.toml
Normal file
17
lib/llvm-backend/Cargo.toml
Normal file
@ -0,0 +1,17 @@
|
||||
[package]
|
||||
name = "llvm-backend"
|
||||
version = "0.1.0"
|
||||
authors = ["Lachlan Sneff <lachlan.sneff@gmail.com>"]
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
wasmer-runtime-core = { path = "../runtime-core", version = "0.1.2" }
|
||||
wasmparser = "0.28.0"
|
||||
inkwell = { git = "https://github.com/TheDan64/inkwell", branch = "llvm7-0" }
|
||||
hashbrown = "0.1.8"
|
||||
|
||||
[dev-dependencies]
|
||||
wabt = "0.7.4"
|
||||
|
||||
[features]
|
||||
debug = ["wasmer-runtime-core/debug"]
|
765
lib/llvm-backend/src/code.rs
Normal file
765
lib/llvm-backend/src/code.rs
Normal file
@ -0,0 +1,765 @@
|
||||
use hashbrown::HashMap;
|
||||
use inkwell::{
|
||||
basic_block::BasicBlock,
|
||||
builder::Builder,
|
||||
context::Context,
|
||||
module::Module,
|
||||
types::{BasicType, BasicTypeEnum, FunctionType},
|
||||
values::{AggregateValue, BasicValue, BasicValueEnum, FunctionValue},
|
||||
IntPredicate,
|
||||
};
|
||||
use wasmer_runtime_core::{
|
||||
module::ModuleInfo,
|
||||
structures::{Map, SliceMap, TypedIndex},
|
||||
types::{FuncIndex, FuncSig, LocalFuncIndex, LocalOrImport, SigIndex, Type},
|
||||
};
|
||||
use wasmparser::{BinaryReaderError, CodeSectionReader, LocalsReader, Operator, OperatorsReader};
|
||||
|
||||
use crate::intrinsics::Intrinsics;
|
||||
use crate::read_info::type_to_type;
|
||||
use crate::state::State;
|
||||
|
||||
fn func_sig_to_llvm(context: &Context, sig: &FuncSig) -> FunctionType {
|
||||
let param_types: Vec<_> = sig
|
||||
.params()
|
||||
.iter()
|
||||
.map(|&ty| type_to_llvm(context, ty))
|
||||
.collect();
|
||||
|
||||
match sig.returns() {
|
||||
[] => context.void_type().fn_type(¶m_types, false),
|
||||
[single_value] => type_to_llvm(context, *single_value).fn_type(¶m_types, false),
|
||||
returns @ _ => {
|
||||
let basic_types: Vec<_> = returns
|
||||
.iter()
|
||||
.map(|&ty| type_to_llvm(context, ty))
|
||||
.collect();
|
||||
|
||||
context
|
||||
.struct_type(&basic_types, false)
|
||||
.fn_type(¶m_types, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn type_to_llvm(context: &Context, ty: Type) -> BasicTypeEnum {
|
||||
match ty {
|
||||
Type::I32 => context.i32_type().as_basic_type_enum(),
|
||||
Type::I64 => context.i64_type().as_basic_type_enum(),
|
||||
Type::F32 => context.f32_type().as_basic_type_enum(),
|
||||
Type::F64 => context.f64_type().as_basic_type_enum(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_function_bodies(
|
||||
info: &ModuleInfo,
|
||||
code_reader: CodeSectionReader,
|
||||
) -> Result<(), BinaryReaderError> {
|
||||
let context = Context::create();
|
||||
let module = context.create_module("module");
|
||||
let builder = context.create_builder();
|
||||
|
||||
let intrinsics = Intrinsics::declare(&module, &context);
|
||||
|
||||
let signatures: Map<SigIndex, FunctionType> = info
|
||||
.signatures
|
||||
.iter()
|
||||
.map(|(_, sig)| func_sig_to_llvm(&context, sig))
|
||||
.collect();
|
||||
let functions: Map<LocalFuncIndex, FunctionValue> = info
|
||||
.func_assoc
|
||||
.iter()
|
||||
.skip(info.imported_functions.len())
|
||||
.map(|(func_index, &sig_index)| {
|
||||
module.add_function(
|
||||
&format!("fn:{}", func_index.index()),
|
||||
signatures[sig_index],
|
||||
None,
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
|
||||
for (local_func_index, body) in code_reader.into_iter().enumerate() {
|
||||
let body = body?;
|
||||
|
||||
let locals_reader = body.get_locals_reader()?;
|
||||
let op_reader = body.get_operators_reader()?;
|
||||
|
||||
parse_function(
|
||||
&context,
|
||||
&module,
|
||||
&builder,
|
||||
&intrinsics,
|
||||
info,
|
||||
&signatures,
|
||||
&functions,
|
||||
LocalFuncIndex::new(local_func_index),
|
||||
locals_reader,
|
||||
op_reader,
|
||||
)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn parse_function(
|
||||
context: &Context,
|
||||
module: &Module,
|
||||
builder: &Builder,
|
||||
intrinsics: &Intrinsics,
|
||||
info: &ModuleInfo,
|
||||
signatures: &SliceMap<SigIndex, FunctionType>,
|
||||
functions: &SliceMap<LocalFuncIndex, FunctionValue>,
|
||||
func_index: LocalFuncIndex,
|
||||
locals_reader: LocalsReader,
|
||||
op_reader: OperatorsReader,
|
||||
) -> Result<(), BinaryReaderError> {
|
||||
let llvm_sig = &signatures[info.func_assoc[func_index.convert_up(info)]];
|
||||
|
||||
let function = functions[func_index];
|
||||
let entry_block = context.append_basic_block(&function, "entry");
|
||||
builder.position_at_end(&entry_block);
|
||||
|
||||
let mut state = State::new();
|
||||
|
||||
let mut locals = Vec::with_capacity(locals_reader.get_count() as usize);
|
||||
locals.extend(function.get_param_iter().enumerate().map(|(index, param)| {
|
||||
let ty = param.get_type();
|
||||
|
||||
let alloca = builder.build_alloca(ty, &state.var_name());
|
||||
builder.build_store(alloca, param);
|
||||
alloca
|
||||
}));
|
||||
|
||||
for (index, local) in locals_reader.into_iter().enumerate().skip(locals.len()) {
|
||||
let (_, ty) = local?;
|
||||
|
||||
let wasmer_ty = type_to_type(ty)?;
|
||||
|
||||
let ty = type_to_llvm(context, wasmer_ty);
|
||||
|
||||
let alloca = builder.build_alloca(ty, &state.var_name());
|
||||
|
||||
let default_value = match wasmer_ty {
|
||||
Type::I32 => context.i32_type().const_int(0, false).as_basic_value_enum(),
|
||||
Type::I64 => context.i64_type().const_int(0, false).as_basic_value_enum(),
|
||||
Type::F32 => context.f32_type().const_float(0.0).as_basic_value_enum(),
|
||||
Type::F64 => context.f64_type().const_float(0.0).as_basic_value_enum(),
|
||||
};
|
||||
|
||||
builder.build_store(alloca, default_value);
|
||||
|
||||
locals.push(alloca);
|
||||
}
|
||||
|
||||
for op in op_reader {
|
||||
match op? {
|
||||
/***************************
|
||||
* Basic instructions.
|
||||
* https://github.com/sunfishcode/wasm-reference-manual/blob/master/WebAssembly.md#basic-instructions
|
||||
***************************/
|
||||
Operator::Nop => {
|
||||
// Do nothing.
|
||||
}
|
||||
Operator::Drop => {
|
||||
state.pop1()?;
|
||||
}
|
||||
|
||||
// Generate const values.
|
||||
Operator::I32Const { value } => {
|
||||
let i = context.i32_type().const_int(value as u64, false);
|
||||
state.push1(i);
|
||||
}
|
||||
Operator::I64Const { value } => {
|
||||
let i = context.i64_type().const_int(value as u64, false);
|
||||
state.push1(i);
|
||||
}
|
||||
Operator::F32Const { value } => {
|
||||
let f = context
|
||||
.f32_type()
|
||||
.const_float(f64::from_bits(value.bits() as u64));
|
||||
state.push1(f);
|
||||
}
|
||||
Operator::F64Const { value } => {
|
||||
let f = context.f64_type().const_float(f64::from_bits(value.bits()));
|
||||
state.push1(f);
|
||||
}
|
||||
|
||||
// Operate on locals.
|
||||
Operator::GetLocal { local_index } => {
|
||||
let pointer_value = locals[local_index as usize];
|
||||
let v = builder.build_load(pointer_value, &state.var_name());
|
||||
state.push1(v);
|
||||
}
|
||||
Operator::SetLocal { local_index } => {
|
||||
let pointer_value = locals[local_index as usize];
|
||||
let v = state.pop1()?;
|
||||
builder.build_store(pointer_value, v);
|
||||
}
|
||||
Operator::TeeLocal { local_index } => {
|
||||
let pointer_value = locals[local_index as usize];
|
||||
let v = state.peek1()?;
|
||||
builder.build_store(pointer_value, v);
|
||||
}
|
||||
|
||||
Operator::GetGlobal { global_index } => unimplemented!(),
|
||||
Operator::SetGlobal { global_index } => unimplemented!(),
|
||||
|
||||
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());
|
||||
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];
|
||||
|
||||
match func_index.local_or_import(info) {
|
||||
LocalOrImport::Local(local_func_index) => {
|
||||
let func_sig = &info.signatures[sigindex];
|
||||
let func_value = functions[local_func_index];
|
||||
let call_site = builder.build_call(
|
||||
func_value,
|
||||
&state.peekn(func_sig.params().len())?.to_vec(),
|
||||
&state.var_name(),
|
||||
);
|
||||
if let Some(basic_value) = call_site.try_as_basic_value().left() {
|
||||
match func_sig.returns().len() {
|
||||
1 => state.push1(basic_value),
|
||||
count @ _ => {
|
||||
// This is a multi-value return.
|
||||
let struct_value = basic_value.into_struct_value();
|
||||
for i in 0..(count as u32) {
|
||||
let value = builder.build_extract_value(
|
||||
struct_value,
|
||||
i,
|
||||
&state.var_name(),
|
||||
);
|
||||
state.push1(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
LocalOrImport::Import(import_func_index) => {
|
||||
// unimplemented!()
|
||||
}
|
||||
}
|
||||
}
|
||||
Operator::CallIndirect { index, table_index } => {
|
||||
unimplemented!("{}, {}", index, table_index);
|
||||
}
|
||||
|
||||
/***************************
|
||||
* Integer Arithmetic instructions.
|
||||
* https://github.com/sunfishcode/wasm-reference-manual/blob/master/WebAssembly.md#integer-arithmetic-instructions
|
||||
***************************/
|
||||
Operator::I32Add | Operator::I64Add => {
|
||||
let (v1, v2) = state.pop2()?;
|
||||
let (v1, v2) = (v1.into_int_value(), v2.into_int_value());
|
||||
let res = builder.build_int_add(v1, v2, &state.var_name());
|
||||
state.push1(res);
|
||||
}
|
||||
Operator::I32Sub | Operator::I64Sub => {
|
||||
let (v1, v2) = state.pop2()?;
|
||||
let (v1, v2) = (v1.into_int_value(), v2.into_int_value());
|
||||
let res = builder.build_int_sub(v1, v2, &state.var_name());
|
||||
state.push1(res);
|
||||
}
|
||||
Operator::I32Mul | Operator::I64Mul => {
|
||||
let (v1, v2) = state.pop2()?;
|
||||
let (v1, v2) = (v1.into_int_value(), v2.into_int_value());
|
||||
let res = builder.build_int_mul(v1, v2, &state.var_name());
|
||||
state.push1(res);
|
||||
}
|
||||
Operator::I32DivS | Operator::I64DivS => {
|
||||
let (v1, v2) = state.pop2()?;
|
||||
let (v1, v2) = (v1.into_int_value(), v2.into_int_value());
|
||||
let res = builder.build_int_signed_div(v1, v2, &state.var_name());
|
||||
state.push1(res);
|
||||
}
|
||||
Operator::I32DivU | Operator::I64DivU => {
|
||||
let (v1, v2) = state.pop2()?;
|
||||
let (v1, v2) = (v1.into_int_value(), v2.into_int_value());
|
||||
let res = builder.build_int_unsigned_div(v1, v2, &state.var_name());
|
||||
state.push1(res);
|
||||
}
|
||||
Operator::I32RemS | Operator::I64RemS => {
|
||||
let (v1, v2) = state.pop2()?;
|
||||
let (v1, v2) = (v1.into_int_value(), v2.into_int_value());
|
||||
let res = builder.build_int_signed_rem(v1, v2, &state.var_name());
|
||||
state.push1(res);
|
||||
}
|
||||
Operator::I32RemU | Operator::I64RemU => {
|
||||
let (v1, v2) = state.pop2()?;
|
||||
let (v1, v2) = (v1.into_int_value(), v2.into_int_value());
|
||||
let res = builder.build_int_unsigned_rem(v1, v2, &state.var_name());
|
||||
state.push1(res);
|
||||
}
|
||||
Operator::I32And | Operator::I64And => {
|
||||
let (v1, v2) = state.pop2()?;
|
||||
let (v1, v2) = (v1.into_int_value(), v2.into_int_value());
|
||||
let res = builder.build_and(v1, v2, &state.var_name());
|
||||
state.push1(res);
|
||||
}
|
||||
Operator::I32Or | Operator::I64Or => {
|
||||
let (v1, v2) = state.pop2()?;
|
||||
let (v1, v2) = (v1.into_int_value(), v2.into_int_value());
|
||||
let res = builder.build_or(v1, v2, &state.var_name());
|
||||
state.push1(res);
|
||||
}
|
||||
Operator::I32Xor | Operator::I64Xor => {
|
||||
let (v1, v2) = state.pop2()?;
|
||||
let (v1, v2) = (v1.into_int_value(), v2.into_int_value());
|
||||
let res = builder.build_xor(v1, v2, &state.var_name());
|
||||
state.push1(res);
|
||||
}
|
||||
Operator::I32Shl | Operator::I64Shl => {
|
||||
let (v1, v2) = state.pop2()?;
|
||||
let (v1, v2) = (v1.into_int_value(), v2.into_int_value());
|
||||
let res = builder.build_left_shift(v1, v2, &state.var_name());
|
||||
state.push1(res);
|
||||
}
|
||||
Operator::I32ShrS | Operator::I64ShrS => {
|
||||
let (v1, v2) = state.pop2()?;
|
||||
let (v1, v2) = (v1.into_int_value(), v2.into_int_value());
|
||||
let res = builder.build_right_shift(v1, v2, true, &state.var_name());
|
||||
state.push1(res);
|
||||
}
|
||||
Operator::I32ShrU | Operator::I64ShrU => {
|
||||
let (v1, v2) = state.pop2()?;
|
||||
let (v1, v2) = (v1.into_int_value(), v2.into_int_value());
|
||||
let res = builder.build_right_shift(v1, v2, false, &state.var_name());
|
||||
state.push1(res);
|
||||
}
|
||||
Operator::I32Rotl => {
|
||||
let (v1, v2) = state.pop2()?;
|
||||
let (v1, v2) = (v1.into_int_value(), v2.into_int_value());
|
||||
let lhs = builder.build_left_shift(v1, v2, &state.var_name());
|
||||
let rhs = {
|
||||
let int_width = context.i32_type().const_int(32 as u64, false);
|
||||
let rhs = builder.build_int_sub(int_width, v2, &state.var_name());
|
||||
builder.build_right_shift(v1, rhs, false, &state.var_name())
|
||||
};
|
||||
let res = builder.build_or(lhs, rhs, &state.var_name());
|
||||
state.push1(res);
|
||||
}
|
||||
Operator::I64Rotl => {
|
||||
let (v1, v2) = state.pop2()?;
|
||||
let (v1, v2) = (v1.into_int_value(), v2.into_int_value());
|
||||
let lhs = builder.build_left_shift(v1, v2, &state.var_name());
|
||||
let rhs = {
|
||||
let int_width = context.i64_type().const_int(64 as u64, false);
|
||||
let rhs = builder.build_int_sub(int_width, v2, &state.var_name());
|
||||
builder.build_right_shift(v1, rhs, false, &state.var_name())
|
||||
};
|
||||
let res = builder.build_or(lhs, rhs, &state.var_name());
|
||||
state.push1(res);
|
||||
}
|
||||
Operator::I32Rotr => {
|
||||
let (v1, v2) = state.pop2()?;
|
||||
let (v1, v2) = (v1.into_int_value(), v2.into_int_value());
|
||||
let lhs = builder.build_right_shift(v1, v2, false, &state.var_name());
|
||||
let rhs = {
|
||||
let int_width = context.i32_type().const_int(32 as u64, false);
|
||||
let rhs = builder.build_int_sub(int_width, v2, &state.var_name());
|
||||
builder.build_left_shift(v1, rhs, &state.var_name())
|
||||
};
|
||||
let res = builder.build_or(lhs, rhs, &state.var_name());
|
||||
state.push1(res);
|
||||
}
|
||||
Operator::I64Rotr => {
|
||||
let (v1, v2) = state.pop2()?;
|
||||
let (v1, v2) = (v1.into_int_value(), v2.into_int_value());
|
||||
let lhs = builder.build_right_shift(v1, v2, false, &state.var_name());
|
||||
let rhs = {
|
||||
let int_width = context.i64_type().const_int(64 as u64, false);
|
||||
let rhs = builder.build_int_sub(int_width, v2, &state.var_name());
|
||||
builder.build_left_shift(v1, rhs, &state.var_name())
|
||||
};
|
||||
let res = builder.build_or(lhs, rhs, &state.var_name());
|
||||
state.push1(res);
|
||||
}
|
||||
Operator::I32Clz => {
|
||||
let input = state.pop1()?;
|
||||
let ensure_defined_zero = context
|
||||
.bool_type()
|
||||
.const_int(1 as u64, false)
|
||||
.as_basic_value_enum();
|
||||
let res = builder
|
||||
.build_call(
|
||||
intrinsics.ctlz_i32,
|
||||
&[input, ensure_defined_zero],
|
||||
&state.var_name(),
|
||||
)
|
||||
.try_as_basic_value()
|
||||
.left()
|
||||
.unwrap();
|
||||
state.push1(res);
|
||||
}
|
||||
Operator::I64Clz => {
|
||||
let input = state.pop1()?;
|
||||
let ensure_defined_zero = context
|
||||
.bool_type()
|
||||
.const_int(1 as u64, false)
|
||||
.as_basic_value_enum();
|
||||
let res = builder
|
||||
.build_call(
|
||||
intrinsics.ctlz_i64,
|
||||
&[input, ensure_defined_zero],
|
||||
&state.var_name(),
|
||||
)
|
||||
.try_as_basic_value()
|
||||
.left()
|
||||
.unwrap();
|
||||
state.push1(res);
|
||||
}
|
||||
Operator::I32Ctz => {
|
||||
let input = state.pop1()?;
|
||||
let ensure_defined_zero = context
|
||||
.bool_type()
|
||||
.const_int(1 as u64, false)
|
||||
.as_basic_value_enum();
|
||||
let res = builder
|
||||
.build_call(
|
||||
intrinsics.cttz_i32,
|
||||
&[input, ensure_defined_zero],
|
||||
&state.var_name(),
|
||||
)
|
||||
.try_as_basic_value()
|
||||
.left()
|
||||
.unwrap();
|
||||
state.push1(res);
|
||||
}
|
||||
Operator::I64Ctz => {
|
||||
let input = state.pop1()?;
|
||||
let ensure_defined_zero = context
|
||||
.bool_type()
|
||||
.const_int(1 as u64, false)
|
||||
.as_basic_value_enum();
|
||||
let res = builder
|
||||
.build_call(
|
||||
intrinsics.cttz_i64,
|
||||
&[input, ensure_defined_zero],
|
||||
&state.var_name(),
|
||||
)
|
||||
.try_as_basic_value()
|
||||
.left()
|
||||
.unwrap();
|
||||
state.push1(res);
|
||||
}
|
||||
Operator::I32Popcnt => {
|
||||
let input = state.pop1()?;
|
||||
let res = builder
|
||||
.build_call(intrinsics.ctpop_i32, &[input], &state.var_name())
|
||||
.try_as_basic_value()
|
||||
.left()
|
||||
.unwrap();
|
||||
state.push1(res);
|
||||
}
|
||||
Operator::I64Popcnt => {
|
||||
let input = state.pop1()?;
|
||||
let res = builder
|
||||
.build_call(intrinsics.ctpop_i64, &[input], &state.var_name())
|
||||
.try_as_basic_value()
|
||||
.left()
|
||||
.unwrap();
|
||||
state.push1(res);
|
||||
}
|
||||
Operator::I32Eqz => {
|
||||
let input = state.pop1()?.into_int_value();
|
||||
let zero = context.i32_type().const_int(0, false);
|
||||
let res =
|
||||
builder.build_int_compare(IntPredicate::EQ, input, zero, &state.var_name());
|
||||
state.push1(res);
|
||||
}
|
||||
Operator::I64Eqz => {
|
||||
let input = state.pop1()?.into_int_value();
|
||||
let zero = context.i64_type().const_int(0, false);
|
||||
let res =
|
||||
builder.build_int_compare(IntPredicate::EQ, input, zero, &state.var_name());
|
||||
state.push1(res);
|
||||
}
|
||||
|
||||
/***************************
|
||||
* Floating-Point Arithmetic instructions.
|
||||
* https://github.com/sunfishcode/wasm-reference-manual/blob/master/WebAssembly.md#floating-point-arithmetic-instructions
|
||||
***************************/
|
||||
Operator::F32Add | Operator::F64Add => {
|
||||
let (v1, v2) = state.pop2()?;
|
||||
let (v1, v2) = (v1.into_float_value(), v2.into_float_value());
|
||||
let res = builder.build_float_add(v1, v2, &state.var_name());
|
||||
state.push1(res);
|
||||
}
|
||||
Operator::F32Sub | Operator::F32Sub => {
|
||||
let (v1, v2) = state.pop2()?;
|
||||
let (v1, v2) = (v1.into_float_value(), v2.into_float_value());
|
||||
let res = builder.build_float_sub(v1, v2, &state.var_name());
|
||||
state.push1(res);
|
||||
}
|
||||
Operator::F32Mul | Operator::F64Mul => {
|
||||
let (v1, v2) = state.pop2()?;
|
||||
let (v1, v2) = (v1.into_float_value(), v2.into_float_value());
|
||||
let res = builder.build_float_mul(v1, v2, &state.var_name());
|
||||
state.push1(res);
|
||||
}
|
||||
Operator::F32Div | Operator::F64Div => {
|
||||
let (v1, v2) = state.pop2()?;
|
||||
let (v1, v2) = (v1.into_float_value(), v2.into_float_value());
|
||||
let res = builder.build_float_div(v1, v2, &state.var_name());
|
||||
state.push1(res);
|
||||
}
|
||||
Operator::F32Sqrt => {
|
||||
let input = state.pop1()?;
|
||||
let res = builder
|
||||
.build_call(intrinsics.sqrt_f32, &[input], &state.var_name())
|
||||
.try_as_basic_value()
|
||||
.left()
|
||||
.unwrap();
|
||||
state.push1(res);
|
||||
}
|
||||
Operator::F64Sqrt => {
|
||||
let input = state.pop1()?;
|
||||
let res = builder
|
||||
.build_call(intrinsics.sqrt_f64, &[input], &state.var_name())
|
||||
.try_as_basic_value()
|
||||
.left()
|
||||
.unwrap();
|
||||
state.push1(res);
|
||||
}
|
||||
Operator::F32Min => {
|
||||
let (v1, v2) = state.pop2()?;
|
||||
let res = builder
|
||||
.build_call(intrinsics.minimum_f32, &[v1, v2], &state.var_name())
|
||||
.try_as_basic_value()
|
||||
.left()
|
||||
.unwrap();
|
||||
state.push1(res);
|
||||
}
|
||||
Operator::F64Min => {
|
||||
let (v1, v2) = state.pop2()?;
|
||||
let res = builder
|
||||
.build_call(intrinsics.minimum_f64, &[v1, v2], &state.var_name())
|
||||
.try_as_basic_value()
|
||||
.left()
|
||||
.unwrap();
|
||||
state.push1(res);
|
||||
}
|
||||
Operator::F32Max => {
|
||||
let (v1, v2) = state.pop2()?;
|
||||
let res = builder
|
||||
.build_call(intrinsics.maximum_f32, &[v1, v2], &state.var_name())
|
||||
.try_as_basic_value()
|
||||
.left()
|
||||
.unwrap();
|
||||
state.push1(res);
|
||||
}
|
||||
Operator::F64Max => {
|
||||
let (v1, v2) = state.pop2()?;
|
||||
let res = builder
|
||||
.build_call(intrinsics.maximum_f64, &[v1, v2], &state.var_name())
|
||||
.try_as_basic_value()
|
||||
.left()
|
||||
.unwrap();
|
||||
state.push1(res);
|
||||
}
|
||||
Operator::F32Ceil => {
|
||||
let input = state.pop1()?;
|
||||
let res = builder
|
||||
.build_call(intrinsics.ceil_f32, &[input], &state.var_name())
|
||||
.try_as_basic_value()
|
||||
.left()
|
||||
.unwrap();
|
||||
state.push1(res);
|
||||
}
|
||||
Operator::F64Ceil => {
|
||||
let input = state.pop1()?;
|
||||
let res = builder
|
||||
.build_call(intrinsics.ceil_f64, &[input], &state.var_name())
|
||||
.try_as_basic_value()
|
||||
.left()
|
||||
.unwrap();
|
||||
state.push1(res);
|
||||
}
|
||||
Operator::F32Floor => {
|
||||
let input = state.pop1()?;
|
||||
let res = builder
|
||||
.build_call(intrinsics.floor_f32, &[input], &state.var_name())
|
||||
.try_as_basic_value()
|
||||
.left()
|
||||
.unwrap();
|
||||
state.push1(res);
|
||||
}
|
||||
Operator::F64Floor => {
|
||||
let input = state.pop1()?;
|
||||
let res = builder
|
||||
.build_call(intrinsics.floor_f64, &[input], &state.var_name())
|
||||
.try_as_basic_value()
|
||||
.left()
|
||||
.unwrap();
|
||||
state.push1(res);
|
||||
}
|
||||
Operator::F32Trunc => {
|
||||
let input = state.pop1()?;
|
||||
let res = builder
|
||||
.build_call(intrinsics.trunc_f32, &[input], &state.var_name())
|
||||
.try_as_basic_value()
|
||||
.left()
|
||||
.unwrap();
|
||||
state.push1(res);
|
||||
}
|
||||
Operator::F64Trunc => {
|
||||
let input = state.pop1()?;
|
||||
let res = builder
|
||||
.build_call(intrinsics.trunc_f64, &[input], &state.var_name())
|
||||
.try_as_basic_value()
|
||||
.left()
|
||||
.unwrap();
|
||||
state.push1(res);
|
||||
}
|
||||
Operator::F32Nearest => {
|
||||
let input = state.pop1()?;
|
||||
let res = builder
|
||||
.build_call(intrinsics.nearbyint_f32, &[input], &state.var_name())
|
||||
.try_as_basic_value()
|
||||
.left()
|
||||
.unwrap();
|
||||
state.push1(res);
|
||||
}
|
||||
Operator::F64Nearest => {
|
||||
let input = state.pop1()?;
|
||||
let res = builder
|
||||
.build_call(intrinsics.nearbyint_f64, &[input], &state.var_name())
|
||||
.try_as_basic_value()
|
||||
.left()
|
||||
.unwrap();
|
||||
state.push1(res);
|
||||
}
|
||||
Operator::F32Abs => {
|
||||
let input = state.pop1()?;
|
||||
let res = builder
|
||||
.build_call(intrinsics.fabs_f32, &[input], &state.var_name())
|
||||
.try_as_basic_value()
|
||||
.left()
|
||||
.unwrap();
|
||||
state.push1(res);
|
||||
}
|
||||
Operator::F64Abs => {
|
||||
let input = state.pop1()?;
|
||||
let res = builder
|
||||
.build_call(intrinsics.fabs_f64, &[input], &state.var_name())
|
||||
.try_as_basic_value()
|
||||
.left()
|
||||
.unwrap();
|
||||
state.push1(res);
|
||||
}
|
||||
Operator::F32Neg | Operator::F64Neg => {
|
||||
let input = state.pop1()?.into_float_value();
|
||||
let res = builder.build_float_neg(input, &state.var_name());
|
||||
state.push1(res);
|
||||
}
|
||||
Operator::F32Copysign => {
|
||||
let input = state.pop1()?;
|
||||
let res = builder
|
||||
.build_call(intrinsics.copysign_f32, &[input], &state.var_name())
|
||||
.try_as_basic_value()
|
||||
.left()
|
||||
.unwrap();
|
||||
state.push1(res);
|
||||
}
|
||||
Operator::F64Copysign => {
|
||||
let input = state.pop1()?;
|
||||
let res = builder
|
||||
.build_call(intrinsics.copysign_f64, &[input], &state.var_name())
|
||||
.try_as_basic_value()
|
||||
.left()
|
||||
.unwrap();
|
||||
state.push1(res);
|
||||
}
|
||||
|
||||
/***************************
|
||||
* Integer Comparison instructions.
|
||||
* https://github.com/sunfishcode/wasm-reference-manual/blob/master/WebAssembly.md#integer-comparison-instructions
|
||||
***************************/
|
||||
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());
|
||||
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());
|
||||
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());
|
||||
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());
|
||||
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());
|
||||
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());
|
||||
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());
|
||||
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());
|
||||
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());
|
||||
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());
|
||||
state.push1(res);
|
||||
}
|
||||
|
||||
/***************************
|
||||
* Floating-Point Comparison instructions.
|
||||
* https://github.com/sunfishcode/wasm-reference-manual/blob/master/WebAssembly.md#floating-point-comparison-instructions
|
||||
***************************/
|
||||
Operator::Unreachable => {
|
||||
// Emit an unreachable instruction.
|
||||
// If llvm cannot prove that this is never touched,
|
||||
// it will emit a `ud2` instruction on x86_64 arches.
|
||||
builder.build_unreachable();
|
||||
}
|
||||
op @ _ => {
|
||||
println!("{}", module.print_to_string().to_string());
|
||||
unimplemented!("{:?}", op);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
61
lib/llvm-backend/src/example.rs
Normal file
61
lib/llvm-backend/src/example.rs
Normal file
@ -0,0 +1,61 @@
|
||||
use inkwell::OptimizationLevel;
|
||||
use inkwell::builder::Builder;
|
||||
use inkwell::context::Context;
|
||||
use inkwell::execution_engine::{ExecutionEngine, JitFunction};
|
||||
use inkwell::module::Module;
|
||||
use inkwell::targets::{InitializationConfig, Target};
|
||||
use std::error::Error;
|
||||
|
||||
/// Convenience type alias for the `sum` function.
|
||||
///
|
||||
/// Calling this is innately `unsafe` because there's no guarantee it doesn't
|
||||
/// do `unsafe` operations internally.
|
||||
type SumFunc = unsafe extern "C" fn(u64, u64, u64) -> u64;
|
||||
|
||||
#[test]
|
||||
fn test_sum() -> Result<(), Box<Error>> {
|
||||
let context = Context::create();
|
||||
let module = context.create_module("sum");
|
||||
let builder = context.create_builder();
|
||||
let execution_engine = module.create_jit_execution_engine(OptimizationLevel::Aggressive)?;
|
||||
|
||||
let sum = jit_compile_sum(&context, &module, &builder, &execution_engine)
|
||||
.ok_or("Unable to JIT compile `sum`")?;
|
||||
|
||||
let x = 1u64;
|
||||
let y = 2u64;
|
||||
let z = 3u64;
|
||||
|
||||
unsafe {
|
||||
println!("{} + {} + {} = {}", x, y, z, sum.call(x, y, z));
|
||||
assert_eq!(sum.call(x, y, z), x + y + z);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn jit_compile_sum(
|
||||
context: &Context,
|
||||
module: &Module,
|
||||
builder: &Builder,
|
||||
execution_engine: &ExecutionEngine,
|
||||
) -> Option<JitFunction<SumFunc>> {
|
||||
let i64_type = context.i64_type();
|
||||
let fn_type = i64_type.fn_type(&[i64_type.into(), i64_type.into(), i64_type.into()], false);
|
||||
|
||||
let function = module.add_function("sum", fn_type, None);
|
||||
let basic_block = context.append_basic_block(&function, "entry");
|
||||
|
||||
builder.position_at_end(&basic_block);
|
||||
|
||||
let x = function.get_nth_param(0)?.into_int_value();
|
||||
let y = function.get_nth_param(1)?.into_int_value();
|
||||
let z = function.get_nth_param(2)?.into_int_value();
|
||||
|
||||
let sum = builder.build_int_add(x, y, "sum");
|
||||
let sum = builder.build_int_add(sum, z, "sum");
|
||||
|
||||
builder.build_return(Some(&sum));
|
||||
|
||||
unsafe { execution_engine.get_function("sum").ok() }
|
||||
}
|
99
lib/llvm-backend/src/intrinsics.rs
Normal file
99
lib/llvm-backend/src/intrinsics.rs
Normal file
@ -0,0 +1,99 @@
|
||||
use inkwell::{context::Context, module::Module, types::BasicType, values::FunctionValue};
|
||||
|
||||
pub struct Intrinsics {
|
||||
pub ctlz_i32: FunctionValue,
|
||||
pub ctlz_i64: FunctionValue,
|
||||
|
||||
pub cttz_i32: FunctionValue,
|
||||
pub cttz_i64: FunctionValue,
|
||||
|
||||
pub ctpop_i32: FunctionValue,
|
||||
pub ctpop_i64: FunctionValue,
|
||||
|
||||
pub sqrt_f32: FunctionValue,
|
||||
pub sqrt_f64: FunctionValue,
|
||||
|
||||
pub minimum_f32: FunctionValue,
|
||||
pub minimum_f64: FunctionValue,
|
||||
|
||||
pub maximum_f32: FunctionValue,
|
||||
pub maximum_f64: FunctionValue,
|
||||
|
||||
pub ceil_f32: FunctionValue,
|
||||
pub ceil_f64: FunctionValue,
|
||||
|
||||
pub floor_f32: FunctionValue,
|
||||
pub floor_f64: FunctionValue,
|
||||
|
||||
pub trunc_f32: FunctionValue,
|
||||
pub trunc_f64: FunctionValue,
|
||||
|
||||
pub nearbyint_f32: FunctionValue,
|
||||
pub nearbyint_f64: FunctionValue,
|
||||
|
||||
pub fabs_f32: FunctionValue,
|
||||
pub fabs_f64: FunctionValue,
|
||||
|
||||
pub copysign_f32: FunctionValue,
|
||||
pub copysign_f64: FunctionValue,
|
||||
}
|
||||
|
||||
impl Intrinsics {
|
||||
pub fn declare(module: &Module, context: &Context) -> Self {
|
||||
let i1_ty = context.bool_type().as_basic_type_enum();
|
||||
let i32_ty = context.i32_type().as_basic_type_enum();
|
||||
let i64_ty = context.i64_type().as_basic_type_enum();
|
||||
let f32_ty = context.f32_type().as_basic_type_enum();
|
||||
let f64_ty = context.f64_type().as_basic_type_enum();
|
||||
|
||||
let ret_i32_take_i32_i1 = i32_ty.fn_type(&[i32_ty, i1_ty], false);
|
||||
let ret_i64_take_i64_i1 = i64_ty.fn_type(&[i64_ty, i1_ty], false);
|
||||
|
||||
let ret_i32_take_i32 = i32_ty.fn_type(&[i32_ty], false);
|
||||
let ret_i64_take_i64 = i64_ty.fn_type(&[i64_ty], false);
|
||||
|
||||
let ret_f32_take_f32 = f32_ty.fn_type(&[f32_ty], false);
|
||||
let ret_f64_take_f64 = f64_ty.fn_type(&[f64_ty], false);
|
||||
|
||||
let ret_f32_take_f32_f32 = f32_ty.fn_type(&[f32_ty, f32_ty], false);
|
||||
let ret_f64_take_f64_f64 = f64_ty.fn_type(&[f64_ty, f64_ty], 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),
|
||||
|
||||
cttz_i32: module.add_function("llvm.cttz.i32", ret_i32_take_i32_i1, None),
|
||||
cttz_i64: module.add_function("llvm.cttz.i64", ret_i64_take_i64_i1, None),
|
||||
|
||||
ctpop_i32: module.add_function("llvm.ctpop.i32", ret_i32_take_i32, None),
|
||||
ctpop_i64: module.add_function("llvm.ctpop.i64", ret_i64_take_i64, None),
|
||||
|
||||
sqrt_f32: module.add_function("llvm.sqrt.f32", ret_f32_take_f32, None),
|
||||
sqrt_f64: module.add_function("llvm.sqrt.f64", ret_f64_take_f64, None),
|
||||
|
||||
minimum_f32: module.add_function("llvm.minimum.f32", ret_f32_take_f32_f32, None),
|
||||
minimum_f64: module.add_function("llvm.minimum.f64", ret_f64_take_f64_f64, None),
|
||||
|
||||
maximum_f32: module.add_function("llvm.maximum.f32", ret_f32_take_f32_f32, None),
|
||||
maximum_f64: module.add_function("llvm.maximum.f64", ret_f64_take_f64_f64, None),
|
||||
|
||||
ceil_f32: module.add_function("llvm.ceil.f32", ret_f32_take_f32, None),
|
||||
ceil_f64: module.add_function("llvm.ceil.f64", ret_f64_take_f64, None),
|
||||
|
||||
floor_f32: module.add_function("llvm.floor.f32", ret_f32_take_f32, None),
|
||||
floor_f64: module.add_function("llvm.floor.f64", ret_f64_take_f64, None),
|
||||
|
||||
trunc_f32: module.add_function("llvm.trunc.f32", ret_f32_take_f32, None),
|
||||
trunc_f64: module.add_function("llvm.trunc.f64", ret_f64_take_f64, None),
|
||||
|
||||
nearbyint_f32: module.add_function("llvm.nearbyint.f32", ret_f32_take_f32, None),
|
||||
nearbyint_f64: module.add_function("llvm.nearbyint.f64", ret_f64_take_f64, None),
|
||||
|
||||
fabs_f32: module.add_function("llvm.fabs.f32", ret_f32_take_f32, None),
|
||||
fabs_f64: module.add_function("llvm.fabs.f64", ret_f64_take_f64, None),
|
||||
|
||||
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),
|
||||
}
|
||||
}
|
||||
}
|
52
lib/llvm-backend/src/lib.rs
Normal file
52
lib/llvm-backend/src/lib.rs
Normal file
@ -0,0 +1,52 @@
|
||||
use wasmer_runtime_core::{
|
||||
backend::{Compiler, Token},
|
||||
error::CompileError,
|
||||
module::{ModuleInfo, ModuleInner},
|
||||
};
|
||||
|
||||
mod code;
|
||||
mod intrinsics;
|
||||
mod read_info;
|
||||
mod state;
|
||||
|
||||
pub struct LLVMCompiler {
|
||||
_private: (),
|
||||
}
|
||||
|
||||
impl LLVMCompiler {
|
||||
pub fn new() -> Self {
|
||||
Self { _private: () }
|
||||
}
|
||||
}
|
||||
|
||||
impl Compiler for LLVMCompiler {
|
||||
fn compile(&self, wasm: &[u8], _: Token) -> Result<ModuleInner, CompileError> {
|
||||
let (_info, _code_reader) = read_info::read_module(wasm).unwrap();
|
||||
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_read_module() {
|
||||
use wabt::wat2wasm;
|
||||
let WAT: &'static str = r#"
|
||||
(module
|
||||
(type $t0 (func (param i32) (result i32)))
|
||||
(import "env" "memory" (memory 1 1))
|
||||
(import "env" "table" (table 10 anyfunc))
|
||||
(import "env" "global" (global i32))
|
||||
(import "env" "print_i32" (func $print_i32 (type $t0)))
|
||||
(func $identity (type $t0) (param $p0 i32) (result i32)
|
||||
get_local $p0)
|
||||
(func $print_num (export "print_num") (type $t0) (param $p0 i32) (result i32)
|
||||
get_global 0
|
||||
call $identity
|
||||
call $print_i32))
|
||||
"#;
|
||||
let wasm = wat2wasm(WAT).unwrap();
|
||||
|
||||
let (info, code_reader) = read_info::read_module(&wasm).unwrap();
|
||||
|
||||
code::parse_function_bodies(&info, code_reader).unwrap();
|
||||
}
|
333
lib/llvm-backend/src/read_info.rs
Normal file
333
lib/llvm-backend/src/read_info.rs
Normal file
@ -0,0 +1,333 @@
|
||||
use wasmer_runtime_core::{
|
||||
backend::Backend,
|
||||
module::{
|
||||
DataInitializer, ExportIndex, ImportName, ModuleInfo, StringTable, StringTableBuilder,
|
||||
TableInitializer,
|
||||
},
|
||||
structures::{Map, TypedIndex},
|
||||
types::{
|
||||
ElementType, FuncIndex, FuncSig, GlobalDescriptor, GlobalIndex, GlobalInit,
|
||||
ImportedGlobalIndex, Initializer, MemoryDescriptor, MemoryIndex, SigIndex, TableDescriptor,
|
||||
TableIndex, Type, Value,
|
||||
},
|
||||
units::Pages,
|
||||
};
|
||||
use wasmparser::{
|
||||
BinaryReaderError, CodeSectionReader, Data, DataKind, Element, ElementKind, Export,
|
||||
ExternalKind, FuncType, Import, ImportSectionEntryType, InitExpr, ModuleReader, Operator,
|
||||
SectionCode, Type as WpType,
|
||||
};
|
||||
|
||||
pub fn read_module(wasm: &[u8]) -> Result<(ModuleInfo, CodeSectionReader), BinaryReaderError> {
|
||||
let mut info = ModuleInfo {
|
||||
memories: Map::new(),
|
||||
globals: Map::new(),
|
||||
tables: Map::new(),
|
||||
|
||||
imported_functions: Map::new(),
|
||||
imported_memories: Map::new(),
|
||||
imported_tables: Map::new(),
|
||||
imported_globals: Map::new(),
|
||||
|
||||
exports: Default::default(),
|
||||
|
||||
data_initializers: Vec::new(),
|
||||
elem_initializers: Vec::new(),
|
||||
|
||||
start_func: None,
|
||||
|
||||
func_assoc: Map::new(),
|
||||
signatures: Map::new(),
|
||||
backend: Backend::LLVM,
|
||||
|
||||
namespace_table: StringTable::new(),
|
||||
name_table: StringTable::new(),
|
||||
};
|
||||
|
||||
let mut reader = ModuleReader::new(wasm)?;
|
||||
let mut code_reader = None;
|
||||
|
||||
loop {
|
||||
if reader.eof() {
|
||||
return Ok((info, code_reader.unwrap()));
|
||||
}
|
||||
|
||||
let section = reader.read()?;
|
||||
|
||||
match section.code {
|
||||
SectionCode::Type => {
|
||||
let type_reader = section.get_type_section_reader()?;
|
||||
|
||||
for ty in type_reader {
|
||||
let ty = ty?;
|
||||
info.signatures.push(func_type_to_func_sig(ty)?);
|
||||
}
|
||||
}
|
||||
SectionCode::Import => {
|
||||
let import_reader = section.get_import_section_reader()?;
|
||||
let mut namespace_builder = StringTableBuilder::new();
|
||||
let mut name_builder = StringTableBuilder::new();
|
||||
|
||||
for import in import_reader {
|
||||
let Import { module, field, ty } = import?;
|
||||
|
||||
let namespace_index = namespace_builder.register(module);
|
||||
let name_index = name_builder.register(field);
|
||||
let import_name = ImportName {
|
||||
namespace_index,
|
||||
name_index,
|
||||
};
|
||||
|
||||
match ty {
|
||||
ImportSectionEntryType::Function(sigindex) => {
|
||||
let sigindex = SigIndex::new(sigindex as usize);
|
||||
info.imported_functions.push(import_name);
|
||||
info.func_assoc.push(sigindex);
|
||||
}
|
||||
ImportSectionEntryType::Table(table_ty) => {
|
||||
assert_eq!(table_ty.element_type, WpType::AnyFunc);
|
||||
let table_desc = TableDescriptor {
|
||||
element: ElementType::Anyfunc,
|
||||
minimum: table_ty.limits.initial,
|
||||
maximum: table_ty.limits.maximum,
|
||||
};
|
||||
|
||||
info.imported_tables.push((import_name, table_desc));
|
||||
}
|
||||
ImportSectionEntryType::Memory(memory_ty) => {
|
||||
let mem_desc = MemoryDescriptor {
|
||||
minimum: Pages(memory_ty.limits.initial),
|
||||
maximum: memory_ty.limits.maximum.map(|max| Pages(max)),
|
||||
shared: memory_ty.shared,
|
||||
};
|
||||
info.imported_memories.push((import_name, mem_desc));
|
||||
}
|
||||
ImportSectionEntryType::Global(global_ty) => {
|
||||
let global_desc = GlobalDescriptor {
|
||||
mutable: global_ty.mutable,
|
||||
ty: type_to_type(global_ty.content_type)?,
|
||||
};
|
||||
info.imported_globals.push((import_name, global_desc));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
SectionCode::Function => {
|
||||
let func_decl_reader = section.get_function_section_reader()?;
|
||||
|
||||
for sigindex in func_decl_reader {
|
||||
let sigindex = sigindex?;
|
||||
|
||||
let sigindex = SigIndex::new(sigindex as usize);
|
||||
info.func_assoc.push(sigindex);
|
||||
}
|
||||
}
|
||||
SectionCode::Table => {
|
||||
let table_decl_reader = section.get_table_section_reader()?;
|
||||
|
||||
for table_ty in table_decl_reader {
|
||||
let table_ty = table_ty?;
|
||||
|
||||
let table_desc = TableDescriptor {
|
||||
element: ElementType::Anyfunc,
|
||||
minimum: table_ty.limits.initial,
|
||||
maximum: table_ty.limits.maximum,
|
||||
};
|
||||
|
||||
info.tables.push(table_desc);
|
||||
}
|
||||
}
|
||||
SectionCode::Memory => {
|
||||
let mem_decl_reader = section.get_memory_section_reader()?;
|
||||
|
||||
for memory_ty in mem_decl_reader {
|
||||
let memory_ty = memory_ty?;
|
||||
|
||||
let mem_desc = MemoryDescriptor {
|
||||
minimum: Pages(memory_ty.limits.initial),
|
||||
maximum: memory_ty.limits.maximum.map(|max| Pages(max)),
|
||||
shared: memory_ty.shared,
|
||||
};
|
||||
|
||||
info.memories.push(mem_desc);
|
||||
}
|
||||
}
|
||||
SectionCode::Global => {
|
||||
let global_decl_reader = section.get_global_section_reader()?;
|
||||
|
||||
for global in global_decl_reader {
|
||||
let global = global?;
|
||||
|
||||
let desc = GlobalDescriptor {
|
||||
mutable: global.ty.mutable,
|
||||
ty: type_to_type(global.ty.content_type)?,
|
||||
};
|
||||
|
||||
let global_init = GlobalInit {
|
||||
desc,
|
||||
init: eval_init_expr(&global.init_expr)?,
|
||||
};
|
||||
|
||||
info.globals.push(global_init);
|
||||
}
|
||||
}
|
||||
SectionCode::Export => {
|
||||
let export_reader = section.get_export_section_reader()?;
|
||||
|
||||
for export in export_reader {
|
||||
let Export { field, kind, index } = export?;
|
||||
|
||||
let export_index = match kind {
|
||||
ExternalKind::Function => ExportIndex::Func(FuncIndex::new(index as usize)),
|
||||
ExternalKind::Table => ExportIndex::Table(TableIndex::new(index as usize)),
|
||||
ExternalKind::Memory => {
|
||||
ExportIndex::Memory(MemoryIndex::new(index as usize))
|
||||
}
|
||||
ExternalKind::Global => {
|
||||
ExportIndex::Global(GlobalIndex::new(index as usize))
|
||||
}
|
||||
};
|
||||
|
||||
info.exports.insert(field.to_string(), export_index);
|
||||
}
|
||||
}
|
||||
SectionCode::Start => {
|
||||
let start_index = section.get_start_section_content()?;
|
||||
|
||||
info.start_func = Some(FuncIndex::new(start_index as usize));
|
||||
}
|
||||
SectionCode::Element => {
|
||||
let element_reader = section.get_element_section_reader()?;
|
||||
|
||||
for element in element_reader {
|
||||
let Element { kind, items } = element?;
|
||||
|
||||
match kind {
|
||||
ElementKind::Active {
|
||||
table_index,
|
||||
init_expr,
|
||||
} => {
|
||||
let table_index = TableIndex::new(table_index as usize);
|
||||
let base = eval_init_expr(&init_expr)?;
|
||||
let items_reader = items.get_items_reader()?;
|
||||
|
||||
let elements: Vec<_> = items_reader
|
||||
.into_iter()
|
||||
.map(|res| res.map(|index| FuncIndex::new(index as usize)))
|
||||
.collect::<Result<_, _>>()?;
|
||||
|
||||
let table_init = TableInitializer {
|
||||
table_index,
|
||||
base,
|
||||
elements,
|
||||
};
|
||||
|
||||
info.elem_initializers.push(table_init);
|
||||
}
|
||||
ElementKind::Passive(_ty) => {
|
||||
return Err(BinaryReaderError {
|
||||
message: "passive tables are not yet supported",
|
||||
offset: -1isize as usize,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
SectionCode::Code => {
|
||||
code_reader = Some(section.get_code_section_reader()?);
|
||||
}
|
||||
SectionCode::Data => {
|
||||
let data_reader = section.get_data_section_reader()?;
|
||||
|
||||
for data in data_reader {
|
||||
let Data { kind, data } = data?;
|
||||
|
||||
match kind {
|
||||
DataKind::Active {
|
||||
memory_index,
|
||||
init_expr,
|
||||
} => {
|
||||
let memory_index = MemoryIndex::new(memory_index as usize);
|
||||
let base = eval_init_expr(&init_expr)?;
|
||||
|
||||
let data_init = DataInitializer {
|
||||
memory_index,
|
||||
base,
|
||||
data: data.to_vec(),
|
||||
};
|
||||
|
||||
info.data_initializers.push(data_init);
|
||||
}
|
||||
DataKind::Passive => {
|
||||
return Err(BinaryReaderError {
|
||||
message: "passive memories are not yet supported",
|
||||
offset: -1isize as usize,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
SectionCode::DataCount => {}
|
||||
SectionCode::Custom { .. } => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn type_to_type(ty: WpType) -> Result<Type, BinaryReaderError> {
|
||||
Ok(match ty {
|
||||
WpType::I32 => Type::I32,
|
||||
WpType::I64 => Type::I64,
|
||||
WpType::F32 => Type::F32,
|
||||
WpType::F64 => Type::F64,
|
||||
WpType::V128 => {
|
||||
return Err(BinaryReaderError {
|
||||
message: "the wasmer llvm backend does not yet support the simd extension",
|
||||
offset: -1isize as usize,
|
||||
});
|
||||
}
|
||||
_ => panic!("broken invariant, invalid type"),
|
||||
})
|
||||
}
|
||||
|
||||
fn func_type_to_func_sig(func_ty: FuncType) -> Result<FuncSig, BinaryReaderError> {
|
||||
assert_eq!(func_ty.form, WpType::Func);
|
||||
|
||||
Ok(FuncSig::new(
|
||||
func_ty
|
||||
.params
|
||||
.iter()
|
||||
.cloned()
|
||||
.map(type_to_type)
|
||||
.collect::<Result<Vec<_>, _>>()?,
|
||||
func_ty
|
||||
.returns
|
||||
.iter()
|
||||
.cloned()
|
||||
.map(type_to_type)
|
||||
.collect::<Result<Vec<_>, _>>()?,
|
||||
))
|
||||
}
|
||||
|
||||
fn eval_init_expr(expr: &InitExpr) -> Result<Initializer, BinaryReaderError> {
|
||||
let mut reader = expr.get_operators_reader();
|
||||
let (op, offset) = reader.read_with_offset()?;
|
||||
Ok(match op {
|
||||
Operator::GetGlobal { global_index } => {
|
||||
Initializer::GetGlobal(ImportedGlobalIndex::new(global_index as usize))
|
||||
}
|
||||
Operator::I32Const { value } => Initializer::Const(Value::I32(value)),
|
||||
Operator::I64Const { value } => Initializer::Const(Value::I64(value)),
|
||||
Operator::F32Const { value } => {
|
||||
Initializer::Const(Value::F32(f32::from_bits(value.bits())))
|
||||
}
|
||||
Operator::F64Const { value } => {
|
||||
Initializer::Const(Value::F64(f64::from_bits(value.bits())))
|
||||
}
|
||||
_ => {
|
||||
return Err(BinaryReaderError {
|
||||
message: "init expr evaluation failed: unsupported opcode",
|
||||
offset,
|
||||
});
|
||||
}
|
||||
})
|
||||
}
|
105
lib/llvm-backend/src/state.rs
Normal file
105
lib/llvm-backend/src/state.rs
Normal file
@ -0,0 +1,105 @@
|
||||
use inkwell::{
|
||||
basic_block::BasicBlock,
|
||||
values::{BasicValue, BasicValueEnum},
|
||||
};
|
||||
use wasmparser::BinaryReaderError;
|
||||
|
||||
enum ControlFrame {
|
||||
If {
|
||||
dest: BasicBlock,
|
||||
stack_size_snapshot: usize,
|
||||
},
|
||||
Block {
|
||||
dest: BasicBlock,
|
||||
stack_size_snapshot: usize,
|
||||
num_ret_values: usize,
|
||||
},
|
||||
}
|
||||
|
||||
pub struct State {
|
||||
stack: Vec<BasicValueEnum>,
|
||||
control_stack: Vec<ControlFrame>,
|
||||
value_counter: usize,
|
||||
}
|
||||
|
||||
impl State {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
stack: vec![],
|
||||
control_stack: vec![],
|
||||
value_counter: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn var_name(&mut self) -> String {
|
||||
let s = self.value_counter.to_string();
|
||||
self.value_counter += 1;
|
||||
s
|
||||
}
|
||||
|
||||
pub fn push1<T: BasicValue>(&mut self, value: T) {
|
||||
self.stack.push(value.as_basic_value_enum())
|
||||
}
|
||||
|
||||
pub fn pop1(&mut self) -> Result<BasicValueEnum, BinaryReaderError> {
|
||||
self.stack.pop().ok_or_else(|| BinaryReaderError {
|
||||
message: "invalid value stack",
|
||||
offset: -1isize as usize,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn pop2(&mut self) -> Result<(BasicValueEnum, BasicValueEnum), BinaryReaderError> {
|
||||
let v2 = self.pop1()?;
|
||||
let v1 = self.pop1()?;
|
||||
Ok((v1, v2))
|
||||
}
|
||||
|
||||
pub fn pop3(
|
||||
&mut self,
|
||||
) -> Result<(BasicValueEnum, BasicValueEnum, BasicValueEnum), BinaryReaderError> {
|
||||
let v3 = self.pop1()?;
|
||||
let v2 = self.pop1()?;
|
||||
let v1 = self.pop1()?;
|
||||
Ok((v1, v2, v3))
|
||||
}
|
||||
|
||||
pub fn peek1(&self) -> Result<BasicValueEnum, BinaryReaderError> {
|
||||
self.stack
|
||||
.get(self.stack.len() - 1)
|
||||
.ok_or_else(|| BinaryReaderError {
|
||||
message: "invalid value stack",
|
||||
offset: -1isize as usize,
|
||||
})
|
||||
.map(|v| *v)
|
||||
}
|
||||
|
||||
pub fn peekn(&self, n: usize) -> Result<&[BasicValueEnum], BinaryReaderError> {
|
||||
self.stack
|
||||
.get(self.stack.len() - n..)
|
||||
.ok_or_else(|| BinaryReaderError {
|
||||
message: "invalid value stack",
|
||||
offset: -1isize as usize,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn popn(&mut self, n: usize) -> Result<(), BinaryReaderError> {
|
||||
if self.stack.len() < n {
|
||||
return Err(BinaryReaderError {
|
||||
message: "invalid value stack",
|
||||
offset: -1isize as usize,
|
||||
});
|
||||
}
|
||||
|
||||
let new_len = self.stack.len() - n;
|
||||
self.stack.truncate(new_len);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn push_block(&mut self, dest: BasicBlock, num_ret_values: usize) {
|
||||
self.control_stack.push(ControlFrame::Block {
|
||||
dest,
|
||||
stack_size_snapshot: self.stack.len(),
|
||||
num_ret_values,
|
||||
});
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user