From d699e610832ef4149529f6e4d56a2f4980b1dbf0 Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Thu, 6 May 2021 09:07:51 -0700 Subject: [PATCH 1/4] Implement bulk memory instructions in compiler-llvm --- lib/compiler-llvm/src/translator/code.rs | 82 ++++++++++++++++++ .../src/translator/intrinsics.rs | 83 +++++++++++++++++++ tests/ignores.txt | 7 -- 3 files changed, 165 insertions(+), 7 deletions(-) diff --git a/lib/compiler-llvm/src/translator/code.rs b/lib/compiler-llvm/src/translator/code.rs index 742ec81a8f4..f74fe59a4a7 100644 --- a/lib/compiler-llvm/src/translator/code.rs +++ b/lib/compiler-llvm/src/translator/code.rs @@ -9388,6 +9388,88 @@ impl<'ctx, 'a> LLVMFunctionCodeGenerator<'ctx, 'a> { size.add_attribute(AttributeLoc::Function, self.intrinsics.readonly); self.state.push1(size.try_as_basic_value().left().unwrap()); } + Operator::MemoryInit { segment, mem } => { + let (dest, src, len) = self.state.pop3()?; + let mem = self + .intrinsics + .i32_ty + .const_int(mem.into(), false) + .as_basic_value_enum(); + let segment = self + .intrinsics + .i32_ty + .const_int(segment.into(), false) + .as_basic_value_enum(); + self.builder.build_call( + self.intrinsics.memory_init, + &[vmctx.as_basic_value_enum(), mem, segment, dest, src, len], + "", + ); + } + Operator::DataDrop { segment } => { + let segment = self + .intrinsics + .i32_ty + .const_int(segment.into(), false) + .as_basic_value_enum(); + self.builder.build_call( + self.intrinsics.data_drop, + &[vmctx.as_basic_value_enum(), segment], + "", + ); + } + Operator::MemoryCopy { src, dst } => { + // ignored until we support multiple memories + let _dst = dst; + let (memory_copy, src) = if let Some(local_memory_index) = self + .wasm_module + .local_memory_index(MemoryIndex::from_u32(src)) + { + (self.intrinsics.memory_copy, local_memory_index.as_u32()) + } else { + (self.intrinsics.imported_memory_copy, src) + }; + + let (dest_pos, src_pos, len) = self.state.pop3()?; + let src_index = self + .intrinsics + .i32_ty + .const_int(src.into(), false) + .as_basic_value_enum(); + self.builder.build_call( + memory_copy, + &[ + vmctx.as_basic_value_enum(), + src_index, + dest_pos, + src_pos, + len, + ], + "", + ); + } + Operator::MemoryFill { mem } => { + let (memory_fill, mem) = if let Some(local_memory_index) = self + .wasm_module + .local_memory_index(MemoryIndex::from_u32(mem)) + { + (self.intrinsics.memory_fill, local_memory_index.as_u32()) + } else { + (self.intrinsics.imported_memory_fill, mem) + }; + + let (dst, val, len) = self.state.pop3()?; + let mem_index = self + .intrinsics + .i32_ty + .const_int(mem.into(), false) + .as_basic_value_enum(); + self.builder.build_call( + memory_fill, + &[vmctx.as_basic_value_enum(), mem_index, dst, val, len], + "", + ); + } /*************************** * Reference types. * https://github.com/WebAssembly/reference-types/blob/master/proposals/reference-types/Overview.md diff --git a/lib/compiler-llvm/src/translator/intrinsics.rs b/lib/compiler-llvm/src/translator/intrinsics.rs index a2274cac5a7..ecc4d059e09 100644 --- a/lib/compiler-llvm/src/translator/intrinsics.rs +++ b/lib/compiler-llvm/src/translator/intrinsics.rs @@ -181,8 +181,15 @@ pub struct Intrinsics<'ctx> { pub imported_table_set: FunctionValue<'ctx>, pub table_grow: FunctionValue<'ctx>, pub imported_table_grow: FunctionValue<'ctx>, + pub memory_init: FunctionValue<'ctx>, + pub data_drop: FunctionValue<'ctx>, pub func_ref: FunctionValue<'ctx>, pub elem_drop: FunctionValue<'ctx>, + pub memory_copy: FunctionValue<'ctx>, + pub imported_memory_copy: FunctionValue<'ctx>, + pub memory_fill: FunctionValue<'ctx>, + pub imported_memory_fill: FunctionValue<'ctx>, + pub throw_trap: FunctionValue<'ctx>, // VM builtins. @@ -594,6 +601,82 @@ impl<'ctx> Intrinsics<'ctx> { ), None, ), + memory_init: module.add_function( + "wasmer_vm_memory32_init", + void_ty.fn_type( + &[ + ctx_ptr_ty.as_basic_type_enum(), + i32_ty_basic, + i32_ty_basic, + i32_ty_basic, + i32_ty_basic, + i32_ty_basic, + ], + false, + ), + None, + ), + memory_copy: module.add_function( + "wasmer_vm_memory32_copy", + void_ty.fn_type( + &[ + ctx_ptr_ty.as_basic_type_enum(), + i32_ty_basic, + i32_ty_basic, + i32_ty_basic, + i32_ty_basic, + ], + false, + ), + None, + ), + imported_memory_copy: module.add_function( + "wasmer_vm_imported_memory32_copy", + void_ty.fn_type( + &[ + ctx_ptr_ty.as_basic_type_enum(), + i32_ty_basic, + i32_ty_basic, + i32_ty_basic, + i32_ty_basic, + ], + false, + ), + None, + ), + memory_fill: module.add_function( + "wasmer_vm_memory32_fill", + void_ty.fn_type( + &[ + ctx_ptr_ty.as_basic_type_enum(), + i32_ty_basic, + i32_ty_basic, + i32_ty_basic, + i32_ty_basic, + ], + false, + ), + None, + ), + imported_memory_fill: module.add_function( + "wasmer_vm_imported_memory32_fill", + void_ty.fn_type( + &[ + ctx_ptr_ty.as_basic_type_enum(), + i32_ty_basic, + i32_ty_basic, + i32_ty_basic, + i32_ty_basic, + ], + false, + ), + None, + ), + data_drop: module.add_function( + "wasmer_vm_data_drop", + void_ty.fn_type(&[ctx_ptr_ty.as_basic_type_enum(), i32_ty_basic], false), + None, + ), func_ref: module.add_function( "wasmer_vm_func_ref", funcref_ty.fn_type(&[ctx_ptr_ty.as_basic_type_enum(), i32_ty_basic], false), diff --git a/tests/ignores.txt b/tests/ignores.txt index add4976cb9d..416ee1ac939 100644 --- a/tests/ignores.txt +++ b/tests/ignores.txt @@ -97,8 +97,6 @@ wasitests::snapshot1::unix_open_special_files on windows # Updated spectests -# bulk memory -cranelift::spec::bulk # new simd cranelift::spec::simd::simd_align cranelift::spec::simd::simd_conversions @@ -134,11 +132,6 @@ cranelift::spec::simd::simd_store32_lane cranelift::spec::simd::simd_store64_lane cranelift::spec::simd::simd_store8_lane -# bulk memory -llvm::spec::bulk -llvm::spec::memory_copy -llvm::spec::memory_fill -llvm::spec::memory_init # new simd llvm::spec::simd::simd_align llvm::spec::simd::simd_conversions From b2aee21e67c61a2ff3de9b6a096ad4296eba455b Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Thu, 6 May 2021 09:25:29 -0700 Subject: [PATCH 2/4] (debug) disable cranelift bulk.wast to test LLVM's --- tests/ignores.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/ignores.txt b/tests/ignores.txt index 416ee1ac939..5bbd639a568 100644 --- a/tests/ignores.txt +++ b/tests/ignores.txt @@ -97,6 +97,8 @@ wasitests::snapshot1::unix_open_special_files on windows # Updated spectests +# bulk memory +cranelift::spec::bulk # new simd cranelift::spec::simd::simd_align cranelift::spec::simd::simd_conversions From 1dc1ceac1f7b6040a4ad9768f2aa3d1dea495780 Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Thu, 6 May 2021 10:54:04 -0700 Subject: [PATCH 3/4] Implement bulk memory instructions in Singlepass --- lib/compiler-singlepass/src/codegen_x64.rs | 154 +++++++++++++++++++++ tests/ignores.txt | 5 - 2 files changed, 154 insertions(+), 5 deletions(-) diff --git a/lib/compiler-singlepass/src/codegen_x64.rs b/lib/compiler-singlepass/src/codegen_x64.rs index 2e3b25422d5..6a7670fcdaf 100644 --- a/lib/compiler-singlepass/src/codegen_x64.rs +++ b/lib/compiler-singlepass/src/codegen_x64.rs @@ -5679,6 +5679,160 @@ impl<'a> FuncGen<'a> { self.assembler .emit_mov(Size::S64, Location::GPR(GPR::RAX), ret); } + Operator::MemoryInit { segment, mem } => { + let len = self.value_stack.pop().unwrap(); + let src = self.value_stack.pop().unwrap(); + let dst = self.value_stack.pop().unwrap(); + self.machine.release_locations_only_regs(&[len, src, dst]); + + self.assembler.emit_mov( + Size::S64, + Location::Memory( + Machine::get_vmctx_reg(), + self.vmoffsets + .vmctx_builtin_function(VMBuiltinFunctionIndex::get_memory_init_index()) + as i32, + ), + Location::GPR(GPR::RAX), + ); + + // TODO: should this be 3? + self.machine.release_locations_only_osr_state(1); + + self.emit_call_sysv( + |this| { + this.assembler.emit_call_register(GPR::RAX); + }, + // [vmctx, memory_index, segment_index, dst, src, len] + [ + Location::Imm32(mem), + Location::Imm32(segment), + dst, + src, + len, + ] + .iter() + .cloned(), + )?; + self.machine + .release_locations_only_stack(&mut self.assembler, &[dst, src, len]); + } + Operator::DataDrop { segment } => { + self.assembler.emit_mov( + Size::S64, + Location::Memory( + Machine::get_vmctx_reg(), + self.vmoffsets + .vmctx_builtin_function(VMBuiltinFunctionIndex::get_data_drop_index()) + as i32, + ), + Location::GPR(GPR::RAX), + ); + + self.emit_call_sysv( + |this| { + this.assembler.emit_call_register(GPR::RAX); + }, + // [vmctx, segment_index] + iter::once(Location::Imm32(segment)), + )?; + } + Operator::MemoryCopy { src, dst } => { + // ignore until we support multiple memories + let _dst = dst; + let len = self.value_stack.pop().unwrap(); + let src_pos = self.value_stack.pop().unwrap(); + let dst_pos = self.value_stack.pop().unwrap(); + self.machine + .release_locations_only_regs(&[len, src_pos, dst_pos]); + + let memory_index = MemoryIndex::new(src as usize); + let (memory_copy_index, memory_index) = + if self.module.local_memory_index(memory_index).is_some() { + ( + VMBuiltinFunctionIndex::get_memory_copy_index(), + memory_index, + ) + } else { + ( + VMBuiltinFunctionIndex::get_imported_memory_copy_index(), + memory_index, + ) + }; + + self.assembler.emit_mov( + Size::S64, + Location::Memory( + Machine::get_vmctx_reg(), + self.vmoffsets.vmctx_builtin_function(memory_copy_index) as i32, + ), + Location::GPR(GPR::RAX), + ); + + // TODO: should this be 3? + self.machine.release_locations_only_osr_state(1); + + self.emit_call_sysv( + |this| { + this.assembler.emit_call_register(GPR::RAX); + }, + // [vmctx, memory_index, dst, src, len] + [ + Location::Imm32(memory_index.index() as u32), + dst_pos, + src_pos, + len, + ] + .iter() + .cloned(), + )?; + self.machine + .release_locations_only_stack(&mut self.assembler, &[dst_pos, src_pos, len]); + } + Operator::MemoryFill { mem } => { + let len = self.value_stack.pop().unwrap(); + let val = self.value_stack.pop().unwrap(); + let dst = self.value_stack.pop().unwrap(); + self.machine.release_locations_only_regs(&[len, val, dst]); + + let memory_index = MemoryIndex::new(mem as usize); + let (memory_fill_index, memory_index) = + if self.module.local_memory_index(memory_index).is_some() { + ( + VMBuiltinFunctionIndex::get_memory_fill_index(), + memory_index, + ) + } else { + ( + VMBuiltinFunctionIndex::get_imported_memory_fill_index(), + memory_index, + ) + }; + + self.assembler.emit_mov( + Size::S64, + Location::Memory( + Machine::get_vmctx_reg(), + self.vmoffsets.vmctx_builtin_function(memory_fill_index) as i32, + ), + Location::GPR(GPR::RAX), + ); + + // TODO: should this be 3? + self.machine.release_locations_only_osr_state(1); + + self.emit_call_sysv( + |this| { + this.assembler.emit_call_register(GPR::RAX); + }, + // [vmctx, memory_index, dst, src, len] + [Location::Imm32(memory_index.index() as u32), dst, val, len] + .iter() + .cloned(), + )?; + self.machine + .release_locations_only_stack(&mut self.assembler, &[dst, val, len]); + } Operator::MemoryGrow { mem, mem_byte: _ } => { let memory_index = MemoryIndex::new(mem as usize); let param_pages = self.value_stack.pop().unwrap(); diff --git a/tests/ignores.txt b/tests/ignores.txt index 5bbd639a568..b02f7963984 100644 --- a/tests/ignores.txt +++ b/tests/ignores.txt @@ -169,8 +169,3 @@ llvm::spec::simd::simd_store32_lane llvm::spec::simd::simd_store64_lane llvm::spec::simd::simd_store8_lane -# bulk memory -singlepass::spec::bulk -singlepass::spec::memory_copy -singlepass::spec::memory_fill -singlepass::spec::memory_init From ac1f331032ed0c5f0b8b60ba4c75f2ea91f31889 Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Thu, 6 May 2021 10:59:20 -0700 Subject: [PATCH 4/4] Add entry about bulk memory in CL --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7443341aa84..47ee3abd40a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ Looking for changes that affect our C API? See the [C API Changelog](lib/c-api/C ## **[Unreleased]** ### Added +- [#2296](https://github.com/wasmerio/wasmer/pull/2296) Add support for the bulk memory proposal in compiler Singlepass and compiler LLVM. - [#2208](https://github.com/wasmerio/wasmer/pull/2208) Add a new CHANGELOG.md specific to our C API to make it easier for users primarily consuming our C API to keep up to date with changes that affect them. - [#2103](https://github.com/wasmerio/wasmer/pull/2103) Add middleware (incl. metering) in the C API. - [#2003](https://github.com/wasmerio/wasmer/pull/2003) Wasmer works with musl, and is built, tested and packaged for musl.