mirror of
https://github.com/fluencelabs/wasmer
synced 2025-04-25 02:12:13 +00:00
Added function compilation
This commit is contained in:
parent
e7788645d7
commit
78cf7800e5
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -589,6 +589,7 @@ dependencies = [
|
|||||||
"cranelift-wasm 0.22.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"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)",
|
"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)",
|
"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)",
|
"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)",
|
"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)",
|
"serde 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
@ -26,4 +26,8 @@ wasmparser = "0.20.0"
|
|||||||
region = "0.3.0"
|
region = "0.3.0"
|
||||||
memmap = "0.6.2"
|
memmap = "0.6.2"
|
||||||
spin = "0.4.9"
|
spin = "0.4.9"
|
||||||
|
log = "0.4.5"
|
||||||
target-lexicon = { version = "0.0.3", default-features = false }
|
target-lexicon = { version = "0.0.3", default-features = false }
|
||||||
|
|
||||||
|
[features]
|
||||||
|
debug = []
|
||||||
|
16
src/main.rs
16
src/main.rs
@ -11,6 +11,22 @@ extern crate wabt;
|
|||||||
extern crate target_lexicon;
|
extern crate target_lexicon;
|
||||||
extern crate spin;
|
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::error::Error;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io;
|
use std::io;
|
||||||
|
@ -182,5 +182,5 @@ macro_rules! wasm_tests {
|
|||||||
wasm_tests!{
|
wasm_tests!{
|
||||||
_type,
|
_type,
|
||||||
br_if,
|
br_if,
|
||||||
// call,
|
call,
|
||||||
}
|
}
|
||||||
|
@ -6,18 +6,22 @@
|
|||||||
//! synchronously instantiate a given webassembly::Module object. However, the
|
//! synchronously instantiate a given webassembly::Module object. However, the
|
||||||
//! primary way to get an Instance is through the asynchronous
|
//! primary way to get an Instance is through the asynchronous
|
||||||
//! webassembly::instantiateStreaming() function.
|
//! 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::marker::PhantomData;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::{mem, slice};
|
use std::{mem, slice};
|
||||||
|
use memmap::MmapMut;
|
||||||
use super::super::common::slice::{BoundedSlice, UncheckedSlice};
|
|
||||||
use spin::RwLock;
|
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(
|
pub fn get_function_addr(
|
||||||
base: *const (),
|
base: *const (),
|
||||||
@ -75,11 +79,49 @@ pub struct Instance {
|
|||||||
|
|
||||||
impl Instance {
|
impl Instance {
|
||||||
/// Create a new `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 tables: Vec<Vec<usize>> = Vec::new();
|
||||||
let mut memories: Vec<LinearMemory> = Vec::new();
|
let mut memories: Vec<LinearMemory> = Vec::new();
|
||||||
let mut globals: Vec<u8> = 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
|
// instantiate_tables
|
||||||
{
|
{
|
||||||
// Reserve table space
|
// Reserve table space
|
||||||
@ -105,8 +147,7 @@ impl Instance {
|
|||||||
// to populate the table.
|
// to populate the table.
|
||||||
|
|
||||||
// let func_index = *elem_index - module.info.imported_funcs.len() as u32;
|
// 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 _;
|
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()),
|
tables: Arc::new(tables.into_iter().map(|table| RwLock::new(table)).collect()),
|
||||||
memories: Arc::new(memories.into_iter().collect()),
|
memories: Arc::new(memories.into_iter().collect()),
|
||||||
globals: globals,
|
globals: globals,
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn memories(&self) -> Arc<Vec<LinearMemory>> {
|
pub fn memories(&self) -> Arc<Vec<LinearMemory>> {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use memmap;
|
use memmap::MmapMut;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
const PAGE_SIZE: u32 = 65536;
|
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
|
/// This linear memory has a stable base address and at the same time allows
|
||||||
/// for dynamical growing.
|
/// for dynamical growing.
|
||||||
pub struct LinearMemory {
|
pub struct LinearMemory {
|
||||||
mmap: memmap::MmapMut,
|
mmap: MmapMut,
|
||||||
// The initial size of the WebAssembly Memory, in units of
|
// The initial size of the WebAssembly Memory, in units of
|
||||||
// WebAssembly pages.
|
// WebAssembly pages.
|
||||||
current: u32,
|
current: u32,
|
||||||
@ -30,12 +30,13 @@ impl LinearMemory {
|
|||||||
pub fn new(initial: u32, maximum: Option<u32>) -> Self {
|
pub fn new(initial: u32, maximum: Option<u32>) -> Self {
|
||||||
assert!(initial <= MAX_PAGES);
|
assert!(initial <= MAX_PAGES);
|
||||||
assert!(maximum.is_none() || maximum.unwrap() <= MAX_PAGES);
|
assert!(maximum.is_none() || maximum.unwrap() <= MAX_PAGES);
|
||||||
|
debug!("Instantiate LinearMemory(initial={:?}, maximum={:?})", initial, maximum);
|
||||||
let len = PAGE_SIZE * match maximum {
|
let len = PAGE_SIZE * match maximum {
|
||||||
Some(val) => val,
|
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 {
|
Self {
|
||||||
mmap,
|
mmap,
|
||||||
current: initial,
|
current: initial,
|
||||||
@ -82,7 +83,7 @@ impl LinearMemory {
|
|||||||
// If we have no maximum, this is a "dynamic" heap, and it's allowed
|
// If we have no maximum, this is a "dynamic" heap, and it's allowed
|
||||||
// to move.
|
// to move.
|
||||||
assert!(self.maximum.is_none());
|
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);
|
new_mmap.copy_from_slice(&self.mmap);
|
||||||
self.mmap = new_mmap;
|
self.mmap = new_mmap;
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ pub mod instance;
|
|||||||
pub mod memory;
|
pub mod memory;
|
||||||
pub mod module;
|
pub mod module;
|
||||||
pub mod utils;
|
pub mod utils;
|
||||||
|
pub mod relocation;
|
||||||
|
|
||||||
use cranelift_native;
|
use cranelift_native;
|
||||||
use std::panic;
|
use std::panic;
|
||||||
@ -47,7 +48,9 @@ pub fn instantiate(
|
|||||||
import_object: Option<ImportObject>,
|
import_object: Option<ImportObject>,
|
||||||
) -> Result<ResultObject, ErrorKind> {
|
) -> Result<ResultObject, ErrorKind> {
|
||||||
let module = compile(buffer_source)?;
|
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 })
|
Ok(ResultObject { module, instance })
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -63,11 +66,15 @@ pub fn instantiate(
|
|||||||
/// webassembly::CompileError.
|
/// webassembly::CompileError.
|
||||||
pub fn compile(buffer_source: Vec<u8>) -> Result<Module, ErrorKind> {
|
pub fn compile(buffer_source: Vec<u8>) -> Result<Module, ErrorKind> {
|
||||||
// TODO: This should be automatically validated when creating the Module
|
// 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()));
|
return Err(ErrorKind::CompileError("Module not valid".to_string()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
debug!("webassembly - creating module");
|
||||||
let module = Module::from_bytes(buffer_source, triple!("riscv64"), None)?;
|
let module = Module::from_bytes(buffer_source, triple!("riscv64"), None)?;
|
||||||
|
debug!("webassembly - module created");
|
||||||
|
|
||||||
Ok(module)
|
Ok(module)
|
||||||
}
|
}
|
||||||
|
@ -450,7 +450,7 @@ impl<'environment> FuncEnvironmentTrait for FuncEnvironment<'environment> {
|
|||||||
fn make_indirect_sig(&mut self, func: &mut ir::Function, index: SignatureIndex) -> ir::SigRef {
|
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
|
// A real implementation would probably change the calling convention and add `vmctx` and
|
||||||
// signature index arguments.
|
// 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))
|
func.import_signature(self.vmctx_sig(index))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
133
src/webassembly/relocation.rs
Normal file
133
src/webassembly/relocation.rs
Normal 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,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user