Improve error handling, and start refactoring Instance.
Introduce proper error handling in several places, and perform a first pass at refactoring Instance to make it easier to use.
This commit is contained in:
@@ -14,6 +14,8 @@ cranelift-codegen = { git = "https://github.com/sunfishcode/cranelift.git", bran
|
|||||||
cranelift-entity = { git = "https://github.com/sunfishcode/cranelift.git", branch = "guard-offset" }
|
cranelift-entity = { git = "https://github.com/sunfishcode/cranelift.git", branch = "guard-offset" }
|
||||||
cranelift-wasm = { git = "https://github.com/sunfishcode/cranelift.git", branch = "guard-offset" }
|
cranelift-wasm = { git = "https://github.com/sunfishcode/cranelift.git", branch = "guard-offset" }
|
||||||
cast = { version = "0.2.2", default-features = false }
|
cast = { version = "0.2.2", default-features = false }
|
||||||
|
failure = "0.1.3"
|
||||||
|
failure_derive = "0.1.3"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["std"]
|
default = ["std"]
|
||||||
|
|||||||
@@ -5,13 +5,11 @@ use cranelift_codegen::binemit;
|
|||||||
use cranelift_codegen::ir;
|
use cranelift_codegen::ir;
|
||||||
use cranelift_codegen::ir::ExternalName;
|
use cranelift_codegen::ir::ExternalName;
|
||||||
use cranelift_codegen::isa;
|
use cranelift_codegen::isa;
|
||||||
use cranelift_codegen::Context;
|
use cranelift_codegen::{CodegenError, Context};
|
||||||
use cranelift_entity::{EntityRef, PrimaryMap};
|
use cranelift_entity::{EntityRef, PrimaryMap};
|
||||||
use cranelift_wasm::{
|
use cranelift_wasm::{DefinedFuncIndex, FuncIndex, FuncTranslator, WasmError};
|
||||||
DefinedFuncIndex, FuncIndex, FuncTranslator, GlobalIndex, MemoryIndex, TableIndex,
|
use environ::{get_func_name, get_memory_grow_name, get_memory_size_name, FuncEnvironment};
|
||||||
};
|
use module::Module;
|
||||||
use environ::{get_func_name, get_memory_grow_name, get_memory_size_name, ModuleTranslation};
|
|
||||||
use std::string::{String, ToString};
|
|
||||||
use std::vec::Vec;
|
use std::vec::Vec;
|
||||||
|
|
||||||
/// The result of compiling a WebAssemby module's functions.
|
/// The result of compiling a WebAssemby module's functions.
|
||||||
@@ -19,30 +17,12 @@ use std::vec::Vec;
|
|||||||
pub struct Compilation {
|
pub struct Compilation {
|
||||||
/// Compiled machine code for the function bodies.
|
/// Compiled machine code for the function bodies.
|
||||||
pub functions: PrimaryMap<DefinedFuncIndex, Vec<u8>>,
|
pub functions: PrimaryMap<DefinedFuncIndex, Vec<u8>>,
|
||||||
|
|
||||||
/// Resolved function addresses for imported functions.
|
|
||||||
pub resolved_func_imports: PrimaryMap<FuncIndex, usize>,
|
|
||||||
|
|
||||||
/// Resolved function addresses for imported tables.
|
|
||||||
pub resolved_table_imports: PrimaryMap<TableIndex, usize>,
|
|
||||||
|
|
||||||
/// Resolved function addresses for imported globals.
|
|
||||||
pub resolved_global_imports: PrimaryMap<GlobalIndex, usize>,
|
|
||||||
|
|
||||||
/// Resolved function addresses for imported memories.
|
|
||||||
pub resolved_memory_imports: PrimaryMap<MemoryIndex, usize>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Compilation {
|
impl Compilation {
|
||||||
/// Allocates the compilation result with the given function bodies.
|
/// Allocates the compilation result with the given function bodies.
|
||||||
pub fn new(functions: PrimaryMap<DefinedFuncIndex, Vec<u8>>) -> Self {
|
pub fn new(functions: PrimaryMap<DefinedFuncIndex, Vec<u8>>) -> Self {
|
||||||
Self {
|
Self { functions }
|
||||||
functions,
|
|
||||||
resolved_func_imports: PrimaryMap::new(),
|
|
||||||
resolved_table_imports: PrimaryMap::new(),
|
|
||||||
resolved_memory_imports: PrimaryMap::new(),
|
|
||||||
resolved_global_imports: PrimaryMap::new(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -139,32 +119,49 @@ pub type Relocations = PrimaryMap<DefinedFuncIndex, Vec<Relocation>>;
|
|||||||
/// Compile the module, producing a compilation result with associated
|
/// Compile the module, producing a compilation result with associated
|
||||||
/// relocations.
|
/// relocations.
|
||||||
pub fn compile_module<'data, 'module>(
|
pub fn compile_module<'data, 'module>(
|
||||||
translation: &ModuleTranslation<'data, 'module>,
|
module: &'module Module,
|
||||||
|
function_body_inputs: &PrimaryMap<DefinedFuncIndex, &'data [u8]>,
|
||||||
isa: &isa::TargetIsa,
|
isa: &isa::TargetIsa,
|
||||||
) -> Result<(Compilation, Relocations), String> {
|
) -> Result<(Compilation, Relocations), CompileError> {
|
||||||
let mut functions = PrimaryMap::new();
|
let mut functions = PrimaryMap::new();
|
||||||
let mut relocations = PrimaryMap::new();
|
let mut relocations = PrimaryMap::new();
|
||||||
for (i, input) in translation.lazy.function_body_inputs.iter() {
|
for (i, input) in function_body_inputs.iter() {
|
||||||
let func_index = translation.module.func_index(i);
|
let func_index = module.func_index(i);
|
||||||
let mut context = Context::new();
|
let mut context = Context::new();
|
||||||
context.func.name = get_func_name(func_index);
|
context.func.name = get_func_name(func_index);
|
||||||
context.func.signature =
|
context.func.signature = module.signatures[module.functions[func_index]].clone();
|
||||||
translation.module.signatures[translation.module.functions[func_index]].clone();
|
|
||||||
|
|
||||||
let mut trans = FuncTranslator::new();
|
let mut trans = FuncTranslator::new();
|
||||||
trans
|
trans
|
||||||
.translate(input, &mut context.func, &mut translation.func_env())
|
.translate(
|
||||||
.map_err(|e| e.to_string())?;
|
input,
|
||||||
|
&mut context.func,
|
||||||
|
&mut FuncEnvironment::new(isa, module),
|
||||||
|
)
|
||||||
|
.map_err(CompileError::Wasm)?;
|
||||||
|
|
||||||
let mut code_buf: Vec<u8> = Vec::new();
|
let mut code_buf: Vec<u8> = Vec::new();
|
||||||
let mut reloc_sink = RelocSink::new();
|
let mut reloc_sink = RelocSink::new();
|
||||||
let mut trap_sink = binemit::NullTrapSink {};
|
let mut trap_sink = binemit::NullTrapSink {};
|
||||||
context
|
context
|
||||||
.compile_and_emit(isa, &mut code_buf, &mut reloc_sink, &mut trap_sink)
|
.compile_and_emit(isa, &mut code_buf, &mut reloc_sink, &mut trap_sink)
|
||||||
.map_err(|e| e.to_string())?;
|
.map_err(CompileError::Codegen)?;
|
||||||
functions.push(code_buf);
|
functions.push(code_buf);
|
||||||
relocations.push(reloc_sink.func_relocs);
|
relocations.push(reloc_sink.func_relocs);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Reorganize where we create the Vec for the resolved imports.
|
// TODO: Reorganize where we create the Vec for the resolved imports.
|
||||||
Ok((Compilation::new(functions), relocations))
|
Ok((Compilation::new(functions), relocations))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// An error while compiling WebAssembly to machine code.
|
||||||
|
#[derive(Fail, Debug)]
|
||||||
|
pub enum CompileError {
|
||||||
|
/// A wasm translation error occured.
|
||||||
|
#[fail(display = "WebAssembly translation error: {}", _0)]
|
||||||
|
Wasm(WasmError),
|
||||||
|
|
||||||
|
/// A compilation error occured.
|
||||||
|
#[fail(display = "Compilation error: {}", _0)]
|
||||||
|
Codegen(CodegenError),
|
||||||
|
}
|
||||||
|
|||||||
@@ -34,6 +34,9 @@ extern crate cranelift_wasm;
|
|||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate alloc;
|
extern crate alloc;
|
||||||
extern crate cast;
|
extern crate cast;
|
||||||
|
extern crate failure;
|
||||||
|
#[macro_use]
|
||||||
|
extern crate failure_derive;
|
||||||
|
|
||||||
mod compilation;
|
mod compilation;
|
||||||
mod environ;
|
mod environ;
|
||||||
@@ -42,7 +45,7 @@ mod tunables;
|
|||||||
mod vmoffsets;
|
mod vmoffsets;
|
||||||
|
|
||||||
pub use compilation::{
|
pub use compilation::{
|
||||||
compile_module, Compilation, RelocSink, Relocation, RelocationTarget, Relocations,
|
compile_module, Compilation, CompileError, RelocSink, Relocation, RelocationTarget, Relocations,
|
||||||
};
|
};
|
||||||
pub use environ::{translate_signature, ModuleEnvironment, ModuleTranslation};
|
pub use environ::{translate_signature, ModuleEnvironment, ModuleTranslation};
|
||||||
pub use module::{
|
pub use module::{
|
||||||
|
|||||||
@@ -21,6 +21,8 @@ libc = { version = "0.2.44", default-features = false }
|
|||||||
errno = "0.2.4"
|
errno = "0.2.4"
|
||||||
memoffset = "0.2.1"
|
memoffset = "0.2.1"
|
||||||
cast = { version = "0.2.2", default-features = false }
|
cast = { version = "0.2.2", default-features = false }
|
||||||
|
failure = "0.1.3"
|
||||||
|
failure_derive = "0.1.3"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
cmake = "0.1.35"
|
cmake = "0.1.35"
|
||||||
|
|||||||
@@ -1,12 +1,15 @@
|
|||||||
//! Support for performing actions with a wasm module from the outside.
|
//! Support for performing actions with a wasm module from the outside.
|
||||||
|
|
||||||
use cranelift_codegen::ir;
|
use cranelift_codegen::ir;
|
||||||
|
use link::LinkError;
|
||||||
|
use std::fmt;
|
||||||
use std::string::String;
|
use std::string::String;
|
||||||
use std::vec::Vec;
|
use std::vec::Vec;
|
||||||
|
use wasmtime_environ::CompileError;
|
||||||
|
|
||||||
/// A runtime value.
|
/// A runtime value.
|
||||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||||
pub enum Value {
|
pub enum RuntimeValue {
|
||||||
/// A runtime value with type i32.
|
/// A runtime value with type i32.
|
||||||
I32(i32),
|
I32(i32),
|
||||||
/// A runtime value with type i64.
|
/// A runtime value with type i64.
|
||||||
@@ -17,61 +20,121 @@ pub enum Value {
|
|||||||
F64(u64),
|
F64(u64),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Value {
|
impl RuntimeValue {
|
||||||
/// Return the type of this `Value`.
|
/// Return the type of this `RuntimeValue`.
|
||||||
pub fn value_type(self) -> ir::Type {
|
pub fn value_type(self) -> ir::Type {
|
||||||
match self {
|
match self {
|
||||||
Value::I32(_) => ir::types::I32,
|
RuntimeValue::I32(_) => ir::types::I32,
|
||||||
Value::I64(_) => ir::types::I64,
|
RuntimeValue::I64(_) => ir::types::I64,
|
||||||
Value::F32(_) => ir::types::F32,
|
RuntimeValue::F32(_) => ir::types::F32,
|
||||||
Value::F64(_) => ir::types::F64,
|
RuntimeValue::F64(_) => ir::types::F64,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Assuming this `Value` holds an `i32`, return that value.
|
/// Assuming this `RuntimeValue` holds an `i32`, return that value.
|
||||||
pub fn unwrap_i32(self) -> i32 {
|
pub fn unwrap_i32(self) -> i32 {
|
||||||
match self {
|
match self {
|
||||||
Value::I32(x) => x,
|
RuntimeValue::I32(x) => x,
|
||||||
_ => panic!("unwrapping value of type {} as i32", self.value_type()),
|
_ => panic!("unwrapping value of type {} as i32", self.value_type()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Assuming this `Value` holds an `i64`, return that value.
|
/// Assuming this `RuntimeValue` holds an `i64`, return that value.
|
||||||
pub fn unwrap_i64(self) -> i64 {
|
pub fn unwrap_i64(self) -> i64 {
|
||||||
match self {
|
match self {
|
||||||
Value::I64(x) => x,
|
RuntimeValue::I64(x) => x,
|
||||||
_ => panic!("unwrapping value of type {} as i64", self.value_type()),
|
_ => panic!("unwrapping value of type {} as i64", self.value_type()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Assuming this `Value` holds an `f32`, return that value.
|
/// Assuming this `RuntimeValue` holds an `f32`, return that value.
|
||||||
pub fn unwrap_f32(self) -> u32 {
|
pub fn unwrap_f32(self) -> f32 {
|
||||||
|
f32::from_bits(self.unwrap_f32_bits())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Assuming this `RuntimeValue` holds an `f32`, return the bits of that value as a `u32`.
|
||||||
|
pub fn unwrap_f32_bits(self) -> u32 {
|
||||||
match self {
|
match self {
|
||||||
Value::F32(x) => x,
|
RuntimeValue::F32(x) => x,
|
||||||
_ => panic!("unwrapping value of type {} as f32", self.value_type()),
|
_ => panic!("unwrapping value of type {} as f32", self.value_type()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Assuming this `Value` holds an `f64`, return that value.
|
/// Assuming this `RuntimeValue` holds an `f64`, return that value.
|
||||||
pub fn unwrap_f64(self) -> u64 {
|
pub fn unwrap_f64(self) -> f64 {
|
||||||
|
f64::from_bits(self.unwrap_f64_bits())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Assuming this `RuntimeValue` holds an `f64`, return the bits of that value as a `u64`.
|
||||||
|
pub fn unwrap_f64_bits(self) -> u64 {
|
||||||
match self {
|
match self {
|
||||||
Value::F64(x) => x,
|
RuntimeValue::F64(x) => x,
|
||||||
_ => panic!("unwrapping value of type {} as f64", self.value_type()),
|
_ => panic!("unwrapping value of type {} as f64", self.value_type()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for RuntimeValue {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
RuntimeValue::I32(x) => write!(f, "{}: i32", x),
|
||||||
|
RuntimeValue::I64(x) => write!(f, "{}: i64", x),
|
||||||
|
RuntimeValue::F32(x) => write!(f, "{}: f32", x),
|
||||||
|
RuntimeValue::F64(x) => write!(f, "{}: f64", x),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// The result of invoking a wasm function or reading a wasm global.
|
/// The result of invoking a wasm function or reading a wasm global.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum ActionOutcome {
|
pub enum ActionOutcome {
|
||||||
/// The action returned normally. Its return values are provided.
|
/// The action returned normally. Its return values are provided.
|
||||||
Returned {
|
Returned {
|
||||||
/// The return values.
|
/// The return values.
|
||||||
values: Vec<Value>,
|
values: Vec<RuntimeValue>,
|
||||||
},
|
},
|
||||||
|
|
||||||
/// A trap occurred while the action was executing.
|
/// A trap occurred while the action was executing.
|
||||||
Trapped {
|
Trapped {
|
||||||
/// The trap message.
|
/// The trap message.
|
||||||
message: String,
|
message: String,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// An error detected while invoking a wasm function or reading a wasm global.
|
||||||
|
/// Note that at this level, traps are not reported errors, but are rather
|
||||||
|
/// returned through `ActionOutcome`.
|
||||||
|
#[derive(Fail, Debug)]
|
||||||
|
pub enum ActionError {
|
||||||
|
/// No field with the specified name was present.
|
||||||
|
#[fail(display = "Unknown field: {}", _0)]
|
||||||
|
Field(String),
|
||||||
|
|
||||||
|
/// An index was out of bounds.
|
||||||
|
#[fail(display = "Index out of bounds: {}", _0)]
|
||||||
|
Index(u64),
|
||||||
|
|
||||||
|
/// The field was present but was the wrong kind (eg. function, table, global, or memory).
|
||||||
|
#[fail(display = "Kind error: {}", _0)]
|
||||||
|
Kind(String),
|
||||||
|
|
||||||
|
/// The field was present but was the wrong type (eg. i32, i64, f32, or f64).
|
||||||
|
#[fail(display = "Type error: {}", _0)]
|
||||||
|
Type(String),
|
||||||
|
|
||||||
|
/// A wasm translation error occured.
|
||||||
|
#[fail(display = "WebAssembly compilation error: {}", _0)]
|
||||||
|
Compile(CompileError),
|
||||||
|
|
||||||
|
/// Some runtime resource was unavailable or insufficient.
|
||||||
|
#[fail(display = "Runtime resource error: {}", _0)]
|
||||||
|
Resource(String),
|
||||||
|
|
||||||
|
/// Link error.
|
||||||
|
#[fail(display = "Link error: {}", _0)]
|
||||||
|
Link(LinkError),
|
||||||
|
|
||||||
|
/// Start function trapped.
|
||||||
|
#[fail(display = "Start function trapped: {}", _0)]
|
||||||
|
Start(String),
|
||||||
|
}
|
||||||
|
|||||||
@@ -28,9 +28,12 @@ impl Code {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Allocate `size` bytes of memory which can be made executable later by
|
/// Allocate `size` bytes of memory which can be made executable later by
|
||||||
/// calling `publish()`.
|
/// calling `publish()`. Note that we allocate the memory as writeable so
|
||||||
/// TODO: alignment
|
/// that it can be written to and patched, though we make it readonly before
|
||||||
pub fn allocate(&mut self, size: usize) -> Result<*mut u8, String> {
|
/// actually executing from it.
|
||||||
|
///
|
||||||
|
/// TODO: Add an alignment flag.
|
||||||
|
fn allocate(&mut self, size: usize) -> Result<*mut u8, String> {
|
||||||
if self.current.len() - self.position < size {
|
if self.current.len() - self.position < size {
|
||||||
self.mmaps.push(mem::replace(
|
self.mmaps.push(mem::replace(
|
||||||
&mut self.current,
|
&mut self.current,
|
||||||
@@ -63,8 +66,8 @@ impl Code {
|
|||||||
if !m.as_ptr().is_null() {
|
if !m.as_ptr().is_null() {
|
||||||
unsafe {
|
unsafe {
|
||||||
region::protect(m.as_mut_ptr(), m.len(), region::Protection::ReadExecute)
|
region::protect(m.as_mut_ptr(), m.len(), region::Protection::ReadExecute)
|
||||||
.expect("unable to make memory readonly");
|
|
||||||
}
|
}
|
||||||
|
.expect("unable to make memory readonly and executable");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.published = self.mmaps.len();
|
self.published = self.mmaps.len();
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ pub enum ExportValue {
|
|||||||
/// A function export value.
|
/// A function export value.
|
||||||
Function {
|
Function {
|
||||||
/// The address of the native-code function.
|
/// The address of the native-code function.
|
||||||
address: usize,
|
address: *const u8,
|
||||||
/// The function signature declaration, used for compatibilty checking.
|
/// The function signature declaration, used for compatibilty checking.
|
||||||
signature: ir::Signature,
|
signature: ir::Signature,
|
||||||
},
|
},
|
||||||
@@ -40,7 +40,7 @@ pub enum ExportValue {
|
|||||||
|
|
||||||
impl ExportValue {
|
impl ExportValue {
|
||||||
/// Construct a function export value.
|
/// Construct a function export value.
|
||||||
pub fn function(address: usize, signature: ir::Signature) -> Self {
|
pub fn function(address: *const u8, signature: ir::Signature) -> Self {
|
||||||
ExportValue::Function { address, signature }
|
ExportValue::Function { address, signature }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,39 +1,65 @@
|
|||||||
//! Support for reading the value of a wasm global from outside the module.
|
//! Support for reading the value of a wasm global from outside the module.
|
||||||
|
|
||||||
use action::Value;
|
use action::{ActionError, RuntimeValue};
|
||||||
use cranelift_codegen::ir;
|
use cranelift_codegen::ir;
|
||||||
|
use cranelift_entity::EntityRef;
|
||||||
use cranelift_wasm::GlobalIndex;
|
use cranelift_wasm::GlobalIndex;
|
||||||
use std::string::String;
|
use instance::Instance;
|
||||||
use vmcontext::VMContext;
|
|
||||||
use wasmtime_environ::{Export, Module};
|
use wasmtime_environ::{Export, Module};
|
||||||
|
|
||||||
/// Jumps to the code region of memory and invoke the exported function
|
/// Reads the value of the named global variable in `module`.
|
||||||
pub fn get(module: &Module, vmctx: *mut VMContext, global_name: &str) -> Result<Value, String> {
|
pub fn get(
|
||||||
|
module: &Module,
|
||||||
|
instance: &mut Instance,
|
||||||
|
global_name: &str,
|
||||||
|
) -> Result<RuntimeValue, ActionError> {
|
||||||
let global_index = match module.exports.get(global_name) {
|
let global_index = match module.exports.get(global_name) {
|
||||||
Some(Export::Global(index)) => *index,
|
Some(Export::Global(index)) => *index,
|
||||||
Some(_) => return Err(format!("exported item \"{}\" is not a global", global_name)),
|
Some(_) => {
|
||||||
None => return Err(format!("no export named \"{}\"", global_name)),
|
return Err(ActionError::Kind(format!(
|
||||||
|
"exported item \"{}\" is not a global",
|
||||||
|
global_name
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
return Err(ActionError::Field(format!(
|
||||||
|
"no export named \"{}\"",
|
||||||
|
global_name
|
||||||
|
)))
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
get_by_index(module, vmctx, global_index)
|
get_by_index(module, instance, global_index)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Reads the value of the indexed global variable in `module`.
|
||||||
pub fn get_by_index(
|
pub fn get_by_index(
|
||||||
module: &Module,
|
module: &Module,
|
||||||
vmctx: *mut VMContext,
|
instance: &mut Instance,
|
||||||
global_index: GlobalIndex,
|
global_index: GlobalIndex,
|
||||||
) -> Result<Value, String> {
|
) -> Result<RuntimeValue, ActionError> {
|
||||||
// TODO: Return Err if the index is out of bounds.
|
|
||||||
unsafe {
|
unsafe {
|
||||||
let vmctx = &mut *vmctx;
|
let vmctx = &mut *instance.vmctx();
|
||||||
let vmglobal = vmctx.global(global_index);
|
let vmglobal = vmctx.global(global_index);
|
||||||
let definition = vmglobal.get_definition(module.is_imported_global(global_index));
|
let definition = vmglobal.get_definition(module.is_imported_global(global_index));
|
||||||
Ok(match module.globals[global_index].ty {
|
Ok(
|
||||||
ir::types::I32 => Value::I32(*definition.as_i32()),
|
match module
|
||||||
ir::types::I64 => Value::I64(*definition.as_i64()),
|
.globals
|
||||||
ir::types::F32 => Value::F32(*definition.as_f32_bits()),
|
.get(global_index)
|
||||||
ir::types::F64 => Value::F64(*definition.as_f64_bits()),
|
.ok_or_else(|| ActionError::Index(global_index.index() as u64))?
|
||||||
other => return Err(format!("global with type {} not supported", other)),
|
.ty
|
||||||
})
|
{
|
||||||
|
ir::types::I32 => RuntimeValue::I32(*definition.as_i32()),
|
||||||
|
ir::types::I64 => RuntimeValue::I64(*definition.as_i64()),
|
||||||
|
ir::types::F32 => RuntimeValue::F32(*definition.as_f32_bits()),
|
||||||
|
ir::types::F64 => RuntimeValue::F64(*definition.as_f64_bits()),
|
||||||
|
other => {
|
||||||
|
return Err(ActionError::Type(format!(
|
||||||
|
"global with type {} not supported",
|
||||||
|
other
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
30
lib/execute/src/imports.rs
Normal file
30
lib/execute/src/imports.rs
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
use cranelift_entity::PrimaryMap;
|
||||||
|
use cranelift_wasm::{FuncIndex, GlobalIndex, MemoryIndex, TableIndex};
|
||||||
|
use vmcontext::{VMGlobal, VMMemory, VMTable};
|
||||||
|
|
||||||
|
/// Resolved import pointers.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Imports {
|
||||||
|
/// Resolved addresses for imported functions.
|
||||||
|
pub functions: PrimaryMap<FuncIndex, *const u8>,
|
||||||
|
|
||||||
|
/// Resolved addresses for imported tables.
|
||||||
|
pub tables: PrimaryMap<TableIndex, *mut VMTable>,
|
||||||
|
|
||||||
|
/// Resolved addresses for imported globals.
|
||||||
|
pub globals: PrimaryMap<GlobalIndex, *mut VMGlobal>,
|
||||||
|
|
||||||
|
/// Resolved addresses for imported memories.
|
||||||
|
pub memories: PrimaryMap<MemoryIndex, *mut VMMemory>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Imports {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
functions: PrimaryMap::new(),
|
||||||
|
tables: PrimaryMap::new(),
|
||||||
|
globals: PrimaryMap::new(),
|
||||||
|
memories: PrimaryMap::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,14 +3,16 @@
|
|||||||
|
|
||||||
use cranelift_entity::EntityRef;
|
use cranelift_entity::EntityRef;
|
||||||
use cranelift_entity::PrimaryMap;
|
use cranelift_entity::PrimaryMap;
|
||||||
use cranelift_wasm::{GlobalIndex, MemoryIndex, TableIndex};
|
use cranelift_wasm::{DefinedFuncIndex, FuncIndex, GlobalIndex, MemoryIndex, TableIndex};
|
||||||
|
use imports::Imports;
|
||||||
use memory::LinearMemory;
|
use memory::LinearMemory;
|
||||||
use sig_registry::SignatureRegistry;
|
use sig_registry::SignatureRegistry;
|
||||||
use std::ptr;
|
use std::ptr;
|
||||||
|
use std::slice;
|
||||||
use std::string::String;
|
use std::string::String;
|
||||||
use table::Table;
|
use table::Table;
|
||||||
use vmcontext::{VMCallerCheckedAnyfunc, VMContext, VMGlobal, VMMemory, VMTable};
|
use vmcontext::{VMCallerCheckedAnyfunc, VMContext, VMGlobal, VMMemory, VMTable};
|
||||||
use wasmtime_environ::{Compilation, DataInitializer, Module};
|
use wasmtime_environ::{DataInitializer, Module};
|
||||||
|
|
||||||
/// An Instance of a WebAssemby module.
|
/// An Instance of a WebAssemby module.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@@ -34,20 +36,29 @@ pub struct Instance {
|
|||||||
/// Table storage base address vector pointed to by vmctx.
|
/// Table storage base address vector pointed to by vmctx.
|
||||||
vmctx_tables: PrimaryMap<TableIndex, VMTable>,
|
vmctx_tables: PrimaryMap<TableIndex, VMTable>,
|
||||||
|
|
||||||
|
/// Pointer values for resolved imports.
|
||||||
|
imports: Imports,
|
||||||
|
|
||||||
|
/// Pointers to functions in executable memory.
|
||||||
|
allocated_functions: PrimaryMap<DefinedFuncIndex, (*mut u8, usize)>,
|
||||||
|
|
||||||
/// Context pointer used by JIT code.
|
/// Context pointer used by JIT code.
|
||||||
vmctx: VMContext,
|
vmctx: VMContext,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Instance {
|
impl Instance {
|
||||||
/// Create a new `Instance`.
|
/// Create a new `Instance`. In order to complete instantiation, call
|
||||||
|
/// `invoke_start_function`. `allocated_functions` holds the function bodies
|
||||||
|
/// which have been placed in executable memory.
|
||||||
pub fn new(
|
pub fn new(
|
||||||
module: &Module,
|
module: &Module,
|
||||||
compilation: &Compilation,
|
allocated_functions: PrimaryMap<DefinedFuncIndex, (*mut u8, usize)>,
|
||||||
data_initializers: &[DataInitializer],
|
data_initializers: &[DataInitializer],
|
||||||
|
imports: Imports,
|
||||||
) -> Result<Self, String> {
|
) -> Result<Self, String> {
|
||||||
let mut sig_registry = instantiate_signatures(module);
|
let mut sig_registry = instantiate_signatures(module);
|
||||||
let mut memories = instantiate_memories(module, data_initializers)?;
|
let mut memories = instantiate_memories(module, data_initializers)?;
|
||||||
let mut tables = instantiate_tables(module, compilation, &mut sig_registry);
|
let mut tables = instantiate_tables(module, &allocated_functions, &mut sig_registry);
|
||||||
|
|
||||||
let mut vmctx_memories = memories
|
let mut vmctx_memories = memories
|
||||||
.values_mut()
|
.values_mut()
|
||||||
@@ -73,6 +84,8 @@ impl Instance {
|
|||||||
vmctx_memories,
|
vmctx_memories,
|
||||||
vmctx_globals,
|
vmctx_globals,
|
||||||
vmctx_tables,
|
vmctx_tables,
|
||||||
|
imports,
|
||||||
|
allocated_functions,
|
||||||
vmctx: VMContext::new(
|
vmctx: VMContext::new(
|
||||||
vmctx_memories_ptr,
|
vmctx_memories_ptr,
|
||||||
vmctx_globals_ptr,
|
vmctx_globals_ptr,
|
||||||
@@ -83,15 +96,27 @@ impl Instance {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Return the vmctx pointer to be passed into JIT code.
|
/// Return the vmctx pointer to be passed into JIT code.
|
||||||
pub fn vmctx(&mut self) -> *mut VMContext {
|
pub fn vmctx(&mut self) -> &mut VMContext {
|
||||||
&mut self.vmctx as *mut VMContext
|
&mut self.vmctx
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return the offset from the vmctx pointer to its containing Instance.
|
/// Return the offset from the vmctx pointer to its containing Instance.
|
||||||
pub fn vmctx_offset() -> isize {
|
pub(crate) fn vmctx_offset() -> isize {
|
||||||
offset_of!(Self, vmctx) as isize
|
offset_of!(Self, vmctx) as isize
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return the pointer to executable memory for the given function index.
|
||||||
|
pub(crate) fn get_allocated_function(&self, index: DefinedFuncIndex) -> Option<&[u8]> {
|
||||||
|
self.allocated_functions
|
||||||
|
.get(index)
|
||||||
|
.map(|(ptr, len)| unsafe { slice::from_raw_parts(*ptr, *len) })
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the pointer to executable memory for the given function index.
|
||||||
|
pub(crate) fn get_imported_function(&self, index: FuncIndex) -> Option<*const u8> {
|
||||||
|
self.imports.functions.get(index).cloned()
|
||||||
|
}
|
||||||
|
|
||||||
/// Grow memory by the specified amount of pages.
|
/// Grow memory by the specified amount of pages.
|
||||||
///
|
///
|
||||||
/// Returns `None` if memory can't be grown by the specified amount
|
/// Returns `None` if memory can't be grown by the specified amount
|
||||||
@@ -163,7 +188,7 @@ fn instantiate_memories(
|
|||||||
/// Allocate memory for just the tables of the current module.
|
/// Allocate memory for just the tables of the current module.
|
||||||
fn instantiate_tables(
|
fn instantiate_tables(
|
||||||
module: &Module,
|
module: &Module,
|
||||||
compilation: &Compilation,
|
allocated_functions: &PrimaryMap<DefinedFuncIndex, (*mut u8, usize)>,
|
||||||
sig_registry: &mut SignatureRegistry,
|
sig_registry: &mut SignatureRegistry,
|
||||||
) -> PrimaryMap<TableIndex, Table> {
|
) -> PrimaryMap<TableIndex, Table> {
|
||||||
let mut tables = PrimaryMap::with_capacity(module.table_plans.len());
|
let mut tables = PrimaryMap::with_capacity(module.table_plans.len());
|
||||||
@@ -177,14 +202,12 @@ fn instantiate_tables(
|
|||||||
let subslice = &mut slice[init.offset..init.offset + init.elements.len()];
|
let subslice = &mut slice[init.offset..init.offset + init.elements.len()];
|
||||||
for (i, func_idx) in init.elements.iter().enumerate() {
|
for (i, func_idx) in init.elements.iter().enumerate() {
|
||||||
let callee_sig = module.functions[*func_idx];
|
let callee_sig = module.functions[*func_idx];
|
||||||
let code_buf = &compilation.functions[module
|
let func_ptr = allocated_functions[module
|
||||||
.defined_func_index(*func_idx)
|
.defined_func_index(*func_idx)
|
||||||
.expect("table element initializer with imported function not supported yet")];
|
.expect("table element initializer with imported function not supported yet")]
|
||||||
|
.0;
|
||||||
let type_id = sig_registry.lookup(callee_sig);
|
let type_id = sig_registry.lookup(callee_sig);
|
||||||
subslice[i] = VMCallerCheckedAnyfunc {
|
subslice[i] = VMCallerCheckedAnyfunc { func_ptr, type_id };
|
||||||
func_ptr: code_buf.as_ptr(),
|
|
||||||
type_id,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,55 +1,87 @@
|
|||||||
//! Support for invoking wasm functions from outside a wasm module.
|
//! Support for invoking wasm functions from outside a wasm module.
|
||||||
|
|
||||||
use action::{ActionOutcome, Value};
|
use action::{ActionError, ActionOutcome, RuntimeValue};
|
||||||
use code::Code;
|
use code::Code;
|
||||||
use cranelift_codegen::ir::InstBuilder;
|
use cranelift_codegen::ir::InstBuilder;
|
||||||
use cranelift_codegen::{binemit, ir, isa, Context};
|
use cranelift_codegen::{binemit, ir, isa, Context};
|
||||||
|
use cranelift_entity::EntityRef;
|
||||||
use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext};
|
use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext};
|
||||||
use cranelift_wasm::FuncIndex;
|
use cranelift_wasm::FuncIndex;
|
||||||
|
use instance::Instance;
|
||||||
use signalhandlers::{ensure_eager_signal_handlers, ensure_full_signal_handlers, TrapContext};
|
use signalhandlers::{ensure_eager_signal_handlers, ensure_full_signal_handlers, TrapContext};
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use std::ptr;
|
use std::ptr;
|
||||||
use std::string::String;
|
|
||||||
use std::vec::Vec;
|
use std::vec::Vec;
|
||||||
use traphandlers::call_wasm;
|
use traphandlers::call_wasm;
|
||||||
use vmcontext::VMContext;
|
use vmcontext::VMContext;
|
||||||
use wasmtime_environ::{Compilation, Export, Module, RelocSink};
|
use wasmtime_environ::{CompileError, Export, Module, RelocSink};
|
||||||
|
|
||||||
/// Jumps to the code region of memory and invoke the exported function
|
/// Calls the given named function, passing its return values and returning
|
||||||
|
/// its results.
|
||||||
pub fn invoke(
|
pub fn invoke(
|
||||||
code: &mut Code,
|
code: &mut Code,
|
||||||
isa: &isa::TargetIsa,
|
isa: &isa::TargetIsa,
|
||||||
module: &Module,
|
module: &Module,
|
||||||
compilation: &Compilation,
|
instance: &mut Instance,
|
||||||
vmctx: *mut VMContext,
|
|
||||||
function: &str,
|
function: &str,
|
||||||
args: &[Value],
|
args: &[RuntimeValue],
|
||||||
) -> Result<ActionOutcome, String> {
|
) -> Result<ActionOutcome, ActionError> {
|
||||||
let fn_index = match module.exports.get(function) {
|
let fn_index = match module.exports.get(function) {
|
||||||
Some(Export::Function(index)) => *index,
|
Some(Export::Function(index)) => *index,
|
||||||
Some(_) => return Err(format!("exported item \"{}\" is not a function", function)),
|
Some(_) => {
|
||||||
None => return Err(format!("no export named \"{}\"", function)),
|
return Err(ActionError::Kind(format!(
|
||||||
|
"exported item \"{}\" is not a function",
|
||||||
|
function
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
return Err(ActionError::Field(format!(
|
||||||
|
"no export named \"{}\"",
|
||||||
|
function
|
||||||
|
)))
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
invoke_by_index(code, isa, module, compilation, vmctx, fn_index, args)
|
invoke_by_index(code, isa, module, instance, fn_index, args)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Invoke the WebAssembly start function of the instance, if one is present.
|
||||||
|
pub fn invoke_start_function(
|
||||||
|
code: &mut Code,
|
||||||
|
isa: &isa::TargetIsa,
|
||||||
|
module: &Module,
|
||||||
|
instance: &mut Instance,
|
||||||
|
) -> Result<ActionOutcome, ActionError> {
|
||||||
|
if let Some(start_index) = module.start_func {
|
||||||
|
invoke_by_index(code, isa, module, instance, start_index, &[])
|
||||||
|
} else {
|
||||||
|
// No start function, just return nothing.
|
||||||
|
Ok(ActionOutcome::Returned { values: vec![] })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Calls the given indexed function, passing its return values and returning
|
||||||
|
/// its results.
|
||||||
pub fn invoke_by_index(
|
pub fn invoke_by_index(
|
||||||
code: &mut Code,
|
code: &mut Code,
|
||||||
isa: &isa::TargetIsa,
|
isa: &isa::TargetIsa,
|
||||||
module: &Module,
|
module: &Module,
|
||||||
compilation: &Compilation,
|
instance: &mut Instance,
|
||||||
vmctx: *mut VMContext,
|
|
||||||
fn_index: FuncIndex,
|
fn_index: FuncIndex,
|
||||||
args: &[Value],
|
args: &[RuntimeValue],
|
||||||
) -> Result<ActionOutcome, String> {
|
) -> Result<ActionOutcome, ActionError> {
|
||||||
// TODO: Return Err if fn_index is out of bounds.
|
|
||||||
let exec_code_buf = match module.defined_func_index(fn_index) {
|
let exec_code_buf = match module.defined_func_index(fn_index) {
|
||||||
Some(def_fn_index) => {
|
Some(def_fn_index) => {
|
||||||
let code_buf = &compilation.functions[def_fn_index];
|
let slice = instance
|
||||||
code.allocate_copy_of_slice(&code_buf)?.as_ptr() as usize
|
.get_allocated_function(def_fn_index)
|
||||||
|
.ok_or_else(|| ActionError::Index(def_fn_index.index() as u64))?;
|
||||||
|
code.allocate_copy_of_slice(slice)
|
||||||
|
.map_err(ActionError::Resource)?
|
||||||
|
.as_ptr()
|
||||||
}
|
}
|
||||||
None => compilation.resolved_func_imports[fn_index],
|
None => instance
|
||||||
|
.get_imported_function(fn_index)
|
||||||
|
.ok_or_else(|| ActionError::Index(fn_index.index() as u64))?,
|
||||||
};
|
};
|
||||||
|
|
||||||
let sig = &module.signatures[module.functions[fn_index]];
|
let sig = &module.signatures[module.functions[fn_index]];
|
||||||
@@ -68,20 +100,24 @@ pub fn invoke_by_index(
|
|||||||
ensure_eager_signal_handlers();
|
ensure_eager_signal_handlers();
|
||||||
ensure_full_signal_handlers(&mut traps);
|
ensure_full_signal_handlers(&mut traps);
|
||||||
if !traps.haveSignalHandlers {
|
if !traps.haveSignalHandlers {
|
||||||
return Err("failed to install signal handlers".to_string());
|
return Err(ActionError::Resource(
|
||||||
|
"failed to install signal handlers".to_string(),
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
call_through_wrapper(code, isa, exec_code_buf, vmctx, args, &sig)
|
call_through_wrapper(code, isa, exec_code_buf, instance, args, &sig)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn call_through_wrapper(
|
fn call_through_wrapper(
|
||||||
code: &mut Code,
|
code: &mut Code,
|
||||||
isa: &isa::TargetIsa,
|
isa: &isa::TargetIsa,
|
||||||
callee: usize,
|
callee: *const u8,
|
||||||
vmctx: *mut VMContext,
|
instance: &mut Instance,
|
||||||
args: &[Value],
|
args: &[RuntimeValue],
|
||||||
sig: &ir::Signature,
|
sig: &ir::Signature,
|
||||||
) -> Result<ActionOutcome, String> {
|
) -> Result<ActionOutcome, ActionError> {
|
||||||
|
let vmctx = instance.vmctx() as *mut VMContext;
|
||||||
|
|
||||||
for (index, value) in args.iter().enumerate() {
|
for (index, value) in args.iter().enumerate() {
|
||||||
assert_eq!(value.value_type(), sig.params[index].value_type);
|
assert_eq!(value.value_type(), sig.params[index].value_type);
|
||||||
}
|
}
|
||||||
@@ -111,16 +147,16 @@ fn call_through_wrapper(
|
|||||||
|
|
||||||
for value in args {
|
for value in args {
|
||||||
match value {
|
match value {
|
||||||
Value::I32(i) => {
|
RuntimeValue::I32(i) => {
|
||||||
callee_args.push(builder.ins().iconst(ir::types::I32, i64::from(*i)))
|
callee_args.push(builder.ins().iconst(ir::types::I32, i64::from(*i)))
|
||||||
}
|
}
|
||||||
Value::I64(i) => callee_args.push(builder.ins().iconst(ir::types::I64, *i)),
|
RuntimeValue::I64(i) => callee_args.push(builder.ins().iconst(ir::types::I64, *i)),
|
||||||
Value::F32(i) => callee_args.push(
|
RuntimeValue::F32(i) => callee_args.push(
|
||||||
builder
|
builder
|
||||||
.ins()
|
.ins()
|
||||||
.f32const(ir::immediates::Ieee32::with_bits(*i)),
|
.f32const(ir::immediates::Ieee32::with_bits(*i)),
|
||||||
),
|
),
|
||||||
Value::F64(i) => callee_args.push(
|
RuntimeValue::F64(i) => callee_args.push(
|
||||||
builder
|
builder
|
||||||
.ins()
|
.ins()
|
||||||
.f64const(ir::immediates::Ieee64::with_bits(*i)),
|
.f64const(ir::immediates::Ieee64::with_bits(*i)),
|
||||||
@@ -162,10 +198,13 @@ fn call_through_wrapper(
|
|||||||
let mut trap_sink = binemit::NullTrapSink {};
|
let mut trap_sink = binemit::NullTrapSink {};
|
||||||
context
|
context
|
||||||
.compile_and_emit(isa, &mut code_buf, &mut reloc_sink, &mut trap_sink)
|
.compile_and_emit(isa, &mut code_buf, &mut reloc_sink, &mut trap_sink)
|
||||||
.map_err(|e| e.to_string())?;
|
.map_err(|error| ActionError::Compile(CompileError::Codegen(error)))?;
|
||||||
assert!(reloc_sink.func_relocs.is_empty());
|
assert!(reloc_sink.func_relocs.is_empty());
|
||||||
|
|
||||||
let exec_code_buf = code.allocate_copy_of_slice(&code_buf)?.as_ptr();
|
let exec_code_buf = code
|
||||||
|
.allocate_copy_of_slice(&code_buf)
|
||||||
|
.map_err(ActionError::Resource)?
|
||||||
|
.as_ptr();
|
||||||
code.publish();
|
code.publish();
|
||||||
|
|
||||||
let func = unsafe { mem::transmute::<_, fn()>(exec_code_buf) };
|
let func = unsafe { mem::transmute::<_, fn()>(exec_code_buf) };
|
||||||
@@ -179,10 +218,10 @@ fn call_through_wrapper(
|
|||||||
let ptr = results_vec.as_ptr().add(index * value_size);
|
let ptr = results_vec.as_ptr().add(index * value_size);
|
||||||
|
|
||||||
match abi_param.value_type {
|
match abi_param.value_type {
|
||||||
ir::types::I32 => Value::I32(ptr::read(ptr as *const i32)),
|
ir::types::I32 => RuntimeValue::I32(ptr::read(ptr as *const i32)),
|
||||||
ir::types::I64 => Value::I64(ptr::read(ptr as *const i64)),
|
ir::types::I64 => RuntimeValue::I64(ptr::read(ptr as *const i64)),
|
||||||
ir::types::F32 => Value::F32(ptr::read(ptr as *const u32)),
|
ir::types::F32 => RuntimeValue::F32(ptr::read(ptr as *const u32)),
|
||||||
ir::types::F64 => Value::F64(ptr::read(ptr as *const u64)),
|
ir::types::F64 => RuntimeValue::F64(ptr::read(ptr as *const u64)),
|
||||||
other => panic!("unsupported value type {:?}", other),
|
other => panic!("unsupported value type {:?}", other),
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -40,15 +40,19 @@ extern crate libc;
|
|||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate memoffset;
|
extern crate memoffset;
|
||||||
extern crate cast;
|
extern crate cast;
|
||||||
|
extern crate failure;
|
||||||
|
#[macro_use]
|
||||||
|
extern crate failure_derive;
|
||||||
|
|
||||||
mod action;
|
mod action;
|
||||||
mod code;
|
mod code;
|
||||||
mod execute;
|
|
||||||
mod export;
|
mod export;
|
||||||
mod get;
|
mod get;
|
||||||
|
mod imports;
|
||||||
mod instance;
|
mod instance;
|
||||||
mod invoke;
|
mod invoke;
|
||||||
mod libcalls;
|
mod libcalls;
|
||||||
|
mod link;
|
||||||
mod memory;
|
mod memory;
|
||||||
mod mmap;
|
mod mmap;
|
||||||
mod sig_registry;
|
mod sig_registry;
|
||||||
@@ -58,13 +62,13 @@ mod traphandlers;
|
|||||||
mod vmcontext;
|
mod vmcontext;
|
||||||
mod world;
|
mod world;
|
||||||
|
|
||||||
pub use action::{ActionOutcome, Value};
|
pub use action::{ActionError, ActionOutcome, RuntimeValue};
|
||||||
pub use code::Code;
|
pub use code::Code;
|
||||||
pub use execute::{compile_and_link_module, finish_instantiation};
|
|
||||||
pub use export::{ExportValue, NullResolver, Resolver};
|
pub use export::{ExportValue, NullResolver, Resolver};
|
||||||
pub use get::get;
|
pub use get::{get, get_by_index};
|
||||||
pub use instance::Instance;
|
pub use instance::Instance;
|
||||||
pub use invoke::invoke;
|
pub use invoke::{invoke, invoke_by_index, invoke_start_function};
|
||||||
|
pub use link::link_module;
|
||||||
pub use traphandlers::{call_wasm, LookupCodeSegment, RecordTrap, Unwind};
|
pub use traphandlers::{call_wasm, LookupCodeSegment, RecordTrap, Unwind};
|
||||||
pub use vmcontext::{VMContext, VMGlobal, VMMemory, VMTable};
|
pub use vmcontext::{VMContext, VMGlobal, VMMemory, VMTable};
|
||||||
pub use world::InstanceWorld;
|
pub use world::InstanceWorld;
|
||||||
|
|||||||
@@ -1,153 +1,162 @@
|
|||||||
//! TODO: Move the contents of this file to other files, as "execute.rs" is
|
|
||||||
//! no longer a descriptive filename.
|
|
||||||
|
|
||||||
use action::ActionOutcome;
|
|
||||||
use code::Code;
|
|
||||||
use cranelift_codegen::binemit::Reloc;
|
use cranelift_codegen::binemit::Reloc;
|
||||||
use cranelift_codegen::isa::TargetIsa;
|
|
||||||
use cranelift_entity::{EntityRef, PrimaryMap};
|
use cranelift_entity::{EntityRef, PrimaryMap};
|
||||||
use cranelift_wasm::{
|
use cranelift_wasm::{
|
||||||
DefinedFuncIndex, Global, GlobalInit, Memory, MemoryIndex, Table, TableElementType,
|
DefinedFuncIndex, Global, GlobalInit, Memory, MemoryIndex, Table, TableElementType,
|
||||||
};
|
};
|
||||||
use export::{ExportValue, Resolver};
|
use export::{ExportValue, Resolver};
|
||||||
use instance::Instance;
|
use imports::Imports;
|
||||||
use invoke::invoke_by_index;
|
|
||||||
use region::{protect, Protection};
|
|
||||||
use std::ptr::write_unaligned;
|
use std::ptr::write_unaligned;
|
||||||
use std::string::String;
|
|
||||||
use std::vec::Vec;
|
use std::vec::Vec;
|
||||||
use vmcontext::VMContext;
|
use vmcontext::VMContext;
|
||||||
|
use vmcontext::{VMGlobal, VMMemory, VMTable};
|
||||||
use wasmtime_environ::{
|
use wasmtime_environ::{
|
||||||
compile_module, Compilation, MemoryPlan, MemoryStyle, Module, ModuleTranslation, Relocation,
|
MemoryPlan, MemoryStyle, Module, Relocation, RelocationTarget, Relocations, TablePlan,
|
||||||
RelocationTarget, TablePlan, TableStyle,
|
TableStyle,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Executes a module that has been translated with the `wasmtime-environ` environment
|
/// A link error, such as incompatible or unmatched imports/exports.
|
||||||
/// implementation.
|
#[derive(Fail, Debug)]
|
||||||
pub fn compile_and_link_module<'data, 'module>(
|
#[fail(display = "Link error: {}", _0)]
|
||||||
isa: &TargetIsa,
|
pub struct LinkError(String);
|
||||||
translation: &ModuleTranslation<'data, 'module>,
|
|
||||||
resolver: &mut Resolver,
|
|
||||||
) -> Result<Compilation, String> {
|
|
||||||
let (mut compilation, relocations) = compile_module(&translation, isa)?;
|
|
||||||
|
|
||||||
for (index, (ref module, ref field)) in translation.module.imported_funcs.iter() {
|
/// Links a module that has been compiled with `compiled_module` in `wasmtime-environ`.
|
||||||
match resolver.resolve(module, field) {
|
pub fn link_module(
|
||||||
|
module: &Module,
|
||||||
|
allocated_functions: &PrimaryMap<DefinedFuncIndex, (*mut u8, usize)>,
|
||||||
|
relocations: Relocations,
|
||||||
|
resolver: &mut Resolver,
|
||||||
|
) -> Result<Imports, LinkError> {
|
||||||
|
let mut imports = Imports::new();
|
||||||
|
|
||||||
|
for (index, (ref module_name, ref field)) in module.imported_funcs.iter() {
|
||||||
|
match resolver.resolve(module_name, field) {
|
||||||
Some(export_value) => match export_value {
|
Some(export_value) => match export_value {
|
||||||
ExportValue::Function { address, signature } => {
|
ExportValue::Function { address, signature } => {
|
||||||
let import_signature =
|
let import_signature = &module.signatures[module.functions[index]];
|
||||||
&translation.module.signatures[translation.module.functions[index]];
|
|
||||||
if signature != *import_signature {
|
if signature != *import_signature {
|
||||||
return Err(format!(
|
return Err(LinkError(
|
||||||
"{}/{}: exported function with signature {} incompatible with function import with signature {}",
|
format!("{}/{}: exported function with signature {} incompatible with function import with signature {}",
|
||||||
module, field,
|
module_name, field,
|
||||||
signature, import_signature,
|
signature, import_signature)
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
compilation.resolved_func_imports.push(address);
|
imports.functions.push(address);
|
||||||
}
|
}
|
||||||
ExportValue::Table { .. }
|
ExportValue::Table { .. }
|
||||||
| ExportValue::Memory { .. }
|
| ExportValue::Memory { .. }
|
||||||
| ExportValue::Global { .. } => {
|
| ExportValue::Global { .. } => {
|
||||||
return Err(format!(
|
return Err(LinkError(format!(
|
||||||
"{}/{}: export not compatible with function import",
|
"{}/{}: export not compatible with function import",
|
||||||
module, field
|
module_name, field
|
||||||
));
|
)));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
None => return Err(format!("{}/{}: no provided import function", module, field)),
|
None => {
|
||||||
|
return Err(LinkError(format!(
|
||||||
|
"{}/{}: no provided import function",
|
||||||
|
module_name, field
|
||||||
|
)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (index, (ref module, ref field)) in translation.module.imported_globals.iter() {
|
}
|
||||||
match resolver.resolve(module, field) {
|
|
||||||
|
for (index, (ref module_name, ref field)) in module.imported_globals.iter() {
|
||||||
|
match resolver.resolve(module_name, field) {
|
||||||
Some(export_value) => match export_value {
|
Some(export_value) => match export_value {
|
||||||
ExportValue::Global { address, global } => {
|
ExportValue::Global { address, global } => {
|
||||||
let imported_global = translation.module.globals[index];
|
let imported_global = module.globals[index];
|
||||||
if !is_global_compatible(&global, &imported_global) {
|
if !is_global_compatible(&global, &imported_global) {
|
||||||
return Err(format!(
|
return Err(LinkError(format!(
|
||||||
"{}/{}: exported global incompatible with global import",
|
"{}/{}: exported global incompatible with global import",
|
||||||
module, field,
|
module_name, field
|
||||||
));
|
)));
|
||||||
}
|
}
|
||||||
compilation.resolved_global_imports.push(address as usize);
|
imports.globals.push(address as *mut VMGlobal);
|
||||||
}
|
}
|
||||||
ExportValue::Table { .. }
|
ExportValue::Table { .. }
|
||||||
| ExportValue::Memory { .. }
|
| ExportValue::Memory { .. }
|
||||||
| ExportValue::Function { .. } => {
|
| ExportValue::Function { .. } => {
|
||||||
return Err(format!(
|
return Err(LinkError(format!(
|
||||||
"{}/{}: exported global incompatible with global import",
|
"{}/{}: exported global incompatible with global import",
|
||||||
module, field
|
module_name, field
|
||||||
));
|
)));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
None => {
|
None => {
|
||||||
return Err(format!(
|
return Err(LinkError(format!(
|
||||||
"no provided import global for {}/{}",
|
"no provided import global for {}/{}",
|
||||||
module, field
|
module_name, field
|
||||||
))
|
)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (index, (ref module, ref field)) in translation.module.imported_tables.iter() {
|
|
||||||
match resolver.resolve(module, field) {
|
for (index, (ref module_name, ref field)) in module.imported_tables.iter() {
|
||||||
|
match resolver.resolve(module_name, field) {
|
||||||
Some(export_value) => match export_value {
|
Some(export_value) => match export_value {
|
||||||
ExportValue::Table { address, table } => {
|
ExportValue::Table { address, table } => {
|
||||||
let import_table = &translation.module.table_plans[index];
|
let import_table = &module.table_plans[index];
|
||||||
if !is_table_compatible(&table, import_table) {
|
if !is_table_compatible(&table, import_table) {
|
||||||
return Err(format!(
|
return Err(LinkError(format!(
|
||||||
"{}/{}: exported table incompatible with table import",
|
"{}/{}: exported table incompatible with table import",
|
||||||
module, field,
|
module_name, field,
|
||||||
));
|
)));
|
||||||
}
|
}
|
||||||
compilation.resolved_table_imports.push(address as usize);
|
imports.tables.push(address as *mut VMTable);
|
||||||
}
|
}
|
||||||
ExportValue::Global { .. }
|
ExportValue::Global { .. }
|
||||||
| ExportValue::Memory { .. }
|
| ExportValue::Memory { .. }
|
||||||
| ExportValue::Function { .. } => {
|
| ExportValue::Function { .. } => {
|
||||||
return Err(format!(
|
return Err(LinkError(format!(
|
||||||
"{}/{}: export not compatible with table import",
|
"{}/{}: export not compatible with table import",
|
||||||
module, field
|
module_name, field
|
||||||
));
|
)));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
None => return Err(format!("no provided import table for {}/{}", module, field)),
|
None => {
|
||||||
|
return Err(LinkError(format!(
|
||||||
|
"no provided import table for {}/{}",
|
||||||
|
module_name, field
|
||||||
|
)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (index, (ref module, ref field)) in translation.module.imported_memories.iter() {
|
}
|
||||||
match resolver.resolve(module, field) {
|
|
||||||
|
for (index, (ref module_name, ref field)) in module.imported_memories.iter() {
|
||||||
|
match resolver.resolve(module_name, field) {
|
||||||
Some(export_value) => match export_value {
|
Some(export_value) => match export_value {
|
||||||
ExportValue::Memory { address, memory } => {
|
ExportValue::Memory { address, memory } => {
|
||||||
let import_memory = &translation.module.memory_plans[index];
|
let import_memory = &module.memory_plans[index];
|
||||||
if is_memory_compatible(&memory, import_memory) {
|
if is_memory_compatible(&memory, import_memory) {
|
||||||
return Err(format!(
|
return Err(LinkError(format!(
|
||||||
"{}/{}: exported memory incompatible with memory import",
|
"{}/{}: exported memory incompatible with memory import",
|
||||||
module, field
|
module_name, field
|
||||||
));
|
)));
|
||||||
}
|
}
|
||||||
compilation.resolved_memory_imports.push(address as usize);
|
imports.memories.push(address as *mut VMMemory);
|
||||||
}
|
}
|
||||||
ExportValue::Table { .. }
|
ExportValue::Table { .. }
|
||||||
| ExportValue::Global { .. }
|
| ExportValue::Global { .. }
|
||||||
| ExportValue::Function { .. } => {
|
| ExportValue::Function { .. } => {
|
||||||
return Err(format!(
|
return Err(LinkError(format!(
|
||||||
"{}/{}: export not compatible with memory import",
|
"{}/{}: export not compatible with memory import",
|
||||||
module, field
|
module_name, field
|
||||||
));
|
)));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
None => {
|
None => {
|
||||||
return Err(format!(
|
return Err(LinkError(format!(
|
||||||
"no provided import memory for {}/{}",
|
"no provided import memory for {}/{}",
|
||||||
module, field
|
module_name, field
|
||||||
))
|
)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply relocations, now that we have virtual addresses for everything.
|
// Apply relocations, now that we have virtual addresses for everything.
|
||||||
relocate(&mut compilation, &relocations, &translation.module)?;
|
relocate(&imports, allocated_functions, relocations, &module);
|
||||||
|
|
||||||
Ok(compilation)
|
Ok(imports)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_global_compatible(exported: &Global, imported: &Global) -> bool {
|
fn is_global_compatible(exported: &Global, imported: &Global) -> bool {
|
||||||
@@ -265,23 +274,19 @@ fn is_memory_compatible(exported: &MemoryPlan, imported: &MemoryPlan) -> bool {
|
|||||||
&& exported_offset_guard_size >= imported_offset_guard_size
|
&& exported_offset_guard_size >= imported_offset_guard_size
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" {
|
|
||||||
pub fn __rust_probestack();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Performs the relocations inside the function bytecode, provided the necessary metadata.
|
/// Performs the relocations inside the function bytecode, provided the necessary metadata.
|
||||||
fn relocate(
|
fn relocate(
|
||||||
compilation: &mut Compilation,
|
imports: &Imports,
|
||||||
relocations: &PrimaryMap<DefinedFuncIndex, Vec<Relocation>>,
|
allocated_functions: &PrimaryMap<DefinedFuncIndex, (*mut u8, usize)>,
|
||||||
|
relocations: PrimaryMap<DefinedFuncIndex, Vec<Relocation>>,
|
||||||
module: &Module,
|
module: &Module,
|
||||||
) -> Result<(), String> {
|
) {
|
||||||
// The relocations are relative to the relocation's address plus four bytes.
|
for (i, function_relocs) in relocations.into_iter() {
|
||||||
for (i, function_relocs) in relocations.iter() {
|
|
||||||
for r in function_relocs {
|
for r in function_relocs {
|
||||||
let target_func_address: usize = match r.reloc_target {
|
let target_func_address: usize = match r.reloc_target {
|
||||||
RelocationTarget::UserFunc(index) => match module.defined_func_index(index) {
|
RelocationTarget::UserFunc(index) => match module.defined_func_index(index) {
|
||||||
Some(f) => compilation.functions[f].as_ptr() as usize,
|
Some(f) => allocated_functions[f].0 as usize,
|
||||||
None => compilation.resolved_func_imports[index],
|
None => imports.functions[index] as usize,
|
||||||
},
|
},
|
||||||
RelocationTarget::MemoryGrow => wasmtime_memory_grow as usize,
|
RelocationTarget::MemoryGrow => wasmtime_memory_grow as usize,
|
||||||
RelocationTarget::MemorySize => wasmtime_memory_size as usize,
|
RelocationTarget::MemorySize => wasmtime_memory_size as usize,
|
||||||
@@ -303,11 +308,11 @@ fn relocate(
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let body = &mut compilation.functions[i];
|
let body = allocated_functions[i].0;
|
||||||
match r.reloc {
|
match r.reloc {
|
||||||
#[cfg(target_pointer_width = "64")]
|
#[cfg(target_pointer_width = "64")]
|
||||||
Reloc::Abs8 => unsafe {
|
Reloc::Abs8 => unsafe {
|
||||||
let reloc_address = body.as_mut_ptr().add(r.offset as usize) as usize;
|
let reloc_address = body.add(r.offset as usize) as usize;
|
||||||
let reloc_addend = r.addend as isize;
|
let reloc_addend = r.addend as isize;
|
||||||
let reloc_abs = (target_func_address as u64)
|
let reloc_abs = (target_func_address as u64)
|
||||||
.checked_add(reloc_addend as u64)
|
.checked_add(reloc_addend as u64)
|
||||||
@@ -316,7 +321,7 @@ fn relocate(
|
|||||||
},
|
},
|
||||||
#[cfg(target_pointer_width = "32")]
|
#[cfg(target_pointer_width = "32")]
|
||||||
Reloc::X86PCRel4 => unsafe {
|
Reloc::X86PCRel4 => unsafe {
|
||||||
let reloc_address = body.as_mut_ptr().add(r.offset as usize) as usize;
|
let reloc_address = body.add(r.offset as usize) as usize;
|
||||||
let reloc_addend = r.addend as isize;
|
let reloc_addend = r.addend as isize;
|
||||||
let reloc_delta_u32 = (target_func_address as u32)
|
let reloc_delta_u32 = (target_func_address as u32)
|
||||||
.wrapping_sub(reloc_address as u32)
|
.wrapping_sub(reloc_address as u32)
|
||||||
@@ -328,9 +333,15 @@ fn relocate(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A declaration for the stack probe function in Rust's standard library, for
|
||||||
|
/// catching callstack overflow.
|
||||||
|
extern "C" {
|
||||||
|
pub fn __rust_probestack();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The implementation of memory.grow.
|
||||||
extern "C" fn wasmtime_memory_grow(size: u32, memory_index: u32, vmctx: *mut VMContext) -> u32 {
|
extern "C" fn wasmtime_memory_grow(size: u32, memory_index: u32, vmctx: *mut VMContext) -> u32 {
|
||||||
let instance = unsafe { (&mut *vmctx).instance() };
|
let instance = unsafe { (&mut *vmctx).instance() };
|
||||||
let memory_index = MemoryIndex::new(memory_index as usize);
|
let memory_index = MemoryIndex::new(memory_index as usize);
|
||||||
@@ -340,53 +351,10 @@ extern "C" fn wasmtime_memory_grow(size: u32, memory_index: u32, vmctx: *mut VMC
|
|||||||
.unwrap_or(u32::max_value())
|
.unwrap_or(u32::max_value())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The implementation of memory.size.
|
||||||
extern "C" fn wasmtime_memory_size(memory_index: u32, vmctx: *mut VMContext) -> u32 {
|
extern "C" fn wasmtime_memory_size(memory_index: u32, vmctx: *mut VMContext) -> u32 {
|
||||||
let instance = unsafe { (&mut *vmctx).instance() };
|
let instance = unsafe { (&mut *vmctx).instance() };
|
||||||
let memory_index = MemoryIndex::new(memory_index as usize);
|
let memory_index = MemoryIndex::new(memory_index as usize);
|
||||||
|
|
||||||
instance.memory_size(memory_index)
|
instance.memory_size(memory_index)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// prepares the execution context
|
|
||||||
pub fn finish_instantiation(
|
|
||||||
code: &mut Code,
|
|
||||||
isa: &TargetIsa,
|
|
||||||
module: &Module,
|
|
||||||
compilation: &Compilation,
|
|
||||||
instance: &mut Instance,
|
|
||||||
) -> Result<(), String> {
|
|
||||||
// TODO: Put all the function bodies into a page-aligned memory region, and
|
|
||||||
// then make them ReadExecute rather than ReadWriteExecute.
|
|
||||||
for code_buf in compilation.functions.values() {
|
|
||||||
match unsafe {
|
|
||||||
protect(
|
|
||||||
code_buf.as_ptr(),
|
|
||||||
code_buf.len(),
|
|
||||||
Protection::ReadWriteExecute,
|
|
||||||
)
|
|
||||||
} {
|
|
||||||
Ok(()) => (),
|
|
||||||
Err(err) => {
|
|
||||||
return Err(format!(
|
|
||||||
"failed to give executable permission to code: {}",
|
|
||||||
err
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(start_index) = module.start_func {
|
|
||||||
let vmctx = instance.vmctx();
|
|
||||||
let result = invoke_by_index(code, isa, module, compilation, vmctx, start_index, &[])?;
|
|
||||||
match result {
|
|
||||||
ActionOutcome::Returned { values } => {
|
|
||||||
assert!(values.is_empty());
|
|
||||||
}
|
|
||||||
ActionOutcome::Trapped { message } => {
|
|
||||||
return Err(format!("start function trapped: {}", message));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
@@ -50,8 +50,8 @@ impl LinearMemory {
|
|||||||
inaccessible_bytes,
|
inaccessible_bytes,
|
||||||
region::Protection::None,
|
region::Protection::None,
|
||||||
)
|
)
|
||||||
.expect("unable to make memory inaccessible");
|
|
||||||
}
|
}
|
||||||
|
.expect("unable to make memory inaccessible");
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
mmap,
|
mmap,
|
||||||
|
|||||||
@@ -1,15 +1,18 @@
|
|||||||
use action::{ActionOutcome, Value};
|
use action::{ActionError, ActionOutcome, RuntimeValue};
|
||||||
use code::Code;
|
use code::Code;
|
||||||
use cranelift_codegen::isa;
|
use cranelift_codegen::isa;
|
||||||
use cranelift_wasm::{GlobalIndex, MemoryIndex};
|
use cranelift_entity::PrimaryMap;
|
||||||
use execute::{compile_and_link_module, finish_instantiation};
|
use cranelift_wasm::{DefinedFuncIndex, GlobalIndex, MemoryIndex};
|
||||||
use export::Resolver;
|
use export::Resolver;
|
||||||
use get::get;
|
use get::get;
|
||||||
use instance::Instance;
|
use instance::Instance;
|
||||||
use invoke::invoke;
|
use invoke::{invoke, invoke_start_function};
|
||||||
|
use link::link_module;
|
||||||
use std::str;
|
use std::str;
|
||||||
use vmcontext::VMGlobal;
|
use vmcontext::VMGlobal;
|
||||||
use wasmtime_environ::{Compilation, Module, ModuleEnvironment, Tunables};
|
use wasmtime_environ::{
|
||||||
|
compile_module, Compilation, CompileError, Module, ModuleEnvironment, Tunables,
|
||||||
|
};
|
||||||
|
|
||||||
/// A module, an instance of that module, and accompanying compilation artifacts.
|
/// A module, an instance of that module, and accompanying compilation artifacts.
|
||||||
///
|
///
|
||||||
@@ -17,7 +20,6 @@ use wasmtime_environ::{Compilation, Module, ModuleEnvironment, Tunables};
|
|||||||
pub struct InstanceWorld {
|
pub struct InstanceWorld {
|
||||||
module: Module,
|
module: Module,
|
||||||
instance: Instance,
|
instance: Instance,
|
||||||
compilation: Compilation,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl InstanceWorld {
|
impl InstanceWorld {
|
||||||
@@ -27,34 +29,61 @@ impl InstanceWorld {
|
|||||||
isa: &isa::TargetIsa,
|
isa: &isa::TargetIsa,
|
||||||
data: &[u8],
|
data: &[u8],
|
||||||
resolver: &mut Resolver,
|
resolver: &mut Resolver,
|
||||||
) -> Result<Self, String> {
|
) -> Result<Self, ActionError> {
|
||||||
let mut module = Module::new();
|
let mut module = Module::new();
|
||||||
// TODO: Allow the tunables to be overridden.
|
// TODO: Allow the tunables to be overridden.
|
||||||
let tunables = Tunables::default();
|
let tunables = Tunables::default();
|
||||||
let (instance, compilation) = {
|
let instance = {
|
||||||
let translation = {
|
// TODO: Untie this.
|
||||||
|
let ((mut compilation, relocations), lazy_data_initializers) = {
|
||||||
|
let (lazy_function_body_inputs, lazy_data_initializers) = {
|
||||||
let environ = ModuleEnvironment::new(isa, &mut module, tunables);
|
let environ = ModuleEnvironment::new(isa, &mut module, tunables);
|
||||||
|
|
||||||
environ.translate(&data).map_err(|e| e.to_string())?
|
let translation = environ
|
||||||
|
.translate(&data)
|
||||||
|
.map_err(|error| ActionError::Compile(CompileError::Wasm(error)))?;
|
||||||
|
|
||||||
|
(
|
||||||
|
translation.lazy.function_body_inputs,
|
||||||
|
translation.lazy.data_initializers,
|
||||||
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
let compilation = compile_and_link_module(isa, &translation, resolver)?;
|
(
|
||||||
|
compile_module(&module, &lazy_function_body_inputs, isa)
|
||||||
|
.map_err(ActionError::Compile)?,
|
||||||
|
lazy_data_initializers,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
let allocated_functions =
|
||||||
|
allocate_functions(code, compilation).map_err(ActionError::Resource)?;
|
||||||
|
|
||||||
|
let resolved = link_module(&module, &allocated_functions, relocations, resolver)
|
||||||
|
.map_err(ActionError::Link)?;
|
||||||
|
|
||||||
let mut instance = Instance::new(
|
let mut instance = Instance::new(
|
||||||
translation.module,
|
&module,
|
||||||
&compilation,
|
allocated_functions,
|
||||||
&translation.lazy.data_initializers,
|
&lazy_data_initializers,
|
||||||
)?;
|
resolved,
|
||||||
|
)
|
||||||
|
.map_err(ActionError::Resource)?;
|
||||||
|
|
||||||
finish_instantiation(code, isa, &translation.module, &compilation, &mut instance)?;
|
// The WebAssembly spec specifies that the start function is
|
||||||
|
// invoked automatically at instantiation time.
|
||||||
|
match invoke_start_function(code, isa, &module, &mut instance)? {
|
||||||
|
ActionOutcome::Returned { .. } => {}
|
||||||
|
ActionOutcome::Trapped { message } => {
|
||||||
|
// Instantiation fails if the start function traps.
|
||||||
|
return Err(ActionError::Start(message));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
(instance, compilation)
|
instance
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self { module, instance })
|
||||||
module,
|
|
||||||
instance,
|
|
||||||
compilation,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Invoke a function in this `InstanceWorld` by name.
|
/// Invoke a function in this `InstanceWorld` by name.
|
||||||
@@ -63,23 +92,21 @@ impl InstanceWorld {
|
|||||||
code: &mut Code,
|
code: &mut Code,
|
||||||
isa: &isa::TargetIsa,
|
isa: &isa::TargetIsa,
|
||||||
function_name: &str,
|
function_name: &str,
|
||||||
args: &[Value],
|
args: &[RuntimeValue],
|
||||||
) -> Result<ActionOutcome, String> {
|
) -> Result<ActionOutcome, ActionError> {
|
||||||
invoke(
|
invoke(
|
||||||
code,
|
code,
|
||||||
isa,
|
isa,
|
||||||
&self.module,
|
&self.module,
|
||||||
&self.compilation,
|
&mut self.instance,
|
||||||
self.instance.vmctx(),
|
|
||||||
&function_name,
|
&function_name,
|
||||||
args,
|
args,
|
||||||
)
|
)
|
||||||
.map_err(|e| e.to_string())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Read a global in this `InstanceWorld` by name.
|
/// Read a global in this `InstanceWorld` by name.
|
||||||
pub fn get(&mut self, global_name: &str) -> Result<Value, String> {
|
pub fn get(&mut self, global_name: &str) -> Result<RuntimeValue, ActionError> {
|
||||||
get(&self.module, self.instance.vmctx(), global_name).map_err(|e| e.to_string())
|
get(&self.module, &mut self.instance, global_name)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a slice of the contents of allocated linear memory.
|
/// Returns a slice of the contents of allocated linear memory.
|
||||||
@@ -92,3 +119,15 @@ impl InstanceWorld {
|
|||||||
self.instance.inspect_global(global_index)
|
self.instance.inspect_global(global_index)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn allocate_functions(
|
||||||
|
code: &mut Code,
|
||||||
|
compilation: Compilation,
|
||||||
|
) -> Result<PrimaryMap<DefinedFuncIndex, (*mut u8, usize)>, String> {
|
||||||
|
let mut result = PrimaryMap::with_capacity(compilation.functions.len());
|
||||||
|
for (_, body) in compilation.functions.into_iter() {
|
||||||
|
let slice = code.allocate_copy_of_slice(&body)?;
|
||||||
|
result.push((slice.as_mut_ptr(), slice.len()));
|
||||||
|
}
|
||||||
|
Ok(result)
|
||||||
|
}
|
||||||
|
|||||||
@@ -13,10 +13,13 @@ readme = "README.md"
|
|||||||
cranelift-codegen = { git = "https://github.com/sunfishcode/cranelift.git", branch = "guard-offset" }
|
cranelift-codegen = { git = "https://github.com/sunfishcode/cranelift.git", branch = "guard-offset" }
|
||||||
cranelift-native = { git = "https://github.com/sunfishcode/cranelift.git", branch = "guard-offset" }
|
cranelift-native = { git = "https://github.com/sunfishcode/cranelift.git", branch = "guard-offset" }
|
||||||
cranelift-wasm = { git = "https://github.com/sunfishcode/cranelift.git", branch = "guard-offset" }
|
cranelift-wasm = { git = "https://github.com/sunfishcode/cranelift.git", branch = "guard-offset" }
|
||||||
|
cranelift-entity = { git = "https://github.com/sunfishcode/cranelift.git", branch = "guard-offset" }
|
||||||
wasmtime-execute = { path = "../execute" }
|
wasmtime-execute = { path = "../execute" }
|
||||||
wasmtime-environ = { path = "../environ" }
|
wasmtime-environ = { path = "../environ" }
|
||||||
wabt = "0.7"
|
wabt = "0.7"
|
||||||
target-lexicon = "0.2.0"
|
target-lexicon = "0.2.0"
|
||||||
|
failure = "0.1.3"
|
||||||
|
failure_derive = "0.1.3"
|
||||||
|
|
||||||
[badges]
|
[badges]
|
||||||
maintenance = { status = "experimental" }
|
maintenance = { status = "experimental" }
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ fn test_directory(out: &mut File, testsuite: &str) -> io::Result<()> {
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
.replace("-", "_")
|
.replace("-", "_")
|
||||||
)?;
|
)?;
|
||||||
writeln!(out, " use super::{{native_isa, wast_file, Path}};")?;
|
writeln!(out, " use super::{{native_isa, WastContext, Path}};")?;
|
||||||
for dir_entry in dir_entries {
|
for dir_entry in dir_entries {
|
||||||
let path = dir_entry.path();
|
let path = dir_entry.path();
|
||||||
let stemstr = path
|
let stemstr = path
|
||||||
@@ -66,9 +66,10 @@ fn test_directory(out: &mut File, testsuite: &str) -> io::Result<()> {
|
|||||||
" fn {}() {{",
|
" fn {}() {{",
|
||||||
avoid_keywords(&stemstr.replace("-", "_"))
|
avoid_keywords(&stemstr.replace("-", "_"))
|
||||||
)?;
|
)?;
|
||||||
|
writeln!(out, " let mut wast_context = WastContext::new();")?;
|
||||||
writeln!(
|
writeln!(
|
||||||
out,
|
out,
|
||||||
" wast_file(Path::new(\"{}\"), &*native_isa()).expect(\"error loading wast file {}\");",
|
" wast_context.run_file(&*native_isa(), Path::new(\"{}\")).expect(\"error running wast file: {}\");",
|
||||||
path.display(),
|
path.display(),
|
||||||
path.display()
|
path.display()
|
||||||
)?;
|
)?;
|
||||||
|
|||||||
@@ -24,6 +24,11 @@
|
|||||||
|
|
||||||
extern crate cranelift_codegen;
|
extern crate cranelift_codegen;
|
||||||
extern crate cranelift_wasm;
|
extern crate cranelift_wasm;
|
||||||
|
#[macro_use]
|
||||||
|
extern crate cranelift_entity;
|
||||||
|
extern crate failure;
|
||||||
|
#[macro_use]
|
||||||
|
extern crate failure_derive;
|
||||||
extern crate target_lexicon;
|
extern crate target_lexicon;
|
||||||
extern crate wabt;
|
extern crate wabt;
|
||||||
extern crate wasmtime_environ;
|
extern crate wasmtime_environ;
|
||||||
@@ -32,4 +37,4 @@ extern crate wasmtime_execute;
|
|||||||
mod spectest;
|
mod spectest;
|
||||||
mod wast;
|
mod wast;
|
||||||
|
|
||||||
pub use wast::{wast_buffer, wast_file};
|
pub use wast::{WastContext, WastError};
|
||||||
|
|||||||
@@ -53,17 +53,17 @@ impl SpecTest {
|
|||||||
Self {
|
Self {
|
||||||
spectest_global_i32: VMGlobal::definition(&Global {
|
spectest_global_i32: VMGlobal::definition(&Global {
|
||||||
ty: types::I32,
|
ty: types::I32,
|
||||||
mutability: false,
|
mutability: true,
|
||||||
initializer: GlobalInit::I32Const(0),
|
initializer: GlobalInit::I32Const(0),
|
||||||
}),
|
}),
|
||||||
spectest_global_f32: VMGlobal::definition(&Global {
|
spectest_global_f32: VMGlobal::definition(&Global {
|
||||||
ty: types::I32,
|
ty: types::I32,
|
||||||
mutability: false,
|
mutability: true,
|
||||||
initializer: GlobalInit::F32Const(0),
|
initializer: GlobalInit::F32Const(0),
|
||||||
}),
|
}),
|
||||||
spectest_global_f64: VMGlobal::definition(&Global {
|
spectest_global_f64: VMGlobal::definition(&Global {
|
||||||
ty: types::I32,
|
ty: types::I32,
|
||||||
mutability: false,
|
mutability: true,
|
||||||
initializer: GlobalInit::F64Const(0),
|
initializer: GlobalInit::F64Const(0),
|
||||||
}),
|
}),
|
||||||
spectest_table: VMTable::definition(ptr::null_mut(), 0),
|
spectest_table: VMTable::definition(ptr::null_mut(), 0),
|
||||||
@@ -79,7 +79,7 @@ impl Resolver for SpecTest {
|
|||||||
match module {
|
match module {
|
||||||
"spectest" => match field {
|
"spectest" => match field {
|
||||||
"print" => Some(ExportValue::function(
|
"print" => Some(ExportValue::function(
|
||||||
spectest_print as usize,
|
spectest_print as *const u8,
|
||||||
translate_signature(
|
translate_signature(
|
||||||
ir::Signature {
|
ir::Signature {
|
||||||
params: vec![],
|
params: vec![],
|
||||||
@@ -90,7 +90,7 @@ impl Resolver for SpecTest {
|
|||||||
),
|
),
|
||||||
)),
|
)),
|
||||||
"print_i32" => Some(ExportValue::function(
|
"print_i32" => Some(ExportValue::function(
|
||||||
spectest_print_i32 as usize,
|
spectest_print_i32 as *const u8,
|
||||||
translate_signature(
|
translate_signature(
|
||||||
ir::Signature {
|
ir::Signature {
|
||||||
params: vec![ir::AbiParam::new(types::I32)],
|
params: vec![ir::AbiParam::new(types::I32)],
|
||||||
@@ -101,7 +101,7 @@ impl Resolver for SpecTest {
|
|||||||
),
|
),
|
||||||
)),
|
)),
|
||||||
"print_i64" => Some(ExportValue::function(
|
"print_i64" => Some(ExportValue::function(
|
||||||
spectest_print_i64 as usize,
|
spectest_print_i64 as *const u8,
|
||||||
translate_signature(
|
translate_signature(
|
||||||
ir::Signature {
|
ir::Signature {
|
||||||
params: vec![ir::AbiParam::new(types::I64)],
|
params: vec![ir::AbiParam::new(types::I64)],
|
||||||
@@ -112,7 +112,7 @@ impl Resolver for SpecTest {
|
|||||||
),
|
),
|
||||||
)),
|
)),
|
||||||
"print_f32" => Some(ExportValue::function(
|
"print_f32" => Some(ExportValue::function(
|
||||||
spectest_print_f32 as usize,
|
spectest_print_f32 as *const u8,
|
||||||
translate_signature(
|
translate_signature(
|
||||||
ir::Signature {
|
ir::Signature {
|
||||||
params: vec![ir::AbiParam::new(types::F32)],
|
params: vec![ir::AbiParam::new(types::F32)],
|
||||||
@@ -123,7 +123,7 @@ impl Resolver for SpecTest {
|
|||||||
),
|
),
|
||||||
)),
|
)),
|
||||||
"print_f64" => Some(ExportValue::function(
|
"print_f64" => Some(ExportValue::function(
|
||||||
spectest_print_f64 as usize,
|
spectest_print_f64 as *const u8,
|
||||||
translate_signature(
|
translate_signature(
|
||||||
ir::Signature {
|
ir::Signature {
|
||||||
params: vec![ir::AbiParam::new(types::F64)],
|
params: vec![ir::AbiParam::new(types::F64)],
|
||||||
@@ -134,7 +134,7 @@ impl Resolver for SpecTest {
|
|||||||
),
|
),
|
||||||
)),
|
)),
|
||||||
"print_i32_f32" => Some(ExportValue::function(
|
"print_i32_f32" => Some(ExportValue::function(
|
||||||
spectest_print_i32_f32 as usize,
|
spectest_print_i32_f32 as *const u8,
|
||||||
translate_signature(
|
translate_signature(
|
||||||
ir::Signature {
|
ir::Signature {
|
||||||
params: vec![
|
params: vec![
|
||||||
@@ -148,7 +148,7 @@ impl Resolver for SpecTest {
|
|||||||
),
|
),
|
||||||
)),
|
)),
|
||||||
"print_f64_f64" => Some(ExportValue::function(
|
"print_f64_f64" => Some(ExportValue::function(
|
||||||
spectest_print_f64_f64 as usize,
|
spectest_print_f64_f64 as *const u8,
|
||||||
translate_signature(
|
translate_signature(
|
||||||
ir::Signature {
|
ir::Signature {
|
||||||
params: vec![
|
params: vec![
|
||||||
|
|||||||
@@ -1,24 +1,98 @@
|
|||||||
use cranelift_codegen::isa;
|
use cranelift_codegen::isa;
|
||||||
|
use cranelift_entity::PrimaryMap;
|
||||||
use spectest::SpecTest;
|
use spectest::SpecTest;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::fs;
|
|
||||||
use std::io;
|
|
||||||
use std::io::Read;
|
use std::io::Read;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::str;
|
use std::{fmt, fs, io, str};
|
||||||
use wabt::script::{self, Action, Command, CommandKind, ModuleBinary, ScriptParser};
|
use wabt::script::{Action, Command, CommandKind, ModuleBinary, ScriptParser, Value};
|
||||||
use wasmtime_execute::{ActionOutcome, Code, InstanceWorld, Value};
|
use wasmtime_execute::{ActionError, ActionOutcome, Code, InstanceWorld, RuntimeValue};
|
||||||
|
|
||||||
struct Instances {
|
/// Translate from a script::Value to a RuntimeValue.
|
||||||
current: Option<InstanceWorld>,
|
fn runtime_value(v: Value) -> RuntimeValue {
|
||||||
namespace: HashMap<String, InstanceWorld>,
|
match v {
|
||||||
|
Value::I32(x) => RuntimeValue::I32(x),
|
||||||
|
Value::I64(x) => RuntimeValue::I64(x),
|
||||||
|
Value::F32(x) => RuntimeValue::F32(x.to_bits()),
|
||||||
|
Value::F64(x) => RuntimeValue::F64(x.to_bits()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Indicates an unknown module was specified.
|
||||||
|
#[derive(Fail, Debug)]
|
||||||
|
pub struct UnknownModule {
|
||||||
|
module: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for UnknownModule {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
match self.module {
|
||||||
|
None => write!(f, "no default module present"),
|
||||||
|
Some(ref name) => write!(f, "no module {} present", name),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Error message used by `WastContext`.
|
||||||
|
#[derive(Fail, Debug)]
|
||||||
|
pub enum WastError {
|
||||||
|
/// An assert command was not satisfied.
|
||||||
|
Assert(String),
|
||||||
|
/// An unknown module name was used.
|
||||||
|
Module(UnknownModule),
|
||||||
|
/// An error occured while performing an action.
|
||||||
|
Action(ActionError),
|
||||||
|
/// An action trapped.
|
||||||
|
Trap(String),
|
||||||
|
/// There was a type error in inputs or outputs of an action.
|
||||||
|
Type(String),
|
||||||
|
/// The was an I/O error while reading the wast file.
|
||||||
|
IO(io::Error),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for WastError {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
match *self {
|
||||||
|
WastError::Assert(ref message) => write!(f, "Assert command failed: {}", message),
|
||||||
|
WastError::Module(ref error) => error.fmt(f),
|
||||||
|
WastError::Action(ref error) => error.fmt(f),
|
||||||
|
WastError::Trap(ref message) => write!(f, "trap: {}", message),
|
||||||
|
WastError::Type(ref message) => write!(f, "type error: {}", message),
|
||||||
|
WastError::IO(ref error) => write!(f, "I/O error: {}", error),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Error message with a source file and line number.
|
||||||
|
#[derive(Fail, Debug)]
|
||||||
|
#[fail(display = "{}:{}: {}", filename, line, error)]
|
||||||
|
pub struct WastFileError {
|
||||||
|
filename: String,
|
||||||
|
line: u64,
|
||||||
|
error: WastError,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An opaque reference to an `InstanceWorld`.
|
||||||
|
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||||
|
pub struct WorldIndex(u32);
|
||||||
|
entity_impl!(WorldIndex, "world");
|
||||||
|
|
||||||
|
/// The wast test script language allows modules to be defined and actions
|
||||||
|
/// to be performed on them.
|
||||||
|
pub struct WastContext {
|
||||||
|
/// A namespace of wasm modules, keyed by an optional name.
|
||||||
|
worlds: PrimaryMap<WorldIndex, InstanceWorld>,
|
||||||
|
current: Option<WorldIndex>,
|
||||||
|
namespace: HashMap<String, WorldIndex>,
|
||||||
code: Code,
|
code: Code,
|
||||||
spectest: SpecTest,
|
spectest: SpecTest,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Instances {
|
impl WastContext {
|
||||||
|
/// Construct a new instance of `WastContext`.
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
worlds: PrimaryMap::new(),
|
||||||
current: None,
|
current: None,
|
||||||
namespace: HashMap::new(),
|
namespace: HashMap::new(),
|
||||||
code: Code::new(),
|
code: Code::new(),
|
||||||
@@ -30,241 +104,329 @@ impl Instances {
|
|||||||
&mut self,
|
&mut self,
|
||||||
isa: &isa::TargetIsa,
|
isa: &isa::TargetIsa,
|
||||||
module: ModuleBinary,
|
module: ModuleBinary,
|
||||||
) -> Result<InstanceWorld, String> {
|
) -> Result<InstanceWorld, ActionError> {
|
||||||
InstanceWorld::new(&mut self.code, isa, &module.into_vec(), &mut self.spectest)
|
InstanceWorld::new(&mut self.code, isa, &module.into_vec(), &mut self.spectest)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn define_unnamed_module(
|
fn get_world(&mut self, module: &Option<String>) -> Result<WorldIndex, WastError> {
|
||||||
&mut self,
|
let index = *if let Some(name) = module {
|
||||||
isa: &isa::TargetIsa,
|
self.namespace.get_mut(name).ok_or_else(|| {
|
||||||
module: ModuleBinary,
|
WastError::Module(UnknownModule {
|
||||||
) -> Result<(), String> {
|
module: Some(name.to_owned()),
|
||||||
self.current = Some(self.instantiate(isa, module)?);
|
})
|
||||||
Ok(())
|
})
|
||||||
|
} else {
|
||||||
|
self.current
|
||||||
|
.as_mut()
|
||||||
|
.ok_or_else(|| WastError::Module(UnknownModule { module: None }))
|
||||||
|
}?;
|
||||||
|
|
||||||
|
Ok(index)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn define_named_module(
|
/// Define a module and register it.
|
||||||
|
pub fn module(
|
||||||
&mut self,
|
&mut self,
|
||||||
isa: &isa::TargetIsa,
|
isa: &isa::TargetIsa,
|
||||||
name: String,
|
name: Option<String>,
|
||||||
module: ModuleBinary,
|
module: ModuleBinary,
|
||||||
) -> Result<(), String> {
|
) -> Result<(), ActionError> {
|
||||||
let world = self.instantiate(isa, module)?;
|
let world = self.instantiate(isa, module)?;
|
||||||
self.namespace.insert(name, world);
|
let index = if let Some(name) = name {
|
||||||
|
self.register(name, world)
|
||||||
|
} else {
|
||||||
|
self.worlds.push(world)
|
||||||
|
};
|
||||||
|
self.current = Some(index);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn perform_action(
|
/// Register a module to make it available for performing actions.
|
||||||
|
pub fn register(&mut self, name: String, world: InstanceWorld) -> WorldIndex {
|
||||||
|
let index = self.worlds.push(world);
|
||||||
|
self.namespace.insert(name, index);
|
||||||
|
index
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Invoke an exported function from a defined module.
|
||||||
|
pub fn invoke(
|
||||||
|
&mut self,
|
||||||
|
isa: &isa::TargetIsa,
|
||||||
|
module: Option<String>,
|
||||||
|
field: &str,
|
||||||
|
args: &[Value],
|
||||||
|
) -> Result<ActionOutcome, WastError> {
|
||||||
|
let mut value_args = Vec::with_capacity(args.len());
|
||||||
|
for arg in args {
|
||||||
|
value_args.push(runtime_value(*arg));
|
||||||
|
}
|
||||||
|
let index = self.get_world(&module)?;
|
||||||
|
self.worlds[index]
|
||||||
|
.invoke(&mut self.code, isa, &field, &value_args)
|
||||||
|
.map_err(WastError::Action)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the value of an exported global from a defined module.
|
||||||
|
pub fn get(&mut self, module: Option<String>, field: &str) -> Result<RuntimeValue, WastError> {
|
||||||
|
let index = self.get_world(&module)?;
|
||||||
|
self.worlds[index].get(&field).map_err(WastError::Action)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn perform_action(
|
||||||
&mut self,
|
&mut self,
|
||||||
isa: &isa::TargetIsa,
|
isa: &isa::TargetIsa,
|
||||||
action: Action,
|
action: Action,
|
||||||
) -> Result<ActionOutcome, String> {
|
) -> Result<ActionOutcome, WastError> {
|
||||||
match action {
|
match action {
|
||||||
Action::Invoke {
|
Action::Invoke {
|
||||||
module,
|
module,
|
||||||
field,
|
field,
|
||||||
args,
|
args,
|
||||||
} => {
|
} => self.invoke(isa, module, &field, &args),
|
||||||
let mut value_args = Vec::with_capacity(args.len());
|
|
||||||
for a in args {
|
|
||||||
value_args.push(match a {
|
|
||||||
script::Value::I32(i) => Value::I32(i),
|
|
||||||
script::Value::I64(i) => Value::I64(i),
|
|
||||||
script::Value::F32(i) => Value::F32(i.to_bits()),
|
|
||||||
script::Value::F64(i) => Value::F64(i.to_bits()),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
match module {
|
|
||||||
None => match self.current {
|
|
||||||
None => Err("invoke performed with no module present".to_string()),
|
|
||||||
Some(ref mut instance_world) => instance_world
|
|
||||||
.invoke(&mut self.code, isa, &field, &value_args)
|
|
||||||
.map_err(|e| {
|
|
||||||
format!("error invoking {} in current module: {}", field, e)
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
Some(name) => self
|
|
||||||
.namespace
|
|
||||||
.get_mut(&name)
|
|
||||||
.ok_or_else(|| format!("module {} not declared", name))?
|
|
||||||
.invoke(&mut self.code, isa, &field, &value_args)
|
|
||||||
.map_err(|e| format!("error invoking {} in module {}: {}", field, name, e)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Action::Get { module, field } => {
|
Action::Get { module, field } => {
|
||||||
let value = match module {
|
let value = self.get(module, &field)?;
|
||||||
None => match self.current {
|
|
||||||
None => return Err("get performed with no module present".to_string()),
|
|
||||||
Some(ref mut instance_world) => {
|
|
||||||
instance_world.get(&field).map_err(|e| {
|
|
||||||
format!("error getting {} in current module: {}", field, e)
|
|
||||||
})?
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Some(name) => self
|
|
||||||
.namespace
|
|
||||||
.get_mut(&name)
|
|
||||||
.ok_or_else(|| format!("module {} not declared", name))?
|
|
||||||
.get(&field)
|
|
||||||
.map_err(|e| {
|
|
||||||
format!("error getting {} in module {}: {}", field, name, e)
|
|
||||||
})?,
|
|
||||||
};
|
|
||||||
Ok(ActionOutcome::Returned {
|
Ok(ActionOutcome::Returned {
|
||||||
values: vec![value],
|
values: vec![value],
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/// Run a wast script from a byte buffer.
|
/// Run a wast script from a byte buffer.
|
||||||
pub fn wast_buffer(name: &str, isa: &isa::TargetIsa, wast: &[u8]) -> Result<(), String> {
|
pub fn run_buffer(
|
||||||
|
&mut self,
|
||||||
|
isa: &isa::TargetIsa,
|
||||||
|
filename: &str,
|
||||||
|
wast: &[u8],
|
||||||
|
) -> Result<(), WastFileError> {
|
||||||
let mut parser = ScriptParser::from_str(str::from_utf8(wast).unwrap()).unwrap();
|
let mut parser = ScriptParser::from_str(str::from_utf8(wast).unwrap()).unwrap();
|
||||||
let mut instances = Instances::new();
|
|
||||||
|
|
||||||
while let Some(Command { kind, line }) = parser.next().unwrap() {
|
while let Some(Command { kind, line }) = parser.next().unwrap() {
|
||||||
match kind {
|
match kind {
|
||||||
CommandKind::Module { module, name } => {
|
CommandKind::Module { module, name } => {
|
||||||
if let Some(name) = name {
|
self.module(isa, name, module)
|
||||||
instances.define_named_module(isa, name, module.clone())?;
|
.map_err(|error| WastFileError {
|
||||||
}
|
filename: filename.to_string(),
|
||||||
|
line,
|
||||||
instances.define_unnamed_module(isa, module)?;
|
error: WastError::Action(error),
|
||||||
|
})?;
|
||||||
}
|
}
|
||||||
CommandKind::Register {
|
CommandKind::Register {
|
||||||
name: _name,
|
name: _name,
|
||||||
as_name: _as_name,
|
as_name: _as_name,
|
||||||
} => {
|
} => {
|
||||||
println!("{}:{}: TODO: Implement register", name, line);
|
println!("{}:{}: TODO: Implement register", filename, line);
|
||||||
}
|
}
|
||||||
CommandKind::PerformAction(action) => match instances.perform_action(isa, action)? {
|
CommandKind::PerformAction(action) => match self
|
||||||
|
.perform_action(isa, action)
|
||||||
|
.map_err(|error| WastFileError {
|
||||||
|
filename: filename.to_string(),
|
||||||
|
line,
|
||||||
|
error,
|
||||||
|
})? {
|
||||||
ActionOutcome::Returned { .. } => {}
|
ActionOutcome::Returned { .. } => {}
|
||||||
ActionOutcome::Trapped { message } => {
|
ActionOutcome::Trapped { message } => {
|
||||||
panic!("{}:{}: a trap occurred: {}", name, line, message);
|
return Err(WastFileError {
|
||||||
|
filename: filename.to_string(),
|
||||||
|
line,
|
||||||
|
error: WastError::Trap(message),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
CommandKind::AssertReturn { action, expected } => {
|
CommandKind::AssertReturn { action, expected } => {
|
||||||
match instances.perform_action(isa, action)? {
|
match self
|
||||||
|
.perform_action(isa, action)
|
||||||
|
.map_err(|error| WastFileError {
|
||||||
|
filename: filename.to_string(),
|
||||||
|
line,
|
||||||
|
error,
|
||||||
|
})? {
|
||||||
ActionOutcome::Returned { values } => {
|
ActionOutcome::Returned { values } => {
|
||||||
for (v, e) in values.iter().zip(expected.iter()) {
|
for (v, e) in values
|
||||||
match *e {
|
.iter()
|
||||||
script::Value::I32(x) => {
|
.cloned()
|
||||||
assert_eq!(x, v.unwrap_i32(), "at {}:{}", name, line)
|
.zip(expected.iter().cloned().map(runtime_value))
|
||||||
|
{
|
||||||
|
if v != e {
|
||||||
|
return Err(WastFileError {
|
||||||
|
filename: filename.to_string(),
|
||||||
|
line,
|
||||||
|
error: WastError::Assert(format!(
|
||||||
|
"expected {}, got {}",
|
||||||
|
e, v
|
||||||
|
)),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
script::Value::I64(x) => {
|
|
||||||
assert_eq!(x, v.unwrap_i64(), "at {}:{}", name, line)
|
|
||||||
}
|
|
||||||
script::Value::F32(x) => {
|
|
||||||
assert_eq!(x.to_bits(), v.unwrap_f32(), "at {}:{}", name, line)
|
|
||||||
}
|
|
||||||
script::Value::F64(x) => {
|
|
||||||
assert_eq!(x.to_bits(), v.unwrap_f64(), "at {}:{}", name, line)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ActionOutcome::Trapped { message } => {
|
ActionOutcome::Trapped { message } => {
|
||||||
panic!(
|
return Err(WastFileError {
|
||||||
"{}:{}: expected normal return, but a trap occurred: {}",
|
filename: filename.to_string(),
|
||||||
name, line, message
|
line,
|
||||||
);
|
error: WastError::Assert(format!("unexpected trap: {}", message)),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
CommandKind::AssertTrap { action, message } => {
|
CommandKind::AssertTrap { action, message } => {
|
||||||
match instances.perform_action(isa, action)? {
|
match self
|
||||||
ActionOutcome::Returned { values } => panic!(
|
.perform_action(isa, action)
|
||||||
"{}:{}: expected trap, but invoke returned with {:?}",
|
.map_err(|error| WastFileError {
|
||||||
name, line, values
|
filename: filename.to_string(),
|
||||||
),
|
line,
|
||||||
|
error,
|
||||||
|
})? {
|
||||||
|
ActionOutcome::Returned { values } => {
|
||||||
|
return Err(WastFileError {
|
||||||
|
filename: filename.to_string(),
|
||||||
|
line,
|
||||||
|
error: WastError::Assert(format!(
|
||||||
|
"expected trap, but invoke returned with {:?}",
|
||||||
|
values
|
||||||
|
)),
|
||||||
|
});
|
||||||
|
}
|
||||||
ActionOutcome::Trapped {
|
ActionOutcome::Trapped {
|
||||||
message: trap_message,
|
message: trap_message,
|
||||||
} => {
|
} => {
|
||||||
println!(
|
println!(
|
||||||
"{}:{}: TODO: Check the assert_trap message: expected {}, got {}",
|
"{}:{}: TODO: Check the assert_trap message: expected {}, got {}",
|
||||||
name, line, message, trap_message
|
filename, line, message, trap_message
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
CommandKind::AssertExhaustion { action } => {
|
CommandKind::AssertExhaustion { action } => {
|
||||||
match instances.perform_action(isa, action)? {
|
match self
|
||||||
ActionOutcome::Returned { values } => panic!(
|
.perform_action(isa, action)
|
||||||
"{}:{}: expected exhaustion, but invoke returned with {:?}",
|
.map_err(|error| WastFileError {
|
||||||
name, line, values
|
filename: filename.to_string(),
|
||||||
),
|
line,
|
||||||
|
error,
|
||||||
|
})? {
|
||||||
|
ActionOutcome::Returned { values } => {
|
||||||
|
return Err(WastFileError {
|
||||||
|
filename: filename.to_string(),
|
||||||
|
line,
|
||||||
|
error: WastError::Assert(format!(
|
||||||
|
"expected callstack exhaustion, but invoke returned with {:?}",
|
||||||
|
values
|
||||||
|
)),
|
||||||
|
});
|
||||||
|
}
|
||||||
ActionOutcome::Trapped { message } => {
|
ActionOutcome::Trapped { message } => {
|
||||||
println!(
|
println!(
|
||||||
"{}:{}: TODO: Check the assert_exhaustion message: {}",
|
"{}:{}: TODO: Check the assert_exhaustion message: {}",
|
||||||
name, line, message
|
filename, line, message
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
CommandKind::AssertReturnCanonicalNan { action } => {
|
CommandKind::AssertReturnCanonicalNan { action } => {
|
||||||
match instances.perform_action(isa, action)? {
|
match self
|
||||||
|
.perform_action(isa, action)
|
||||||
|
.map_err(|error| WastFileError {
|
||||||
|
filename: filename.to_string(),
|
||||||
|
line,
|
||||||
|
error,
|
||||||
|
})? {
|
||||||
ActionOutcome::Returned { values } => {
|
ActionOutcome::Returned { values } => {
|
||||||
for v in values.iter() {
|
for v in values.iter() {
|
||||||
match v {
|
match v {
|
||||||
Value::I32(_) | Value::I64(_) => {
|
RuntimeValue::I32(_) | RuntimeValue::I64(_) => {
|
||||||
panic!("unexpected integer type in NaN test");
|
return Err(WastFileError {
|
||||||
|
filename: filename.to_string(),
|
||||||
|
line,
|
||||||
|
error: WastError::Type(format!(
|
||||||
|
"unexpected integer type in NaN test"
|
||||||
|
)),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
RuntimeValue::F32(x) => {
|
||||||
|
if (x & 0x7fffffff) != 0x7fc00000 {
|
||||||
|
return Err(WastFileError {
|
||||||
|
filename: filename.to_string(),
|
||||||
|
line,
|
||||||
|
error: WastError::Assert(format!(
|
||||||
|
"expected canonical NaN"
|
||||||
|
)),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
RuntimeValue::F64(x) => {
|
||||||
|
if (x & 0x7fffffffffffffff) != 0x7ff8000000000000 {
|
||||||
|
return Err(WastFileError {
|
||||||
|
filename: filename.to_string(),
|
||||||
|
line,
|
||||||
|
error: WastError::Assert(format!(
|
||||||
|
"expected canonical NaN"
|
||||||
|
)),
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Value::F32(x) => assert_eq!(
|
|
||||||
x & 0x7fffffff,
|
|
||||||
0x7fc00000,
|
|
||||||
"expected canonical NaN at {}:{}",
|
|
||||||
name,
|
|
||||||
line
|
|
||||||
),
|
|
||||||
Value::F64(x) => assert_eq!(
|
|
||||||
x & 0x7fffffffffffffff,
|
|
||||||
0x7ff8000000000000,
|
|
||||||
"expected canonical NaN at {}:{}",
|
|
||||||
name,
|
|
||||||
line
|
|
||||||
),
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ActionOutcome::Trapped { message } => {
|
ActionOutcome::Trapped { message } => {
|
||||||
panic!(
|
return Err(WastFileError {
|
||||||
"{}:{}: expected canonical NaN return, but a trap occurred: {}",
|
filename: filename.to_string(),
|
||||||
name, line, message
|
line,
|
||||||
);
|
error: WastError::Assert(format!("unexpected trap: {}", message)),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
CommandKind::AssertReturnArithmeticNan { action } => {
|
CommandKind::AssertReturnArithmeticNan { action } => {
|
||||||
match instances.perform_action(isa, action)? {
|
match self
|
||||||
|
.perform_action(isa, action)
|
||||||
|
.map_err(|error| WastFileError {
|
||||||
|
filename: filename.to_string(),
|
||||||
|
line,
|
||||||
|
error,
|
||||||
|
})? {
|
||||||
ActionOutcome::Returned { values } => {
|
ActionOutcome::Returned { values } => {
|
||||||
for v in values.iter() {
|
for v in values.iter() {
|
||||||
match v {
|
match v {
|
||||||
Value::I32(_) | Value::I64(_) => {
|
RuntimeValue::I32(_) | RuntimeValue::I64(_) => {
|
||||||
panic!("unexpected integer type in NaN test");
|
return Err(WastFileError {
|
||||||
|
filename: filename.to_string(),
|
||||||
|
line,
|
||||||
|
error: WastError::Type(format!(
|
||||||
|
"unexpected integer type in NaN test",
|
||||||
|
)),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
RuntimeValue::F32(x) => {
|
||||||
|
if (x & 0x00400000) != 0x00400000 {
|
||||||
|
return Err(WastFileError {
|
||||||
|
filename: filename.to_string(),
|
||||||
|
line,
|
||||||
|
error: WastError::Assert(format!(
|
||||||
|
"expected arithmetic NaN"
|
||||||
|
)),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
RuntimeValue::F64(x) => {
|
||||||
|
if (x & 0x0008000000000000) != 0x0008000000000000 {
|
||||||
|
return Err(WastFileError {
|
||||||
|
filename: filename.to_string(),
|
||||||
|
line,
|
||||||
|
error: WastError::Assert(format!(
|
||||||
|
"expected arithmetic NaN"
|
||||||
|
)),
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Value::F32(x) => assert_eq!(
|
|
||||||
x & 0x00400000,
|
|
||||||
0x00400000,
|
|
||||||
"expected arithmetic NaN at {}:{}",
|
|
||||||
name,
|
|
||||||
line
|
|
||||||
),
|
|
||||||
Value::F64(x) => assert_eq!(
|
|
||||||
x & 0x0008000000000000,
|
|
||||||
0x0008000000000000,
|
|
||||||
"expected arithmetic NaN at {}:{}",
|
|
||||||
name,
|
|
||||||
line
|
|
||||||
),
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ActionOutcome::Trapped { message } => {
|
ActionOutcome::Trapped { message } => {
|
||||||
panic!(
|
return Err(WastFileError {
|
||||||
"{}:{}: expected canonical NaN return, but a trap occurred: {}",
|
filename: filename.to_string(),
|
||||||
name, line, message
|
line,
|
||||||
);
|
error: WastError::Assert(format!("unexpected trap: {}", message)),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -272,36 +434,32 @@ pub fn wast_buffer(name: &str, isa: &isa::TargetIsa, wast: &[u8]) -> Result<(),
|
|||||||
module: _module,
|
module: _module,
|
||||||
message: _message,
|
message: _message,
|
||||||
} => {
|
} => {
|
||||||
println!("{}:{}: TODO: Implement assert_invalid", name, line);
|
println!("{}:{}: TODO: Implement assert_invalid", filename, line);
|
||||||
}
|
}
|
||||||
CommandKind::AssertMalformed {
|
CommandKind::AssertMalformed {
|
||||||
module: _module,
|
module: _module,
|
||||||
message: _message,
|
message: _message,
|
||||||
} => {
|
} => {
|
||||||
println!("{}:{}: TODO: Implement assert_malformed", name, line);
|
println!("{}:{}: TODO: Implement assert_malformed", filename, line);
|
||||||
}
|
}
|
||||||
CommandKind::AssertUninstantiable { module, message } => {
|
CommandKind::AssertUninstantiable { module, message } => {
|
||||||
let _err = instances
|
let _err = self.module(isa, None, module).expect_err(&format!(
|
||||||
.define_unnamed_module(isa, module)
|
|
||||||
.expect_err(&format!(
|
|
||||||
"{}:{}: uninstantiable module was successfully instantiated",
|
"{}:{}: uninstantiable module was successfully instantiated",
|
||||||
name, line
|
filename, line
|
||||||
));
|
));
|
||||||
println!(
|
println!(
|
||||||
"{}:{}: TODO: Check the assert_uninstantiable message: {}",
|
"{}:{}: TODO: Check the assert_uninstantiable message: {}",
|
||||||
name, line, message
|
filename, line, message
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
CommandKind::AssertUnlinkable { module, message } => {
|
CommandKind::AssertUnlinkable { module, message } => {
|
||||||
let _err = instances
|
let _err = self.module(isa, None, module).expect_err(&format!(
|
||||||
.define_unnamed_module(isa, module)
|
|
||||||
.expect_err(&format!(
|
|
||||||
"{}:{}: unlinkable module was successfully linked",
|
"{}:{}: unlinkable module was successfully linked",
|
||||||
name, line
|
filename, line
|
||||||
));
|
));
|
||||||
println!(
|
println!(
|
||||||
"{}:{}: TODO: Check the assert_unlinkable message: {}",
|
"{}:{}: TODO: Check the assert_unlinkable message: {}",
|
||||||
name, line, message
|
filename, line, message
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -311,9 +469,15 @@ pub fn wast_buffer(name: &str, isa: &isa::TargetIsa, wast: &[u8]) -> Result<(),
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Run a wast script from a file.
|
/// Run a wast script from a file.
|
||||||
pub fn wast_file(path: &Path, isa: &isa::TargetIsa) -> Result<(), String> {
|
pub fn run_file(&mut self, isa: &isa::TargetIsa, path: &Path) -> Result<(), WastFileError> {
|
||||||
let wast = read_to_end(path).map_err(|e| e.to_string())?;
|
let filename = path.display().to_string();
|
||||||
wast_buffer(&path.display().to_string(), isa, &wast)
|
let buffer = read_to_end(path).map_err(|e| WastFileError {
|
||||||
|
filename,
|
||||||
|
line: 0,
|
||||||
|
error: WastError::IO(e),
|
||||||
|
})?;
|
||||||
|
self.run_buffer(isa, &path.display().to_string(), &buffer)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_to_end(path: &Path) -> Result<Vec<u8>, io::Error> {
|
fn read_to_end(path: &Path) -> Result<Vec<u8>, io::Error> {
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ use cranelift_codegen::isa;
|
|||||||
use cranelift_codegen::settings;
|
use cranelift_codegen::settings;
|
||||||
use cranelift_codegen::settings::Configurable;
|
use cranelift_codegen::settings::Configurable;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use wasmtime_wast::wast_file;
|
use wasmtime_wast::WastContext;
|
||||||
|
|
||||||
include!(concat!(env!("OUT_DIR"), "/run_wast_files.rs"));
|
include!(concat!(env!("OUT_DIR"), "/run_wast_files.rs"));
|
||||||
|
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ use cranelift_codegen::settings;
|
|||||||
use cranelift_codegen::settings::Configurable;
|
use cranelift_codegen::settings::Configurable;
|
||||||
use docopt::Docopt;
|
use docopt::Docopt;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use wasmtime_wast::wast_file;
|
use wasmtime_wast::WastContext;
|
||||||
|
|
||||||
static LOG_FILENAME_PREFIX: &str = "cranelift.dbg.";
|
static LOG_FILENAME_PREFIX: &str = "cranelift.dbg.";
|
||||||
|
|
||||||
@@ -94,9 +94,10 @@ fn main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let isa = isa_builder.finish(settings::Flags::new(flag_builder));
|
let isa = isa_builder.finish(settings::Flags::new(flag_builder));
|
||||||
|
let mut wast_context = WastContext::new();
|
||||||
for filename in &args.arg_file {
|
for filename in &args.arg_file {
|
||||||
let path = Path::new(&filename);
|
wast_context
|
||||||
wast_file(path, &*isa)
|
.run_file(&*isa, Path::new(&filename))
|
||||||
.unwrap_or_else(|e| panic!("error reading file {}: {}", path.display(), e));
|
.unwrap_or_else(|e| panic!("{}", e));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -146,7 +146,12 @@ fn handle_module(path: PathBuf, target: &Option<String>, output: &str) -> Result
|
|||||||
.map_err(|err| format!("{}", err))?;
|
.map_err(|err| format!("{}", err))?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let (compilation, relocations) = compile_module(&translation, &*isa)?;
|
let (compilation, relocations) = compile_module(
|
||||||
|
&translation.module,
|
||||||
|
&translation.lazy.function_body_inputs,
|
||||||
|
&*isa,
|
||||||
|
)
|
||||||
|
.map_err(|e| e.to_string())?;
|
||||||
|
|
||||||
emit_module(&mut obj, &translation.module, &compilation, &relocations)?;
|
emit_module(&mut obj, &translation.module, &compilation, &relocations)?;
|
||||||
|
|
||||||
|
|||||||
@@ -105,6 +105,13 @@ fn main() {
|
|||||||
.deserialize()
|
.deserialize()
|
||||||
})
|
})
|
||||||
.unwrap_or_else(|e| e.exit());
|
.unwrap_or_else(|e| e.exit());
|
||||||
|
|
||||||
|
if args.flag_debug {
|
||||||
|
pretty_env_logger::init();
|
||||||
|
} else {
|
||||||
|
file_per_thread_logger::initialize(LOG_FILENAME_PREFIX);
|
||||||
|
}
|
||||||
|
|
||||||
let isa_builder = cranelift_native::builder().unwrap_or_else(|_| {
|
let isa_builder = cranelift_native::builder().unwrap_or_else(|_| {
|
||||||
panic!("host machine is not a supported target");
|
panic!("host machine is not a supported target");
|
||||||
});
|
});
|
||||||
@@ -115,18 +122,13 @@ fn main() {
|
|||||||
flag_builder.enable("enable_verifier").unwrap();
|
flag_builder.enable("enable_verifier").unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
if args.flag_debug {
|
|
||||||
pretty_env_logger::init();
|
|
||||||
} else {
|
|
||||||
file_per_thread_logger::initialize(LOG_FILENAME_PREFIX);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Enable optimization if requested.
|
// Enable optimization if requested.
|
||||||
if args.flag_optimize {
|
if args.flag_optimize {
|
||||||
flag_builder.set("opt_level", "best").unwrap();
|
flag_builder.set("opt_level", "best").unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
let isa = isa_builder.finish(settings::Flags::new(flag_builder));
|
let isa = isa_builder.finish(settings::Flags::new(flag_builder));
|
||||||
|
|
||||||
for filename in &args.arg_file {
|
for filename in &args.arg_file {
|
||||||
let path = Path::new(&filename);
|
let path = Path::new(&filename);
|
||||||
match handle_module(&args, path, &*isa) {
|
match handle_module(&args, path, &*isa) {
|
||||||
@@ -149,10 +151,14 @@ fn handle_module(args: &Args, path: &Path, isa: &TargetIsa) -> Result<(), String
|
|||||||
}
|
}
|
||||||
let mut resolver = NullResolver {};
|
let mut resolver = NullResolver {};
|
||||||
let mut code = Code::new();
|
let mut code = Code::new();
|
||||||
let mut world = InstanceWorld::new(&mut code, isa, &data, &mut resolver)?;
|
let mut world =
|
||||||
|
InstanceWorld::new(&mut code, isa, &data, &mut resolver).map_err(|e| e.to_string())?;
|
||||||
|
|
||||||
if let Some(ref f) = args.flag_invoke {
|
if let Some(ref f) = args.flag_invoke {
|
||||||
match world.invoke(&mut code, isa, &f, &[])? {
|
match world
|
||||||
|
.invoke(&mut code, isa, &f, &[])
|
||||||
|
.map_err(|e| e.to_string())?
|
||||||
|
{
|
||||||
ActionOutcome::Returned { .. } => {}
|
ActionOutcome::Returned { .. } => {}
|
||||||
ActionOutcome::Trapped { message } => {
|
ActionOutcome::Trapped { message } => {
|
||||||
return Err(format!("Trap from within function {}: {}", f, message));
|
return Err(format!("Trap from within function {}: {}", f, message));
|
||||||
|
|||||||
Reference in New Issue
Block a user