Merge branch 'master' into command/pyodide

This commit is contained in:
Jesús Leganés-Combarro 'piranna
2019-06-08 19:48:25 +02:00
22 changed files with 936 additions and 36 deletions

View File

@ -6,6 +6,7 @@ Blocks of changes will separated by version increments.
## **[Unreleased]** ## **[Unreleased]**
- [#484](https://github.com/wasmerio/wasmer/pull/484) Fix bugs in emscripten socket syscalls
- [#476](https://github.com/wasmerio/wasmer/pull/476) Fix bug with wasi::environ_get, fix off by one error in wasi::environ_sizes_get - [#476](https://github.com/wasmerio/wasmer/pull/476) Fix bug with wasi::environ_get, fix off by one error in wasi::environ_sizes_get
- [#470](https://github.com/wasmerio/wasmer/pull/470) Add mapdir support to Emscripten, implement getdents for Unix - [#470](https://github.com/wasmerio/wasmer/pull/470) Add mapdir support to Emscripten, implement getdents for Unix
- [#467](https://github.com/wasmerio/wasmer/pull/467) `wasmer_instantiate` returns better error messages in the runtime C API - [#467](https://github.com/wasmerio/wasmer/pull/467) `wasmer_instantiate` returns better error messages in the runtime C API

5
Cargo.lock generated
View File

@ -1460,7 +1460,12 @@ dependencies = [
name = "wasmer-middleware-common" name = "wasmer-middleware-common"
version = "0.4.2" version = "0.4.2"
dependencies = [ dependencies = [
"criterion 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
"wabt 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)",
"wasmer-clif-backend 0.4.2",
"wasmer-llvm-backend 0.4.2",
"wasmer-runtime-core 0.4.2", "wasmer-runtime-core 0.4.2",
"wasmer-singlepass-backend 0.4.2",
] ]
[[package]] [[package]]

View File

@ -48,18 +48,21 @@ do-install:
test: test:
# We use one thread so the emscripten stdouts doesn't collide # We use one thread so the emscripten stdouts doesn't collide
cargo test --all --exclude wasmer-runtime-c-api --exclude wasmer-emscripten --exclude wasmer-spectests --exclude wasmer-singlepass-backend --exclude wasmer-wasi -- $(runargs) cargo test --all --exclude wasmer-runtime-c-api --exclude wasmer-emscripten --exclude wasmer-spectests --exclude wasmer-singlepass-backend --exclude wasmer-wasi --exclude wasmer-middleware-common -- $(runargs)
# cargo test --all --exclude wasmer-emscripten -- --test-threads=1 $(runargs) # cargo test --all --exclude wasmer-emscripten -- --test-threads=1 $(runargs)
cargo test --manifest-path lib/spectests/Cargo.toml --features clif cargo test --manifest-path lib/spectests/Cargo.toml --features clif
cargo test --manifest-path lib/middleware-common/Cargo.toml --features clif
@if [ ! -z "${CIRCLE_JOB}" ]; then rm -f /home/circleci/project/target/debug/deps/libcranelift_wasm* && rm -f /Users/distiller/project/target/debug/deps/libcranelift_wasm*; fi; @if [ ! -z "${CIRCLE_JOB}" ]; then rm -f /home/circleci/project/target/debug/deps/libcranelift_wasm* && rm -f /Users/distiller/project/target/debug/deps/libcranelift_wasm*; fi;
cargo test --manifest-path lib/spectests/Cargo.toml --features llvm cargo test --manifest-path lib/spectests/Cargo.toml --features llvm
cargo test --manifest-path lib/runtime/Cargo.toml --features llvm cargo test --manifest-path lib/runtime/Cargo.toml --features llvm
cargo test --manifest-path lib/middleware-common/Cargo.toml --features llvm
cargo build -p wasmer-runtime-c-api cargo build -p wasmer-runtime-c-api
cargo test -p wasmer-runtime-c-api -- --nocapture cargo test -p wasmer-runtime-c-api -- --nocapture
test-singlepass: test-singlepass:
cargo test --manifest-path lib/spectests/Cargo.toml --features singlepass cargo test --manifest-path lib/spectests/Cargo.toml --features singlepass
cargo test --manifest-path lib/runtime/Cargo.toml --features singlepass cargo test --manifest-path lib/runtime/Cargo.toml --features singlepass
cargo test --manifest-path lib/middleware-common/Cargo.toml --features singlepass
test-emscripten-llvm: test-emscripten-llvm:
cargo test --manifest-path lib/emscripten/Cargo.toml --features llvm -- --test-threads=1 $(runargs) cargo test --manifest-path lib/emscripten/Cargo.toml --features llvm -- --test-threads=1 $(runargs)

View File

@ -0,0 +1,55 @@
(module
(func $main (export "main")
(i32.const 0)
(i32.const 0)
(i32.add)
(i32.const 0)
(i32.const 0)
(i32.add)
(i32.const 0)
(i32.const 0)
(i32.add)
(i32.const 0)
(i32.const 0)
(i32.add)
(i32.const 0)
(i32.const 0)
(i32.add)
(i32.const 0)
(i32.const 0)
(i32.add)
(i32.const 0)
(i32.const 0)
(i32.add)
(i32.const 0)
(i32.const 0)
(i32.add)
(block
(i32.const 0)
(i32.const 0)
(i32.add)
(i32.const 0)
(i32.const 0)
(i32.add)
(br 0)
(unreachable)
)
(drop)
(drop)
(drop)
(drop)
(drop)
(drop)
(drop)
(drop)
)
)

View File

@ -1122,6 +1122,7 @@ impl FunctionCodeGenerator<CodegenError> for CraneliftFunctionCodeGenerator {
fn feed_event(&mut self, event: Event, _module_info: &ModuleInfo) -> Result<(), CodegenError> { fn feed_event(&mut self, event: Event, _module_info: &ModuleInfo) -> Result<(), CodegenError> {
let op = match event { let op = match event {
Event::Wasm(x) => x, Event::Wasm(x) => x,
Event::WasmOwned(ref x) => x,
Event::Internal(_x) => { Event::Internal(_x) => {
return Ok(()); return Ok(());
} }

View File

@ -535,13 +535,25 @@ pub fn ___syscall146(ctx: &mut Ctx, _which: i32, mut varargs: VarArgs) -> i32 {
} }
pub fn ___syscall168(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 { pub fn ___syscall168(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 {
debug!("emscripten::___syscall168"); debug!("emscripten::___syscall168 - stub");
-1 -1
} }
pub fn ___syscall191(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 { pub fn ___syscall191(ctx: &mut Ctx, _which: i32, mut varargs: VarArgs) -> i32 {
debug!("emscripten::___syscall191 - stub"); let _resource: i32 = varargs.get(ctx);
-1 debug!(
"emscripten::___syscall191 - mostly stub, resource: {}",
_resource
);
let rlim_emptr: i32 = varargs.get(ctx);
let rlim_ptr = emscripten_memory_pointer!(ctx.memory(0), rlim_emptr) as *mut u8;
let rlim = unsafe { slice::from_raw_parts_mut(rlim_ptr, 16) };
// set all to RLIM_INIFINTY
LittleEndian::write_i64(&mut rlim[..], -1);
LittleEndian::write_i64(&mut rlim[8..], -1);
0
} }
pub fn ___syscall193(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 { pub fn ___syscall193(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 {
@ -753,19 +765,23 @@ pub fn ___syscall340(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_in
debug!("emscripten::___syscall340 (prlimit64), {}", _which); debug!("emscripten::___syscall340 (prlimit64), {}", _which);
// NOTE: Doesn't really matter. Wasm modules cannot exceed WASM_PAGE_SIZE anyway. // NOTE: Doesn't really matter. Wasm modules cannot exceed WASM_PAGE_SIZE anyway.
let _pid: i32 = varargs.get(ctx); let _pid: i32 = varargs.get(ctx);
let _resource: i32 = varargs.get(ctx); let resource: i32 = varargs.get(ctx);
let _new_limit: u32 = varargs.get(ctx); let _new_limit: u32 = varargs.get(ctx);
let old_limit: u32 = varargs.get(ctx); let old_limit: u32 = varargs.get(ctx);
let val = match resource {
// RLIMIT_NOFILE
7 => 1024,
_ => -1, // RLIM_INFINITY
};
if old_limit != 0 { if old_limit != 0 {
// just report no limits // just report no limits
let buf_ptr = emscripten_memory_pointer!(ctx.memory(0), old_limit) as *mut u8; let buf_ptr = emscripten_memory_pointer!(ctx.memory(0), old_limit) as *mut u8;
let buf = unsafe { slice::from_raw_parts_mut(buf_ptr, 16) }; let buf = unsafe { slice::from_raw_parts_mut(buf_ptr, 16) };
LittleEndian::write_i32(&mut buf[..], -1); // RLIM_INFINITY LittleEndian::write_i64(&mut buf[..], val);
LittleEndian::write_i32(&mut buf[4..], -1); // RLIM_INFINITY LittleEndian::write_i64(&mut buf[8..], val);
LittleEndian::write_i32(&mut buf[8..], -1); // RLIM_INFINITY
LittleEndian::write_i32(&mut buf[12..], -1); // RLIM_INFINITY
} }
0 0

View File

@ -610,15 +610,24 @@ pub fn ___syscall102(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_in
// recvfrom (socket: c_int, buf: *const c_void, len: size_t, flags: c_int, addr: *const sockaddr, addrlen: socklen_t) -> ssize_t // recvfrom (socket: c_int, buf: *const c_void, len: size_t, flags: c_int, addr: *const sockaddr, addrlen: socklen_t) -> ssize_t
let socket = socket_varargs.get(ctx); let socket = socket_varargs.get(ctx);
let buf: u32 = socket_varargs.get(ctx); let buf: u32 = socket_varargs.get(ctx);
let flags = socket_varargs.get(ctx);
let len: i32 = socket_varargs.get(ctx); let len: i32 = socket_varargs.get(ctx);
let flags: i32 = socket_varargs.get(ctx);
let address: u32 = socket_varargs.get(ctx); let address: u32 = socket_varargs.get(ctx);
let address_len: u32 = socket_varargs.get(ctx); let address_len: u32 = socket_varargs.get(ctx);
let buf_addr = emscripten_memory_pointer!(ctx.memory(0), buf) as _; let buf_addr = emscripten_memory_pointer!(ctx.memory(0), buf) as _;
let address = emscripten_memory_pointer!(ctx.memory(0), address) as *mut sockaddr; let address = emscripten_memory_pointer!(ctx.memory(0), address) as *mut sockaddr;
let address_len_addr = let address_len_addr =
emscripten_memory_pointer!(ctx.memory(0), address_len) as *mut socklen_t; emscripten_memory_pointer!(ctx.memory(0), address_len) as *mut socklen_t;
unsafe { recvfrom(socket, buf_addr, flags, len, address, address_len_addr) as i32 } unsafe {
recvfrom(
socket,
buf_addr,
len as usize,
flags,
address,
address_len_addr,
) as i32
}
} }
14 => { 14 => {
debug!("socket: setsockopt"); debug!("socket: setsockopt");
@ -764,7 +773,10 @@ pub fn ___syscall142(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_in
let exceptfds: u32 = varargs.get(ctx); let exceptfds: u32 = varargs.get(ctx);
let _timeout: i32 = varargs.get(ctx); let _timeout: i32 = varargs.get(ctx);
assert!(nfds <= 64, "`nfds` must be less than or equal to 64"); if nfds > 1024 {
// EINVAL
return -22;
}
assert!(exceptfds == 0, "`exceptfds` is not supporrted"); assert!(exceptfds == 0, "`exceptfds` is not supporrted");
let readfds_ptr = emscripten_memory_pointer!(ctx.memory(0), readfds) as _; let readfds_ptr = emscripten_memory_pointer!(ctx.memory(0), readfds) as _;

View File

@ -475,6 +475,7 @@ impl FunctionCodeGenerator<CodegenError> for LLVMFunctionCodeGenerator {
Event::Internal(_x) => { Event::Internal(_x) => {
return Ok(()); return Ok(());
} }
Event::WasmOwned(ref x) => x,
}; };
let mut state = &mut self.state; let mut state = &mut self.state;

View File

@ -163,6 +163,7 @@ impl Intrinsics {
let stack_lower_bound_ty = i8_ty; let stack_lower_bound_ty = i8_ty;
let memory_base_ty = i8_ty; let memory_base_ty = i8_ty;
let memory_bound_ty = void_ty; let memory_bound_ty = void_ty;
let internals_ty = i64_ty;
let local_function_ty = i8_ptr_ty; let local_function_ty = i8_ptr_ty;
let anyfunc_ty = context.struct_type( let anyfunc_ty = context.struct_type(
@ -218,6 +219,9 @@ impl Intrinsics {
memory_bound_ty memory_bound_ty
.ptr_type(AddressSpace::Generic) .ptr_type(AddressSpace::Generic)
.as_basic_type_enum(), .as_basic_type_enum(),
internals_ty
.ptr_type(AddressSpace::Generic)
.as_basic_type_enum(),
local_function_ty local_function_ty
.ptr_type(AddressSpace::Generic) .ptr_type(AddressSpace::Generic)
.as_basic_type_enum(), .as_basic_type_enum(),

View File

@ -8,4 +8,20 @@ authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"]
edition = "2018" edition = "2018"
[dependencies] [dependencies]
wasmer-runtime-core = { path = "../runtime-core", version = "0.4.2" } wasmer-runtime-core = { path = "../runtime-core" }
wasmer-clif-backend = { path = "../clif-backend", version = "0.4.2" }
wasmer-llvm-backend = { path = "../llvm-backend", version = "0.4.2", optional = true }
wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.4.2", optional = true }
[dev-dependencies]
wabt = "0.7.4"
criterion = "0.2"
[features]
clif = []
llvm = ["wasmer-llvm-backend"]
singlepass = ["wasmer-singlepass-backend"]
[[bench]]
name = "metering_benchmark"
harness = false

View File

@ -0,0 +1,230 @@
#[macro_use]
extern crate criterion;
use criterion::black_box;
use criterion::{Benchmark, Criterion};
use wabt::wat2wasm;
use wasmer_middleware_common::metering::Metering;
use wasmer_runtime_core::vm::Ctx;
use wasmer_runtime_core::{backend::Compiler, compile_with, imports, Func};
//export function add_to(x: i32, y: i32): i32 {
// for(var i = 0; i < x; i++){
// if(i % 1 == 0){
// y += i;
// } else {
// y *= i
// }
// }
// return y;
//}
static WAT: &'static str = r#"
(module
(type $t0 (func (param i32 i32) (result i32)))
(type $t1 (func))
(func $add_to (export "add_to") (type $t0) (param $p0 i32) (param $p1 i32) (result i32)
(local $l0 i32)
block $B0
i32.const 0
set_local $l0
loop $L1
get_local $l0
get_local $p0
i32.lt_s
i32.eqz
br_if $B0
get_local $l0
i32.const 1
i32.rem_s
i32.const 0
i32.eq
if $I2
get_local $p1
get_local $l0
i32.add
set_local $p1
else
get_local $p1
get_local $l0
i32.mul
set_local $p1
end
get_local $l0
i32.const 1
i32.add
set_local $l0
br $L1
unreachable
end
unreachable
end
get_local $p1)
(func $f1 (type $t1))
(table $table (export "table") 1 anyfunc)
(memory $memory (export "memory") 0)
(global $g0 i32 (i32.const 8))
(elem (i32.const 0) $f1))
"#;
static WAT_GAS: &'static str = r#"
(module
(type $t0 (func (param i32 i32) (result i32)))
(type $t1 (func))
(type $t2 (func (param i32)))
(import "env" "gas" (func $env.gas (type $t2)))
(func $add_to (type $t0) (param $p0 i32) (param $p1 i32) (result i32)
(local $l0 i32)
i32.const 3
call $env.gas
block $B0
i32.const 5
call $env.gas
i32.const 0
set_local $l0
loop $L1
i32.const 18
call $env.gas
get_local $l0
get_local $p0
i32.lt_s
i32.eqz
br_if $B0
get_local $l0
i32.const 1
i32.rem_s
i32.const 0
i32.eq
if $I2
i32.const 5
call $env.gas
get_local $p1
get_local $l0
i32.add
set_local $p1
else
i32.const 5
call $env.gas
get_local $p1
get_local $l0
i32.mul
set_local $p1
end
get_local $l0
i32.const 1
i32.add
set_local $l0
br $L1
unreachable
end
unreachable
end
get_local $p1)
(func $f2 (type $t1)
i32.const 1
call $env.gas)
(table $table 1 anyfunc)
(memory $memory 0)
(global $g0 i32 (i32.const 8))
(export "memory" (memory 0))
(export "table" (table 0))
(export "add_to" (func $add_to))
(elem (i32.const 0) $f2))
"#;
#[cfg(feature = "llvm")]
fn get_compiler(limit: u64, metering: bool) -> impl Compiler {
use wasmer_llvm_backend::code::LLVMModuleCodeGenerator;
use wasmer_runtime_core::codegen::{MiddlewareChain, StreamingCompiler};
let c: StreamingCompiler<LLVMModuleCodeGenerator, _, _, _, _> =
StreamingCompiler::new(move || {
let mut chain = MiddlewareChain::new();
if metering {
chain.push(Metering::new(limit));
}
chain
});
c
}
#[cfg(feature = "singlepass")]
fn get_compiler(limit: u64, metering: bool) -> impl Compiler {
use wasmer_runtime_core::codegen::{MiddlewareChain, StreamingCompiler};
use wasmer_singlepass_backend::ModuleCodeGenerator as SinglePassMCG;
let c: StreamingCompiler<SinglePassMCG, _, _, _, _> = StreamingCompiler::new(move || {
let mut chain = MiddlewareChain::new();
if metering {
chain.push(Metering::new(limit));
}
chain
});
c
}
#[cfg(not(any(feature = "llvm", feature = "clif", feature = "singlepass")))]
fn get_compiler(_limit: u64, metering: bool) -> impl Compiler {
panic!("compiler not specified, activate a compiler via features");
use wasmer_clif_backend::CraneliftCompiler;
CraneliftCompiler::new()
}
#[cfg(feature = "clif")]
fn get_compiler(_limit: u64, metering: bool) -> impl Compiler {
panic!("cranelift does not implement metering");
use wasmer_clif_backend::CraneliftCompiler;
CraneliftCompiler::new()
}
fn gas(ctx: &mut Ctx, gas_amount: u32) {
use wasmer_middleware_common::metering;
let used = metering::get_points_used_ctx(ctx);
metering::set_points_used_ctx(ctx, used + u64::from(gas_amount));
()
}
fn bench_metering(c: &mut Criterion) {
use wasmer_middleware_common::metering;
c.bench(
"Meter",
Benchmark::new("No Metering", |b| {
let compiler = get_compiler(0, false);
let wasm_binary = wat2wasm(WAT).unwrap();
let module = compile_with(&wasm_binary, &compiler).unwrap();
let import_object = imports! {};
let mut instance = module.instantiate(&import_object).unwrap();
let add_to: Func<(i32, i32), i32> = instance.func("add_to").unwrap();
b.iter(|| black_box(add_to.call(100, 4)))
})
.with_function("Gas Metering", |b| {
let compiler = get_compiler(0, false);
let gas_wasm_binary = wat2wasm(WAT_GAS).unwrap();
let gas_module = compile_with(&gas_wasm_binary, &compiler).unwrap();
let gas_import_object = imports! {
"env" => {
"gas" => Func::new(gas),
},
};
let mut gas_instance = gas_module.instantiate(&gas_import_object).unwrap();
let gas_add_to: Func<(i32, i32), i32> = gas_instance.func("add_to").unwrap();
b.iter(|| black_box(gas_add_to.call(100, 4)))
})
.with_function("Built-in Metering", |b| {
let metering_compiler = get_compiler(std::u64::MAX, true);
let wasm_binary = wat2wasm(WAT).unwrap();
let metering_module = compile_with(&wasm_binary, &metering_compiler).unwrap();
let metering_import_object = imports! {};
let mut metering_instance = metering_module
.instantiate(&metering_import_object)
.unwrap();
metering::set_points_used(&mut metering_instance, 0u64);
let metering_add_to: Func<(i32, i32), i32> = metering_instance.func("add_to").unwrap();
b.iter(|| black_box(metering_add_to.call(100, 4)))
}),
);
}
criterion_group!(benches, bench_metering);
criterion_main!(benches);

View File

@ -1,3 +1,4 @@
#![deny(unused_imports, unused_variables, unused_unsafe, unreachable_patterns)] #![deny(unused_imports, unused_variables, unused_unsafe, unreachable_patterns)]
pub mod call_trace; pub mod call_trace;
pub mod metering;

View File

@ -0,0 +1,291 @@
use wasmer_runtime_core::{
codegen::{Event, EventSink, FunctionMiddleware, InternalEvent},
module::ModuleInfo,
vm::{Ctx, InternalField},
wasmparser::{Operator, Type as WpType},
Instance,
};
static INTERNAL_FIELD: InternalField = InternalField::allocate();
/// Metering is a compiler middleware that calculates the cost of WebAssembly instructions at compile
/// time and will count the cost of executed instructions at runtime. Within the Metering functionality,
/// this instruction cost is called `points`.
///
/// The Metering struct takes a `limit` parameter which is the maximum number of points which can be
/// used by an instance during a function call. If this limit is exceeded, the function call will
/// trap. Each instance has a `points_used` field which can be used to track points used during
/// a function call and should be set back to zero after a function call.
///
/// Each compiler backend with Metering enabled should produce the same cost used at runtime for
/// the same function calls so we can say that the metering is deterministic.
///
pub struct Metering {
limit: u64,
current_block: u64,
}
impl Metering {
pub fn new(limit: u64) -> Metering {
Metering {
limit,
current_block: 0,
}
}
}
#[derive(Copy, Clone, Debug)]
pub struct ExecutionLimitExceededError;
impl FunctionMiddleware for Metering {
type Error = String;
fn feed_event<'a, 'b: 'a>(
&mut self,
op: Event<'a, 'b>,
_module_info: &ModuleInfo,
sink: &mut EventSink<'a, 'b>,
) -> Result<(), Self::Error> {
match op {
Event::Internal(InternalEvent::FunctionBegin(_)) => {
self.current_block = 0;
}
Event::Wasm(&ref op) | Event::WasmOwned(ref op) => {
self.current_block += 1;
match *op {
Operator::Loop { .. }
| Operator::Block { .. }
| Operator::End
| Operator::If { .. }
| Operator::Else
| Operator::Unreachable
| Operator::Br { .. }
| Operator::BrTable { .. }
| Operator::BrIf { .. }
| Operator::Call { .. }
| Operator::CallIndirect { .. }
| Operator::Return => {
sink.push(Event::Internal(InternalEvent::GetInternal(
INTERNAL_FIELD.index() as _,
)));
sink.push(Event::WasmOwned(Operator::I64Const {
value: self.current_block as i64,
}));
sink.push(Event::WasmOwned(Operator::I64Add));
sink.push(Event::Internal(InternalEvent::SetInternal(
INTERNAL_FIELD.index() as _,
)));
self.current_block = 0;
}
_ => {}
}
match *op {
Operator::Br { .. }
| Operator::BrTable { .. }
| Operator::BrIf { .. }
| Operator::Call { .. }
| Operator::CallIndirect { .. } => {
sink.push(Event::Internal(InternalEvent::GetInternal(
INTERNAL_FIELD.index() as _,
)));
sink.push(Event::WasmOwned(Operator::I64Const {
value: self.limit as i64,
}));
sink.push(Event::WasmOwned(Operator::I64GeU));
sink.push(Event::WasmOwned(Operator::If {
ty: WpType::EmptyBlockType,
}));
sink.push(Event::Internal(InternalEvent::Breakpoint(Box::new(
move |ctx| unsafe {
(ctx.throw)(Box::new(ExecutionLimitExceededError));
},
))));
sink.push(Event::WasmOwned(Operator::End));
}
_ => {}
}
}
_ => {}
}
sink.push(op);
Ok(())
}
}
/// Returns the number of points used by an Instance.
pub fn get_points_used(instance: &Instance) -> u64 {
instance.get_internal(&INTERNAL_FIELD)
}
/// Sets the number of points used by an Instance.
pub fn set_points_used(instance: &mut Instance, value: u64) {
instance.set_internal(&INTERNAL_FIELD, value);
}
/// Returns the number of points used in a Ctx.
pub fn get_points_used_ctx(ctx: &Ctx) -> u64 {
ctx.get_internal(&INTERNAL_FIELD)
}
/// Sets the number of points used in a Ctx.
pub fn set_points_used_ctx(ctx: &mut Ctx, value: u64) {
ctx.set_internal(&INTERNAL_FIELD, value);
}
#[cfg(all(test, feature = "singlepass"))]
mod tests {
use super::*;
use wabt::wat2wasm;
use wasmer_runtime_core::{backend::Compiler, compile_with, imports, Func};
#[cfg(feature = "llvm")]
fn get_compiler(limit: u64) -> impl Compiler {
use wasmer_llvm_backend::code::LLVMModuleCodeGenerator;
use wasmer_runtime_core::codegen::{MiddlewareChain, StreamingCompiler};
let c: StreamingCompiler<LLVMModuleCodeGenerator, _, _, _, _> =
StreamingCompiler::new(move || {
let mut chain = MiddlewareChain::new();
chain.push(Metering::new(limit));
chain
});
c
}
#[cfg(feature = "singlepass")]
fn get_compiler(limit: u64) -> impl Compiler {
use wasmer_runtime_core::codegen::{MiddlewareChain, StreamingCompiler};
use wasmer_singlepass_backend::ModuleCodeGenerator as SinglePassMCG;
let c: StreamingCompiler<SinglePassMCG, _, _, _, _> = StreamingCompiler::new(move || {
let mut chain = MiddlewareChain::new();
chain.push(Metering::new(limit));
chain
});
c
}
#[cfg(not(any(feature = "llvm", feature = "clif", feature = "singlepass")))]
fn get_compiler(_limit: u64) -> impl Compiler {
panic!("compiler not specified, activate a compiler via features");
use wasmer_clif_backend::CraneliftCompiler;
CraneliftCompiler::new()
}
#[cfg(feature = "clif")]
fn get_compiler(_limit: u64) -> impl Compiler {
panic!("cranelift does not implement metering");
use wasmer_clif_backend::CraneliftCompiler;
CraneliftCompiler::new()
}
// Assemblyscript
// export function add_to(x: i32, y: i32): i32 {
// for(var i = 0; i < x; i++){
// if(i % 1 == 0){
// y += i;
// } else {
// y *= i
// }
// }
// return y;
// }
static WAT: &'static str = r#"
(module
(type $t0 (func (param i32 i32) (result i32)))
(type $t1 (func))
(func $add_to (export "add_to") (type $t0) (param $p0 i32) (param $p1 i32) (result i32)
(local $l0 i32)
block $B0
i32.const 0
set_local $l0
loop $L1
get_local $l0
get_local $p0
i32.lt_s
i32.eqz
br_if $B0
get_local $l0
i32.const 1
i32.rem_s
i32.const 0
i32.eq
if $I2
get_local $p1
get_local $l0
i32.add
set_local $p1
else
get_local $p1
get_local $l0
i32.mul
set_local $p1
end
get_local $l0
i32.const 1
i32.add
set_local $l0
br $L1
unreachable
end
unreachable
end
get_local $p1)
(func $f1 (type $t1))
(table $table (export "table") 1 anyfunc)
(memory $memory (export "memory") 0)
(global $g0 i32 (i32.const 8))
(elem (i32.const 0) $f1))
"#;
#[test]
fn test_points_reduced_after_call() {
let wasm_binary = wat2wasm(WAT).unwrap();
let limit = 100u64;
let module = compile_with(&wasm_binary, &get_compiler(limit)).unwrap();
let import_object = imports! {};
let mut instance = module.instantiate(&import_object).unwrap();
set_points_used(&mut instance, 0u64);
let add_to: Func<(i32, i32), i32> = instance.func("add_to").unwrap();
let value = add_to.call(3, 4).unwrap();
// verify it returns the correct value
assert_eq!(value, 7);
// verify is uses the correct number of points
assert_eq!(get_points_used(&instance), 74);
}
#[test]
fn test_traps_after_costly_call() {
use wasmer_runtime_core::error::RuntimeError;
let wasm_binary = wat2wasm(WAT).unwrap();
let limit = 100u64;
let module = compile_with(&wasm_binary, &get_compiler(limit)).unwrap();
let import_object = imports! {};
let mut instance = module.instantiate(&import_object).unwrap();
set_points_used(&mut instance, 0u64);
let add_to: Func<(i32, i32), i32> = instance.func("add_to").unwrap();
let result = add_to.call(10_000_000, 4);
let err = result.unwrap_err();
match err {
RuntimeError::Error { data } => {
assert!(data.downcast_ref::<ExecutionLimitExceededError>().is_some());
}
_ => unreachable!(),
}
// verify is uses the correct number of points
assert_eq!(get_points_used(&instance), 109); // Used points will be slightly more than `limit` because of the way we do gas checking.
}
}

View File

@ -15,7 +15,17 @@ use crate::{
}, },
vm, vm,
}; };
use std::slice; use std::{fmt::Debug, slice};
pub const INTERNALS_SIZE: usize = 256;
pub(crate) struct Internals(pub(crate) [u64; INTERNALS_SIZE]);
impl Debug for Internals {
fn fmt(&self, formatter: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
write!(formatter, "Internals({:?})", &self.0[..])
}
}
/// The `LocalBacking` "owns" the memory used by all the local resources of an Instance. /// The `LocalBacking` "owns" the memory used by all the local resources of an Instance.
/// That is, local memories, tables, and globals (as well as some additional /// That is, local memories, tables, and globals (as well as some additional
@ -40,6 +50,8 @@ pub struct LocalBacking {
/// as well) are subject to change. /// as well) are subject to change.
pub(crate) dynamic_sigindices: BoxedMap<SigIndex, vm::SigId>, pub(crate) dynamic_sigindices: BoxedMap<SigIndex, vm::SigId>,
pub(crate) local_functions: BoxedMap<LocalFuncIndex, *const vm::Func>, pub(crate) local_functions: BoxedMap<LocalFuncIndex, *const vm::Func>,
pub(crate) internals: Internals,
} }
impl LocalBacking { impl LocalBacking {
@ -66,6 +78,8 @@ impl LocalBacking {
dynamic_sigindices, dynamic_sigindices,
local_functions, local_functions,
internals: Internals([0; INTERNALS_SIZE]),
} }
} }

View File

@ -8,6 +8,7 @@ use crate::{
types::{FuncIndex, FuncSig, SigIndex}, types::{FuncIndex, FuncSig, SigIndex},
}; };
use smallvec::SmallVec; use smallvec::SmallVec;
use std::any::Any;
use std::fmt; use std::fmt;
use std::fmt::Debug; use std::fmt::Debug;
use std::marker::PhantomData; use std::marker::PhantomData;
@ -19,6 +20,7 @@ use wasmparser::{Operator, Type as WpType};
pub enum Event<'a, 'b> { pub enum Event<'a, 'b> {
Internal(InternalEvent), Internal(InternalEvent),
Wasm(&'b Operator<'a>), Wasm(&'b Operator<'a>),
WasmOwned(Operator<'a>),
} }
pub enum InternalEvent { pub enum InternalEvent {
@ -41,7 +43,9 @@ impl fmt::Debug for InternalEvent {
} }
} }
pub struct BkptInfo {} pub struct BkptInfo {
pub throw: unsafe fn(Box<dyn Any>) -> !,
}
pub trait ModuleCodeGenerator<FCG: FunctionCodeGenerator<E>, RM: RunnableModule, E: Debug> { pub trait ModuleCodeGenerator<FCG: FunctionCodeGenerator<E>, RM: RunnableModule, E: Debug> {
/// Creates a new module code generator. /// Creates a new module code generator.

View File

@ -13,7 +13,7 @@ use crate::{
table::Table, table::Table,
typed_func::{Func, Wasm, WasmTrapInfo, WasmTypeList}, typed_func::{Func, Wasm, WasmTrapInfo, WasmTypeList},
types::{FuncIndex, FuncSig, GlobalIndex, LocalOrImport, MemoryIndex, TableIndex, Type, Value}, types::{FuncIndex, FuncSig, GlobalIndex, LocalOrImport, MemoryIndex, TableIndex, Type, Value},
vm, vm::{self, InternalField},
}; };
use smallvec::{smallvec, SmallVec}; use smallvec::{smallvec, SmallVec};
use std::{mem, ptr::NonNull, sync::Arc}; use std::{mem, ptr::NonNull, sync::Arc};
@ -372,6 +372,14 @@ impl Instance {
pub fn module(&self) -> Module { pub fn module(&self) -> Module {
Module::new(Arc::clone(&self.module)) Module::new(Arc::clone(&self.module))
} }
pub fn get_internal(&self, field: &InternalField) -> u64 {
self.inner.backing.internals.0[field.index()]
}
pub fn set_internal(&mut self, field: &InternalField, value: u64) {
self.inner.backing.internals.0[field.index()] = value;
}
} }
impl InstanceInner { impl InstanceInner {

View File

@ -57,6 +57,8 @@ pub use self::module::Module;
pub use self::typed_func::Func; pub use self::typed_func::Func;
use std::sync::Arc; use std::sync::Arc;
pub use wasmparser;
use self::cache::{Artifact, Error as CacheError}; use self::cache::{Artifact, Error as CacheError};
pub mod prelude { pub mod prelude {

View File

@ -1,4 +1,4 @@
pub use crate::backing::{ImportBacking, LocalBacking}; pub use crate::backing::{ImportBacking, LocalBacking, INTERNALS_SIZE};
use crate::{ use crate::{
memory::{Memory, MemoryType}, memory::{Memory, MemoryType},
module::{ModuleInfo, ModuleInner}, module::{ModuleInfo, ModuleInner},
@ -6,7 +6,13 @@ use crate::{
types::{LocalOrImport, MemoryIndex}, types::{LocalOrImport, MemoryIndex},
vmcalls, vmcalls,
}; };
use std::{ffi::c_void, mem, ptr}; use std::{
cell::UnsafeCell,
ffi::c_void,
mem, ptr,
sync::atomic::{AtomicUsize, Ordering},
sync::Once,
};
use hashbrown::HashMap; use hashbrown::HashMap;
@ -92,6 +98,43 @@ pub struct InternalCtx {
pub memory_base: *mut u8, pub memory_base: *mut u8,
pub memory_bound: usize, pub memory_bound: usize,
pub internals: *mut [u64; INTERNALS_SIZE], // TODO: Make this dynamic?
}
static INTERNAL_FIELDS: AtomicUsize = AtomicUsize::new(0);
pub struct InternalField {
init: Once,
inner: UnsafeCell<usize>,
}
unsafe impl Send for InternalField {}
unsafe impl Sync for InternalField {}
impl InternalField {
pub const fn allocate() -> InternalField {
InternalField {
init: Once::new(),
inner: UnsafeCell::new(::std::usize::MAX),
}
}
pub fn index(&self) -> usize {
let inner: *mut usize = self.inner.get();
self.init.call_once(|| {
let idx = INTERNAL_FIELDS.fetch_add(1, Ordering::SeqCst);
if idx >= INTERNALS_SIZE {
INTERNAL_FIELDS.fetch_sub(1, Ordering::SeqCst);
panic!("at most {} internal fields are supported", INTERNALS_SIZE);
} else {
unsafe {
*inner = idx;
}
}
});
unsafe { *inner }
}
} }
#[repr(C)] #[repr(C)]
@ -200,6 +243,8 @@ impl Ctx {
memory_base: mem_base, memory_base: mem_base,
memory_bound: mem_bound, memory_bound: mem_bound,
internals: &mut local_backing.internals.0,
}, },
local_functions: local_backing.local_functions.as_ptr(), local_functions: local_backing.local_functions.as_ptr(),
@ -249,6 +294,8 @@ impl Ctx {
memory_base: mem_base, memory_base: mem_base,
memory_bound: mem_bound, memory_bound: mem_bound,
internals: &mut local_backing.internals.0,
}, },
local_functions: local_backing.local_functions.as_ptr(), local_functions: local_backing.local_functions.as_ptr(),
@ -303,6 +350,18 @@ impl Ctx {
pub fn dynamic_sigindice_count(&self) -> usize { pub fn dynamic_sigindice_count(&self) -> usize {
unsafe { (*self.local_backing).dynamic_sigindices.len() } unsafe { (*self.local_backing).dynamic_sigindices.len() }
} }
/// Returns the value of the specified internal field.
pub fn get_internal(&self, field: &InternalField) -> u64 {
unsafe { (*self.internal.internals)[field.index()] }
}
/// Writes the value to the specified internal field.
pub fn set_internal(&mut self, field: &InternalField, value: u64) {
unsafe {
(*self.internal.internals)[field.index()] = value;
}
}
} }
#[doc(hidden)] #[doc(hidden)]
@ -356,9 +415,13 @@ impl Ctx {
11 * (mem::size_of::<usize>() as u8) 11 * (mem::size_of::<usize>() as u8)
} }
pub fn offset_local_functions() -> u8 { pub fn offset_internals() -> u8 {
12 * (mem::size_of::<usize>() as u8) 12 * (mem::size_of::<usize>() as u8)
} }
pub fn offset_local_functions() -> u8 {
13 * (mem::size_of::<usize>() as u8)
}
} }
enum InnerFunc {} enum InnerFunc {}
@ -572,6 +635,11 @@ mod vm_offset_tests {
offset_of!(InternalCtx => memory_bound).get_byte_offset(), offset_of!(InternalCtx => memory_bound).get_byte_offset(),
); );
assert_eq!(
Ctx::offset_internals() as usize,
offset_of!(InternalCtx => internals).get_byte_offset(),
);
assert_eq!( assert_eq!(
Ctx::offset_local_functions() as usize, Ctx::offset_local_functions() as usize,
offset_of!(Ctx => local_functions).get_byte_offset(), offset_of!(Ctx => local_functions).get_byte_offset(),
@ -684,6 +752,8 @@ mod vm_ctx_tests {
dynamic_sigindices: Map::new().into_boxed_map(), dynamic_sigindices: Map::new().into_boxed_map(),
local_functions: Map::new().into_boxed_map(), local_functions: Map::new().into_boxed_map(),
internals: crate::backing::Internals([0; crate::backing::INTERNALS_SIZE]),
}; };
let mut import_backing = ImportBacking { let mut import_backing = ImportBacking {
memories: Map::new().into_boxed_map(), memories: Map::new().into_boxed_map(),

View File

@ -27,7 +27,7 @@ use wasmer_runtime_core::{
FuncIndex, FuncSig, GlobalIndex, LocalFuncIndex, LocalOrImport, MemoryIndex, SigIndex, FuncIndex, FuncSig, GlobalIndex, LocalFuncIndex, LocalOrImport, MemoryIndex, SigIndex,
TableIndex, Type, TableIndex, Type,
}, },
vm::{self, LocalGlobal, LocalTable}, vm::{self, LocalGlobal, LocalTable, INTERNALS_SIZE},
}; };
use wasmparser::{Operator, Type as WpType}; use wasmparser::{Operator, Type as WpType};
@ -1504,6 +1504,7 @@ impl FunctionCodeGenerator<CodegenError> for X64FunctionCode {
let op = match ev { let op = match ev {
Event::Wasm(x) => x, Event::Wasm(x) => x,
Event::WasmOwned(ref x) => x,
Event::Internal(x) => { Event::Internal(x) => {
match x { match x {
InternalEvent::Breakpoint(callback) => { InternalEvent::Breakpoint(callback) => {
@ -1513,8 +1514,71 @@ impl FunctionCodeGenerator<CodegenError> for X64FunctionCode {
.unwrap() .unwrap()
.insert(a.get_offset(), callback); .insert(a.get_offset(), callback);
} }
InternalEvent::FunctionBegin(_) | InternalEvent::FunctionEnd => {} InternalEvent::FunctionBegin(_) | InternalEvent::FunctionEnd => {},
_ => unimplemented!(), InternalEvent::GetInternal(idx) => {
let idx = idx as usize;
assert!(idx < INTERNALS_SIZE);
let tmp = self.machine.acquire_temp_gpr().unwrap();
// Load `internals` pointer.
a.emit_mov(
Size::S64,
Location::Memory(
Machine::get_vmctx_reg(),
vm::Ctx::offset_internals() as i32,
),
Location::GPR(tmp),
);
let loc = self.machine.acquire_locations(
a,
&[WpType::I64],
false,
)[0];
self.value_stack.push((loc, LocalOrTemp::Temp));
// Move internal into the result location.
Self::emit_relaxed_binop(
a,
&mut self.machine,
Assembler::emit_mov,
Size::S64,
Location::Memory(tmp, (idx * 8) as i32),
loc,
);
self.machine.release_temp_gpr(tmp);
}
InternalEvent::SetInternal(idx) => {
let idx = idx as usize;
assert!(idx < INTERNALS_SIZE);
let tmp = self.machine.acquire_temp_gpr().unwrap();
// Load `internals` pointer.
a.emit_mov(
Size::S64,
Location::Memory(
Machine::get_vmctx_reg(),
vm::Ctx::offset_internals() as i32,
),
Location::GPR(tmp),
);
let loc = get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap());
// Move internal into storage.
Self::emit_relaxed_binop(
a,
&mut self.machine,
Assembler::emit_mov,
Size::S64,
loc,
Location::Memory(tmp, (idx * 8) as i32),
);
self.machine.release_temp_gpr(tmp);
}
//_ => unimplemented!(),
} }
return Ok(()); return Ok(());
} }
@ -2644,7 +2708,14 @@ impl FunctionCodeGenerator<CodegenError> for X64FunctionCode {
let tmp_out = self.machine.acquire_temp_gpr().unwrap(); let tmp_out = self.machine.acquire_temp_gpr().unwrap();
let tmp_in = self.machine.acquire_temp_xmm().unwrap(); let tmp_in = self.machine.acquire_temp_xmm().unwrap();
a.emit_mov(Size::S32, loc, Location::XMM(tmp_in)); Self::emit_relaxed_binop(
a,
&mut self.machine,
Assembler::emit_mov,
Size::S32,
loc,
Location::XMM(tmp_in),
);
Self::emit_f32_int_conv_check(a, &mut self.machine, tmp_in, -1.0, 4294967296.0); Self::emit_f32_int_conv_check(a, &mut self.machine, tmp_in, -1.0, 4294967296.0);
a.emit_cvttss2si_64(XMMOrMemory::XMM(tmp_in), tmp_out); a.emit_cvttss2si_64(XMMOrMemory::XMM(tmp_in), tmp_out);
@ -2662,7 +2733,14 @@ impl FunctionCodeGenerator<CodegenError> for X64FunctionCode {
let tmp_out = self.machine.acquire_temp_gpr().unwrap(); let tmp_out = self.machine.acquire_temp_gpr().unwrap();
let tmp_in = self.machine.acquire_temp_xmm().unwrap(); let tmp_in = self.machine.acquire_temp_xmm().unwrap();
a.emit_mov(Size::S32, loc, Location::XMM(tmp_in)); Self::emit_relaxed_binop(
a,
&mut self.machine,
Assembler::emit_mov,
Size::S32,
loc,
Location::XMM(tmp_in),
);
Self::emit_f32_int_conv_check( Self::emit_f32_int_conv_check(
a, a,
&mut self.machine, &mut self.machine,
@ -2686,7 +2764,14 @@ impl FunctionCodeGenerator<CodegenError> for X64FunctionCode {
let tmp_out = self.machine.acquire_temp_gpr().unwrap(); let tmp_out = self.machine.acquire_temp_gpr().unwrap();
let tmp_in = self.machine.acquire_temp_xmm().unwrap(); let tmp_in = self.machine.acquire_temp_xmm().unwrap();
a.emit_mov(Size::S32, loc, Location::XMM(tmp_in)); Self::emit_relaxed_binop(
a,
&mut self.machine,
Assembler::emit_mov,
Size::S32,
loc,
Location::XMM(tmp_in),
);
Self::emit_f32_int_conv_check( Self::emit_f32_int_conv_check(
a, a,
&mut self.machine, &mut self.machine,
@ -2724,7 +2809,14 @@ impl FunctionCodeGenerator<CodegenError> for X64FunctionCode {
let tmp_out = self.machine.acquire_temp_gpr().unwrap(); let tmp_out = self.machine.acquire_temp_gpr().unwrap();
let tmp_in = self.machine.acquire_temp_xmm().unwrap(); // xmm2 let tmp_in = self.machine.acquire_temp_xmm().unwrap(); // xmm2
a.emit_mov(Size::S32, loc, Location::XMM(tmp_in)); Self::emit_relaxed_binop(
a,
&mut self.machine,
Assembler::emit_mov,
Size::S32,
loc,
Location::XMM(tmp_in),
);
Self::emit_f32_int_conv_check( Self::emit_f32_int_conv_check(
a, a,
&mut self.machine, &mut self.machine,
@ -2772,7 +2864,14 @@ impl FunctionCodeGenerator<CodegenError> for X64FunctionCode {
let tmp_out = self.machine.acquire_temp_gpr().unwrap(); let tmp_out = self.machine.acquire_temp_gpr().unwrap();
let tmp_in = self.machine.acquire_temp_xmm().unwrap(); let tmp_in = self.machine.acquire_temp_xmm().unwrap();
a.emit_mov(Size::S64, loc, Location::XMM(tmp_in)); Self::emit_relaxed_binop(
a,
&mut self.machine,
Assembler::emit_mov,
Size::S64,
loc,
Location::XMM(tmp_in),
);
Self::emit_f64_int_conv_check(a, &mut self.machine, tmp_in, -1.0, 4294967296.0); Self::emit_f64_int_conv_check(a, &mut self.machine, tmp_in, -1.0, 4294967296.0);
a.emit_cvttsd2si_64(XMMOrMemory::XMM(tmp_in), tmp_out); a.emit_cvttsd2si_64(XMMOrMemory::XMM(tmp_in), tmp_out);
@ -2826,7 +2925,14 @@ impl FunctionCodeGenerator<CodegenError> for X64FunctionCode {
let tmp_out = self.machine.acquire_temp_gpr().unwrap(); let tmp_out = self.machine.acquire_temp_gpr().unwrap();
let tmp_in = self.machine.acquire_temp_xmm().unwrap(); let tmp_in = self.machine.acquire_temp_xmm().unwrap();
a.emit_mov(Size::S64, loc, Location::XMM(tmp_in)); Self::emit_relaxed_binop(
a,
&mut self.machine,
Assembler::emit_mov,
Size::S64,
loc,
Location::XMM(tmp_in),
);
Self::emit_f64_int_conv_check( Self::emit_f64_int_conv_check(
a, a,
&mut self.machine, &mut self.machine,
@ -2850,7 +2956,14 @@ impl FunctionCodeGenerator<CodegenError> for X64FunctionCode {
let tmp_out = self.machine.acquire_temp_gpr().unwrap(); let tmp_out = self.machine.acquire_temp_gpr().unwrap();
let tmp_in = self.machine.acquire_temp_xmm().unwrap(); // xmm2 let tmp_in = self.machine.acquire_temp_xmm().unwrap(); // xmm2
a.emit_mov(Size::S64, loc, Location::XMM(tmp_in)); Self::emit_relaxed_binop(
a,
&mut self.machine,
Assembler::emit_mov,
Size::S64,
loc,
Location::XMM(tmp_in),
);
Self::emit_f64_int_conv_check( Self::emit_f64_int_conv_check(
a, a,
&mut self.machine, &mut self.machine,

View File

@ -257,6 +257,7 @@ impl Machine {
pub fn release_locations_keep_state<E: Emitter>(&self, assembler: &mut E, locs: &[Location]) { pub fn release_locations_keep_state<E: Emitter>(&self, assembler: &mut E, locs: &[Location]) {
let mut delta_stack_offset: usize = 0; let mut delta_stack_offset: usize = 0;
let mut stack_offset = self.stack_offset.0;
for loc in locs.iter().rev() { for loc in locs.iter().rev() {
match *loc { match *loc {
@ -265,9 +266,10 @@ impl Machine {
unreachable!(); unreachable!();
} }
let offset = (-x) as usize; let offset = (-x) as usize;
if offset != self.stack_offset.0 { if offset != stack_offset {
unreachable!(); unreachable!();
} }
stack_offset -= 8;
delta_stack_offset += 8; delta_stack_offset += 8;
} }
_ => {} _ => {}
@ -417,3 +419,19 @@ impl Machine {
} }
} }
} }
#[cfg(test)]
mod test {
use super::*;
use dynasmrt::x64::Assembler;
#[test]
fn test_release_locations_keep_state_nopanic() {
let mut machine = Machine::new();
let mut assembler = Assembler::new().unwrap();
let locs = machine.acquire_locations(&mut assembler, &[WpType::I32; 10], false);
machine.release_locations_keep_state(&mut assembler, &locs);
}
}

View File

@ -35,7 +35,7 @@ extern "C" fn signal_trap_handler(
let bkpt_map = BKPT_MAP.with(|x| x.borrow().last().map(|x| x.clone())); let bkpt_map = BKPT_MAP.with(|x| x.borrow().last().map(|x| x.clone()));
if let Some(bkpt_map) = bkpt_map { if let Some(bkpt_map) = bkpt_map {
if let Some(ref x) = bkpt_map.get(&(ip as usize)) { if let Some(ref x) = bkpt_map.get(&(ip as usize)) {
(x)(BkptInfo {}); (x)(BkptInfo { throw: throw });
return; return;
} }
} }
@ -128,6 +128,15 @@ pub fn call_protected<T>(f: impl FnOnce() -> T) -> Result<T, CallProtError> {
} }
} }
pub unsafe fn throw(payload: Box<dyn Any>) -> ! {
let jmp_buf = SETJMP_BUFFER.with(|buf| buf.get());
if *jmp_buf == [0; SETJMP_BUFFER_LEN] {
::std::process::abort();
}
TRAP_EARLY_DATA.with(|cell| cell.replace(Some(payload)));
longjmp(jmp_buf as *mut ::nix::libc::c_void, 0xffff);
}
/// Unwinds to last protected_call. /// Unwinds to last protected_call.
pub unsafe fn do_unwind(signum: i32, siginfo: *const c_void, ucontext: *const c_void) -> ! { pub unsafe fn do_unwind(signum: i32, siginfo: *const c_void, ucontext: *const c_void) -> ! {
// Since do_unwind is only expected to get called from WebAssembly code which doesn't hold any host resources (locks etc.) // Since do_unwind is only expected to get called from WebAssembly code which doesn't hold any host resources (locks etc.)

View File

@ -16,19 +16,21 @@ use structopt::StructOpt;
use wasmer::*; use wasmer::*;
use wasmer_clif_backend::CraneliftCompiler; use wasmer_clif_backend::CraneliftCompiler;
#[cfg(feature = "backend:llvm")] #[cfg(feature = "backend:llvm")]
use wasmer_llvm_backend::LLVMCompiler; use wasmer_llvm_backend::code::LLVMModuleCodeGenerator;
use wasmer_runtime::{ use wasmer_runtime::{
cache::{Cache as BaseCache, FileSystemCache, WasmHash, WASMER_VERSION_HASH}, cache::{Cache as BaseCache, FileSystemCache, WasmHash, WASMER_VERSION_HASH},
error::RuntimeError, error::RuntimeError,
Func, Value, Func, Value,
}; };
#[cfg(feature = "backend:singlepass")]
use wasmer_runtime_core::codegen::{MiddlewareChain, StreamingCompiler};
use wasmer_runtime_core::{ use wasmer_runtime_core::{
self, self,
backend::{Compiler, CompilerConfig, MemoryBoundCheckMode}, backend::{Compiler, CompilerConfig, MemoryBoundCheckMode},
loader::{Instance as LoadedInstance, LocalLoader}, loader::{Instance as LoadedInstance, LocalLoader},
}; };
#[cfg(feature = "backend:singlepass")] #[cfg(feature = "backend:singlepass")]
use wasmer_singlepass_backend::SinglePassCompiler; use wasmer_singlepass_backend::ModuleCodeGenerator as SinglePassMCG;
#[cfg(feature = "wasi")] #[cfg(feature = "wasi")]
use wasmer_wasi; use wasmer_wasi;
@ -118,6 +120,9 @@ struct Run {
/// Application arguments /// Application arguments
#[structopt(name = "--", raw(multiple = "true"))] #[structopt(name = "--", raw(multiple = "true"))]
args: Vec<String>, args: Vec<String>,
#[structopt(long = "inst-limit")]
instruction_limit: Option<u64>,
} }
#[allow(dead_code)] #[allow(dead_code)]
@ -339,12 +344,33 @@ fn execute_wasm(options: &Run) -> Result<(), String> {
let compiler: Box<dyn Compiler> = match options.backend { let compiler: Box<dyn Compiler> = match options.backend {
#[cfg(feature = "backend:singlepass")] #[cfg(feature = "backend:singlepass")]
Backend::Singlepass => Box::new(SinglePassCompiler::new()), Backend::Singlepass => {
let c: StreamingCompiler<SinglePassMCG, _, _, _, _> = StreamingCompiler::new(|| {
let mut chain = MiddlewareChain::new();
use wasmer_middleware_common::metering::Metering;
if let Some(limit) = options.instruction_limit {
chain.push(Metering::new(limit));
}
chain
});
Box::new(c)
}
#[cfg(not(feature = "backend:singlepass"))] #[cfg(not(feature = "backend:singlepass"))]
Backend::Singlepass => return Err("The singlepass backend is not enabled".to_string()), Backend::Singlepass => return Err("The singlepass backend is not enabled".to_string()),
Backend::Cranelift => Box::new(CraneliftCompiler::new()), Backend::Cranelift => Box::new(CraneliftCompiler::new()),
#[cfg(feature = "backend:llvm")] #[cfg(feature = "backend:llvm")]
Backend::LLVM => Box::new(LLVMCompiler::new()), Backend::LLVM => {
let c: StreamingCompiler<LLVMModuleCodeGenerator, _, _, _, _> =
StreamingCompiler::new(|| {
let mut chain = MiddlewareChain::new();
use wasmer_middleware_common::metering::Metering;
if let Some(limit) = options.instruction_limit {
chain.push(Metering::new(limit));
}
chain
});
Box::new(c)
}
#[cfg(not(feature = "backend:llvm"))] #[cfg(not(feature = "backend:llvm"))]
Backend::LLVM => return Err("the llvm backend is not enabled".to_string()), Backend::LLVM => return Err("the llvm backend is not enabled".to_string()),
}; };