separate the context intialization from the function execution (#21)

the start function is called from the initialization phase
This commit is contained in:
Geoffroy Couprie
2018-11-27 14:33:56 +01:00
committed by Dan Gohman
parent 95fba6a9de
commit bf5a06bc95
4 changed files with 60 additions and 20 deletions

View File

@@ -46,7 +46,7 @@ mod vmcontext;
pub use compilation::{compile_module, Compilation, Relocation, RelocationTarget, Relocations}; pub use compilation::{compile_module, Compilation, Relocation, RelocationTarget, Relocations};
pub use environ::{ModuleEnvironment, ModuleTranslation}; pub use environ::{ModuleEnvironment, ModuleTranslation};
pub use module::{DataInitializer, Module, TableElements}; pub use module::{DataInitializer, Export, Module, TableElements};
#[cfg(not(feature = "std"))] #[cfg(not(feature = "std"))]
mod std { mod std {

View File

@@ -11,7 +11,7 @@ use std::ptr::{self, write_unaligned};
use std::string::String; use std::string::String;
use std::vec::Vec; use std::vec::Vec;
use wasmtime_environ::{ use wasmtime_environ::{
compile_module, Compilation, Module, ModuleTranslation, Relocation, RelocationTarget, compile_module, Compilation, Export, Module, ModuleTranslation, Relocation, RelocationTarget,
}; };
/// Executes a module that has been translated with the `wasmtime-environ` environment /// Executes a module that has been translated with the `wasmtime-environ` environment
@@ -132,16 +132,13 @@ fn make_vmctx(instance: &mut Instance, mem_base_addrs: &mut [*mut u8]) -> Vec<*m
vmctx vmctx
} }
/// Jumps to the code region of memory and execute the start function of the module. /// prepares the execution context
pub fn execute( pub fn finish_instantiation(
module: &Module, module: &Module,
compilation: &Compilation, compilation: &Compilation,
instance: &mut Instance, instance: &mut Instance,
) -> Result<(), String> { ) -> Result<Vec<*mut u8>, String> {
let start_index = module // TODO: Put all the function bodies into a page-aligned memory region, and
.start_func
.ok_or_else(|| String::from("No start function defined, aborting execution"))?;
// FIXME: Put all the function bodies into a page-aligned memory region, and
// then make them ReadExecute rather than ReadWriteExecute. // then make them ReadExecute rather than ReadWriteExecute.
for code_buf in compilation.functions.values() { for code_buf in compilation.functions.values() {
match unsafe { match unsafe {
@@ -161,19 +158,21 @@ pub fn execute(
} }
} }
let code_buf =
&compilation.functions[module
.defined_func_index(start_index)
.expect("imported start functions not supported yet")];
// Collect all memory base addresses and Vec. // Collect all memory base addresses and Vec.
let mut mem_base_addrs = instance let mut mem_base_addrs = instance
.memories .memories
.values_mut() .values_mut()
.map(LinearMemory::base_addr) .map(LinearMemory::base_addr)
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let vmctx = make_vmctx(instance, &mut mem_base_addrs); let vmctx = make_vmctx(instance, &mut mem_base_addrs);
if let Some(start_index) = module.start_func {
let code_buf =
&compilation.functions[module
.defined_func_index(start_index)
.expect("imported start functions not supported yet")];
// Rather than writing inline assembly to jump to the code region, we use the fact that // Rather than writing inline assembly to jump to the code region, we use the fact that
// the Rust ABI for calling a function with no arguments and no return matches the one of // the Rust ABI for calling a function with no arguments and no return matches the one of
// the generated code. Thanks to this, we can transmute the code region into a first-class // the generated code. Thanks to this, we can transmute the code region into a first-class
@@ -182,5 +181,36 @@ pub fn execute(
let start_func = transmute::<_, fn(*const *mut u8)>(code_buf.as_ptr()); let start_func = transmute::<_, fn(*const *mut u8)>(code_buf.as_ptr());
start_func(vmctx.as_ptr()); start_func(vmctx.as_ptr());
} }
}
Ok(vmctx)
}
/// Jumps to the code region of memory and execute the exported function
pub fn execute(
module: &Module,
compilation: &Compilation,
vmctx: &mut Vec<*mut u8>,
function: &str,
) -> Result<(), String> {
let fn_index = match module.exports.get(function) {
Some(Export::Function(index)) => *index,
Some(_) => return Err(format!("exported item \"{}\" is not a function", function)),
None => return Err(format!("no export named \"{}\"", function)),
};
let code_buf =
&compilation.functions[module
.defined_func_index(fn_index)
.expect("imported start functions not supported yet")];
// Rather than writing inline assembly to jump to the code region, we use the fact that
// the Rust ABI for calling a function with no arguments and no return matches the one of
// the generated code. Thanks to this, we can transmute the code region into a first-class
// Rust function and call it.
unsafe {
let func = transmute::<_, fn(*const *mut u8)>(code_buf.as_ptr());
func(vmctx.as_ptr());
}
Ok(()) Ok(())
} }

View File

@@ -41,7 +41,7 @@ mod execute;
mod instance; mod instance;
mod memory; mod memory;
pub use execute::{compile_and_link_module, execute}; pub use execute::{compile_and_link_module, execute, finish_instantiation};
pub use instance::Instance; pub use instance::Instance;
#[cfg(not(feature = "std"))] #[cfg(not(feature = "std"))]

View File

@@ -60,7 +60,7 @@ use std::path::PathBuf;
use std::process::{exit, Command}; use std::process::{exit, Command};
use tempdir::TempDir; use tempdir::TempDir;
use wasmtime_environ::{Module, ModuleEnvironment}; use wasmtime_environ::{Module, ModuleEnvironment};
use wasmtime_execute::{compile_and_link_module, execute, Instance}; use wasmtime_execute::{compile_and_link_module, execute, finish_instantiation, Instance};
const USAGE: &str = " const USAGE: &str = "
Wasm to Cranelift IL translation utility. Wasm to Cranelift IL translation utility.
@@ -69,11 +69,13 @@ The translation is dependent on the environment chosen.
Usage: Usage:
wasmtime [-mop] <file>... wasmtime [-mop] <file>...
wasmtime [-mop] <file>... --function=<fn>
wasmtime --help | --version wasmtime --help | --version
Options: Options:
-o, --optimize runs optimization passes on the translated functions -o, --optimize runs optimization passes on the translated functions
-m, --memory interactive memory inspector after execution -m, --memory interactive memory inspector after execution
--function=<fn> name of function to run
-h, --help print this help message -h, --help print this help message
--version print the Cranelift version --version print the Cranelift version
"; ";
@@ -83,6 +85,7 @@ struct Args {
arg_file: Vec<String>, arg_file: Vec<String>,
flag_memory: bool, flag_memory: bool,
flag_optimize: bool, flag_optimize: bool,
flag_function: Option<String>,
} }
fn read_to_end(path: PathBuf) -> Result<Vec<u8>, io::Error> { fn read_to_end(path: PathBuf) -> Result<Vec<u8>, io::Error> {
@@ -162,7 +165,14 @@ fn handle_module(args: &Args, path: PathBuf, isa: &TargetIsa) -> Result<(), Stri
&compilation, &compilation,
&translation.lazy.data_initializers, &translation.lazy.data_initializers,
); );
execute(&translation.module, &compilation, &mut instance)?;
let mut context =
finish_instantiation(&translation.module, &compilation, &mut instance)?;
if let Some(ref f) = args.flag_function {
execute(&translation.module, &compilation, &mut context, &f)?;
}
instance instance
} }
Err(s) => { Err(s) => {