More infrastructure.
Improve handling of memory.grow/size, add a standalone wast runner, test harness improvements.
This commit is contained in:
@@ -8,7 +8,7 @@ use cranelift_codegen::isa;
|
||||
use cranelift_codegen::Context;
|
||||
use cranelift_entity::{EntityRef, PrimaryMap};
|
||||
use cranelift_wasm::{DefinedFuncIndex, FuncIndex, FuncTranslator};
|
||||
use environ::{get_func_name, ModuleTranslation};
|
||||
use environ::{get_func_name, get_memory_grow_name, get_memory_size_name, ModuleTranslation};
|
||||
use std::string::{String, ToString};
|
||||
use std::vec::Vec;
|
||||
|
||||
@@ -49,13 +49,13 @@ impl binemit::RelocSink for RelocSink {
|
||||
name: &ExternalName,
|
||||
addend: binemit::Addend,
|
||||
) {
|
||||
let reloc_target = if let ExternalName::User { namespace, index } = *name {
|
||||
let reloc_target = if *name == get_memory_grow_name() {
|
||||
RelocationTarget::MemoryGrow
|
||||
} else if *name == get_memory_size_name() {
|
||||
RelocationTarget::MemorySize
|
||||
} else if let ExternalName::User { namespace, index } = *name {
|
||||
debug_assert!(namespace == 0);
|
||||
RelocationTarget::UserFunc(FuncIndex::new(index as usize))
|
||||
} else if *name == ExternalName::testcase("wasmtime_memory_grow") {
|
||||
RelocationTarget::MemoryGrow
|
||||
} else if *name == ExternalName::testcase("wasmtime_memory_size") {
|
||||
RelocationTarget::MemorySize
|
||||
} else if let ExternalName::LibCall(libcall) = *name {
|
||||
RelocationTarget::LibCall(libcall)
|
||||
} else {
|
||||
|
||||
@@ -3,7 +3,7 @@ use cranelift_codegen::ir;
|
||||
use cranelift_codegen::ir::immediates::{Imm64, Offset32, Uimm64};
|
||||
use cranelift_codegen::ir::types::*;
|
||||
use cranelift_codegen::ir::{
|
||||
AbiParam, ArgumentPurpose, ExtFuncData, ExternalName, FuncRef, Function, InstBuilder, Signature,
|
||||
AbiParam, ArgumentPurpose, ExtFuncData, FuncRef, Function, InstBuilder, Signature,
|
||||
};
|
||||
use cranelift_codegen::isa;
|
||||
use cranelift_entity::EntityRef;
|
||||
@@ -26,6 +26,16 @@ pub fn get_func_name(func_index: FuncIndex) -> ir::ExternalName {
|
||||
ir::ExternalName::user(0, func_index.as_u32())
|
||||
}
|
||||
|
||||
/// Compute a `ir::ExternalName` for the `memory.grow` libcall.
|
||||
pub fn get_memory_grow_name() -> ir::ExternalName {
|
||||
ir::ExternalName::user(1, 0)
|
||||
}
|
||||
|
||||
/// Compute a `ir::ExternalName` for the `memory.size` libcall.
|
||||
pub fn get_memory_size_name() -> ir::ExternalName {
|
||||
ir::ExternalName::user(1, 1)
|
||||
}
|
||||
|
||||
/// Object containing the standalone environment information. To be passed after creation as
|
||||
/// argument to `compile_module`.
|
||||
pub struct ModuleEnvironment<'data, 'module> {
|
||||
@@ -97,11 +107,11 @@ pub struct FuncEnvironment<'module_environment> {
|
||||
/// The Cranelift global holding the base address of the globals vector.
|
||||
globals_base: Option<ir::GlobalValue>,
|
||||
|
||||
/// The external function declaration for implementing wasm's `current_memory`.
|
||||
current_memory_extfunc: Option<FuncRef>,
|
||||
/// The external function declaration for implementing wasm's `memory.size`.
|
||||
memory_size_extfunc: Option<FuncRef>,
|
||||
|
||||
/// The external function declaration for implementing wasm's `grow_memory`.
|
||||
grow_memory_extfunc: Option<FuncRef>,
|
||||
/// The external function declaration for implementing wasm's `memory.grow`.
|
||||
memory_grow_extfunc: Option<FuncRef>,
|
||||
|
||||
/// Offsets to struct fields accessed by JIT code.
|
||||
offsets: VMOffsets,
|
||||
@@ -119,8 +129,8 @@ impl<'module_environment> FuncEnvironment<'module_environment> {
|
||||
memories_base: None,
|
||||
tables_base: None,
|
||||
globals_base: None,
|
||||
current_memory_extfunc: None,
|
||||
grow_memory_extfunc: None,
|
||||
memory_size_extfunc: None,
|
||||
memory_grow_extfunc: None,
|
||||
offsets: VMOffsets::new(isa.frontend_config().pointer_bytes()),
|
||||
}
|
||||
}
|
||||
@@ -484,7 +494,7 @@ impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'m
|
||||
_heap: ir::Heap,
|
||||
val: ir::Value,
|
||||
) -> WasmResult<ir::Value> {
|
||||
let grow_mem_func = self.grow_memory_extfunc.unwrap_or_else(|| {
|
||||
let memory_grow_func = self.memory_grow_extfunc.unwrap_or_else(|| {
|
||||
let sig_ref = pos.func.import_signature(Signature {
|
||||
call_conv: self.isa.frontend_config().default_call_conv,
|
||||
params: vec![
|
||||
@@ -497,17 +507,18 @@ impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'m
|
||||
// We currently allocate all code segments independently, so nothing
|
||||
// is colocated.
|
||||
let colocated = false;
|
||||
// FIXME: Use a real ExternalName system.
|
||||
pos.func.import_function(ExtFuncData {
|
||||
name: ExternalName::testcase("grow_memory"),
|
||||
name: get_memory_grow_name(),
|
||||
signature: sig_ref,
|
||||
colocated,
|
||||
})
|
||||
});
|
||||
self.grow_memory_extfunc = Some(grow_mem_func);
|
||||
self.memory_grow_extfunc = Some(memory_grow_func);
|
||||
let memory_index = pos.ins().iconst(I32, index.index() as i64);
|
||||
let vmctx = pos.func.special_param(ArgumentPurpose::VMContext).unwrap();
|
||||
let call_inst = pos.ins().call(grow_mem_func, &[val, memory_index, vmctx]);
|
||||
let call_inst = pos
|
||||
.ins()
|
||||
.call(memory_grow_func, &[val, memory_index, vmctx]);
|
||||
Ok(*pos.func.dfg.inst_results(call_inst).first().unwrap())
|
||||
}
|
||||
|
||||
@@ -517,7 +528,7 @@ impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'m
|
||||
index: MemoryIndex,
|
||||
_heap: ir::Heap,
|
||||
) -> WasmResult<ir::Value> {
|
||||
let cur_mem_func = self.current_memory_extfunc.unwrap_or_else(|| {
|
||||
let memory_size_func = self.memory_size_extfunc.unwrap_or_else(|| {
|
||||
let sig_ref = pos.func.import_signature(Signature {
|
||||
call_conv: self.isa.frontend_config().default_call_conv,
|
||||
params: vec![
|
||||
@@ -529,17 +540,16 @@ impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'m
|
||||
// We currently allocate all code segments independently, so nothing
|
||||
// is colocated.
|
||||
let colocated = false;
|
||||
// FIXME: Use a real ExternalName system.
|
||||
pos.func.import_function(ExtFuncData {
|
||||
name: ExternalName::testcase("current_memory"),
|
||||
name: get_memory_size_name(),
|
||||
signature: sig_ref,
|
||||
colocated,
|
||||
})
|
||||
});
|
||||
self.current_memory_extfunc = Some(cur_mem_func);
|
||||
self.memory_size_extfunc = Some(memory_size_func);
|
||||
let memory_index = pos.ins().iconst(I32, index.index() as i64);
|
||||
let vmctx = pos.func.special_param(ArgumentPurpose::VMContext).unwrap();
|
||||
let call_inst = pos.ins().call(cur_mem_func, &[memory_index, vmctx]);
|
||||
let call_inst = pos.ins().call(memory_size_func, &[memory_index, vmctx]);
|
||||
Ok(*pos.func.dfg.inst_results(call_inst).first().unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,6 +49,7 @@ pub use compilation::{
|
||||
pub use environ::{ModuleEnvironment, ModuleTranslation};
|
||||
pub use module::{DataInitializer, Export, MemoryPlan, MemoryStyle, Module, TableElements};
|
||||
pub use tunables::Tunables;
|
||||
pub use vmoffsets::VMOffsets;
|
||||
|
||||
/// WebAssembly page sizes are defined to be 64KiB.
|
||||
pub const WASM_PAGE_SIZE: u32 = 0x10000;
|
||||
|
||||
@@ -52,17 +52,23 @@ pub enum MemoryStyle {
|
||||
|
||||
impl MemoryStyle {
|
||||
/// Decide on an implementation style for the given `Memory`.
|
||||
pub fn for_memory(memory: Memory, tunables: &Tunables) -> Self {
|
||||
pub fn for_memory(memory: Memory, tunables: &Tunables) -> (Self, u64) {
|
||||
if let Some(maximum) = memory.maximum {
|
||||
// A heap with a declared maximum is prepared to be used with
|
||||
// threads and therefore be immovable, so make it static.
|
||||
MemoryStyle::Static {
|
||||
bound: cmp::max(tunables.static_memory_bound, maximum),
|
||||
}
|
||||
(
|
||||
MemoryStyle::Static {
|
||||
bound: cmp::max(tunables.static_memory_bound, maximum),
|
||||
},
|
||||
tunables.static_memory_offset_guard_size,
|
||||
)
|
||||
} else {
|
||||
// A heap without a declared maximum is likely to want to be small
|
||||
// at least some of the time, so make it dynamic.
|
||||
MemoryStyle::Dynamic
|
||||
(
|
||||
MemoryStyle::Dynamic,
|
||||
tunables.dynamic_memory_offset_guard_size,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -82,10 +88,11 @@ pub struct MemoryPlan {
|
||||
impl MemoryPlan {
|
||||
/// Draw up a plan for implementing a `Memory`.
|
||||
pub fn for_memory(memory: Memory, tunables: &Tunables) -> Self {
|
||||
let (style, offset_guard_size) = MemoryStyle::for_memory(memory, tunables);
|
||||
Self {
|
||||
memory,
|
||||
style: MemoryStyle::for_memory(memory, tunables),
|
||||
offset_guard_size: tunables.offset_guard_size,
|
||||
style,
|
||||
offset_guard_size,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,8 +4,11 @@ pub struct Tunables {
|
||||
/// For static heaps, the size of the heap protected by bounds checking.
|
||||
pub static_memory_bound: u32,
|
||||
|
||||
/// The size of the offset guard.
|
||||
pub offset_guard_size: u64,
|
||||
/// The size of the offset guard for static heaps.
|
||||
pub static_memory_offset_guard_size: u64,
|
||||
|
||||
/// The size of the offset guard for dynamic heaps.
|
||||
pub dynamic_memory_offset_guard_size: u64,
|
||||
}
|
||||
|
||||
impl Default for Tunables {
|
||||
@@ -17,11 +20,17 @@ impl Default for Tunables {
|
||||
/// need for explicit bounds checks.
|
||||
static_memory_bound: 0x1_0000,
|
||||
|
||||
/// Size in bytes of the offset guard.
|
||||
/// Size in bytes of the offset guard for static memories.
|
||||
///
|
||||
/// Allocating 2 GiB of address space lets us translate wasm
|
||||
/// offsets into x86 offsets as aggressively as we can.
|
||||
offset_guard_size: 0x8000_0000,
|
||||
static_memory_offset_guard_size: 0x8000_0000,
|
||||
|
||||
/// Size in bytes of the offset guard for dynamic memories.
|
||||
///
|
||||
/// Allocate a small guard to optimize common cases but without
|
||||
/// wasting too much memor.
|
||||
dynamic_memory_offset_guard_size: 0x1_0000,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,16 +72,10 @@ impl VMOffsets {
|
||||
2 * self.pointer_size
|
||||
}
|
||||
|
||||
/// The offset of the `instance` field.
|
||||
#[allow(dead_code)]
|
||||
pub fn vmctx_instance(&self) -> u8 {
|
||||
3 * self.pointer_size
|
||||
}
|
||||
|
||||
/// Return the size of `VMContext`.
|
||||
#[allow(dead_code)]
|
||||
pub fn size_of_vmctx(&self) -> u8 {
|
||||
4 * self.pointer_size
|
||||
3 * self.pointer_size
|
||||
}
|
||||
|
||||
/// Return the offset from the `memories` pointer to `VMMemory` index `index`.
|
||||
|
||||
Reference in New Issue
Block a user