mirror of
https://github.com/fluencelabs/wasmer
synced 2025-06-23 05:31:32 +00:00
Merge branch 'master' into command/pyodide
This commit is contained in:
@ -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
5
Cargo.lock
generated
@ -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]]
|
||||||
|
5
Makefile
5
Makefile
@ -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)
|
||||||
|
55
examples/single_pass_tests/pr-486.wat
Normal file
55
examples/single_pass_tests/pr-486.wat
Normal 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)
|
||||||
|
)
|
||||||
|
)
|
@ -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(());
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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 _;
|
||||||
|
@ -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;
|
||||||
|
@ -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(),
|
||||||
|
@ -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
|
230
lib/middleware-common/benches/metering_benchmark.rs
Normal file
230
lib/middleware-common/benches/metering_benchmark.rs
Normal 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);
|
@ -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;
|
||||||
|
291
lib/middleware-common/src/metering.rs
Normal file
291
lib/middleware-common/src/metering.rs
Normal 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.
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -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]),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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.
|
||||||
|
@ -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 {
|
||||||
|
@ -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 {
|
||||||
|
@ -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(),
|
||||||
|
@ -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,
|
||||||
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
@ -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.)
|
||||||
|
@ -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()),
|
||||||
};
|
};
|
||||||
|
Reference in New Issue
Block a user