diff --git a/Cargo.toml b/Cargo.toml index 8e8458925c..19d88d8abd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,8 +18,8 @@ name = "wasm2obj" path = "src/wasm2obj.rs" [dependencies] -cranelift-codegen = "0.20.0" -cranelift-native = "0.20.0" +cranelift-codegen = "0.22.0" +cranelift-native = "0.22.0" wasmtime-environ = { path = "lib/environ" } wasmtime-execute = { path = "lib/execute" } wasmtime-obj = { path = "lib/obj" } diff --git a/fuzz/Cargo.toml b/fuzz/Cargo.toml index 3c588e327a..ae5a89800f 100644 --- a/fuzz/Cargo.toml +++ b/fuzz/Cargo.toml @@ -10,9 +10,9 @@ cargo-fuzz = true [dependencies] wasmtime-environ = { path = "../lib/environ" } wasmtime-execute = { path = "../lib/execute" } -cranelift-codegen = "0.20.0" -cranelift-wasm = "0.20.1" -cranelift-native = "0.20.0" +cranelift-codegen = "0.22.0" +cranelift-wasm = "0.22.0" +cranelift-native = "0.22.0" libfuzzer-sys = { git = "https://github.com/rust-fuzz/libfuzzer-sys.git" } wasmparser = { version = "0.17.2", default-features = false } diff --git a/lib/environ/Cargo.toml b/lib/environ/Cargo.toml index 87d2ab391e..196db3c5c4 100644 --- a/lib/environ/Cargo.toml +++ b/lib/environ/Cargo.toml @@ -10,10 +10,11 @@ license = "Apache-2.0 WITH LLVM-exception" readme = "README.md" [dependencies] -cranelift-codegen = "0.20.0" -cranelift-entity = "0.20.1" -cranelift-wasm = "0.20.1" +cranelift-codegen = "0.22.0" +cranelift-entity = "0.22.0" +cranelift-wasm = "0.22.0" target-lexicon = "0.0.3" +memoffset = "0.2.1" [badges] maintenance = { status = "experimental" } diff --git a/lib/environ/src/environ.rs b/lib/environ/src/environ.rs index f22a894c7e..295b69ff07 100644 --- a/lib/environ/src/environ.rs +++ b/lib/environ/src/environ.rs @@ -6,14 +6,14 @@ use cranelift_codegen::ir::{ AbiParam, ArgumentPurpose, ExtFuncData, ExternalName, FuncRef, Function, InstBuilder, Signature, }; use cranelift_codegen::isa; -use cranelift_codegen::settings; use cranelift_entity::EntityRef; use cranelift_wasm::{ self, translate_module, FuncIndex, Global, GlobalIndex, GlobalVariable, Memory, MemoryIndex, SignatureIndex, Table, TableIndex, WasmResult, }; 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. 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. pub lazy: LazyContents<'data>, + + /// The ISA's target front-end configuration. + frontend_config: isa::TargetFrontendConfig, } impl<'data, 'module> ModuleEnvironment<'data, 'module> { @@ -41,6 +44,7 @@ impl<'data, 'module> ModuleEnvironment<'data, 'module> { isa, module, 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. pub module: &'module_environment Module, + /// The Cranelift global holding the vmctx address. + pub vmctx: Option, + /// The Cranelift global holding the base address of the memories vector. pub memories_base: Option, + /// The Cranelift global holding the base address of the tables vector. + pub tables_base: Option, + /// The Cranelift global holding the base address of the globals vector. pub globals_base: Option, @@ -97,7 +107,9 @@ impl<'module_environment> FuncEnvironment<'module_environment> { Self { isa, module, + vmctx: None, memories_base: None, + tables_base: None, globals_base: None, current_memory_extfunc: None, grow_memory_extfunc: None, @@ -115,6 +127,14 @@ impl<'module_environment> FuncEnvironment<'module_environment> { fn pointer_bytes(&self) -> usize { 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 @@ -126,8 +146,8 @@ impl<'data, 'module> cranelift_wasm::ModuleEnvironment<'data> get_func_name(func_index) } - fn flags(&self) -> &settings::Flags { - self.isa.flags() + fn target_config(&self) -> &isa::TargetFrontendConfig { + &self.frontend_config } 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> { - fn flags(&self) -> &settings::Flags { - &self.isa.flags() - } - - fn triple(&self) -> &Triple { - self.isa.triple() + fn target_config(&self) -> isa::TargetFrontendConfig { + self.isa.frontend_config() } 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 new_base = func.create_global_value(ir::GlobalValueData::VMContext { - offset: Offset32::new(0), + let new_base = func.create_global_value(ir::GlobalValueData::Load { + 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); new_base }); - let offset = index * pointer_bytes; - let offset32 = offset as i32; - debug_assert_eq!(offset32 as usize, offset); - let gv = func.create_global_value(ir::GlobalValueData::Deref { + // For now, give each global gets a pointer-sized region of + // storage, regardless of its type. + let offset = index.index() * mem::size_of::<*mut u8>(); + let gv = func.create_global_value(ir::GlobalValueData::IAddImm { base: globals_base, - offset: Offset32::new(offset32), - memory_type: self.pointer_type(), + offset: Imm64::new(offset as i64), + global_type: self.pointer_type(), }); GlobalVariable::Memory { 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 { - let pointer_bytes = self.pointer_bytes(); + let vmctx = self.vmctx(func); let memories_base = self.memories_base.unwrap_or_else(|| { - let new_base = func.create_global_value(ir::GlobalValueData::VMContext { - offset: Offset32::new(pointer_bytes as i32), + let new_base = func.create_global_value(ir::GlobalValueData::Load { + 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); new_base }); - let offset = index * pointer_bytes; + let offset = index.index() * mem::size_of::(); let offset32 = offset as i32; 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, - offset: Offset32::new(offset32), - memory_type: self.pointer_type(), - }); - let heap_base = func.create_global_value(ir::GlobalValueData::Deref { - base: heap_base_addr, - offset: Offset32::new(0), - memory_type: self.pointer_type(), + offset: Offset32::new(offset32 + offset_of!(vmcontext::VMMemory, base) as i32), + global_type: self.pointer_type(), + readonly: readonly_base, }); func.create_heap(ir::HeapData { base: heap_base, min_size: 0.into(), - guard_size: 0x8000_0000.into(), - style: ir::HeapStyle::Static { - bound: 0x1_0000_0000.into(), - }, + guard_size, + style: heap_style, index_type: I32, }) } - fn make_table(&mut self, func: &mut ir::Function, _index: TableIndex) -> ir::Table { - let pointer_bytes = self.pointer_bytes(); - let base_gv_addr = func.create_global_value(ir::GlobalValueData::VMContext { - offset: Offset32::new(pointer_bytes as i32 * 2), + fn make_table(&mut self, func: &mut ir::Function, index: TableIndex) -> ir::Table { + let vmctx = self.vmctx(func); + let tables_base = self.tables_base.unwrap_or_else(|| { + 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 { - base: base_gv_addr, - offset: 0.into(), - memory_type: self.pointer_type(), + let offset = index.index() * mem::size_of::(); + let offset32 = offset as i32; + debug_assert_eq!(offset32 as usize, offset); + 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 { - offset: Offset32::new(pointer_bytes as i32 * 3), - }); - let bound_gv = func.create_global_value(ir::GlobalValueData::Deref { - base: bound_gv_addr, - offset: 0.into(), - memory_type: I32, + let bound_gv = func.create_global_value(ir::GlobalValueData::Load { + base: tables_base, + offset: Offset32::new( + offset32 + offset_of!(vmcontext::VMTable, current_num_elements) as i32, + ), + global_type: I32, + readonly: false, }); func.create_table(ir::TableData { @@ -375,7 +430,11 @@ impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'m ) -> WasmResult { // TODO: Cranelift's call_indirect doesn't implement signature checking, // 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); @@ -411,7 +470,7 @@ impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'m ) -> WasmResult { let grow_mem_func = self.grow_memory_extfunc.unwrap_or_else(|| { 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![ 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); - 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 call_inst = pos.ins().call(grow_mem_func, &[val, memory_index, vmctx]); Ok(*pos.func.dfg.inst_results(call_inst).first().unwrap()) @@ -444,7 +503,7 @@ impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'m ) -> WasmResult { let cur_mem_func = self.current_memory_extfunc.unwrap_or_else(|| { 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![ AbiParam::new(I32), 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); - 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 call_inst = pos.ins().call(cur_mem_func, &[memory_index, vmctx]); Ok(*pos.func.dfg.inst_results(call_inst).first().unwrap()) diff --git a/lib/environ/src/lib.rs b/lib/environ/src/lib.rs index 810a8258b9..2c99a49a9a 100644 --- a/lib/environ/src/lib.rs +++ b/lib/environ/src/lib.rs @@ -36,10 +36,13 @@ extern crate cranelift_codegen; extern crate cranelift_entity; extern crate cranelift_wasm; extern crate target_lexicon; +#[macro_use] +extern crate memoffset; mod compilation; mod environ; mod module; +mod vmcontext; pub use compilation::{compile_module, Compilation, Relocation, RelocationTarget, Relocations}; pub use environ::{ModuleEnvironment, ModuleTranslation}; diff --git a/lib/environ/src/vmcontext.rs b/lib/environ/src/vmcontext.rs new file mode 100644 index 0000000000..098b22c454 --- /dev/null +++ b/lib/environ/src/vmcontext.rs @@ -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, +} diff --git a/lib/execute/Cargo.toml b/lib/execute/Cargo.toml index 2dd1e48735..16af933a91 100644 --- a/lib/execute/Cargo.toml +++ b/lib/execute/Cargo.toml @@ -9,9 +9,9 @@ repository = "https://github.com/CraneStation/wasmtime" license = "Apache-2.0 WITH LLVM-exception" [dependencies] -cranelift-codegen = "0.20.0" -cranelift-entity = "0.20.1" -cranelift-wasm = "0.20.1" +cranelift-codegen = "0.22.0" +cranelift-entity = "0.22.0" +cranelift-wasm = "0.22.0" region = "0.3.0" wasmtime-environ = { path = "../environ" } -memmap = "0.6.2" +memmap = "0.7.0" diff --git a/lib/execute/src/execute.rs b/lib/execute/src/execute.rs index edbdb30d09..90e0f1ee9b 100644 --- a/lib/execute/src/execute.rs +++ b/lib/execute/src/execute.rs @@ -84,6 +84,7 @@ fn relocate( extern "C" fn grow_memory(size: u32, memory_index: u32, vmctx: *mut *mut u8) -> u32 { unsafe { + // FIXME: update the VMMemory's size let instance = (*vmctx.offset(4)) as *mut Instance; (*instance) .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 { unsafe { + // FIXME: read the VMMemory's size instead let instance = (*vmctx.offset(4)) as *mut Instance; (*instance) .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())) .unwrap_or((ptr::null_mut(), 0)); + // FIXME: Actually use environ's VMContext struct let mut vmctx = Vec::new(); 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); + // FIXME: These need to be VMTable now vmctx.push(default_table_ptr); vmctx.push(default_table_len as *mut u8); vmctx.push(instance as *mut Instance as *mut u8); @@ -134,7 +139,7 @@ pub fn execute( let start_index = module .start_func .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. for code_buf in compilation.functions.values() { match unsafe { diff --git a/lib/obj/Cargo.toml b/lib/obj/Cargo.toml index 85b92e72ae..c37d1253e8 100644 --- a/lib/obj/Cargo.toml +++ b/lib/obj/Cargo.toml @@ -8,7 +8,7 @@ categories = ["wasm"] license = "Apache-2.0 WITH LLVM-exception" [dependencies] -cranelift-codegen = "0.20.0" -cranelift-entity = "0.20.1" +cranelift-codegen = "0.22.0" +cranelift-entity = "0.22.0" wasmtime-environ = { path = "../environ" } faerie = "0.5.0"