mirror of
https://github.com/fluencelabs/wasmer
synced 2024-12-12 22:05:33 +00:00
Merge branch 'master' into command/pyodide
This commit is contained in:
commit
ff21d9e95f
@ -6,6 +6,7 @@ Blocks of changes will separated by version increments.
|
||||
|
||||
## **[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
|
||||
- [#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
|
||||
|
5
Cargo.lock
generated
5
Cargo.lock
generated
@ -1460,7 +1460,12 @@ dependencies = [
|
||||
name = "wasmer-middleware-common"
|
||||
version = "0.4.2"
|
||||
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-singlepass-backend 0.4.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
5
Makefile
5
Makefile
@ -48,18 +48,21 @@ do-install:
|
||||
|
||||
test:
|
||||
# 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 --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;
|
||||
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/middleware-common/Cargo.toml --features llvm
|
||||
cargo build -p wasmer-runtime-c-api
|
||||
cargo test -p wasmer-runtime-c-api -- --nocapture
|
||||
|
||||
test-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/middleware-common/Cargo.toml --features singlepass
|
||||
|
||||
test-emscripten-llvm:
|
||||
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> {
|
||||
let op = match event {
|
||||
Event::Wasm(x) => x,
|
||||
Event::WasmOwned(ref x) => x,
|
||||
Event::Internal(_x) => {
|
||||
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 {
|
||||
debug!("emscripten::___syscall168");
|
||||
debug!("emscripten::___syscall168 - stub");
|
||||
-1
|
||||
}
|
||||
|
||||
pub fn ___syscall191(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 {
|
||||
debug!("emscripten::___syscall191 - stub");
|
||||
-1
|
||||
pub fn ___syscall191(ctx: &mut Ctx, _which: i32, mut varargs: VarArgs) -> i32 {
|
||||
let _resource: i32 = varargs.get(ctx);
|
||||
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 {
|
||||
@ -753,19 +765,23 @@ pub fn ___syscall340(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_in
|
||||
debug!("emscripten::___syscall340 (prlimit64), {}", _which);
|
||||
// NOTE: Doesn't really matter. Wasm modules cannot exceed WASM_PAGE_SIZE anyway.
|
||||
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 old_limit: u32 = varargs.get(ctx);
|
||||
|
||||
let val = match resource {
|
||||
// RLIMIT_NOFILE
|
||||
7 => 1024,
|
||||
_ => -1, // RLIM_INFINITY
|
||||
};
|
||||
|
||||
if old_limit != 0 {
|
||||
// just report no limits
|
||||
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) };
|
||||
|
||||
LittleEndian::write_i32(&mut buf[..], -1); // RLIM_INFINITY
|
||||
LittleEndian::write_i32(&mut buf[4..], -1); // RLIM_INFINITY
|
||||
LittleEndian::write_i32(&mut buf[8..], -1); // RLIM_INFINITY
|
||||
LittleEndian::write_i32(&mut buf[12..], -1); // RLIM_INFINITY
|
||||
LittleEndian::write_i64(&mut buf[..], val);
|
||||
LittleEndian::write_i64(&mut buf[8..], val);
|
||||
}
|
||||
|
||||
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
|
||||
let socket = 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 flags: i32 = socket_varargs.get(ctx);
|
||||
let address: 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 address = emscripten_memory_pointer!(ctx.memory(0), address) as *mut sockaddr;
|
||||
let address_len_addr =
|
||||
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 => {
|
||||
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 _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");
|
||||
|
||||
let readfds_ptr = emscripten_memory_pointer!(ctx.memory(0), readfds) as _;
|
||||
|
@ -475,6 +475,7 @@ impl FunctionCodeGenerator<CodegenError> for LLVMFunctionCodeGenerator {
|
||||
Event::Internal(_x) => {
|
||||
return Ok(());
|
||||
}
|
||||
Event::WasmOwned(ref x) => x,
|
||||
};
|
||||
|
||||
let mut state = &mut self.state;
|
||||
|
@ -163,6 +163,7 @@ impl Intrinsics {
|
||||
let stack_lower_bound_ty = i8_ty;
|
||||
let memory_base_ty = i8_ty;
|
||||
let memory_bound_ty = void_ty;
|
||||
let internals_ty = i64_ty;
|
||||
let local_function_ty = i8_ptr_ty;
|
||||
|
||||
let anyfunc_ty = context.struct_type(
|
||||
@ -218,6 +219,9 @@ impl Intrinsics {
|
||||
memory_bound_ty
|
||||
.ptr_type(AddressSpace::Generic)
|
||||
.as_basic_type_enum(),
|
||||
internals_ty
|
||||
.ptr_type(AddressSpace::Generic)
|
||||
.as_basic_type_enum(),
|
||||
local_function_ty
|
||||
.ptr_type(AddressSpace::Generic)
|
||||
.as_basic_type_enum(),
|
||||
|
@ -8,4 +8,20 @@ authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"]
|
||||
edition = "2018"
|
||||
|
||||
[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)]
|
||||
|
||||
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,
|
||||
};
|
||||
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.
|
||||
/// 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.
|
||||
pub(crate) dynamic_sigindices: BoxedMap<SigIndex, vm::SigId>,
|
||||
pub(crate) local_functions: BoxedMap<LocalFuncIndex, *const vm::Func>,
|
||||
|
||||
pub(crate) internals: Internals,
|
||||
}
|
||||
|
||||
impl LocalBacking {
|
||||
@ -66,6 +78,8 @@ impl LocalBacking {
|
||||
|
||||
dynamic_sigindices,
|
||||
local_functions,
|
||||
|
||||
internals: Internals([0; INTERNALS_SIZE]),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -8,6 +8,7 @@ use crate::{
|
||||
types::{FuncIndex, FuncSig, SigIndex},
|
||||
};
|
||||
use smallvec::SmallVec;
|
||||
use std::any::Any;
|
||||
use std::fmt;
|
||||
use std::fmt::Debug;
|
||||
use std::marker::PhantomData;
|
||||
@ -19,6 +20,7 @@ use wasmparser::{Operator, Type as WpType};
|
||||
pub enum Event<'a, 'b> {
|
||||
Internal(InternalEvent),
|
||||
Wasm(&'b Operator<'a>),
|
||||
WasmOwned(Operator<'a>),
|
||||
}
|
||||
|
||||
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> {
|
||||
/// Creates a new module code generator.
|
||||
|
@ -13,7 +13,7 @@ use crate::{
|
||||
table::Table,
|
||||
typed_func::{Func, Wasm, WasmTrapInfo, WasmTypeList},
|
||||
types::{FuncIndex, FuncSig, GlobalIndex, LocalOrImport, MemoryIndex, TableIndex, Type, Value},
|
||||
vm,
|
||||
vm::{self, InternalField},
|
||||
};
|
||||
use smallvec::{smallvec, SmallVec};
|
||||
use std::{mem, ptr::NonNull, sync::Arc};
|
||||
@ -372,6 +372,14 @@ impl Instance {
|
||||
pub fn module(&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 {
|
||||
|
@ -57,6 +57,8 @@ pub use self::module::Module;
|
||||
pub use self::typed_func::Func;
|
||||
use std::sync::Arc;
|
||||
|
||||
pub use wasmparser;
|
||||
|
||||
use self::cache::{Artifact, Error as CacheError};
|
||||
|
||||
pub mod prelude {
|
||||
|
@ -1,4 +1,4 @@
|
||||
pub use crate::backing::{ImportBacking, LocalBacking};
|
||||
pub use crate::backing::{ImportBacking, LocalBacking, INTERNALS_SIZE};
|
||||
use crate::{
|
||||
memory::{Memory, MemoryType},
|
||||
module::{ModuleInfo, ModuleInner},
|
||||
@ -6,7 +6,13 @@ use crate::{
|
||||
types::{LocalOrImport, MemoryIndex},
|
||||
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;
|
||||
|
||||
@ -92,6 +98,43 @@ pub struct InternalCtx {
|
||||
|
||||
pub memory_base: *mut u8,
|
||||
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)]
|
||||
@ -200,6 +243,8 @@ impl Ctx {
|
||||
|
||||
memory_base: mem_base,
|
||||
memory_bound: mem_bound,
|
||||
|
||||
internals: &mut local_backing.internals.0,
|
||||
},
|
||||
local_functions: local_backing.local_functions.as_ptr(),
|
||||
|
||||
@ -249,6 +294,8 @@ impl Ctx {
|
||||
|
||||
memory_base: mem_base,
|
||||
memory_bound: mem_bound,
|
||||
|
||||
internals: &mut local_backing.internals.0,
|
||||
},
|
||||
local_functions: local_backing.local_functions.as_ptr(),
|
||||
|
||||
@ -303,6 +350,18 @@ impl Ctx {
|
||||
pub fn dynamic_sigindice_count(&self) -> usize {
|
||||
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)]
|
||||
@ -356,9 +415,13 @@ impl Ctx {
|
||||
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)
|
||||
}
|
||||
|
||||
pub fn offset_local_functions() -> u8 {
|
||||
13 * (mem::size_of::<usize>() as u8)
|
||||
}
|
||||
}
|
||||
|
||||
enum InnerFunc {}
|
||||
@ -572,6 +635,11 @@ mod vm_offset_tests {
|
||||
offset_of!(InternalCtx => memory_bound).get_byte_offset(),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
Ctx::offset_internals() as usize,
|
||||
offset_of!(InternalCtx => internals).get_byte_offset(),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
Ctx::offset_local_functions() as usize,
|
||||
offset_of!(Ctx => local_functions).get_byte_offset(),
|
||||
@ -684,6 +752,8 @@ mod vm_ctx_tests {
|
||||
|
||||
dynamic_sigindices: 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 {
|
||||
memories: Map::new().into_boxed_map(),
|
||||
|
@ -27,7 +27,7 @@ use wasmer_runtime_core::{
|
||||
FuncIndex, FuncSig, GlobalIndex, LocalFuncIndex, LocalOrImport, MemoryIndex, SigIndex,
|
||||
TableIndex, Type,
|
||||
},
|
||||
vm::{self, LocalGlobal, LocalTable},
|
||||
vm::{self, LocalGlobal, LocalTable, INTERNALS_SIZE},
|
||||
};
|
||||
use wasmparser::{Operator, Type as WpType};
|
||||
|
||||
@ -1504,6 +1504,7 @@ impl FunctionCodeGenerator<CodegenError> for X64FunctionCode {
|
||||
|
||||
let op = match ev {
|
||||
Event::Wasm(x) => x,
|
||||
Event::WasmOwned(ref x) => x,
|
||||
Event::Internal(x) => {
|
||||
match x {
|
||||
InternalEvent::Breakpoint(callback) => {
|
||||
@ -1513,8 +1514,71 @@ impl FunctionCodeGenerator<CodegenError> for X64FunctionCode {
|
||||
.unwrap()
|
||||
.insert(a.get_offset(), callback);
|
||||
}
|
||||
InternalEvent::FunctionBegin(_) | InternalEvent::FunctionEnd => {}
|
||||
_ => unimplemented!(),
|
||||
InternalEvent::FunctionBegin(_) | InternalEvent::FunctionEnd => {},
|
||||
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(());
|
||||
}
|
||||
@ -2644,7 +2708,14 @@ impl FunctionCodeGenerator<CodegenError> for X64FunctionCode {
|
||||
let tmp_out = self.machine.acquire_temp_gpr().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);
|
||||
|
||||
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_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,
|
||||
@ -2686,7 +2764,14 @@ impl FunctionCodeGenerator<CodegenError> for X64FunctionCode {
|
||||
let tmp_out = self.machine.acquire_temp_gpr().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,
|
||||
@ -2724,7 +2809,14 @@ impl FunctionCodeGenerator<CodegenError> for X64FunctionCode {
|
||||
let tmp_out = self.machine.acquire_temp_gpr().unwrap();
|
||||
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(
|
||||
a,
|
||||
&mut self.machine,
|
||||
@ -2772,7 +2864,14 @@ impl FunctionCodeGenerator<CodegenError> for X64FunctionCode {
|
||||
let tmp_out = self.machine.acquire_temp_gpr().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);
|
||||
|
||||
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_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,
|
||||
@ -2850,7 +2956,14 @@ impl FunctionCodeGenerator<CodegenError> for X64FunctionCode {
|
||||
let tmp_out = self.machine.acquire_temp_gpr().unwrap();
|
||||
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(
|
||||
a,
|
||||
&mut self.machine,
|
||||
|
@ -257,6 +257,7 @@ impl Machine {
|
||||
|
||||
pub fn release_locations_keep_state<E: Emitter>(&self, assembler: &mut E, locs: &[Location]) {
|
||||
let mut delta_stack_offset: usize = 0;
|
||||
let mut stack_offset = self.stack_offset.0;
|
||||
|
||||
for loc in locs.iter().rev() {
|
||||
match *loc {
|
||||
@ -265,9 +266,10 @@ impl Machine {
|
||||
unreachable!();
|
||||
}
|
||||
let offset = (-x) as usize;
|
||||
if offset != self.stack_offset.0 {
|
||||
if offset != stack_offset {
|
||||
unreachable!();
|
||||
}
|
||||
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()));
|
||||
if let Some(bkpt_map) = bkpt_map {
|
||||
if let Some(ref x) = bkpt_map.get(&(ip as usize)) {
|
||||
(x)(BkptInfo {});
|
||||
(x)(BkptInfo { throw: throw });
|
||||
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.
|
||||
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.)
|
||||
|
@ -16,19 +16,21 @@ use structopt::StructOpt;
|
||||
use wasmer::*;
|
||||
use wasmer_clif_backend::CraneliftCompiler;
|
||||
#[cfg(feature = "backend:llvm")]
|
||||
use wasmer_llvm_backend::LLVMCompiler;
|
||||
use wasmer_llvm_backend::code::LLVMModuleCodeGenerator;
|
||||
use wasmer_runtime::{
|
||||
cache::{Cache as BaseCache, FileSystemCache, WasmHash, WASMER_VERSION_HASH},
|
||||
error::RuntimeError,
|
||||
Func, Value,
|
||||
};
|
||||
#[cfg(feature = "backend:singlepass")]
|
||||
use wasmer_runtime_core::codegen::{MiddlewareChain, StreamingCompiler};
|
||||
use wasmer_runtime_core::{
|
||||
self,
|
||||
backend::{Compiler, CompilerConfig, MemoryBoundCheckMode},
|
||||
loader::{Instance as LoadedInstance, LocalLoader},
|
||||
};
|
||||
#[cfg(feature = "backend:singlepass")]
|
||||
use wasmer_singlepass_backend::SinglePassCompiler;
|
||||
use wasmer_singlepass_backend::ModuleCodeGenerator as SinglePassMCG;
|
||||
#[cfg(feature = "wasi")]
|
||||
use wasmer_wasi;
|
||||
|
||||
@ -118,6 +120,9 @@ struct Run {
|
||||
/// Application arguments
|
||||
#[structopt(name = "--", raw(multiple = "true"))]
|
||||
args: Vec<String>,
|
||||
|
||||
#[structopt(long = "inst-limit")]
|
||||
instruction_limit: Option<u64>,
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
@ -339,12 +344,33 @@ fn execute_wasm(options: &Run) -> Result<(), String> {
|
||||
|
||||
let compiler: Box<dyn Compiler> = match options.backend {
|
||||
#[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"))]
|
||||
Backend::Singlepass => return Err("The singlepass backend is not enabled".to_string()),
|
||||
Backend::Cranelift => Box::new(CraneliftCompiler::new()),
|
||||
#[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"))]
|
||||
Backend::LLVM => return Err("the llvm backend is not enabled".to_string()),
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user