More infrastructure.

Improve handling of memory.grow/size, add a standalone wast runner,
test harness improvements.
This commit is contained in:
Dan Gohman
2018-12-03 04:59:40 -08:00
parent 83f8a31010
commit 7faa15d7ac
15 changed files with 316 additions and 82 deletions

View File

@@ -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 {

View File

@@ -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())
}
}

View File

@@ -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;

View File

@@ -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,
}
}
}

View File

@@ -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,
}
}
}

View File

@@ -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`.