Update to Cranelift 0.22.

This commit is contained in:
Dan Gohman
2018-11-16 10:49:37 -08:00
parent 9e56ed5aad
commit 0a0108f959
9 changed files with 171 additions and 70 deletions

View File

@@ -18,8 +18,8 @@ name = "wasm2obj"
path = "src/wasm2obj.rs" path = "src/wasm2obj.rs"
[dependencies] [dependencies]
cranelift-codegen = "0.20.0" cranelift-codegen = "0.22.0"
cranelift-native = "0.20.0" cranelift-native = "0.22.0"
wasmtime-environ = { path = "lib/environ" } wasmtime-environ = { path = "lib/environ" }
wasmtime-execute = { path = "lib/execute" } wasmtime-execute = { path = "lib/execute" }
wasmtime-obj = { path = "lib/obj" } wasmtime-obj = { path = "lib/obj" }

View File

@@ -10,9 +10,9 @@ cargo-fuzz = true
[dependencies] [dependencies]
wasmtime-environ = { path = "../lib/environ" } wasmtime-environ = { path = "../lib/environ" }
wasmtime-execute = { path = "../lib/execute" } wasmtime-execute = { path = "../lib/execute" }
cranelift-codegen = "0.20.0" cranelift-codegen = "0.22.0"
cranelift-wasm = "0.20.1" cranelift-wasm = "0.22.0"
cranelift-native = "0.20.0" cranelift-native = "0.22.0"
libfuzzer-sys = { git = "https://github.com/rust-fuzz/libfuzzer-sys.git" } libfuzzer-sys = { git = "https://github.com/rust-fuzz/libfuzzer-sys.git" }
wasmparser = { version = "0.17.2", default-features = false } wasmparser = { version = "0.17.2", default-features = false }

View File

@@ -10,10 +10,11 @@ license = "Apache-2.0 WITH LLVM-exception"
readme = "README.md" readme = "README.md"
[dependencies] [dependencies]
cranelift-codegen = "0.20.0" cranelift-codegen = "0.22.0"
cranelift-entity = "0.20.1" cranelift-entity = "0.22.0"
cranelift-wasm = "0.20.1" cranelift-wasm = "0.22.0"
target-lexicon = "0.0.3" target-lexicon = "0.0.3"
memoffset = "0.2.1"
[badges] [badges]
maintenance = { status = "experimental" } maintenance = { status = "experimental" }

View File

