Files
wasmtime/src/module.rs
2019-02-25 15:35:45 +01:00

619 lines
16 KiB
Rust

use crate::microwasm;
use backend::TranslatedCodeSection;
use cranelift_codegen::{
ir::{self, AbiParam, Signature as CraneliftSignature},
isa,
};
use error::Error;
use std::{
convert::TryInto,
hash::{Hash, Hasher},
mem,
};
use translate_sections;
use wasmparser::{FuncType, MemoryType, ModuleReader, SectionCode, TableType, Type};
pub trait AsValueType {
const TYPE: Type;
}
pub trait TypeList {
const TYPE_LIST: &'static [Type];
}
impl<T> TypeList for T
where
T: AsValueType,
{
const TYPE_LIST: &'static [Type] = &[T::TYPE];
}
impl AsValueType for i32 {
const TYPE: Type = Type::I32;
}
impl AsValueType for i64 {
const TYPE: Type = Type::I64;
}
impl AsValueType for u32 {
const TYPE: Type = Type::I32;
}
impl AsValueType for u64 {
const TYPE: Type = Type::I64;
}
impl AsValueType for f32 {
const TYPE: Type = Type::F32;
}
impl AsValueType for f64 {
const TYPE: Type = Type::F64;
}
pub trait FunctionArgs<O> {
type FuncType;
unsafe fn call(self, func: Self::FuncType, vm_ctx: *const u8) -> O;
fn into_func(start: *const u8) -> Self::FuncType;
}
type VmCtxPtr = u64;
macro_rules! impl_function_args {
($first:ident $(, $rest:ident)*) => {
impl<Output, $first, $($rest),*> FunctionArgs<Output> for ($first, $($rest),*) {
type FuncType = unsafe extern "sysv64" fn(VmCtxPtr, $first $(, $rest)*) -> Output;
#[allow(non_snake_case)]
unsafe fn call(self, func: Self::FuncType, vm_ctx: *const u8) -> Output {
let ($first, $($rest),*) = self;
func(vm_ctx as VmCtxPtr, $first $(, $rest)*)
}
fn into_func(start: *const u8) -> Self::FuncType {
unsafe { mem::transmute(start) }
}
}
impl<$first: AsValueType, $($rest: AsValueType),*> TypeList for ($first, $($rest),*) {
const TYPE_LIST: &'static [Type] = &[$first::TYPE, $($rest::TYPE),*];
}
impl_function_args!($($rest),*);
};
() => {
impl<Output> FunctionArgs<Output> for () {
type FuncType = unsafe extern "sysv64" fn(VmCtxPtr) -> Output;
unsafe fn call(self, func: Self::FuncType, vm_ctx: *const u8) -> Output {
func(vm_ctx as VmCtxPtr)
}
fn into_func(start: *const u8) -> Self::FuncType {
unsafe { mem::transmute(start) }
}
}
impl TypeList for () {
const TYPE_LIST: &'static [Type] = &[];
}
};
}
impl_function_args!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S);
#[derive(Default)]
pub struct TranslatedModule {
translated_code_section: Option<TranslatedCodeSection>,
types: SimpleContext,
// TODO: Should we wrap this in a `Mutex` so that calling functions from multiple
// threads doesn't cause data races?
table: Option<(TableType, Vec<u32>)>,
memory: Option<MemoryType>,
}
pub fn quickhash<H: Hash>(h: H) -> u64 {
let mut hasher = std::collections::hash_map::DefaultHasher::new();
h.hash(&mut hasher);
hasher.finish()
}
impl TranslatedModule {
pub fn instantiate(mut self) -> ExecutableModule {
let table = {
let code_section = self
.translated_code_section
.as_ref()
.expect("We don't currently support a table section without a code section");
let types = &self.types;
self.table
.as_mut()
.map(|&mut (_, ref mut idxs)| {
let initial = idxs
.iter()
.map(|i| {
let start = code_section.func_start(*i as _);
let ty = types.func_type(*i);
RuntimeFunc {
func_start: start,
sig_hash: quickhash(ty) as u32,
}
})
.collect::<Vec<_>>();
let out = BoxSlice::from(initial.into_boxed_slice());
out
})
.unwrap_or(BoxSlice {
ptr: std::ptr::NonNull::dangling().as_ptr(),
len: 0,
})
};
let mem_size = self.memory.map(|m| m.limits.initial).unwrap_or(0) as usize;
let mem: BoxSlice<_> = vec![0u8; mem_size * WASM_PAGE_SIZE]
.into_boxed_slice()
.into();
let ctx = if mem.len > 0 || table.len > 0 {
Some(Box::new(VmCtx { table, mem }))
} else {
None
};
ExecutableModule {
module: self,
context: ctx,
}
}
pub fn disassemble(&self) {
self.translated_code_section
.as_ref()
.expect("no code section")
.disassemble();
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum ExecutionError {
FuncIndexOutOfBounds,
TypeMismatch,
}
pub struct ExecutableModule {
module: TranslatedModule,
context: Option<Box<VmCtx>>,
}
impl ExecutableModule {
/// Executes the function _without checking types_. This can cause undefined
/// memory to be accessed.
pub unsafe fn execute_func_unchecked<Args: FunctionArgs<T>, T>(
&self,
func_idx: u32,
args: Args,
) -> T {
let code_section = self
.module
.translated_code_section
.as_ref()
.expect("no code section");
let start_buf = code_section.func_start(func_idx as usize);
args.call(
Args::into_func(start_buf),
self.context
.as_ref()
.map(|ctx| (&**ctx) as *const VmCtx as *const u8)
.unwrap_or(std::ptr::null()),
)
}
pub fn execute_func<Args: FunctionArgs<T> + TypeList, T: TypeList>(
&self,
func_idx: u32,
args: Args,
) -> Result<T, ExecutionError> {
let module = &self.module;
if func_idx as usize >= module.types.func_ty_indicies.len() {
return Err(ExecutionError::FuncIndexOutOfBounds);
}
let type_ = module.types.func_type(func_idx);
// TODO: Handle "compatible" types (i.e. f32 and i32)
if (&type_.params[..], &type_.returns[..]) != (Args::TYPE_LIST, T::TYPE_LIST) {
return Err(ExecutionError::TypeMismatch);
}
Ok(unsafe { self.execute_func_unchecked(func_idx, args) })
}
pub fn disassemble(&self) {
self.module.disassemble();
}
}
type FuncRef = *const u8;
pub struct RuntimeFunc {
sig_hash: u32,
func_start: FuncRef,
}
unsafe impl Send for RuntimeFunc {}
unsafe impl Sync for RuntimeFunc {}
impl RuntimeFunc {
pub fn offset_of_sig_hash() -> usize {
offset_of!(Self, sig_hash)
}
pub fn offset_of_func_start() -> usize {
offset_of!(Self, func_start)
}
}
struct BoxSlice<T> {
len: usize,
ptr: *mut T,
}
impl<T> From<Box<[T]>> for BoxSlice<T> {
fn from(mut other: Box<[T]>) -> Self {
let out = BoxSlice {
len: other.len(),
ptr: other.as_mut_ptr(),
};
mem::forget(other);
out
}
}
unsafe impl<T: Send> Send for BoxSlice<T> {}
unsafe impl<T: Sync> Sync for BoxSlice<T> {}
impl<T> Drop for BoxSlice<T> {
fn drop(&mut self) {
unsafe { Vec::from_raw_parts(self.ptr, self.len, self.len) };
}
}
pub struct VmCtx {
table: BoxSlice<RuntimeFunc>,
mem: BoxSlice<u8>,
}
impl VmCtx {
pub fn offset_of_memory_ptr() -> u8 {
offset_of!(Self, mem.ptr)
.try_into()
.expect("Offset exceeded size of u8")
}
pub fn offset_of_memory_len() -> u8 {
offset_of!(Self, mem.len)
.try_into()
.expect("Offset exceeded size of u8")
}
pub fn offset_of_funcs_ptr() -> u8 {
offset_of!(Self, table.ptr)
.try_into()
.expect("Offset exceeded size of u8")
}
pub fn offset_of_funcs_len() -> u8 {
offset_of!(Self, table.len)
.try_into()
.expect("Offset exceeded size of u8")
}
}
#[derive(Default, Debug)]
pub struct SimpleContext {
types: Vec<FuncType>,
func_ty_indicies: Vec<u32>,
}
const WASM_PAGE_SIZE: usize = 65_536;
pub trait Signature {
type Type: SigType;
fn params(&self) -> &[Self::Type];
fn returns(&self) -> &[Self::Type];
}
pub trait SigType {
fn to_microwasm_type(&self) -> microwasm::SignlessType;
fn is_float(&self) -> bool;
}
impl SigType for AbiParam {
fn to_microwasm_type(&self) -> microwasm::SignlessType {
use microwasm::{Size::*, Type::*};
if self.value_type.is_int() {
match self.value_type.bits() {
32 => Int(_32),
64 => Int(_64),
_ => unimplemented!(),
}
} else if self.value_type.is_float() {
match self.value_type.bits() {
32 => Float(_32),
64 => Float(_64),
_ => unimplemented!(),
}
} else {
unimplemented!()
}
}
fn is_float(&self) -> bool {
self.value_type.is_float()
}
}
impl Signature for CraneliftSignature {
type Type = AbiParam;
fn params(&self) -> &[Self::Type] {
// TODO: We want to instead add the `VMContext` to the signature used by
// cranelift, removing the special-casing from the internals.
assert_eq!(self.params[0].purpose, ir::ArgumentPurpose::VMContext);
assert_eq!(self.call_conv, isa::CallConv::SystemV);
&self.params[1..]
}
fn returns(&self) -> &[Self::Type] {
assert_eq!(self.call_conv, isa::CallConv::SystemV);
&self.returns
}
}
impl SigType for wasmparser::Type {
fn to_microwasm_type(&self) -> microwasm::SignlessType {
microwasm::Type::from_wasm(*self).unwrap()
}
fn is_float(&self) -> bool {
match self {
wasmparser::Type::F32 | wasmparser::Type::F64 => true,
_ => false,
}
}
}
impl Signature for FuncType {
type Type = wasmparser::Type;
fn params(&self) -> &[Self::Type] {
&*self.params
}
fn returns(&self) -> &[Self::Type] {
&*self.returns
}
}
pub trait ModuleContext {
type Signature: Signature + Hash;
fn func_type_index(&self, func_idx: u32) -> u32;
fn signature(&self, index: u32) -> &Self::Signature;
fn offset_of_memory_ptr(&self) -> u8;
fn offset_of_memory_len(&self) -> u8;
fn offset_of_funcs_ptr(&self) -> u8;
fn offset_of_funcs_len(&self) -> u8;
fn func_index(&self, defined_func_index: u32) -> u32;
fn defined_func_index(&self, func_index: u32) -> Option<u32>;
fn defined_func_type(&self, func_idx: u32) -> &Self::Signature {
// TODO: This assumes that there are no imported functions.
self.func_type(self.func_index(func_idx))
}
fn func_type(&self, func_idx: u32) -> &Self::Signature {
// TODO: This assumes that there are no imported functions.
self.signature(self.func_type_index(func_idx))
}
}
impl ModuleContext for SimpleContext {
type Signature = FuncType;
// TODO: We don't support external functions yet
fn func_index(&self, func_idx: u32) -> u32 {
func_idx
}
fn defined_func_index(&self, func_idx: u32) -> Option<u32> {
Some(func_idx)
}
fn func_type_index(&self, func_idx: u32) -> u32 {
self.func_ty_indicies[func_idx as usize]
}
fn signature(&self, index: u32) -> &Self::Signature {
&self.types[index as usize]
}
fn offset_of_memory_ptr(&self) -> u8 {
VmCtx::offset_of_memory_ptr()
}
fn offset_of_memory_len(&self) -> u8 {
VmCtx::offset_of_memory_len()
}
fn offset_of_funcs_ptr(&self) -> u8 {
VmCtx::offset_of_funcs_ptr()
}
fn offset_of_funcs_len(&self) -> u8 {
VmCtx::offset_of_funcs_len()
}
// TODO: type of a global
}
pub fn translate(data: &[u8]) -> Result<ExecutableModule, Error> {
translate_only(data).map(|m| m.instantiate())
}
/// Translate from a slice of bytes holding a wasm module.
pub fn translate_only(data: &[u8]) -> Result<TranslatedModule, Error> {
let mut reader = ModuleReader::new(data)?;
let mut output = TranslatedModule::default();
let mut table = None;
reader.skip_custom_sections()?;
if reader.eof() {
return Ok(output);
}
let mut section = reader.read()?;
if let SectionCode::Type = section.code {
let types_reader = section.get_type_section_reader()?;
output.types.types = translate_sections::type_(types_reader)?;
reader.skip_custom_sections()?;
if reader.eof() {
return Ok(output);
}
section = reader.read()?;
}
if let SectionCode::Import = section.code {
let imports = section.get_import_section_reader()?;
translate_sections::import(imports)?;
reader.skip_custom_sections()?;
if reader.eof() {
return Ok(output);
}
section = reader.read()?;
}
if let SectionCode::Function = section.code {
let functions = section.get_function_section_reader()?;
output.types.func_ty_indicies = translate_sections::function(functions)?;
reader.skip_custom_sections()?;
if reader.eof() {
return Ok(output);
}
section = reader.read()?;
}
if let SectionCode::Table = section.code {
let tables = section.get_table_section_reader()?;
let mut tables = translate_sections::table(tables)?;
assert!(tables.len() <= 1);
table = tables.drain(..).next();
reader.skip_custom_sections()?;
if reader.eof() {
return Ok(output);
}
section = reader.read()?;
}
if let SectionCode::Memory = section.code {
let memories = section.get_memory_section_reader()?;
let mem = translate_sections::memory(memories)?;
assert!(
mem.len() <= 1,
"Multiple memory sections not yet unimplemented"
);
if !mem.is_empty() {
let mem = mem[0];
assert_eq!(Some(mem.limits.initial), mem.limits.maximum);
output.memory = Some(mem);
}
reader.skip_custom_sections()?;
if reader.eof() {
return Ok(output);
}
section = reader.read()?;
}
if let SectionCode::Global = section.code {
let globals = section.get_global_section_reader()?;
translate_sections::global(globals)?;
reader.skip_custom_sections()?;
if reader.eof() {
return Ok(output);
}
section = reader.read()?;
}
if let SectionCode::Export = section.code {
let exports = section.get_export_section_reader()?;
translate_sections::export(exports)?;
reader.skip_custom_sections()?;
if reader.eof() {
return Ok(output);
}
section = reader.read()?;
}
if let SectionCode::Start = section.code {
let start = section.get_start_section_content()?;
translate_sections::start(start)?;
reader.skip_custom_sections()?;
if reader.eof() {
return Ok(output);
}
section = reader.read()?;
}
if let SectionCode::Element = section.code {
let elements = section.get_element_section_reader()?;
let elements = translate_sections::element(elements)?;
output.table = Some((
table.expect("Element section with no table section"),
elements,
));
reader.skip_custom_sections()?;
if reader.eof() {
return Ok(output);
}
section = reader.read()?;
}
if let SectionCode::Code = section.code {
let code = section.get_code_section_reader()?;
output.translated_code_section = Some(translate_sections::code(code, &output.types)?);
reader.skip_custom_sections()?;
if reader.eof() {
return Ok(output);
}
section = reader.read()?;
}
if let SectionCode::Data = section.code {
let data = section.get_data_section_reader()?;
translate_sections::data(data)?;
}
assert!(reader.eof());
Ok(output)
}