diff --git a/build.rs b/build.rs index a55f3b5da16f..eada6eceaac5 100644 --- a/build.rs +++ b/build.rs @@ -200,11 +200,6 @@ fn ignore(testsuite: &str, testname: &str, strategy: &str) -> bool { ("simd", "simd_load") => return true, // FIXME Unsupported feature: proposed SIMD operator I32x4TruncSatF32x4S ("simd", "simd_splat") => return true, // FIXME Unsupported feature: proposed SIMD operator I32x4TruncSatF32x4S - // Still working on implementing these. See #929. - ("reference_types", "table_fill") => { - return true; - } - // TODO(#1886): Ignore reference types tests if this isn't x64, // because Cranelift only supports reference types on x64. ("reference_types", _) => { diff --git a/crates/environ/src/func_environ.rs b/crates/environ/src/func_environ.rs index e325d7696963..2def7447dabe 100644 --- a/crates/environ/src/func_environ.rs +++ b/crates/environ/src/func_environ.rs @@ -170,6 +170,10 @@ declare_builtin_functions! { table_grow_funcref(vmctx, i32, i32, pointer) -> (i32); /// Returns an index for Wasm's `table.grow` instruction for `externref`s. table_grow_externref(vmctx, i32, i32, reference) -> (i32); + /// Returns an index for Wasm's `table.fill` instruction for `externref`s. + table_fill_externref(vmctx, i32, i32, reference, i32) -> (); + /// Returns an index for Wasm's `table.fill` instruction for `funcref`s. + table_fill_funcref(vmctx, i32, i32, pointer, i32) -> (); /// Returns an index to drop a `VMExternRef`. drop_externref(pointer) -> (); /// Returns an index to do a GC and then insert a `VMExternRef` into the @@ -1009,15 +1013,41 @@ impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'m fn translate_table_fill( &mut self, - _: cranelift_codegen::cursor::FuncCursor<'_>, - _: TableIndex, - _: ir::Value, - _: ir::Value, - _: ir::Value, + mut pos: cranelift_codegen::cursor::FuncCursor<'_>, + table_index: TableIndex, + dst: ir::Value, + val: ir::Value, + len: ir::Value, ) -> WasmResult<()> { - Err(WasmError::Unsupported( - "the `table.fill` instruction is not supported yet".into(), - )) + let (builtin_idx, builtin_sig) = + match self.module.table_plans[table_index].table.wasm_ty { + WasmType::FuncRef => ( + BuiltinFunctionIndex::table_fill_funcref(), + self.builtin_function_signatures + .table_fill_funcref(&mut pos.func), + ), + WasmType::ExternRef => ( + BuiltinFunctionIndex::table_fill_externref(), + self.builtin_function_signatures + .table_fill_externref(&mut pos.func), + ), + _ => return Err(WasmError::Unsupported( + "`table.fill` with a table element type that is not `funcref` or `externref`" + .into(), + )), + }; + + let (vmctx, builtin_addr) = + self.translate_load_builtin_function_address(&mut pos, builtin_idx); + + let table_index_arg = pos.ins().iconst(I32, table_index.as_u32() as i64); + pos.ins().call_indirect( + builtin_sig, + builtin_addr, + &[vmctx, table_index_arg, dst, val, len], + ); + + Ok(()) } fn translate_ref_null( diff --git a/crates/runtime/src/libcalls.rs b/crates/runtime/src/libcalls.rs index c1ff290656d4..4dcb0963de4a 100644 --- a/crates/runtime/src/libcalls.rs +++ b/crates/runtime/src/libcalls.rs @@ -265,6 +265,41 @@ pub unsafe extern "C" fn wasmtime_table_grow( } } +/// Implementation of `table.fill`. +pub unsafe extern "C" fn wasmtime_table_fill( + vmctx: *mut VMContext, + table_index: u32, + dst: u32, + // NB: we don't know whether this is a `VMExternRef` or a pointer to a + // `VMCallerCheckedAnyfunc` until we look at the table's element type. + val: *mut u8, + len: u32, +) { + let result = { + let instance = (&mut *vmctx).instance(); + let table_index = TableIndex::from_u32(table_index); + let table = instance.get_table(table_index); + match table.element_type() { + TableElementType::Func => { + let val = val as *mut VMCallerCheckedAnyfunc; + table.fill(dst, val.into(), len) + } + TableElementType::Val(ty) => { + debug_assert_eq!(ty, crate::ref_type()); + let val = if val.is_null() { + None + } else { + Some(VMExternRef::clone_from_raw(val)) + }; + table.fill(dst, val.into(), len) + } + } + }; + if let Err(trap) = result { + raise_lib_trap(trap); + } +} + /// Implementation of `table.copy`. pub unsafe extern "C" fn wasmtime_table_copy( vmctx: *mut VMContext, diff --git a/crates/runtime/src/table.rs b/crates/runtime/src/table.rs index f3136d3f3203..462480bc7672 100644 --- a/crates/runtime/src/table.rs +++ b/crates/runtime/src/table.rs @@ -67,6 +67,26 @@ impl Table { } } + /// Fill `table[dst..dst + len]` with `val`. + /// + /// Returns a trap error on out-of-bounds accesses. + pub fn fill(&self, dst: u32, val: TableElement, len: u32) -> Result<(), Trap> { + let start = dst; + let end = start + .checked_add(len) + .ok_or_else(|| Trap::wasm(ir::TrapCode::TableOutOfBounds))?; + + if end > self.size() { + return Err(Trap::wasm(ir::TrapCode::TableOutOfBounds)); + } + + for i in start..end { + self.set(i, val.clone()).unwrap(); + } + + Ok(()) + } + /// Grow table by the specified amount of elements. /// /// Returns the previous size of the table if growth is successful. diff --git a/crates/runtime/src/vmcontext.rs b/crates/runtime/src/vmcontext.rs index 971bd1eb7299..b3c1aea6144a 100644 --- a/crates/runtime/src/vmcontext.rs +++ b/crates/runtime/src/vmcontext.rs @@ -594,6 +594,10 @@ impl VMBuiltinFunctionsArray { wasmtime_externref_global_get as usize; ptrs[BuiltinFunctionIndex::externref_global_set().index() as usize] = wasmtime_externref_global_set as usize; + ptrs[BuiltinFunctionIndex::table_fill_externref().index() as usize] = + wasmtime_table_fill as usize; + ptrs[BuiltinFunctionIndex::table_fill_funcref().index() as usize] = + wasmtime_table_fill as usize; if cfg!(debug_assertions) { for i in 0..ptrs.len() {