@@ -6,14 +6,14 @@ use cranelift_codegen::ir::{
AbiParam, ArgumentPurpose, ExtFuncData, ExternalName, FuncRef, Function, InstBuilder, Signature, AbiParam, ArgumentPurpose, ExtFuncData, ExternalName, FuncRef, Function, InstBuilder, Signature,
}; };
use cranelift_codegen::isa; use cranelift_codegen::isa;
use cranelift_codegen::settings;
use cranelift_entity::EntityRef; use cranelift_entity::EntityRef;
use cranelift_wasm::{ use cranelift_wasm::{
self, translate_module, FuncIndex, Global, GlobalIndex, GlobalVariable, Memory, MemoryIndex, self, translate_module, FuncIndex, Global, GlobalIndex, GlobalVariable, Memory, MemoryIndex,
SignatureIndex, Table, TableIndex, WasmResult, SignatureIndex, Table, TableIndex, WasmResult,
}; };
use module::{DataInitializer, Export, LazyContents, Module, TableElements}; use module::{DataInitializer, Export, LazyContents, Module, TableElements};
use target_lexicon::Triple; use std::mem;
use vmcontext;
/// Compute a `ir::ExternalName` for a given wasm function index. /// Compute a `ir::ExternalName` for a given wasm function index.
pub fn get_func_name(func_index: FuncIndex) -> ir::ExternalName { pub fn get_func_name(func_index: FuncIndex) -> ir::ExternalName {
@@ -32,6 +32,9 @@ pub struct ModuleEnvironment<'data, 'module> {
/// References to information to be decoded later. /// References to information to be decoded later.
pub lazy: LazyContents<'data>, pub lazy: LazyContents<'data>,
/// The ISA's target front-end configuration.
frontend_config: isa::TargetFrontendConfig,
} }
impl<'data, 'module> ModuleEnvironment<'data, 'module> { impl<'data, 'module> ModuleEnvironment<'data, 'module> {
@@ -41,6 +44,7 @@ impl<'data, 'module> ModuleEnvironment<'data, 'module> {
isa, isa,
module, module,
lazy: LazyContents::new(), lazy: LazyContents::new(),
frontend_config: isa.frontend_config(),
} }
} }
@@ -76,9 +80,15 @@ pub struct FuncEnvironment<'module_environment> {
/// The module-level environment which this function-level environment belongs to. /// The module-level environment which this function-level environment belongs to.
pub module: &'module_environment Module, pub module: &'module_environment Module,
/// The Cranelift global holding the vmctx address.
pub vmctx: Option<ir::GlobalValue>,
/// The Cranelift global holding the base address of the memories vector. /// The Cranelift global holding the base address of the memories vector.
pub memories_base: Option<ir::GlobalValue>, pub memories_base: Option<ir::GlobalValue>,
/// The Cranelift global holding the base address of the tables vector.
pub tables_base: Option<ir::GlobalValue>,
/// The Cranelift global holding the base address of the globals vector. /// The Cranelift global holding the base address of the globals vector.
pub globals_base: Option<ir::GlobalValue>, pub globals_base: Option<ir::GlobalValue>,
@@ -97,7 +107,9 @@ impl<'module_environment> FuncEnvironment<'module_environment> {
Self { Self {
isa, isa,
module, module,
vmctx: None,
memories_base: None, memories_base: None,
tables_base: None,
globals_base: None, globals_base: None,
current_memory_extfunc: None, current_memory_extfunc: None,
grow_memory_extfunc: None, grow_memory_extfunc: None,
@@ -115,6 +127,14 @@ impl<'module_environment> FuncEnvironment<'module_environment> {
fn pointer_bytes(&self) -> usize { fn pointer_bytes(&self) -> usize {
usize::from(self.isa.pointer_bytes()) usize::from(self.isa.pointer_bytes())
} }
fn vmctx(&mut self, func: &mut Function) -> ir::GlobalValue {
self.vmctx.unwrap_or_else(|| {
let vmctx = func.create_global_value(ir::GlobalValueData::VMContext);
self.vmctx = Some(vmctx);
vmctx
})
}
} }
/// This trait is useful for `translate_module` because it tells how to translate /// This trait is useful for `translate_module` because it tells how to translate
@@ -126,8 +146,8 @@ impl<'data, 'module> cranelift_wasm::ModuleEnvironment<'data>
get_func_name(func_index) get_func_name(func_index)
} }
fn flags(&self) -> &settings::Flags { fn target_config(&self) -> &isa::TargetFrontendConfig {
self.isa.flags() &self.frontend_config
} }
fn declare_signature(&mut self, sig: &ir::Signature) { fn declare_signature(&mut self, sig: &ir::Signature) {
@@ -253,30 +273,29 @@ impl<'data, 'module> cranelift_wasm::ModuleEnvironment<'data>
} }
impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'module_environment> { impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'module_environment> {
fn flags(&self) -> &settings::Flags { fn target_config(&self) -> isa::TargetFrontendConfig {
&self.isa.flags() self.isa.frontend_config()
}
fn triple(&self) -> &Triple {
self.isa.triple()
} }
fn make_global(&mut self, func: &mut ir::Function, index: GlobalIndex) -> GlobalVariable { fn make_global(&mut self, func: &mut ir::Function, index: GlobalIndex) -> GlobalVariable {
let pointer_bytes = self.pointer_bytes(); let vmctx = self.vmctx(func);
let globals_base = self.globals_base.unwrap_or_else(|| { let globals_base = self.globals_base.unwrap_or_else(|| {
let new_base = func.create_global_value(ir::GlobalValueData::VMContext { let new_base = func.create_global_value(ir::GlobalValueData::Load {
offset: Offset32::new(0), base: vmctx,
offset: Offset32::new(offset_of!(vmcontext::VMContext, globals) as i32),
global_type: self.pointer_type(),
readonly: true,
}); });
self.globals_base = Some(new_base); self.globals_base = Some(new_base);
new_base new_base
}); });
let offset = index * pointer_bytes; // For now, give each global gets a pointer-sized region of
let offset32 = offset as i32; // storage, regardless of its type.
debug_assert_eq!(offset32 as usize, offset); let offset = index.index() * mem::size_of::<*mut u8>();
let gv = func.create_global_value(ir::GlobalValueData::Deref { let gv = func.create_global_value(ir::GlobalValueData::IAddImm {
base: globals_base, base: globals_base,
offset: Offset32::new(offset32), offset: Imm64::new(offset as i64),
memory_type: self.pointer_type(), global_type: self.pointer_type(),
}); });
GlobalVariable::Memory { GlobalVariable::Memory {
gv, gv,
@@ -285,55 +304,91 @@ impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'m
} }
fn make_heap(&mut self, func: &mut ir::Function, index: MemoryIndex) -> ir::Heap { fn make_heap(&mut self, func: &mut ir::Function, index: MemoryIndex) -> ir::Heap {
let pointer_bytes = self.pointer_bytes(); let vmctx = self.vmctx(func);
let memories_base = self.memories_base.unwrap_or_else(|| { let memories_base = self.memories_base.unwrap_or_else(|| {
let new_base = func.create_global_value(ir::GlobalValueData::VMContext { let new_base = func.create_global_value(ir::GlobalValueData::Load {
offset: Offset32::new(pointer_bytes as i32), base: vmctx,
offset: Offset32::new(offset_of!(vmcontext::VMContext, memories) as i32),
global_type: self.pointer_type(),
readonly: true,
}); });
self.memories_base = Some(new_base); self.memories_base = Some(new_base);
new_base new_base
}); });
let offset = index * pointer_bytes; let offset = index.index() * mem::size_of::<vmcontext::VMMemory>();
let offset32 = offset as i32; let offset32 = offset as i32;
debug_assert_eq!(offset32 as usize, offset); debug_assert_eq!(offset32 as usize, offset);
let heap_base_addr = func.create_global_value(ir::GlobalValueData::Deref { // If we have a declared maximum, we can make this a "static" heap, which is
// allocated up front and never moved.
let (guard_size, heap_style, readonly_base) =
if self.module.memories[index].maximum.is_some() {
(
0x8000_0000.into(),
ir::HeapStyle::Static {
bound: 0x1_0000_0000.into(),
},
true,
)
} else {
let heap_bound = func.create_global_value(ir::GlobalValueData::Load {
base: memories_base,
offset: Offset32::new(
offset32 + offset_of!(vmcontext::VMMemory, current_length) as i32,
),
global_type: self.pointer_type(),
readonly: false,
});
(
0.into(),
ir::HeapStyle::Dynamic {
bound_gv: heap_bound,
},
false,
)
};
let heap_base = func.create_global_value(ir::GlobalValueData::Load {
base: memories_base, base: memories_base,
offset: Offset32::new(offset32), offset: Offset32::new(offset32 + offset_of!(vmcontext::VMMemory, base) as i32),
memory_type: self.pointer_type(), global_type: self.pointer_type(),
}); readonly: readonly_base,
let heap_base = func.create_global_value(ir::GlobalValueData::Deref {
base: heap_base_addr,
offset: Offset32::new(0),
memory_type: self.pointer_type(),
}); });
func.create_heap(ir::HeapData { func.create_heap(ir::HeapData {
base: heap_base, base: heap_base,
min_size: 0.into(), min_size: 0.into(),
guard_size: 0x8000_0000.into(), guard_size,
style: ir::HeapStyle::Static { style: heap_style,
bound: 0x1_0000_0000.into(),
},
index_type: I32, index_type: I32,
}) })
} }
fn make_table(&mut self, func: &mut ir::Function, _index: TableIndex) -> ir::Table { fn make_table(&mut self, func: &mut ir::Function, index: TableIndex) -> ir::Table {
let pointer_bytes = self.pointer_bytes(); let vmctx = self.vmctx(func);
let base_gv_addr = func.create_global_value(ir::GlobalValueData::VMContext { let tables_base = self.tables_base.unwrap_or_else(|| {
offset: Offset32::new(pointer_bytes as i32 * 2), let new_base = func.create_global_value(ir::GlobalValueData::Load {
base: vmctx,
offset: Offset32::new(offset_of!(vmcontext::VMContext, tables) as i32),
global_type: self.pointer_type(),
readonly: true,
});
self.tables_base = Some(new_base);
new_base
}); });
let base_gv = func.create_global_value(ir::GlobalValueData::Deref { let offset = index.index() * mem::size_of::<vmcontext::VMTable>();
base: base_gv_addr, let offset32 = offset as i32;
offset: 0.into(), debug_assert_eq!(offset32 as usize, offset);
memory_type: self.pointer_type(), let base_gv = func.create_global_value(ir::GlobalValueData::Load {
base: tables_base,
offset: Offset32::new(offset32 + offset_of!(vmcontext::VMTable, base) as i32),
global_type: self.pointer_type(),
readonly: false,
}); });
let bound_gv_addr = func.create_global_value(ir::GlobalValueData::VMContext { let bound_gv = func.create_global_value(ir::GlobalValueData::Load {
offset: Offset32::new(pointer_bytes as i32 * 3), base: tables_base,
}); offset: Offset32::new(
let bound_gv = func.create_global_value(ir::GlobalValueData::Deref { offset32 + offset_of!(vmcontext::VMTable, current_num_elements) as i32,
base: bound_gv_addr, ),
offset: 0.into(), global_type: I32,
memory_type: I32, readonly: false,
}); });
func.create_table(ir::TableData { func.create_table(ir::TableData {
@@ -375,7 +430,11 @@ impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'m
) -> WasmResult<ir::Inst> { ) -> WasmResult<ir::Inst> {
// TODO: Cranelift's call_indirect doesn't implement signature checking, // TODO: Cranelift's call_indirect doesn't implement signature checking,
// so we need to implement it ourselves. // so we need to implement it ourselves.
debug_assert_eq!(table_index, 0, "non-default tables not supported yet"); debug_assert_eq!(
table_index.index(),
0,
"non-default tables not supported yet"
);
let table_entry_addr = pos.ins().table_addr(I64, table, callee, 0); let table_entry_addr = pos.ins().table_addr(I64, table, callee, 0);
@@ -411,7 +470,7 @@ impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'m
) -> WasmResult<ir::Value> { ) -> WasmResult<ir::Value> {
let grow_mem_func = self.grow_memory_extfunc.unwrap_or_else(|| { let grow_mem_func = self.grow_memory_extfunc.unwrap_or_else(|| {
let sig_ref = pos.func.import_signature(Signature { let sig_ref = pos.func.import_signature(Signature {
call_conv: self.isa.flags().call_conv(), call_conv: self.isa.frontend_config().default_call_conv,
params: vec![ params: vec![
AbiParam::new(I32), AbiParam::new(I32),
AbiParam::new(I32), AbiParam::new(I32),
@@ -430,7 +489,7 @@ impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'m
}) })
}); });
self.grow_memory_extfunc = Some(grow_mem_func); self.grow_memory_extfunc = Some(grow_mem_func);
let memory_index = pos.ins().iconst(I32, index as i64); let memory_index = pos.ins().iconst(I32, index.index() as i64);
let vmctx = pos.func.special_param(ArgumentPurpose::VMContext).unwrap(); 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(grow_mem_func, &[val, memory_index, vmctx]);
Ok(*pos.func.dfg.inst_results(call_inst).first().unwrap()) Ok(*pos.func.dfg.inst_results(call_inst).first().unwrap())
@@ -444,7 +503,7 @@ impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'m
) -> WasmResult<ir::Value> { ) -> WasmResult<ir::Value> {
let cur_mem_func = self.current_memory_extfunc.unwrap_or_else(|| { let cur_mem_func = self.current_memory_extfunc.unwrap_or_else(|| {
let sig_ref = pos.func.import_signature(Signature { let sig_ref = pos.func.import_signature(Signature {
call_conv: self.isa.flags().call_conv(), call_conv: self.isa.frontend_config().default_call_conv,
params: vec![ params: vec![
AbiParam::new(I32), AbiParam::new(I32),
AbiParam::special(self.pointer_type(), ArgumentPurpose::VMContext), AbiParam::special(self.pointer_type(), ArgumentPurpose::VMContext),
@@ -462,7 +521,7 @@ impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'m
}) })
}); });
self.current_memory_extfunc = Some(cur_mem_func); self.current_memory_extfunc = Some(cur_mem_func);
let memory_index = pos.ins().iconst(I32, index as i64); let memory_index = pos.ins().iconst(I32, index.index() as i64);
let vmctx = pos.func.special_param(ArgumentPurpose::VMContext).unwrap(); 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(cur_mem_func, &[memory_index, vmctx]);
Ok(*pos.func.dfg.inst_results(call_inst).first().unwrap()) Ok(*pos.func.dfg.inst_results(call_inst).first().unwrap())

View File

@@ -36,10 +36,13 @@ extern crate cranelift_codegen;
extern crate cranelift_entity; extern crate cranelift_entity;
extern crate cranelift_wasm; extern crate cranelift_wasm;
extern crate target_lexicon; extern crate target_lexicon;
#[macro_use]
extern crate memoffset;
mod compilation; mod compilation;
mod environ; mod environ;
mod module; mod module;
mod vmcontext;
pub use compilation::{compile_module, Compilation, Relocation, RelocationTarget, Relocations}; pub use compilation::{compile_module, Compilation, Relocation, RelocationTarget, Relocations};
pub use environ::{ModuleEnvironment, ModuleTranslation}; pub use environ::{ModuleEnvironment, ModuleTranslation};

View File

@@ -0,0 +1,33 @@
/// The main fields a JIT needs to access to utilize a WebAssembly linear,
/// memory, namely the start address and the size in bytes.
#[repr(C, packed)]
pub struct VMMemory {
pub base: *mut u8,
pub current_length: usize,
}
/// The main fields a JIT needs to access to utilize a WebAssembly table,
/// namely the start address and the number of elements.
#[repr(C, packed)]
pub struct VMTable {
pub base: *mut u8,
pub current_num_elements: usize,
}
/// The VM "context", which is pointed to by the `vmctx` arg in Cranelift.
/// This has pointers to the globals, memories, tables, and other runtime
/// state associated with the current instance.
#[repr(C, packed)]
pub struct VMContext {
/// A pointer to an array of globals.
pub globals: *mut u8,
/// A pointer to an array of `VMMemory` instances, indexed by
/// WebAssembly memory index.
pub memories: *mut VMMemory,
/// A pointer to an array of `VMTable` instances, indexed by
/// WebAssembly table index.
pub tables: *mut VMTable,
/// A pointer to extra runtime state that isn't directly accessed
/// from JIT code.
pub instance: *mut u8,
}

View File

@@ -9,9 +9,9 @@ repository = "https://github.com/CraneStation/wasmtime"
license = "Apache-2.0 WITH LLVM-exception" license = "Apache-2.0 WITH LLVM-exception"
[dependencies] [dependencies]
cranelift-codegen = "0.20.0" cranelift-codegen = "0.22.0"
cranelift-entity = "0.20.1" cranelift-entity = "0.22.0"
cranelift-wasm = "0.20.1" cranelift-wasm = "0.22.0"
region = "0.3.0" region = "0.3.0"
wasmtime-environ = { path = "../environ" } wasmtime-environ = { path = "../environ" }
memmap = "0.6.2" memmap = "0.7.0"

View File

@@ -84,6 +84,7 @@ fn relocate<F>(
extern "C" fn grow_memory(size: u32, memory_index: u32, vmctx: *mut *mut u8) -> u32 { extern "C" fn grow_memory(size: u32, memory_index: u32, vmctx: *mut *mut u8) -> u32 {
unsafe { unsafe {
// FIXME: update the VMMemory's size
let instance = (*vmctx.offset(4)) as *mut Instance; let instance = (*vmctx.offset(4)) as *mut Instance;
(*instance) (*instance)
.memory_mut(memory_index as MemoryIndex) .memory_mut(memory_index as MemoryIndex)
@@ -94,6 +95,7 @@ extern "C" fn grow_memory(size: u32, memory_index: u32, vmctx: *mut *mut u8) ->
extern "C" fn current_memory(memory_index: u32, vmctx: *mut *mut u8) -> u32 { extern "C" fn current_memory(memory_index: u32, vmctx: *mut *mut u8) -> u32 {
unsafe { unsafe {
// FIXME: read the VMMemory's size instead
let instance = (*vmctx.offset(4)) as *mut Instance; let instance = (*vmctx.offset(4)) as *mut Instance;
(*instance) (*instance)
.memory_mut(memory_index as MemoryIndex) .memory_mut(memory_index as MemoryIndex)
@@ -115,9 +117,12 @@ fn make_vmctx(instance: &mut Instance, mem_base_addrs: &mut [*mut u8]) -> Vec<*m
.map(|table| (table.as_mut_ptr() as *mut u8, table.len())) .map(|table| (table.as_mut_ptr() as *mut u8, table.len()))
.unwrap_or((ptr::null_mut(), 0)); .unwrap_or((ptr::null_mut(), 0));
// FIXME: Actually use environ's VMContext struct
let mut vmctx = Vec::new(); let mut vmctx = Vec::new();
vmctx.push(instance.globals.as_mut_ptr()); vmctx.push(instance.globals.as_mut_ptr());
// FIXME: These need to be VMMemory now
vmctx.push(mem_base_addrs.as_mut_ptr() as *mut u8); vmctx.push(mem_base_addrs.as_mut_ptr() as *mut u8);
// FIXME: These need to be VMTable now
vmctx.push(default_table_ptr); vmctx.push(default_table_ptr);
vmctx.push(default_table_len as *mut u8); vmctx.push(default_table_len as *mut u8);
vmctx.push(instance as *mut Instance as *mut u8); vmctx.push(instance as *mut Instance as *mut u8);
@@ -134,7 +139,7 @@ pub fn execute(
let start_index = module let start_index = module
.start_func .start_func
.ok_or_else(|| String::from("No start function defined, aborting execution"))?; .ok_or_else(|| String::from("No start function defined, aborting execution"))?;
// TODO: Put all the function bodies into a page-aligned memory region, and // FIXME: Put all the function bodies into a page-aligned memory region, and
// then make them ReadExecute rather than ReadWriteExecute. // then make them ReadExecute rather than ReadWriteExecute.
for code_buf in compilation.functions.values() { for code_buf in compilation.functions.values() {
match unsafe { match unsafe {

View File

@@ -8,7 +8,7 @@ categories = ["wasm"]
license = "Apache-2.0 WITH LLVM-exception" license = "Apache-2.0 WITH LLVM-exception"
[dependencies] [dependencies]
cranelift-codegen = "0.20.0" cranelift-codegen = "0.22.0"
cranelift-entity = "0.20.1" cranelift-entity = "0.22.0"
wasmtime-environ = { path = "../environ" } wasmtime-environ = { path = "../environ" }
faerie = "0.5.0" faerie = "0.5.0"