Added function compilation

This commit is contained in:
Syrus Akbary 2018-10-15 02:48:59 +02:00
parent e7788645d7
commit 78cf7800e5
9 changed files with 226 additions and 23 deletions

1
Cargo.lock generated
View File

@ -589,6 +589,7 @@ dependencies = [
"cranelift-wasm 0.22.0 (registry+https://github.com/rust-lang/crates.io-index)",
"docopt 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"error-chain 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
"memmap 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
"region 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)",

View File

@ -26,4 +26,8 @@ wasmparser = "0.20.0"
region = "0.3.0"
memmap = "0.6.2"
spin = "0.4.9"
log = "0.4.5"
target-lexicon = { version = "0.0.3", default-features = false }
[features]
debug = []

View File

@ -11,6 +11,22 @@ extern crate wabt;
extern crate target_lexicon;
extern crate spin;
use std::time::{Duration, Instant};
// #[cfg(feature = "debug")]
macro_rules! debug {
($($arg:tt)*) => {
println!($($arg)*);
}
}
// #[cfg(not(feature = "debug"))]
// macro_rules! debug {
// ($($arg:tt)*) => {}
// }
// #[macro_use] extern crate log;
use std::error::Error;
use std::fs::File;
use std::io;

View File

@ -182,5 +182,5 @@ macro_rules! wasm_tests {
wasm_tests!{
_type,
br_if,
// call,
call,
}

View File

@ -6,18 +6,22 @@
//! synchronously instantiate a given webassembly::Module object. However, the
//! primary way to get an Instance is through the asynchronous
//! webassembly::instantiateStreaming() function.
use super::module::Module;
use super::module::{DataInitializer, Exportable};
use cranelift_entity::EntityRef;
use cranelift_wasm::{FuncIndex, GlobalInit};
use super::memory::LinearMemory;
use std::marker::PhantomData;
use std::sync::Arc;
use std::{mem, slice};
use super::super::common::slice::{BoundedSlice, UncheckedSlice};
use memmap::MmapMut;
use spin::RwLock;
use region;
use cranelift_entity::EntityRef;
use cranelift_wasm::{FuncIndex, GlobalInit};
use cranelift_codegen::{Context, isa};
use super::errors::ErrorKind;
use super::memory::LinearMemory;
use super::relocation::{RelocSink, TrapSink};
use super::module::Module;
use super::module::{DataInitializer, Exportable};
use super::super::common::slice::{BoundedSlice, UncheckedSlice};
pub fn get_function_addr(
base: *const (),
@ -75,11 +79,49 @@ pub struct Instance {
impl Instance {
/// Create a new `Instance`.
pub fn new(module: &Module, code_base: *const (), functions: &[usize]) -> Instance {
pub fn new(module: &Module, code_base: *const ()) -> Result<Instance, ErrorKind> {
let mut tables: Vec<Vec<usize>> = Vec::new();
let mut memories: Vec<LinearMemory> = Vec::new();
let mut globals: Vec<u8> = Vec::new();
let mut functions: Vec<usize> = Vec::with_capacity(module.info.function_bodies.len());
// instantiate functions
{
let isa = isa::lookup(module.info.triple.clone())
.unwrap()
.finish(module.info.flags.clone());
let mut total_size: usize = 0;
let mut context_and_offsets = Vec::with_capacity(module.info.function_bodies.len());
// Compile the functions
for function_body in module.info.function_bodies.values() {
let mut context = Context::for_function(function_body.to_owned());
let code_size_offset = context.compile(&*isa).map_err(|e| ErrorKind::CompileError(e.to_string()))? as usize;
total_size += code_size_offset;
context_and_offsets.push((context, code_size_offset));
}
// Allocate the total memory for this functions
let map = MmapMut::map_anon(total_size).unwrap();
let region_start = map.as_ptr();
// // Emit this functions to memory
for (ref func_context, func_offset) in context_and_offsets.iter() {
let mut trap_sink = TrapSink::new(*func_offset);
let mut reloc_sink = RelocSink::new();
unsafe {
func_context.emit_to_memory(&*isa, (region_start as usize + func_offset) as *mut u8, &mut reloc_sink, &mut trap_sink);
}
}
// Set protection of this memory region to Read + Execute
// so we are able to execute them
unsafe {
region::protect(region_start, total_size, region::Protection::ReadExecute)
.expect("unable to make memory readable+executable");
}
}
// instantiate_tables
{
// Reserve table space
@ -105,8 +147,7 @@ impl Instance {
// to populate the table.
// let func_index = *elem_index - module.info.imported_funcs.len() as u32;
let func_addr = get_function_addr(code_base, functions, *&func_index);
let func_addr = get_function_addr(code_base, &functions, *&func_index);
table[base + table_element.offset + i] = func_addr as _;
}
}
@ -154,11 +195,11 @@ impl Instance {
}
};
Instance {
Ok(Instance {
tables: Arc::new(tables.into_iter().map(|table| RwLock::new(table)).collect()),
memories: Arc::new(memories.into_iter().collect()),
globals: globals,
}
})
}
pub fn memories(&self) -> Arc<Vec<LinearMemory>> {

View File

@ -1,4 +1,4 @@
use memmap;
use memmap::MmapMut;
use std::fmt;
const PAGE_SIZE: u32 = 65536;
@ -9,7 +9,7 @@ const MAX_PAGES: u32 = 65536;
/// This linear memory has a stable base address and at the same time allows
/// for dynamical growing.
pub struct LinearMemory {
mmap: memmap::MmapMut,
mmap: MmapMut,
// The initial size of the WebAssembly Memory, in units of
// WebAssembly pages.
current: u32,
@ -30,12 +30,13 @@ impl LinearMemory {
pub fn new(initial: u32, maximum: Option<u32>) -> Self {
assert!(initial <= MAX_PAGES);
assert!(maximum.is_none() || maximum.unwrap() <= MAX_PAGES);
debug!("Instantiate LinearMemory(initial={:?}, maximum={:?})", initial, maximum);
let len = PAGE_SIZE * match maximum {
Some(val) => val,
None => initial,
None => if initial > 0 { initial } else { 1 },
};
let mmap = memmap::MmapMut::map_anon(len as usize).unwrap();
let mmap = MmapMut::map_anon(len as usize).unwrap();
debug!("LinearMemory instantiated");
Self {
mmap,
current: initial,
@ -82,7 +83,7 @@ impl LinearMemory {
// If we have no maximum, this is a "dynamic" heap, and it's allowed
// to move.
assert!(self.maximum.is_none());
let mut new_mmap = memmap::MmapMut::map_anon(new_bytes).unwrap();
let mut new_mmap = MmapMut::map_anon(new_bytes).unwrap();
new_mmap.copy_from_slice(&self.mmap);
self.mmap = new_mmap;
}

View File

@ -3,6 +3,7 @@ pub mod instance;
pub mod memory;
pub mod module;
pub mod utils;
pub mod relocation;
use cranelift_native;
use std::panic;
@ -47,7 +48,9 @@ pub fn instantiate(
import_object: Option<ImportObject>,
) -> Result<ResultObject, ErrorKind> {
let module = compile(buffer_source)?;
let instance = Instance::new(&module, ptr::null(), &vec![]);
debug!("webassembly - creating instance");
let instance = Instance::new(&module, ptr::null())?;
debug!("webassembly - instance created");
Ok(ResultObject { module, instance })
}
@ -63,11 +66,15 @@ pub fn instantiate(
/// webassembly::CompileError.
pub fn compile(buffer_source: Vec<u8>) -> Result<Module, ErrorKind> {
// TODO: This should be automatically validated when creating the Module
if !validate(&buffer_source) {
let valid = validate(&buffer_source);
debug!("webassembly - valid {:?}", valid);
if !valid {
return Err(ErrorKind::CompileError("Module not valid".to_string()));
}
debug!("webassembly - creating module");
let module = Module::from_bytes(buffer_source, triple!("riscv64"), None)?;
debug!("webassembly - module created");
Ok(module)
}

View File

@ -450,7 +450,7 @@ impl<'environment> FuncEnvironmentTrait for FuncEnvironment<'environment> {
fn make_indirect_sig(&mut self, func: &mut ir::Function, index: SignatureIndex) -> ir::SigRef {
// A real implementation would probably change the calling convention and add `vmctx` and
// signature index arguments.
// func.import_signature(self.module.signatures[index].clone())
// func.import_signature(self.mod_info.signatures[index].clone())
func.import_signature(self.vmctx_sig(index))
}

View File

@ -0,0 +1,133 @@
use cranelift_codegen::binemit;
use cranelift_codegen::ir::{self, TrapCode, SourceLoc, ExternalName};
#[derive(Debug)]
pub struct Relocation {
/// The relocation code.
pub reloc: binemit::Reloc,
/// The offset where to apply the relocation.
pub offset: binemit::CodeOffset,
/// The addend to add to the relocation value.
pub addend: binemit::Addend,
}
/// Specify the type of relocation
#[derive(Debug)]
pub enum RelocationType {
Normal(u32),
Intrinsic(String),
}
/// Implementation of a relocation sink that just saves all the information for later
pub struct RelocSink {
// func: &'func ir::Function,
/// Relocations recorded for the function.
pub func_relocs: Vec<(Relocation, RelocationType)>,
}
impl binemit::RelocSink for RelocSink {
fn reloc_ebb(
&mut self,
_offset: binemit::CodeOffset,
_reloc: binemit::Reloc,
_ebb_offset: binemit::CodeOffset,
) {
// This should use the `offsets` field of `ir::Function`.
unimplemented!();
}
fn reloc_external(
&mut self,
offset: binemit::CodeOffset,
reloc: binemit::Reloc,
name: &ExternalName,
addend: binemit::Addend,
) {
match *name {
ExternalName::User {
namespace: 0,
index,
} => {
self.func_relocs.push(
(
Relocation {
reloc,
offset,
addend,
},
RelocationType::Normal(index as _),
)
);
},
ExternalName::TestCase {
length,
ascii,
} => {
let (slice, _) = ascii.split_at(length as usize);
let name = String::from_utf8(slice.to_vec()).unwrap();
self.func_relocs.push(
(
Relocation {
reloc,
offset,
addend,
},
RelocationType::Intrinsic(name),
)
);
},
_ => {
unimplemented!();
}
}
}
fn reloc_jt(
&mut self,
_offset: binemit::CodeOffset,
_reloc: binemit::Reloc,
_jt: ir::JumpTable,
) {
unimplemented!();
}
}
impl RelocSink {
pub fn new() -> RelocSink {
RelocSink {
func_relocs: Vec::new(),
}
}
}
pub struct TrapData {
pub offset: usize,
pub code: TrapCode,
}
/// Simple implementation of a TrapSink
/// that saves the info for later.
pub struct TrapSink {
current_func_offset: usize,
trap_datas: Vec<TrapData>,
}
impl TrapSink {
pub fn new(current_func_offset: usize) -> TrapSink {
TrapSink {
current_func_offset,
trap_datas: Vec::new(),
}
}
}
impl binemit::TrapSink for TrapSink {
fn trap(&mut self, offset: u32, _: SourceLoc, code: TrapCode) {
self.trap_datas.push(TrapData {
offset: self.current_func_offset + offset as usize,
code,
});
}
}