From 6992828f3e7d15ef12f67e7dea37759762478813 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sa=C3=BAl=20Cabrera?= Date: Wed, 4 Oct 2023 20:48:25 -0400 Subject: [PATCH] winch(x64): Add support for table instructions This change adds support for the following table insructions: `elem.drop`, `table.copy`, `table.set`, `table.get`, `table.fill`, `table.grow`, `table.size`, `table.init`. This change also introduces partial support for the `Ref` WebAssembly type, more conretely the `Func` heap type, which means that all the table instructions above, only work this WebAssembly type as of this change. Finally, this change is also a small follow up to the primitives introduced in https://github.com/bytecodealliance/wasmtime/pull/7100, more concretely: * `FnCall::with_lib`: tracks the presence of a libcall and ensures that any result registers are freed right when the call is emitted. * `MacroAssembler::table_elem_addr` returns an address rather than the value of the address, making it convenient for other use cases like `table.set`. -- prtest:full --- build.rs | 29 +- fuzz/fuzz_targets/differential.rs | 10 +- tests/misc_testsuite/winch/table_fill.wast | 65 ++++ tests/misc_testsuite/winch/table_get.wast | 13 + tests/misc_testsuite/winch/table_grow.wast | 26 ++ tests/misc_testsuite/winch/table_set.wast | 27 ++ winch/codegen/src/abi/mod.rs | 12 +- winch/codegen/src/codegen/call.rs | 26 +- winch/codegen/src/codegen/context.rs | 8 +- winch/codegen/src/codegen/env.rs | 78 +++-- winch/codegen/src/codegen/mod.rs | 90 +++++- winch/codegen/src/isa/aarch64/masm.rs | 16 +- winch/codegen/src/isa/x64/abi.rs | 19 +- winch/codegen/src/isa/x64/masm.rs | 69 ++-- winch/codegen/src/masm.rs | 20 +- winch/codegen/src/stack.rs | 27 +- winch/codegen/src/visitor.rs | 300 +++++++++++++++--- .../x64/call_indirect/call_indirect.wat | 140 ++++---- winch/filetests/filetests/x64/table/fill.wat | 99 ++++++ winch/filetests/filetests/x64/table/get.wat | 54 ++++ winch/filetests/filetests/x64/table/grow.wat | 30 ++ .../filetests/x64/table/init_copy_drop.wat | 225 +++++++++++++ winch/filetests/filetests/x64/table/set.wat | 105 ++++++ winch/filetests/filetests/x64/table/size.wat | 15 + 24 files changed, 1303 insertions(+), 200 deletions(-) create mode 100644 tests/misc_testsuite/winch/table_fill.wast create mode 100644 tests/misc_testsuite/winch/table_get.wast create mode 100644 tests/misc_testsuite/winch/table_grow.wast create mode 100644 tests/misc_testsuite/winch/table_set.wast create mode 100644 winch/filetests/filetests/x64/table/fill.wat create mode 100644 winch/filetests/filetests/x64/table/get.wat create mode 100644 winch/filetests/filetests/x64/table/grow.wat create mode 100644 winch/filetests/filetests/x64/table/init_copy_drop.wat create mode 100644 winch/filetests/filetests/x64/table/set.wat create mode 100644 winch/filetests/filetests/x64/table/size.wat diff --git a/build.rs b/build.rs index 041373eba0c3..64584ca886c4 100644 --- a/build.rs +++ b/build.rs @@ -205,19 +205,34 @@ fn ignore(testsuite: &str, testname: &str, strategy: &str) -> bool { // We ignore tests that assert for traps on windows, given // that Winch doesn't encode unwind information for Windows, yet. if strategy == "Winch" { + let assert_trap = [ + "i32", + "i64", + "call_indirect", + "table_fill", + "table_init", + "table_copy", + "table_set", + "table_get", + ] + .contains(&testname); + + if assert_trap && env::var("CARGO_CFG_TARGET_OS").unwrap().as_str() == "windows" { + return true; + } + if testsuite == "misc_testsuite" { // The misc/call_indirect is fully supported by Winch. - if testname == "call_indirect" { - return false; + if testname != "call_indirect" { + return true; } } - if testsuite != "winch" { - return true; + if testsuite == "spec_testsuite" { + // The official table init and table copy tests are now supported. + return !["table_init", "table_copy"].contains(&testname); } - let assert_trap = ["i32", "i64", "call_indirect"].contains(&testname); - - if assert_trap && env::var("CARGO_CFG_TARGET_OS").unwrap().as_str() == "windows" { + if testsuite != "winch" { return true; } } diff --git a/fuzz/fuzz_targets/differential.rs b/fuzz/fuzz_targets/differential.rs index 34164666ebf0..d4bdd3bdd35d 100644 --- a/fuzz/fuzz_targets/differential.rs +++ b/fuzz/fuzz_targets/differential.rs @@ -375,7 +375,15 @@ fn winch_supports_module(module: &[u8]) -> bool { | F64Abs { .. } | F32Neg { .. } | F64Neg { .. } - | CallIndirect { .. } => {} + | CallIndirect { .. } + | ElemDrop { .. } + | TableCopy { .. } + | TableSet { .. } + | TableGet { .. } + | TableFill { .. } + | TableGrow { .. } + | TableSize { .. } + | TableInit { .. } => {} _ => { supported = false; break 'main; diff --git a/tests/misc_testsuite/winch/table_fill.wast b/tests/misc_testsuite/winch/table_fill.wast new file mode 100644 index 000000000000..56a98c164eb6 --- /dev/null +++ b/tests/misc_testsuite/winch/table_fill.wast @@ -0,0 +1,65 @@ +(module + (type $t0 (func)) + (func $f1 (type $t0)) + (func $f2 (type $t0)) + (func $f3 (type $t0)) + + ;; Define two tables of funcref + (table $t1 3 funcref) + (table $t2 10 funcref) + + ;; Initialize table $t1 with functions $f1, $f2, $f3 + (elem (i32.const 0) $f1 $f2 $f3) + + ;; Function to fill table $t1 using a function reference from table $t2 + (func (export "fill") (param $i i32) (param $r i32) (param $n i32) + (local $ref funcref) + (local.set $ref (table.get $t1 (local.get $r))) + (table.fill $t2 (local.get $i) (local.get $ref) (local.get $n)) + ) + + (func (export "get") (param $i i32) (result funcref) + (table.get $t2 (local.get $i)) + ) +) + +(assert_return (invoke "get" (i32.const 1)) (ref.null func)) +(assert_return (invoke "get" (i32.const 2)) (ref.null func)) +(assert_return (invoke "get" (i32.const 3)) (ref.null func)) +(assert_return (invoke "get" (i32.const 4)) (ref.null func)) +(assert_return (invoke "get" (i32.const 5)) (ref.null func)) + +(assert_return (invoke "fill" (i32.const 2) (i32.const 0) (i32.const 3))) +(assert_return (invoke "get" (i32.const 1)) (ref.null func)) +(assert_return (invoke "get" (i32.const 2)) (ref.func 0)) +(assert_return (invoke "get" (i32.const 3)) (ref.func 0)) +(assert_return (invoke "get" (i32.const 4)) (ref.func 0)) +(assert_return (invoke "get" (i32.const 5)) (ref.null func)) + +(assert_return (invoke "fill" (i32.const 4) (i32.const 1) (i32.const 2))) +(assert_return (invoke "get" (i32.const 3)) (ref.func 0)) +(assert_return (invoke "get" (i32.const 4)) (ref.func 1)) +(assert_return (invoke "get" (i32.const 5)) (ref.func 1)) +(assert_return (invoke "get" (i32.const 6)) (ref.null func)) + +(assert_return (invoke "fill" (i32.const 4) (i32.const 2) (i32.const 0))) +(assert_return (invoke "get" (i32.const 3)) (ref.func 0)) +(assert_return (invoke "get" (i32.const 4)) (ref.func 1)) +(assert_return (invoke "get" (i32.const 5)) (ref.func 1)) + +(assert_return (invoke "fill" (i32.const 8) (i32.const 0) (i32.const 2))) +(assert_return (invoke "get" (i32.const 7)) (ref.null func)) +(assert_return (invoke "get" (i32.const 8)) (ref.func 0)) +(assert_return (invoke "get" (i32.const 9)) (ref.func 0)) + +(assert_return (invoke "fill" (i32.const 9) (i32.const 2) (i32.const 1))) +(assert_return (invoke "get" (i32.const 8)) (ref.func 0)) +(assert_return (invoke "get" (i32.const 9)) (ref.func 2)) + +(assert_return (invoke "fill" (i32.const 10) (i32.const 1) (i32.const 0))) +(assert_return (invoke "get" (i32.const 9)) (ref.func 2)) + +(assert_trap + (invoke "fill" (i32.const 8) (i32.const 0) (i32.const 3)) + "out of bounds table access" +) diff --git a/tests/misc_testsuite/winch/table_get.wast b/tests/misc_testsuite/winch/table_get.wast new file mode 100644 index 000000000000..09f57cace9f3 --- /dev/null +++ b/tests/misc_testsuite/winch/table_get.wast @@ -0,0 +1,13 @@ +(module + (table $t3 3 funcref) + (elem (table $t3) (i32.const 1) func $dummy) + (func $dummy) + (func $f3 (export "get-funcref") (param $i i32) (result funcref) + (table.get $t3 (local.get $i)) + ) +) + +(assert_return (invoke "get-funcref" (i32.const 0)) (ref.null func)) +(assert_trap (invoke "get-funcref" (i32.const 3)) "out of bounds table access") +(assert_trap (invoke "get-funcref" (i32.const -1)) "out of bounds table access") + diff --git a/tests/misc_testsuite/winch/table_grow.wast b/tests/misc_testsuite/winch/table_grow.wast new file mode 100644 index 000000000000..be293cb79469 --- /dev/null +++ b/tests/misc_testsuite/winch/table_grow.wast @@ -0,0 +1,26 @@ +(module + (table $t1 0 funcref) + + (func (export "grow-by-10") (param $r funcref) (result i32) + (table.grow $t1 (local.get $r) (i32.const 10)) + ) + (func (export "grow-over") (param $r funcref) (result i32) + (table.grow $t1 (local.get $r) (i32.const 0xffff_fff0)) + ) + + (func (export "size") (result i32) + (table.size $t1)) +) + +(assert_return (invoke "size") (i32.const 0)) +(assert_return (invoke "grow-by-10" (ref.null func)) (i32.const 0)) +(assert_return (invoke "size") (i32.const 10)) + +(module + (table $t 0x10 funcref) + (func $f (export "grow") (param $r funcref) (result i32) + (table.grow $t (local.get $r) (i32.const 0xffff_fff0)) + ) +) + +(assert_return (invoke "grow" (ref.null func)) (i32.const -1)) diff --git a/tests/misc_testsuite/winch/table_set.wast b/tests/misc_testsuite/winch/table_set.wast new file mode 100644 index 000000000000..fb1f783883f2 --- /dev/null +++ b/tests/misc_testsuite/winch/table_set.wast @@ -0,0 +1,27 @@ +(module + (table $t3 2 funcref) + (elem (table $t3) (i32.const 1) func $dummy) + (func $dummy) + + (func $f3 (export "get-funcref") (param $i i32) (result funcref) + (table.get $t3 (local.get $i)) + ) + + (func (export "set-funcref") (param $i i32) (param $r funcref) + (table.set $t3 (local.get $i) (local.get $r)) + ) + (func (export "set-funcref-from") (param $i i32) (param $j i32) + (table.set $t3 (local.get $i) (table.get $t3 (local.get $j))) + ) +) + +(assert_return (invoke "get-funcref" (i32.const 0)) (ref.null func)) +(assert_return (invoke "set-funcref-from" (i32.const 0) (i32.const 1))) +(assert_return (invoke "set-funcref" (i32.const 0) (ref.null func))) +(assert_return (invoke "get-funcref" (i32.const 0)) (ref.null func)) + +(assert_trap (invoke "set-funcref" (i32.const 3) (ref.null func)) "out of bounds table access") +(assert_trap (invoke "set-funcref" (i32.const -1) (ref.null func)) "out of bounds table access") + +(assert_trap (invoke "set-funcref-from" (i32.const 3) (i32.const 1)) "out of bounds table access") +(assert_trap (invoke "set-funcref-from" (i32.const -1) (i32.const 1)) "out of bounds table access") diff --git a/winch/codegen/src/abi/mod.rs b/winch/codegen/src/abi/mod.rs index 5362a81cfc78..a92bf6bebe47 100644 --- a/winch/codegen/src/abi/mod.rs +++ b/winch/codegen/src/abi/mod.rs @@ -47,7 +47,7 @@ use crate::isa::{reg::Reg, CallingConvention}; use crate::masm::OperandSize; use smallvec::SmallVec; use std::ops::{Add, BitAnd, Not, Sub}; -use wasmtime_environ::{WasmFuncType, WasmType}; +use wasmtime_environ::{WasmFuncType, WasmHeapType, WasmType}; pub(crate) mod local; pub(crate) use local::*; @@ -267,7 +267,15 @@ pub(crate) fn ty_size(ty: &WasmType) -> u32 { match *ty { WasmType::I32 | WasmType::F32 => 4, WasmType::I64 | WasmType::F64 => 8, - _ => panic!(), + WasmType::Ref(rt) => match rt.heap_type { + // TODO: Similar to the comment in visitor.rs at impl From for + // OperandSize, Once Wasmtime supports 32-bit architectures, this will + // need to be updated to derive operand size from the target's pointer + // size. + WasmHeapType::Func => 8, + ht => unimplemented!("Support for WasmHeapType: {ht}"), + }, + t => unimplemented!("Support for WasmType: {t}"), } } diff --git a/winch/codegen/src/codegen/call.rs b/winch/codegen/src/codegen/call.rs index 6b311e94848b..8713a80cb819 100644 --- a/winch/codegen/src/codegen/call.rs +++ b/winch/codegen/src/codegen/call.rs @@ -1,7 +1,7 @@ //! Function call emission. For more details around the ABI and //! calling convention, see [ABI]. use crate::{ - abi::{ABIArg, ABISig, ABI}, + abi::{ABIArg, ABIResult, ABISig, ABI}, codegen::{BuiltinFunction, CodeGenContext}, masm::{CalleeKind, MacroAssembler, OperandSize}, reg::Reg, @@ -65,6 +65,8 @@ pub(crate) struct FnCall<'a> { arg_stack_space: u32, /// The ABI-specific signature of the callee. pub abi_sig: &'a ABISig, + /// Whether this a built-in function call. + lib: bool, } impl<'a> FnCall<'a> { @@ -74,6 +76,7 @@ impl<'a> FnCall<'a> { abi_sig: &callee_sig, arg_stack_space: callee_sig.stack_bytes, call_stack_space: None, + lib: false, } } @@ -238,6 +241,7 @@ impl<'a> FnCall<'a> { ) where F: FnMut(&mut CodeGenContext, &mut M, &mut Self, Reg), { + self.lib = true; // When dealing with libcalls, we don't have all the information // upfront (all necessary arguments in the stack) in order to optimize // saving the live registers, so we save all the values available in @@ -288,6 +292,26 @@ impl<'a> FnCall<'a> { regalloc.free(v.get_reg().into()); } }); + + // When emitting built-calls we ensure that none of the registers + // (params and results) used as part of the ABI signature are + // allocatable throughout the lifetime of the `with_lib` callback, since + // such registers will be used to assign arguments and hold results. + // After executing the callback, it's only safe to free the param + // registers, since the depending on the signature, the caller + // will push any result registers to the stack, keeping those registers allocated. + // Here we ensure that any allocated result registers are correctly + // freed before finalizing the function call and pushing any results to + // the value stack. + if self.lib { + match self.abi_sig.result { + ABIResult::Reg { reg, .. } => { + assert!(!context.regalloc.reg_available(reg)); + context.free_reg(reg); + } + _ => {} + } + } context.push_abi_results(&self.abi_sig.result, masm); } diff --git a/winch/codegen/src/codegen/context.rs b/winch/codegen/src/codegen/context.rs index 1f7658611911..d2821aa3ec54 100644 --- a/winch/codegen/src/codegen/context.rs +++ b/winch/codegen/src/codegen/context.rs @@ -1,4 +1,4 @@ -use wasmtime_environ::WasmType; +use wasmtime_environ::{WasmHeapType, WasmType}; use super::ControlStackFrame; use crate::{ @@ -63,7 +63,11 @@ impl<'a> CodeGenContext<'a> { match ty { I32 | I64 => self.reg_for_class(RegClass::Int, masm), F32 | F64 => self.reg_for_class(RegClass::Float, masm), - t => panic!("unsupported type {:?}", t), + Ref(rt) => match rt.heap_type { + WasmHeapType::Func => self.reg_for_class(RegClass::Int, masm), + ht => unimplemented!("Support for WasmHeapType: {ht}"), + }, + t => unimplemented!("Support for WasmType: {t}"), } } diff --git a/winch/codegen/src/codegen/env.rs b/winch/codegen/src/codegen/env.rs index 8e2d36c05f3f..32f40cc791bb 100644 --- a/winch/codegen/src/codegen/env.rs +++ b/winch/codegen/src/codegen/env.rs @@ -1,12 +1,20 @@ -use crate::{codegen::BuiltinFunctions, CallingConvention}; +use crate::{ + codegen::{BuiltinFunctions, OperandSize}, + CallingConvention, +}; use smallvec::{smallvec, SmallVec}; +use std::collections::{ + hash_map::Entry::{Occupied, Vacant}, + HashMap, +}; use wasmparser::BlockType; use wasmtime_environ::{ - FuncIndex, GlobalIndex, ModuleTranslation, ModuleTypes, PtrSize, TableIndex, TypeConvert, - TypeIndex, VMOffsets, WasmFuncType, WasmType, + FuncIndex, GlobalIndex, ModuleTranslation, ModuleTypes, PtrSize, TableIndex, TablePlan, + TypeConvert, TypeIndex, VMOffsets, WasmFuncType, WasmType, }; /// Table metadata. +#[derive(Debug, Copy, Clone)] pub struct TableData { /// The offset to the base of the table. pub offset: u32, @@ -15,8 +23,10 @@ pub struct TableData { /// If the table is imported, return the base /// offset of the `from` field in `VMTableImport`. pub base: Option, - /// The size of the table elements, in bytes. - pub element_size: u8, + /// The size of the table elements. + pub(crate) element_size: OperandSize, + /// The size of the current elements field. + pub(crate) current_elements_size: OperandSize, } /// A function callee. @@ -53,6 +63,8 @@ pub struct FuncEnv<'a, P: PtrSize> { pub builtins: BuiltinFunctions, /// The module's function types. pub types: &'a ModuleTypes, + /// Track resolved table information. + resolved_tables: HashMap, } pub fn ptr_type_from_ptr_size(size: u8) -> WasmType { @@ -77,6 +89,7 @@ impl<'a, P: PtrSize> FuncEnv<'a, P> { translation, builtins: BuiltinFunctions::new(size, call_conv, builtins_base), types, + resolved_tables: HashMap::new(), } } @@ -141,27 +154,40 @@ impl<'a, P: PtrSize> FuncEnv<'a, P> { } /// Returns the table information for the given table index. - pub fn resolve_table_data(&self, index: TableIndex) -> TableData { - let (from_offset, base_offset, current_elems_offset) = - match self.translation.module.defined_table_index(index) { - Some(defined) => ( - None, - self.vmoffsets.vmctx_vmtable_definition_base(defined), - self.vmoffsets - .vmctx_vmtable_definition_current_elements(defined), - ), - None => ( - Some(self.vmoffsets.vmctx_vmtable_import_from(index)), - self.vmoffsets.vmtable_definition_base().into(), - self.vmoffsets.vmtable_definition_current_elements().into(), - ), - }; - - TableData { - base: from_offset, - offset: base_offset, - current_elems_offset, - element_size: self.vmoffsets.ptr.size(), + pub fn resolve_table_data(&mut self, index: TableIndex) -> TableData { + match self.resolved_tables.entry(index) { + Occupied(entry) => *entry.get(), + Vacant(entry) => { + let (from_offset, base_offset, current_elems_offset) = + match self.translation.module.defined_table_index(index) { + Some(defined) => ( + None, + self.vmoffsets.vmctx_vmtable_definition_base(defined), + self.vmoffsets + .vmctx_vmtable_definition_current_elements(defined), + ), + None => ( + Some(self.vmoffsets.vmctx_vmtable_import_from(index)), + self.vmoffsets.vmtable_definition_base().into(), + self.vmoffsets.vmtable_definition_current_elements().into(), + ), + }; + + *entry.insert(TableData { + base: from_offset, + offset: base_offset, + current_elems_offset, + element_size: OperandSize::from_bytes(self.vmoffsets.ptr.size()), + current_elements_size: OperandSize::from_bytes( + self.vmoffsets.size_of_vmtable_definition_current_elements(), + ), + }) + } } } + + /// Get a [`TablePlan`] from a [`TableIndex`]. + pub fn table_plan(&mut self, index: TableIndex) -> &TablePlan { + &self.translation.module.table_plans[index] + } } diff --git a/winch/codegen/src/codegen/mod.rs b/winch/codegen/src/codegen/mod.rs index 6771e15ea668..8526929e8567 100644 --- a/winch/codegen/src/codegen/mod.rs +++ b/winch/codegen/src/codegen/mod.rs @@ -1,6 +1,7 @@ use crate::{ abi::{ABISig, ABI}, isa::reg::Reg, + masm::RegImm, masm::{CmpKind, MacroAssembler, OperandSize, TrapCode}, stack::{TypedReg, Val}, CallingConvention, @@ -8,7 +9,9 @@ use crate::{ use anyhow::Result; use smallvec::SmallVec; use wasmparser::{BinaryReader, FuncValidator, Operator, ValidatorResources, VisitOperator}; -use wasmtime_environ::{PtrSize, TypeIndex, WasmFuncType, WasmType}; +use wasmtime_environ::{ + PtrSize, TableIndex, TypeIndex, WasmFuncType, WasmHeapType, WasmType, FUNCREF_MASK, +}; mod context; pub(crate) use context::*; @@ -254,6 +257,7 @@ where /// * A function import. /// * A funcref. pub fn emit_call(&mut self, callee: Callee) { + let ptr_type = self.env.ptr_type(); match callee { Callee::Import(callee) => { let mut params = Vec::with_capacity(callee.ty.params().len() + 2); @@ -268,10 +272,7 @@ where .vmoffsets .vmctx_vmfunction_import_vmctx(callee.index); let callee_vmctx_addr = self.masm.address_at_vmctx(callee_vmctx_offset); - // FIXME Remove harcoded operand size, this will be needed - // once 32-bit architectures are supported. - self.masm - .load(callee_vmctx_addr, callee_vmctx, OperandSize::S64); + self.masm.load_ptr(callee_vmctx_addr, callee_vmctx); let callee_body_offset = self .env @@ -284,8 +285,12 @@ where // and second arguments. let stack = &mut self.context.stack; let location = stack.len() - (sig.params().len() - 2); - stack.insert(location as usize, TypedReg::i64(caller_vmctx).into()); - stack.insert(location as usize, TypedReg::i64(callee_vmctx).into()); + let values = [ + TypedReg::new(ptr_type, callee_vmctx).into(), + TypedReg::new(ptr_type, caller_vmctx).into(), + ] + .into_iter(); + self.context.stack.insert_many(location, values); let abi_sig = ::sig(&sig, &CallingConvention::Default); FnCall::new(&abi_sig) @@ -302,7 +307,6 @@ where Callee::FuncRef(ty) => { // Get type for the caller and callee VMContext. - let ptr_type = self.env.ptr_type(); let abi_sig = ::sig(&ty, &CallingConvention::Default); // Pop the funcref pointer to a register and allocate a register to hold the // address of the funcref. Since the callee is not addressed from a global non @@ -404,10 +408,78 @@ where match &ty { I32 | I64 | F32 | F64 => self.masm.store(src.into(), addr, ty.into()), - _ => panic!("Unsupported type {:?}", ty), + Ref(rt) => match rt.heap_type { + WasmHeapType::Func => self.masm.store_ptr(src.into(), addr), + ht => unimplemented!("Support for WasmHeapType: {ht}"), + }, + _ => unimplemented!("Support for WasmType {ty}"), } }); } + + /// Emits a series of instructions to lazily initialize a function reference. + pub fn emit_lazy_init_funcref( + table_data: &TableData, + table_index: TableIndex, + ptr_type: WasmType, + context: &mut CodeGenContext, + masm: &mut M, + call: &mut FnCall, + callee: Reg, + ) { + let index = context.pop_to_reg(masm, None); + let elem_value: Reg = context.any_gpr(masm).into(); + let base = context.any_gpr(masm); + let elem_addr = masm.table_elem_address(index.into(), base, &table_data, context); + masm.load_ptr(elem_addr, elem_value); + + let defined = masm.get_label(); + let cont = masm.get_label(); + + // Preemptively move the table element address to the + // result register, to avoid conflicts at the control flow merge. + let result = call.abi_sig.result.result_reg().unwrap(); + masm.mov(elem_value.into(), result, ptr_type.into()); + + // Push the builtin function arguments to the stack. + context + .stack + .push(TypedReg::new(ptr_type, ::vmctx_reg()).into()); + context.stack.push(table_index.as_u32().try_into().unwrap()); + context.stack.push(index.into()); + + masm.branch( + CmpKind::Ne, + elem_value.into(), + elem_value, + defined, + ptr_type.into(), + ); + + call.calculate_call_stack_space(context) + .reg(masm, context, callee); + // We know the signature of the libcall in this case, so we assert that there's + // one element in the stack and that it's the ABI signature's result register. + let top = context.stack.peek().unwrap(); + let top = top.get_reg(); + debug_assert!(top.reg == result); + masm.jmp(cont); + + // In the defined case, mask the funcref address in place, by peeking into the + // last element of the value stack, which was pushed by the `indirect` function + // call above. + masm.bind(defined); + let imm = RegImm::i64(FUNCREF_MASK as i64); + let dst = top.into(); + masm.and(dst, dst, imm, top.ty.into()); + + masm.bind(cont); + // The indirect call above, will take care of freeing the registers used as + // params. + // So we only free the params used to lazily initialize the func ref. + context.free_reg(base); + context.free_reg(elem_value); + } } /// Returns the index of the [`ControlStackFrame`] for the given diff --git a/winch/codegen/src/isa/aarch64/masm.rs b/winch/codegen/src/isa/aarch64/masm.rs index 880357d60587..f6032a9461ac 100644 --- a/winch/codegen/src/isa/aarch64/masm.rs +++ b/winch/codegen/src/isa/aarch64/masm.rs @@ -100,10 +100,14 @@ impl Masm for MacroAssembler { fn table_elem_address( &mut self, _index: Reg, - _size: OperandSize, + _base: Reg, _table_data: &TableData, _context: &mut CodeGenContext, - ) -> Reg { + ) -> Self::Address { + todo!() + } + + fn table_size(&mut self, _table_data: &TableData, _context: &mut CodeGenContext) { todo!() } @@ -119,6 +123,10 @@ impl Masm for MacroAssembler { todo!() } + fn store_ptr(&mut self, _src: Reg, _dst: Self::Address) { + todo!() + } + fn store(&mut self, src: RegImm, dst: Address, size: OperandSize) { let src = match src { RegImm::Imm(v) => { @@ -350,6 +358,10 @@ impl Masm for MacroAssembler { todo!() } + fn trapz(&mut self, _src: Reg, _code: TrapCode) { + todo!() + } + fn trapif(&mut self, _cc: CmpKind, _code: TrapCode) { todo!() } diff --git a/winch/codegen/src/isa/x64/abi.rs b/winch/codegen/src/isa/x64/abi.rs index be52fe9fb455..6fed025684fa 100644 --- a/winch/codegen/src/isa/x64/abi.rs +++ b/winch/codegen/src/isa/x64/abi.rs @@ -5,7 +5,7 @@ use crate::{ masm::OperandSize, }; use smallvec::SmallVec; -use wasmtime_environ::{WasmFuncType, WasmType}; +use wasmtime_environ::{WasmFuncType, WasmHeapType, WasmType}; /// Helper environment to track argument-register /// assignment in x64. @@ -142,6 +142,10 @@ impl ABI for X64ABI { // NOTE This should be updated when supporting multi-value. WasmType::I32 | WasmType::I64 => regs::rax(), WasmType::F32 | WasmType::F64 => regs::xmm0(), + WasmType::Ref(rt) => { + assert!(rt.heap_type == WasmHeapType::Func); + regs::rax() + } t => panic!("Unsupported return type {:?}", t), }; ABIResult::reg(ty, reg) @@ -171,9 +175,13 @@ impl ABI for X64ABI { fn stack_arg_slot_size_for_type(ty: WasmType) -> u32 { match ty { + WasmType::Ref(rt) => match rt.heap_type { + WasmHeapType::Func => Self::word_bytes(), + ht => unimplemented!("Support for WasmHeapType: {ht}"), + }, WasmType::F64 | WasmType::I32 | WasmType::I64 => Self::word_bytes(), WasmType::F32 => Self::word_bytes() / 2, - _ => unreachable!(), + ty => unimplemented!("Support for WasmType: {ty}"), } } } @@ -186,6 +194,11 @@ impl X64ABI { fastcall: bool, ) -> ABIArg { let (reg, ty) = match wasm_arg { + ty @ WasmType::Ref(rt) => match rt.heap_type { + WasmHeapType::Func => (Self::int_reg_for(index_env.next_gpr(), fastcall), ty), + ht => unimplemented!("Support for WasmHeapType: {ht}"), + }, + ty @ (WasmType::I32 | WasmType::I64) => { (Self::int_reg_for(index_env.next_gpr(), fastcall), ty) } @@ -194,7 +207,7 @@ impl X64ABI { (Self::float_reg_for(index_env.next_fpr(), fastcall), ty) } - ty => unreachable!("Unsupported argument type {:?}", ty), + ty => unimplemented!("Support for argument of WasmType: {ty}"), }; let default = || { diff --git a/winch/codegen/src/isa/x64/masm.rs b/winch/codegen/src/isa/x64/masm.rs index 8d89bd435c79..25264de83e5a 100644 --- a/winch/codegen/src/isa/x64/masm.rs +++ b/winch/codegen/src/isa/x64/masm.rs @@ -9,7 +9,7 @@ use crate::masm::{ CmpKind, DivKind, Imm as I, MacroAssembler as Masm, OperandSize, RegImm, RemKind, RoundingMode, ShiftKind, TrapCode, }; -use crate::{abi::ABI, masm::StackSlot}; +use crate::{abi::ABI, masm::StackSlot, stack::TypedReg}; use crate::{ abi::{self, align_to, calculate_frame_adjustment, LocalSlot}, codegen::{ptr_type_from_ptr_size, CodeGenContext, TableData}, @@ -112,14 +112,13 @@ impl Masm for MacroAssembler { fn table_elem_address( &mut self, index: Reg, - size: OperandSize, + ptr_base: Reg, table_data: &TableData, context: &mut CodeGenContext, - ) -> Reg { + ) -> Self::Address { let vmctx = ::vmctx_reg(); let scratch = regs::scratch(); let bound = context.any_gpr(self); - let ptr_base = context.any_gpr(self); let tmp = context.any_gpr(self); if let Some(offset) = table_data.base { @@ -127,47 +126,68 @@ impl Masm for MacroAssembler { // load the address into a register to further use it as // the table address. self.asm - .mov_mr(&Address::offset(vmctx, offset), ptr_base, OperandSize::S64); + .mov_mr(&self.address_at_vmctx(offset), ptr_base, self.ptr_size); } else { // Else, simply move the vmctx register into the addr register as // the base to calculate the table address. - self.asm.mov_rr(vmctx, ptr_base, OperandSize::S64); + self.asm.mov_rr(vmctx, ptr_base, self.ptr_size); }; // OOB check. - let bound_addr = Address::offset(ptr_base, table_data.current_elems_offset); - self.asm.mov_mr(&bound_addr, bound, OperandSize::S64); - self.asm.cmp_rr(bound, index, size); + let bound_addr = self.address_at_reg(ptr_base, table_data.current_elems_offset); + let bound_size = table_data.current_elements_size; + self.asm.mov_mr(&bound_addr, bound, bound_size); + self.asm.cmp_rr(bound, index, bound_size); self.asm.trapif(CmpKind::GeU, TrapCode::TableOutOfBounds); // Move the index into the scratch register to calcualte the table // element address. // Moving the value of the index register to the scratch register // also avoids overwriting the context of the index register. - self.asm.mov_rr(index, scratch, OperandSize::S32); - self.asm - .mul_ir(table_data.element_size as i32, scratch, OperandSize::S64); + self.asm.mov_rr(index, scratch, bound_size); + self.asm.mul_ir( + table_data.element_size.bytes() as i32, + scratch, + table_data.element_size, + ); self.asm.mov_mr( - &Address::offset(ptr_base, table_data.offset), + &self.address_at_reg(ptr_base, table_data.offset), ptr_base, - OperandSize::S64, + self.ptr_size, ); // Copy the value of the table base into a temporary register // so that we can use it later in case of a misspeculation. - self.asm.mov_rr(ptr_base, tmp, OperandSize::S64); + self.asm.mov_rr(ptr_base, tmp, self.ptr_size); // Calculate the address of the table element. - self.asm.add_rr(scratch, ptr_base, OperandSize::S64); + self.asm.add_rr(scratch, ptr_base, self.ptr_size); if self.shared_flags.enable_table_access_spectre_mitigation() { // Perform a bounds check and override the value of the // table element address in case the index is out of bounds. self.asm.cmp_rr(bound, index, OperandSize::S32); - self.asm.cmov(tmp, ptr_base, CmpKind::GeU, OperandSize::S64); + self.asm.cmov(tmp, ptr_base, CmpKind::GeU, self.ptr_size); } - self.asm - .mov_mr(&Address::offset(ptr_base, 0), ptr_base, OperandSize::S64); context.free_reg(bound); context.free_reg(tmp); - ptr_base + self.address_at_reg(ptr_base, 0) + } + + fn table_size(&mut self, table_data: &TableData, context: &mut CodeGenContext) { + let vmctx = ::vmctx_reg(); + let scratch = regs::scratch(); + let size = context.any_gpr(self); + + if let Some(offset) = table_data.base { + self.asm + .mov_mr(&self.address_at_vmctx(offset), scratch, self.ptr_size); + } else { + self.asm.mov_rr(vmctx, scratch, self.ptr_size); + }; + + let size_addr = Address::offset(scratch, table_data.current_elems_offset); + self.asm + .mov_mr(&size_addr, size, table_data.current_elements_size); + + context.stack.push(TypedReg::i32(size).into()); } fn address_from_sp(&self, offset: u32) -> Self::Address { @@ -182,6 +202,10 @@ impl Masm for MacroAssembler { Address::offset(::vmctx_reg(), offset) } + fn store_ptr(&mut self, src: Reg, dst: Self::Address) { + self.store(src.into(), dst, self.ptr_size); + } + fn store(&mut self, src: RegImm, dst: Address, size: OperandSize) { match src { RegImm::Imm(imm) => match imm { @@ -708,6 +732,11 @@ impl Masm for MacroAssembler { self.asm.trapif(cc, code); } + fn trapz(&mut self, src: Reg, code: TrapCode) { + self.asm.test_rr(src, src, self.ptr_size); + self.asm.trapif(CmpKind::Eq, code); + } + fn jmp_table(&mut self, targets: &[MachLabel], index: Reg, tmp: Reg) { // At least one default target. assert!(targets.len() >= 1); diff --git a/winch/codegen/src/masm.rs b/winch/codegen/src/masm.rs index ec258b7b39af..ab032cfb7253 100644 --- a/winch/codegen/src/masm.rs +++ b/winch/codegen/src/masm.rs @@ -32,7 +32,7 @@ pub struct StackSlot { pub size: u32, } -/// Kinds of binary comparison in WebAssembly. The [`masm`] implementation for +/// Kinds of binary comparison in WebAssembly. The [`MacroAssembler`] implementation for /// each ISA is responsible for emitting the correct sequence of instructions /// when lowering to machine code. #[derive(Debug, Clone, Copy, Eq, PartialEq)] @@ -305,15 +305,18 @@ pub(crate) trait MacroAssembler { /// Get the address of a local slot. fn local_address(&mut self, local: &LocalSlot) -> Self::Address; - /// Loads the address of the table element at a given index. - /// Returns a register that contains address of the table element. + /// Loads the address of the table element at a given index. Returns the + /// address of the table element using the provided register as base. fn table_elem_address( &mut self, index: Reg, - size: OperandSize, + base: Reg, table_data: &TableData, context: &mut CodeGenContext, - ) -> Reg; + ) -> Self::Address; + + /// Retrieves the size of the table, pushing the result to the value stack. + fn table_size(&mut self, table_data: &TableData, context: &mut CodeGenContext); /// Constructs an address with an offset that is relative to the /// current position of the stack pointer (e.g. [sp + (sp_offset - @@ -349,6 +352,10 @@ pub(crate) trait MacroAssembler { /// to the pointer size of the target. fn load_ptr(&mut self, src: Self::Address, dst: Reg); + /// Alias for `MacroAssembler::store` with the operand size corresponding + /// to the pointer size of the target. + fn store_ptr(&mut self, src: Reg, dst: Self::Address); + /// Pop a value from the machine stack into the given register. fn pop(&mut self, dst: Reg, size: OperandSize); @@ -517,4 +524,7 @@ pub(crate) trait MacroAssembler { /// Traps if the condition code is met. fn trapif(&mut self, cc: CmpKind, code: TrapCode); + + /// Trap if the source register is zero. + fn trapz(&mut self, src: Reg, code: TrapCode); } diff --git a/winch/codegen/src/stack.rs b/winch/codegen/src/stack.rs index 203eeab01742..391388a8bf4b 100644 --- a/winch/codegen/src/stack.rs +++ b/winch/codegen/src/stack.rs @@ -27,6 +27,14 @@ impl TypedReg { reg, } } + + /// Create an i64 [`TypedReg`]. + pub fn i32(reg: Reg) -> Self { + Self { + ty: WasmType::I32, + reg, + } + } } impl From for Reg { @@ -225,9 +233,22 @@ impl Stack { } } - /// Insert a new value at the specified index. - pub fn insert(&mut self, at: usize, val: Val) { - self.inner.insert(at, val); + /// Extend the stack with the given elements. + pub fn extend(&mut self, values: impl Iterator) { + self.inner.extend(values); + } + + /// Inserts many values at the given index. + pub fn insert_many(&mut self, at: usize, values: impl Iterator) { + debug_assert!(at <= self.len()); + // If last, simply extend. + if at == self.inner.len() { + self.inner.extend(values); + } else { + let mut tail = self.inner.split_off(at); + self.inner.extend(values); + self.inner.append(&mut tail); + } } /// Get the length of the stack. diff --git a/winch/codegen/src/visitor.rs b/winch/codegen/src/visitor.rs index fc96b7d00d72..44ef32bda392 100644 --- a/winch/codegen/src/visitor.rs +++ b/winch/codegen/src/visitor.rs @@ -10,11 +10,13 @@ use crate::masm::{ CmpKind, DivKind, MacroAssembler, OperandSize, RegImm, RemKind, RoundingMode, ShiftKind, }; use crate::stack::{TypedReg, Val}; +use cranelift_codegen::ir::TrapCode; use smallvec::SmallVec; use wasmparser::BrTable; use wasmparser::{BlockType, Ieee32, Ieee64, VisitOperator}; use wasmtime_environ::{ - FuncIndex, GlobalIndex, TableIndex, TableStyle, TypeIndex, WasmType, FUNCREF_MASK, + FuncIndex, GlobalIndex, TableIndex, TableStyle, TypeIndex, WasmHeapType, WasmType, + FUNCREF_INIT_BIT, }; /// A macro to define unsupported WebAssembly operators. @@ -132,7 +134,14 @@ macro_rules! def_unsupported { (emit Drop $($rest:tt)*) => {}; (emit BrTable $($rest:tt)*) => {}; (emit CallIndirect $($rest:tt)*) => {}; - + (emit TableInit $($rest:tt)*) => {}; + (emit TableCopy $($rest:tt)*) => {}; + (emit TableGet $($rest:tt)*) => {}; + (emit TableSet $($rest:tt)*) => {}; + (emit TableGrow $($rest:tt)*) => {}; + (emit TableSize $($rest:tt)*) => {}; + (emit TableFill $($rest:tt)*) => {}; + (emit ElemDrop $($rest:tt)*) => {}; (emit $unsupported:tt $($rest:tt)*) => {$($rest)*}; } @@ -608,7 +617,11 @@ where .unwrap_or_else(|| panic!("valid local at slot = {}", index)); match slot.ty { I32 | I64 | F32 | F64 => context.stack.push(Val::local(index, slot.ty)), - _ => panic!("Unsupported type {:?} for local", slot.ty), + Ref(rt) => match rt.heap_type { + WasmHeapType::Func => context.stack.push(Val::local(index, slot.ty)), + ht => unimplemented!("Support for WasmHeapType: {ht}"), + }, + t => unimplemented!("Support local type: {t}"), } } @@ -639,55 +652,15 @@ where &mut self.context, &builtin, |cx, masm, call, callee| { - // Calculate the table element address. - let index = cx.pop_to_reg(masm, None); - let elem_addr = - masm.table_elem_address(index.into(), index.ty.into(), &table_data, cx); - - let defined = masm.get_label(); - let cont = masm.get_label(); - - // Preemptively move the table element address to the - // result register, to avoid conflicts at the control flow merge. - let result = call.abi_sig.result.result_reg().unwrap(); - masm.mov(elem_addr.into(), result, ptr_type.into()); - cx.free_reg(result); - - // Push the builtin function arguments to the stack. - cx.stack - .push(TypedReg::new(ptr_type, ::vmctx_reg()).into()); - cx.stack.push(table_index.as_u32().try_into().unwrap()); - cx.stack.push(index.into()); - - masm.branch( - CmpKind::Ne, - elem_addr.into(), - elem_addr, - defined, - ptr_type.into(), + CodeGen::emit_lazy_init_funcref( + &table_data, + table_index, + ptr_type, + cx, + masm, + call, + callee, ); - - call.calculate_call_stack_space(cx).reg(masm, cx, callee); - // We know the signature of the libcall in this case, so we assert that there's - // one element in the stack and that it's the ABI signature's result register. - let top = cx.stack.peek().unwrap(); - let top = top.get_reg(); - debug_assert!(top.reg == result); - masm.jmp(cont); - - // In the defined case, mask the funcref address in place, by peeking into the - // last element of the value stack, which was pushed by the `indirect` function - // call above. - masm.bind(defined); - let imm = RegImm::i64(FUNCREF_MASK as i64); - let dst = top.into(); - masm.and(dst, dst, imm, top.ty.into()); - - masm.bind(cont); - // The indirect call above, will take care of freeing the registers used as - // params. - // So we only free the params used to lazily initialize the func ref. - cx.free_reg(elem_addr); }, ); @@ -695,6 +668,8 @@ where match self.env.translation.module.table_plans[table_index].style { TableStyle::CallerChecksSignature => { let funcref_ptr = self.context.stack.peek().map(|v| v.get_reg()).unwrap(); + self.masm + .trapz(funcref_ptr.into(), TrapCode::IndirectCallToNull); self.emit_typecheck_funcref(funcref_ptr.into(), type_index); } } @@ -705,6 +680,217 @@ where self.emit_call(self.env.funcref(type_index)); } + fn visit_table_init(&mut self, elem: u32, table: u32) { + let ptr_type = self.env.ptr_type(); + let table_init = self.env.builtins.table_init::(); + let vmctx = TypedReg::new(ptr_type, ::vmctx_reg()); + + FnCall::new(&table_init.sig).with_lib( + self.masm, + &mut self.context, + &table_init, + |cx, masm, call, callee| { + // table.init requires at least 3 elements on the value stack. + debug_assert!(cx.stack.len() >= 3); + let extra_args = [ + vmctx.into(), + table.try_into().unwrap(), + elem.try_into().unwrap(), + ] + .into_iter(); + let at = cx.stack.len() - 3; + cx.stack.insert_many(at, extra_args); + // Finalize the call. + call.calculate_call_stack_space(cx).reg(masm, cx, callee); + }, + ); + } + + fn visit_table_copy(&mut self, dst: u32, src: u32) { + let ptr_type = self.env.ptr_type(); + let table_copy = self.env.builtins.table_copy::(); + let vmctx = TypedReg::new(ptr_type, ::vmctx_reg()); + + FnCall::new(&table_copy.sig).with_lib( + self.masm, + &mut self.context, + &table_copy, + |cx, masm, call, callee| { + // table.copy requires at least 3 elemenents in the value stack. + debug_assert!(cx.stack.len() >= 3); + let at = cx.stack.len() - 3; + cx.stack.insert_many( + at, + [ + vmctx.into(), + dst.try_into().unwrap(), + src.try_into().unwrap(), + ] + .into_iter(), + ); + call.calculate_call_stack_space(cx).reg(masm, cx, callee); + }, + ) + } + + fn visit_table_get(&mut self, table: u32) { + let ptr_type = self.env.ptr_type(); + let table_index = TableIndex::from_u32(table); + let table_data = self.env.resolve_table_data(table_index); + let plan = self.env.table_plan(table_index); + let heap_type = plan.table.wasm_ty.heap_type; + let style = plan.style.clone(); + let table_get = self + .env + .builtins + .table_get_lazy_init_func_ref::(); + + FnCall::new(&table_get.sig).with_lib( + self.masm, + &mut self.context, + &table_get, + |cx, masm, call, callee| { + match heap_type { + WasmHeapType::Func => match style { + TableStyle::CallerChecksSignature => { + CodeGen::emit_lazy_init_funcref( + &table_data, + table_index, + ptr_type, + cx, + masm, + call, + callee, + ); + } + }, + t => unimplemented!("Support for WasmHeapType: {t}"), + }; + }, + ); + } + + fn visit_table_grow(&mut self, table: u32) { + let ptr_type = self.env.ptr_type(); + let table_index = TableIndex::from_u32(table); + let table_plan = self.env.table_plan(table_index); + let vmctx = TypedReg::new(ptr_type, ::vmctx_reg()); + let builtin = match table_plan.table.wasm_ty.heap_type { + WasmHeapType::Func => self.env.builtins.table_grow_func_ref::(), + ty => unimplemented!("Support for HeapType: {ty}"), + }; + + FnCall::new(&builtin.sig).with_lib( + self.masm, + &mut self.context, + &builtin, + |cx, masm, call, callee| { + let len = cx.stack.len(); + // table.grow requires at least 2 elements on the value stack. + debug_assert!(len >= 2); + // The table_grow builtin expects the parameters in a different + // order. + // The value stack at this point should contain: + // [ init_value | delta ] (stack top) + // but the builtin function expects the init value as the last + // argument. + cx.stack.inner_mut().swap(len - 1, len - 2); + let at = len - 2; + cx.stack + .insert_many(at, [vmctx.into(), table.try_into().unwrap()].into_iter()); + + call.calculate_call_stack_space(cx).reg(masm, cx, callee); + }, + ); + } + + fn visit_table_size(&mut self, table: u32) { + let table_index = TableIndex::from_u32(table); + let table_data = self.env.resolve_table_data(table_index); + self.masm.table_size(&table_data, &mut self.context); + } + + fn visit_table_fill(&mut self, table: u32) { + let ptr_type = self.env.ptr_type(); + let vmctx = TypedReg::new(ptr_type, ::vmctx_reg()); + let table_index = TableIndex::from_u32(table); + let table_plan = self.env.table_plan(table_index); + let builtin = match table_plan.table.wasm_ty.heap_type { + WasmHeapType::Func => self.env.builtins.table_fill_func_ref::(), + ty => unimplemented!("Support for heap type: {ty}"), + }; + + FnCall::new(&builtin.sig).with_lib( + self.masm, + &mut self.context, + &builtin, + |cx, masm, call, callee| { + // table.fill requires at least 3 values on the value stack. + debug_assert!(cx.stack.len() >= 3); + let at = cx.stack.len() - 3; + cx.stack + .insert_many(at, [vmctx.into(), table.try_into().unwrap()].into_iter()); + dbg!(&cx.stack); + + call.calculate_call_stack_space(cx).reg(masm, cx, callee); + }, + ); + } + + fn visit_table_set(&mut self, table: u32) { + let ptr_type = self.env.ptr_type(); + let table_index = TableIndex::from_u32(table); + let table_data = self.env.resolve_table_data(table_index); + let plan = self.env.table_plan(table_index); + match plan.table.wasm_ty.heap_type { + WasmHeapType::Func => match plan.style { + TableStyle::CallerChecksSignature => { + let value = self.context.pop_to_reg(self.masm, None); + let index = self.context.pop_to_reg(self.masm, None); + let base = self.context.any_gpr(self.masm); + let elem_addr = self.masm.table_elem_address( + index.into(), + base, + &table_data, + &mut self.context, + ); + + // Set the initialized bit. + self.masm.or( + value.into(), + value.into(), + RegImm::i64(FUNCREF_INIT_BIT as i64), + ptr_type.into(), + ); + + self.masm.store_ptr(value.into(), elem_addr); + + self.context.free_reg(value); + self.context.free_reg(index); + self.context.free_reg(base); + } + }, + ty => unimplemented!("Support for WasmHeapType: {ty}"), + }; + } + + fn visit_elem_drop(&mut self, index: u32) { + let ptr_type = self.env.ptr_type(); + let elem_drop = self.env.builtins.elem_drop::(); + let vmctx = TypedReg::new(ptr_type, ::vmctx_reg()); + + FnCall::new(&elem_drop.sig).with_lib( + self.masm, + &mut self.context, + &elem_drop, + |cx, masm, call, callee| { + cx.stack + .extend([vmctx.into(), index.try_into().unwrap()].into_iter()); + call.calculate_call_stack_space(cx).reg(masm, cx, callee); + }, + ); + } + fn visit_nop(&mut self) {} fn visit_if(&mut self, blockty: BlockType) { @@ -916,7 +1102,17 @@ impl From for OperandSize { match ty { WasmType::I32 | WasmType::F32 => OperandSize::S32, WasmType::I64 | WasmType::F64 => OperandSize::S64, - ty => todo!("unsupported type {:?}", ty), + WasmType::Ref(rt) => { + match rt.heap_type { + // TODO: Harcoded size, assuming 64-bit support only. Once + // Wasmtime supports 32-bit architectures, this will need + // to be updated in such a way that the calculation of the + // OperandSize will depend on the target's pointer size. + WasmHeapType::Func => OperandSize::S64, + t => unimplemented!("Support for WasmHeapType: {t}"), + } + } + ty => unimplemented!("Support for WasmType {ty}"), } } } diff --git a/winch/filetests/filetests/x64/call_indirect/call_indirect.wat b/winch/filetests/filetests/x64/call_indirect/call_indirect.wat index b2dd60224111..2719ef8f9009 100644 --- a/winch/filetests/filetests/x64/call_indirect/call_indirect.wat +++ b/winch/filetests/filetests/x64/call_indirect/call_indirect.wat @@ -41,7 +41,7 @@ ;; 21: 85c0 test eax, eax ;; 23: 0f840a000000 je 0x33 ;; 29: b801000000 mov eax, 1 -;; 2e: e913010000 jmp 0x146 +;; 2e: e925010000 jmp 0x158 ;; 33: 8b44240c mov eax, dword ptr [rsp + 0xc] ;; 37: 83e802 sub eax, 2 ;; 3a: 50 push rax @@ -49,19 +49,19 @@ ;; 3f: 498b4b48 mov rcx, qword ptr [r11 + 0x48] ;; 43: bb00000000 mov ebx, 0 ;; 48: 4d89f1 mov r9, r14 -;; 4b: 4d8b4150 mov r8, qword ptr [r9 + 0x50] -;; 4f: 4439c3 cmp ebx, r8d -;; 52: 0f83f4000000 jae 0x14c +;; 4b: 458b5150 mov r10d, dword ptr [r9 + 0x50] +;; 4f: 4439d3 cmp ebx, r10d +;; 52: 0f8306010000 jae 0x15e ;; 58: 4189db mov r11d, ebx ;; 5b: 4d6bdb08 imul r11, r11, 8 ;; 5f: 4d8b4948 mov r9, qword ptr [r9 + 0x48] -;; 63: 4d89ca mov r10, r9 +;; 63: 4d89cc mov r12, r9 ;; 66: 4d01d9 add r9, r11 -;; 69: 4439c3 cmp ebx, r8d -;; 6c: 4d0f43ca cmovae r9, r10 -;; 70: 4d8b09 mov r9, qword ptr [r9] -;; 73: 4c89c8 mov rax, r9 -;; 76: 4d85c9 test r9, r9 +;; 69: 4439d3 cmp ebx, r10d +;; 6c: 4d0f43cc cmovae r9, r12 +;; 70: 4d8b01 mov r8, qword ptr [r9] +;; 73: 4c89c0 mov rax, r8 +;; 76: 4d85c0 test r8, r8 ;; 79: 0f8519000000 jne 0x98 ;; 7f: 4883ec08 sub rsp, 8 ;; 83: 4c89f7 mov rdi, r14 @@ -71,60 +71,66 @@ ;; 8f: 4883c408 add rsp, 8 ;; 93: e904000000 jmp 0x9c ;; 98: 4883e0fe and rax, 0xfffffffffffffffe -;; 9c: 4d8b5e40 mov r11, qword ptr [r14 + 0x40] -;; a0: 418b0b mov ecx, dword ptr [r11] -;; a3: 8b5018 mov edx, dword ptr [rax + 0x18] -;; a6: 39d1 cmp ecx, edx -;; a8: 0f85a0000000 jne 0x14e -;; ae: 488b4810 mov rcx, qword ptr [rax + 0x10] -;; b2: 4883ec08 sub rsp, 8 -;; b6: 8b7c2408 mov edi, dword ptr [rsp + 8] -;; ba: ffd1 call rcx -;; bc: 4883c410 add rsp, 0x10 -;; c0: 8b4c240c mov ecx, dword ptr [rsp + 0xc] -;; c4: 83e901 sub ecx, 1 -;; c7: 50 push rax -;; c8: 51 push rcx -;; c9: 4d8b5e38 mov r11, qword ptr [r14 + 0x38] -;; cd: 498b4b48 mov rcx, qword ptr [r11 + 0x48] -;; d1: bb00000000 mov ebx, 0 -;; d6: 4d89f1 mov r9, r14 -;; d9: 4d8b4150 mov r8, qword ptr [r9 + 0x50] -;; dd: 4439c3 cmp ebx, r8d -;; e0: 0f836a000000 jae 0x150 -;; e6: 4189db mov r11d, ebx -;; e9: 4d6bdb08 imul r11, r11, 8 -;; ed: 4d8b4948 mov r9, qword ptr [r9 + 0x48] -;; f1: 4d89ca mov r10, r9 -;; f4: 4d01d9 add r9, r11 -;; f7: 4439c3 cmp ebx, r8d -;; fa: 4d0f43ca cmovae r9, r10 -;; fe: 4d8b09 mov r9, qword ptr [r9] -;; 101: 4c89c8 mov rax, r9 -;; 104: 4d85c9 test r9, r9 -;; 107: 0f8511000000 jne 0x11e -;; 10d: 4c89f7 mov rdi, r14 -;; 110: be00000000 mov esi, 0 -;; 115: 89da mov edx, ebx -;; 117: ffd1 call rcx -;; 119: e904000000 jmp 0x122 -;; 11e: 4883e0fe and rax, 0xfffffffffffffffe -;; 122: 4d8b5e40 mov r11, qword ptr [r14 + 0x40] -;; 126: 418b0b mov ecx, dword ptr [r11] -;; 129: 8b5018 mov edx, dword ptr [rax + 0x18] -;; 12c: 39d1 cmp ecx, edx -;; 12e: 0f851e000000 jne 0x152 -;; 134: 488b4810 mov rcx, qword ptr [rax + 0x10] -;; 138: 8b3c24 mov edi, dword ptr [rsp] -;; 13b: ffd1 call rcx -;; 13d: 4883c408 add rsp, 8 -;; 141: 59 pop rcx -;; 142: 01c1 add ecx, eax -;; 144: 89c8 mov eax, ecx -;; 146: 4883c410 add rsp, 0x10 -;; 14a: 5d pop rbp -;; 14b: c3 ret -;; 14c: 0f0b ud2 -;; 14e: 0f0b ud2 -;; 150: 0f0b ud2 -;; 152: 0f0b ud2 +;; 9c: 4885c0 test rax, rax +;; 9f: 0f84bb000000 je 0x160 +;; a5: 4d8b5e40 mov r11, qword ptr [r14 + 0x40] +;; a9: 418b0b mov ecx, dword ptr [r11] +;; ac: 8b5018 mov edx, dword ptr [rax + 0x18] +;; af: 39d1 cmp ecx, edx +;; b1: 0f85ab000000 jne 0x162 +;; b7: 488b4810 mov rcx, qword ptr [rax + 0x10] +;; bb: 4883ec08 sub rsp, 8 +;; bf: 8b7c2408 mov edi, dword ptr [rsp + 8] +;; c3: ffd1 call rcx +;; c5: 4883c410 add rsp, 0x10 +;; c9: 8b4c240c mov ecx, dword ptr [rsp + 0xc] +;; cd: 83e901 sub ecx, 1 +;; d0: 50 push rax +;; d1: 51 push rcx +;; d2: 4d8b5e38 mov r11, qword ptr [r14 + 0x38] +;; d6: 498b4b48 mov rcx, qword ptr [r11 + 0x48] +;; da: bb00000000 mov ebx, 0 +;; df: 4d89f1 mov r9, r14 +;; e2: 458b5150 mov r10d, dword ptr [r9 + 0x50] +;; e6: 4439d3 cmp ebx, r10d +;; e9: 0f8375000000 jae 0x164 +;; ef: 4189db mov r11d, ebx +;; f2: 4d6bdb08 imul r11, r11, 8 +;; f6: 4d8b4948 mov r9, qword ptr [r9 + 0x48] +;; fa: 4d89cc mov r12, r9 +;; fd: 4d01d9 add r9, r11 +;; 100: 4439d3 cmp ebx, r10d +;; 103: 4d0f43cc cmovae r9, r12 +;; 107: 4d8b01 mov r8, qword ptr [r9] +;; 10a: 4c89c0 mov rax, r8 +;; 10d: 4d85c0 test r8, r8 +;; 110: 0f8511000000 jne 0x127 +;; 116: 4c89f7 mov rdi, r14 +;; 119: be00000000 mov esi, 0 +;; 11e: 89da mov edx, ebx +;; 120: ffd1 call rcx +;; 122: e904000000 jmp 0x12b +;; 127: 4883e0fe and rax, 0xfffffffffffffffe +;; 12b: 4885c0 test rax, rax +;; 12e: 0f8432000000 je 0x166 +;; 134: 4d8b5e40 mov r11, qword ptr [r14 + 0x40] +;; 138: 418b0b mov ecx, dword ptr [r11] +;; 13b: 8b5018 mov edx, dword ptr [rax + 0x18] +;; 13e: 39d1 cmp ecx, edx +;; 140: 0f8522000000 jne 0x168 +;; 146: 488b4810 mov rcx, qword ptr [rax + 0x10] +;; 14a: 8b3c24 mov edi, dword ptr [rsp] +;; 14d: ffd1 call rcx +;; 14f: 4883c408 add rsp, 8 +;; 153: 59 pop rcx +;; 154: 01c1 add ecx, eax +;; 156: 89c8 mov eax, ecx +;; 158: 4883c410 add rsp, 0x10 +;; 15c: 5d pop rbp +;; 15d: c3 ret +;; 15e: 0f0b ud2 +;; 160: 0f0b ud2 +;; 162: 0f0b ud2 +;; 164: 0f0b ud2 +;; 166: 0f0b ud2 +;; 168: 0f0b ud2 diff --git a/winch/filetests/filetests/x64/table/fill.wat b/winch/filetests/filetests/x64/table/fill.wat new file mode 100644 index 000000000000..0b7960d823ca --- /dev/null +++ b/winch/filetests/filetests/x64/table/fill.wat @@ -0,0 +1,99 @@ +;;! target = "x86_64" +(module + (type $t0 (func)) + (func $f1 (type $t0)) + (func $f2 (type $t0)) + (func $f3 (type $t0)) + + ;; Define two tables of funcref + (table $t1 3 funcref) + (table $t2 10 funcref) + + ;; Initialize table $t1 with functions $f1, $f2, $f3 + (elem (i32.const 0) $f1 $f2 $f3) + + ;; Function to fill table $t1 using a function reference from table $t2 + (func (export "fill") (param $i i32) (param $r i32) (param $n i32) + (local $ref funcref) + (local.set $ref (table.get $t1 (local.get $r))) + (table.fill $t2 (local.get $i) (local.get $ref) (local.get $n)) + ) +) +;; 0: 55 push rbp +;; 1: 4889e5 mov rbp, rsp +;; 4: 4883ec08 sub rsp, 8 +;; 8: 4c893424 mov qword ptr [rsp], r14 +;; c: 4883c408 add rsp, 8 +;; 10: 5d pop rbp +;; 11: c3 ret +;; +;; 0: 55 push rbp +;; 1: 4889e5 mov rbp, rsp +;; 4: 4883ec08 sub rsp, 8 +;; 8: 4c893424 mov qword ptr [rsp], r14 +;; c: 4883c408 add rsp, 8 +;; 10: 5d pop rbp +;; 11: c3 ret +;; +;; 0: 55 push rbp +;; 1: 4889e5 mov rbp, rsp +;; 4: 4883ec08 sub rsp, 8 +;; 8: 4c893424 mov qword ptr [rsp], r14 +;; c: 4883c408 add rsp, 8 +;; 10: 5d pop rbp +;; 11: c3 ret +;; +;; 0: 55 push rbp +;; 1: 4889e5 mov rbp, rsp +;; 4: 4883ec20 sub rsp, 0x20 +;; 8: 897c241c mov dword ptr [rsp + 0x1c], edi +;; c: 89742418 mov dword ptr [rsp + 0x18], esi +;; 10: 89542414 mov dword ptr [rsp + 0x14], edx +;; 14: 4c89742404 mov qword ptr [rsp + 4], r14 +;; 19: 448b5c2418 mov r11d, dword ptr [rsp + 0x18] +;; 1e: 4153 push r11 +;; 20: 4d8b5e38 mov r11, qword ptr [r14 + 0x38] +;; 24: 498b4b48 mov rcx, qword ptr [r11 + 0x48] +;; 28: 5b pop rbx +;; 29: 4d89f1 mov r9, r14 +;; 2c: 458b5150 mov r10d, dword ptr [r9 + 0x50] +;; 30: 4439d3 cmp ebx, r10d +;; 33: 0f8384000000 jae 0xbd +;; 39: 4189db mov r11d, ebx +;; 3c: 4d6bdb08 imul r11, r11, 8 +;; 40: 4d8b4948 mov r9, qword ptr [r9 + 0x48] +;; 44: 4d89cc mov r12, r9 +;; 47: 4d01d9 add r9, r11 +;; 4a: 4439d3 cmp ebx, r10d +;; 4d: 4d0f43cc cmovae r9, r12 +;; 51: 4d8b01 mov r8, qword ptr [r9] +;; 54: 4c89c0 mov rax, r8 +;; 57: 4d85c0 test r8, r8 +;; 5a: 0f8511000000 jne 0x71 +;; 60: 4c89f7 mov rdi, r14 +;; 63: be00000000 mov esi, 0 +;; 68: 89da mov edx, ebx +;; 6a: ffd1 call rcx +;; 6c: e904000000 jmp 0x75 +;; 71: 4883e0fe and rax, 0xfffffffffffffffe +;; 75: 488944240c mov qword ptr [rsp + 0xc], rax +;; 7a: 448b5c241c mov r11d, dword ptr [rsp + 0x1c] +;; 7f: 4153 push r11 +;; 81: 4c8b5c2414 mov r11, qword ptr [rsp + 0x14] +;; 86: 4153 push r11 +;; 88: 448b5c2424 mov r11d, dword ptr [rsp + 0x24] +;; 8d: 4153 push r11 +;; 8f: 4d8b5e38 mov r11, qword ptr [r14 + 0x38] +;; 93: 498b4368 mov rax, qword ptr [r11 + 0x68] +;; 97: 4883ec08 sub rsp, 8 +;; 9b: 4c89f7 mov rdi, r14 +;; 9e: be01000000 mov esi, 1 +;; a3: 8b542418 mov edx, dword ptr [rsp + 0x18] +;; a7: 488b4c2410 mov rcx, qword ptr [rsp + 0x10] +;; ac: 448b442408 mov r8d, dword ptr [rsp + 8] +;; b1: ffd0 call rax +;; b3: 4883c420 add rsp, 0x20 +;; b7: 4883c420 add rsp, 0x20 +;; bb: 5d pop rbp +;; bc: c3 ret +;; bd: 0f0b ud2 diff --git a/winch/filetests/filetests/x64/table/get.wat b/winch/filetests/filetests/x64/table/get.wat new file mode 100644 index 000000000000..2c28b304d48d --- /dev/null +++ b/winch/filetests/filetests/x64/table/get.wat @@ -0,0 +1,54 @@ +;;! target = "x86_64" +(module + (table $t3 3 funcref) + (elem (table $t3) (i32.const 1) func $dummy) + (func $dummy) + (func $f3 (export "get-funcref") (param $i i32) (result funcref) + (table.get $t3 (local.get $i)) + ) +) + + +;; 0: 55 push rbp +;; 1: 4889e5 mov rbp, rsp +;; 4: 4883ec08 sub rsp, 8 +;; 8: 4c893424 mov qword ptr [rsp], r14 +;; c: 4883c408 add rsp, 8 +;; 10: 5d pop rbp +;; 11: c3 ret +;; +;; 0: 55 push rbp +;; 1: 4889e5 mov rbp, rsp +;; 4: 4883ec10 sub rsp, 0x10 +;; 8: 897c240c mov dword ptr [rsp + 0xc], edi +;; c: 4c89742404 mov qword ptr [rsp + 4], r14 +;; 11: 448b5c240c mov r11d, dword ptr [rsp + 0xc] +;; 16: 4153 push r11 +;; 18: 4d8b5e38 mov r11, qword ptr [r14 + 0x38] +;; 1c: 498b4b48 mov rcx, qword ptr [r11 + 0x48] +;; 20: 5b pop rbx +;; 21: 4d89f1 mov r9, r14 +;; 24: 458b5150 mov r10d, dword ptr [r9 + 0x50] +;; 28: 4439d3 cmp ebx, r10d +;; 2b: 0f8342000000 jae 0x73 +;; 31: 4189db mov r11d, ebx +;; 34: 4d6bdb08 imul r11, r11, 8 +;; 38: 4d8b4948 mov r9, qword ptr [r9 + 0x48] +;; 3c: 4d89cc mov r12, r9 +;; 3f: 4d01d9 add r9, r11 +;; 42: 4439d3 cmp ebx, r10d +;; 45: 4d0f43cc cmovae r9, r12 +;; 49: 4d8b01 mov r8, qword ptr [r9] +;; 4c: 4c89c0 mov rax, r8 +;; 4f: 4d85c0 test r8, r8 +;; 52: 0f8511000000 jne 0x69 +;; 58: 4c89f7 mov rdi, r14 +;; 5b: be00000000 mov esi, 0 +;; 60: 89da mov edx, ebx +;; 62: ffd1 call rcx +;; 64: e904000000 jmp 0x6d +;; 69: 4883e0fe and rax, 0xfffffffffffffffe +;; 6d: 4883c410 add rsp, 0x10 +;; 71: 5d pop rbp +;; 72: c3 ret +;; 73: 0f0b ud2 diff --git a/winch/filetests/filetests/x64/table/grow.wat b/winch/filetests/filetests/x64/table/grow.wat new file mode 100644 index 000000000000..b5f97c33fc94 --- /dev/null +++ b/winch/filetests/filetests/x64/table/grow.wat @@ -0,0 +1,30 @@ +;;! target = "x86_64" + +(module + (table $t1 0 funcref) + + (func (export "grow-by-10") (param $r funcref) (result i32) + (table.grow $t1 (local.get $r) (i32.const 10)) + ) +) + + +;; 0: 55 push rbp +;; 1: 4889e5 mov rbp, rsp +;; 4: 4883ec10 sub rsp, 0x10 +;; 8: 48897c2408 mov qword ptr [rsp + 8], rdi +;; d: 4c893424 mov qword ptr [rsp], r14 +;; 11: 4c8b5c2408 mov r11, qword ptr [rsp + 8] +;; 16: 4153 push r11 +;; 18: 4d8b5e38 mov r11, qword ptr [r14 + 0x38] +;; 1c: 498b5b50 mov rbx, qword ptr [r11 + 0x50] +;; 20: 4883ec08 sub rsp, 8 +;; 24: 4c89f7 mov rdi, r14 +;; 27: be00000000 mov esi, 0 +;; 2c: ba0a000000 mov edx, 0xa +;; 31: 488b4c2408 mov rcx, qword ptr [rsp + 8] +;; 36: ffd3 call rbx +;; 38: 4883c410 add rsp, 0x10 +;; 3c: 4883c410 add rsp, 0x10 +;; 40: 5d pop rbp +;; 41: c3 ret diff --git a/winch/filetests/filetests/x64/table/init_copy_drop.wat b/winch/filetests/filetests/x64/table/init_copy_drop.wat new file mode 100644 index 000000000000..523f2a9838d9 --- /dev/null +++ b/winch/filetests/filetests/x64/table/init_copy_drop.wat @@ -0,0 +1,225 @@ +;;! target = "x86_64" + +(module + (type (func (result i32))) ;; type #0 + (import "a" "ef0" (func (result i32))) ;; index 0 + (import "a" "ef1" (func (result i32))) + (import "a" "ef2" (func (result i32))) + (import "a" "ef3" (func (result i32))) + (import "a" "ef4" (func (result i32))) ;; index 4 + (table $t0 30 30 funcref) + (table $t1 30 30 funcref) + (elem (table $t0) (i32.const 2) func 3 1 4 1) + (elem funcref + (ref.func 2) (ref.func 7) (ref.func 1) (ref.func 8)) + (elem (table $t0) (i32.const 12) func 7 5 2 3 6) + (elem funcref + (ref.func 5) (ref.func 9) (ref.func 2) (ref.func 7) (ref.func 6)) + (func (result i32) (i32.const 5)) ;; index 5 + (func (result i32) (i32.const 6)) + (func (result i32) (i32.const 7)) + (func (result i32) (i32.const 8)) + (func (result i32) (i32.const 9)) ;; index 9 + (func (export "test") + (table.init $t0 1 (i32.const 7) (i32.const 0) (i32.const 4)) + (elem.drop 1) + (table.init $t0 3 (i32.const 15) (i32.const 1) (i32.const 3)) + (elem.drop 3) + (table.copy $t0 0 (i32.const 20) (i32.const 15) (i32.const 5)) + (table.copy $t0 0 (i32.const 21) (i32.const 29) (i32.const 1)) + (table.copy $t0 0 (i32.const 24) (i32.const 10) (i32.const 1)) + (table.copy $t0 0 (i32.const 13) (i32.const 11) (i32.const 4)) + (table.copy $t0 0 (i32.const 19) (i32.const 20) (i32.const 5))) + (func (export "check") (param i32) (result i32) + (call_indirect $t0 (type 0) (local.get 0))) +) +;; 0: 55 push rbp +;; 1: 4889e5 mov rbp, rsp +;; 4: 4883ec08 sub rsp, 8 +;; 8: 4c893424 mov qword ptr [rsp], r14 +;; c: b805000000 mov eax, 5 +;; 11: 4883c408 add rsp, 8 +;; 15: 5d pop rbp +;; 16: c3 ret +;; +;; 0: 55 push rbp +;; 1: 4889e5 mov rbp, rsp +;; 4: 4883ec08 sub rsp, 8 +;; 8: 4c893424 mov qword ptr [rsp], r14 +;; c: b806000000 mov eax, 6 +;; 11: 4883c408 add rsp, 8 +;; 15: 5d pop rbp +;; 16: c3 ret +;; +;; 0: 55 push rbp +;; 1: 4889e5 mov rbp, rsp +;; 4: 4883ec08 sub rsp, 8 +;; 8: 4c893424 mov qword ptr [rsp], r14 +;; c: b807000000 mov eax, 7 +;; 11: 4883c408 add rsp, 8 +;; 15: 5d pop rbp +;; 16: c3 ret +;; +;; 0: 55 push rbp +;; 1: 4889e5 mov rbp, rsp +;; 4: 4883ec08 sub rsp, 8 +;; 8: 4c893424 mov qword ptr [rsp], r14 +;; c: b808000000 mov eax, 8 +;; 11: 4883c408 add rsp, 8 +;; 15: 5d pop rbp +;; 16: c3 ret +;; +;; 0: 55 push rbp +;; 1: 4889e5 mov rbp, rsp +;; 4: 4883ec08 sub rsp, 8 +;; 8: 4c893424 mov qword ptr [rsp], r14 +;; c: b809000000 mov eax, 9 +;; 11: 4883c408 add rsp, 8 +;; 15: 5d pop rbp +;; 16: c3 ret +;; +;; 0: 55 push rbp +;; 1: 4889e5 mov rbp, rsp +;; 4: 4883ec08 sub rsp, 8 +;; 8: 4c893424 mov qword ptr [rsp], r14 +;; c: 4d8b5e38 mov r11, qword ptr [r14 + 0x38] +;; 10: 498b4310 mov rax, qword ptr [r11 + 0x10] +;; 14: 4883ec08 sub rsp, 8 +;; 18: 4c89f7 mov rdi, r14 +;; 1b: be00000000 mov esi, 0 +;; 20: ba01000000 mov edx, 1 +;; 25: b907000000 mov ecx, 7 +;; 2a: 41b800000000 mov r8d, 0 +;; 30: 41b904000000 mov r9d, 4 +;; 36: ffd0 call rax +;; 38: 4883c408 add rsp, 8 +;; 3c: 4d8b5e38 mov r11, qword ptr [r14 + 0x38] +;; 40: 498b4318 mov rax, qword ptr [r11 + 0x18] +;; 44: 4883ec08 sub rsp, 8 +;; 48: 4c89f7 mov rdi, r14 +;; 4b: be01000000 mov esi, 1 +;; 50: ffd0 call rax +;; 52: 4883c408 add rsp, 8 +;; 56: 4d8b5e38 mov r11, qword ptr [r14 + 0x38] +;; 5a: 498b4310 mov rax, qword ptr [r11 + 0x10] +;; 5e: 4883ec08 sub rsp, 8 +;; 62: 4c89f7 mov rdi, r14 +;; 65: be00000000 mov esi, 0 +;; 6a: ba03000000 mov edx, 3 +;; 6f: b90f000000 mov ecx, 0xf +;; 74: 41b801000000 mov r8d, 1 +;; 7a: 41b903000000 mov r9d, 3 +;; 80: ffd0 call rax +;; 82: 4883c408 add rsp, 8 +;; 86: 4d8b5e38 mov r11, qword ptr [r14 + 0x38] +;; 8a: 498b4318 mov rax, qword ptr [r11 + 0x18] +;; 8e: 4883ec08 sub rsp, 8 +;; 92: 4c89f7 mov rdi, r14 +;; 95: be03000000 mov esi, 3 +;; 9a: ffd0 call rax +;; 9c: 4883c408 add rsp, 8 +;; a0: 4d8b5e38 mov r11, qword ptr [r14 + 0x38] +;; a4: 498b4308 mov rax, qword ptr [r11 + 8] +;; a8: 4883ec08 sub rsp, 8 +;; ac: 4c89f7 mov rdi, r14 +;; af: be00000000 mov esi, 0 +;; b4: ba00000000 mov edx, 0 +;; b9: b914000000 mov ecx, 0x14 +;; be: 41b80f000000 mov r8d, 0xf +;; c4: 41b905000000 mov r9d, 5 +;; ca: ffd0 call rax +;; cc: 4883c408 add rsp, 8 +;; d0: 4d8b5e38 mov r11, qword ptr [r14 + 0x38] +;; d4: 498b4308 mov rax, qword ptr [r11 + 8] +;; d8: 4883ec08 sub rsp, 8 +;; dc: 4c89f7 mov rdi, r14 +;; df: be00000000 mov esi, 0 +;; e4: ba00000000 mov edx, 0 +;; e9: b915000000 mov ecx, 0x15 +;; ee: 41b81d000000 mov r8d, 0x1d +;; f4: 41b901000000 mov r9d, 1 +;; fa: ffd0 call rax +;; fc: 4883c408 add rsp, 8 +;; 100: 4d8b5e38 mov r11, qword ptr [r14 + 0x38] +;; 104: 498b4308 mov rax, qword ptr [r11 + 8] +;; 108: 4883ec08 sub rsp, 8 +;; 10c: 4c89f7 mov rdi, r14 +;; 10f: be00000000 mov esi, 0 +;; 114: ba00000000 mov edx, 0 +;; 119: b918000000 mov ecx, 0x18 +;; 11e: 41b80a000000 mov r8d, 0xa +;; 124: 41b901000000 mov r9d, 1 +;; 12a: ffd0 call rax +;; 12c: 4883c408 add rsp, 8 +;; 130: 4d8b5e38 mov r11, qword ptr [r14 + 0x38] +;; 134: 498b4308 mov rax, qword ptr [r11 + 8] +;; 138: 4883ec08 sub rsp, 8 +;; 13c: 4c89f7 mov rdi, r14 +;; 13f: be00000000 mov esi, 0 +;; 144: ba00000000 mov edx, 0 +;; 149: b90d000000 mov ecx, 0xd +;; 14e: 41b80b000000 mov r8d, 0xb +;; 154: 41b904000000 mov r9d, 4 +;; 15a: ffd0 call rax +;; 15c: 4883c408 add rsp, 8 +;; 160: 4d8b5e38 mov r11, qword ptr [r14 + 0x38] +;; 164: 498b4308 mov rax, qword ptr [r11 + 8] +;; 168: 4883ec08 sub rsp, 8 +;; 16c: 4c89f7 mov rdi, r14 +;; 16f: be00000000 mov esi, 0 +;; 174: ba00000000 mov edx, 0 +;; 179: b913000000 mov ecx, 0x13 +;; 17e: 41b814000000 mov r8d, 0x14 +;; 184: 41b905000000 mov r9d, 5 +;; 18a: ffd0 call rax +;; 18c: 4883c408 add rsp, 8 +;; 190: 4883c408 add rsp, 8 +;; 194: 5d pop rbp +;; 195: c3 ret +;; +;; 0: 55 push rbp +;; 1: 4889e5 mov rbp, rsp +;; 4: 4883ec10 sub rsp, 0x10 +;; 8: 897c240c mov dword ptr [rsp + 0xc], edi +;; c: 4c89742404 mov qword ptr [rsp + 4], r14 +;; 11: 448b5c240c mov r11d, dword ptr [rsp + 0xc] +;; 16: 4153 push r11 +;; 18: 4d8b5e38 mov r11, qword ptr [r14 + 0x38] +;; 1c: 498b4b48 mov rcx, qword ptr [r11 + 0x48] +;; 20: 5b pop rbx +;; 21: 4d89f1 mov r9, r14 +;; 24: 458b91f0000000 mov r10d, dword ptr [r9 + 0xf0] +;; 2b: 4439d3 cmp ebx, r10d +;; 2e: 0f8366000000 jae 0x9a +;; 34: 4189db mov r11d, ebx +;; 37: 4d6bdb08 imul r11, r11, 8 +;; 3b: 4d8b89e8000000 mov r9, qword ptr [r9 + 0xe8] +;; 42: 4d89cc mov r12, r9 +;; 45: 4d01d9 add r9, r11 +;; 48: 4439d3 cmp ebx, r10d +;; 4b: 4d0f43cc cmovae r9, r12 +;; 4f: 4d8b01 mov r8, qword ptr [r9] +;; 52: 4c89c0 mov rax, r8 +;; 55: 4d85c0 test r8, r8 +;; 58: 0f8511000000 jne 0x6f +;; 5e: 4c89f7 mov rdi, r14 +;; 61: be00000000 mov esi, 0 +;; 66: 89da mov edx, ebx +;; 68: ffd1 call rcx +;; 6a: e904000000 jmp 0x73 +;; 6f: 4883e0fe and rax, 0xfffffffffffffffe +;; 73: 4885c0 test rax, rax +;; 76: 0f8420000000 je 0x9c +;; 7c: 4d8b5e40 mov r11, qword ptr [r14 + 0x40] +;; 80: 418b0b mov ecx, dword ptr [r11] +;; 83: 8b5018 mov edx, dword ptr [rax + 0x18] +;; 86: 39d1 cmp ecx, edx +;; 88: 0f8510000000 jne 0x9e +;; 8e: 488b4810 mov rcx, qword ptr [rax + 0x10] +;; 92: ffd1 call rcx +;; 94: 4883c410 add rsp, 0x10 +;; 98: 5d pop rbp +;; 99: c3 ret +;; 9a: 0f0b ud2 +;; 9c: 0f0b ud2 +;; 9e: 0f0b ud2 diff --git a/winch/filetests/filetests/x64/table/set.wat b/winch/filetests/filetests/x64/table/set.wat new file mode 100644 index 000000000000..55c325fcb011 --- /dev/null +++ b/winch/filetests/filetests/x64/table/set.wat @@ -0,0 +1,105 @@ +;;! target = "x86_64" + + +(module + (table $t3 2 funcref) + (elem (table $t3) (i32.const 1) func $dummy) + (func $dummy) + + (func (export "set-funcref") (param $i i32) (param $r funcref) + (table.set $t3 (local.get $i) (local.get $r)) + ) + (func (export "set-funcref-from") (param $i i32) (param $j i32) + (table.set $t3 (local.get $i) (table.get $t3 (local.get $j))) + ) +) + +;; 0: 55 push rbp +;; 1: 4889e5 mov rbp, rsp +;; 4: 4883ec08 sub rsp, 8 +;; 8: 4c893424 mov qword ptr [rsp], r14 +;; c: 4883c408 add rsp, 8 +;; 10: 5d pop rbp +;; 11: c3 ret +;; +;; 0: 55 push rbp +;; 1: 4889e5 mov rbp, rsp +;; 4: 4883ec18 sub rsp, 0x18 +;; 8: 897c2414 mov dword ptr [rsp + 0x14], edi +;; c: 4889742408 mov qword ptr [rsp + 8], rsi +;; 11: 4c893424 mov qword ptr [rsp], r14 +;; 15: 488b442408 mov rax, qword ptr [rsp + 8] +;; 1a: 8b4c2414 mov ecx, dword ptr [rsp + 0x14] +;; 1e: 4c89f2 mov rdx, r14 +;; 21: 8b5a50 mov ebx, dword ptr [rdx + 0x50] +;; 24: 39d9 cmp ecx, ebx +;; 26: 0f8324000000 jae 0x50 +;; 2c: 4189cb mov r11d, ecx +;; 2f: 4d6bdb08 imul r11, r11, 8 +;; 33: 488b5248 mov rdx, qword ptr [rdx + 0x48] +;; 37: 4889d6 mov rsi, rdx +;; 3a: 4c01da add rdx, r11 +;; 3d: 39d9 cmp ecx, ebx +;; 3f: 480f43d6 cmovae rdx, rsi +;; 43: 4883c801 or rax, 1 +;; 47: 488902 mov qword ptr [rdx], rax +;; 4a: 4883c418 add rsp, 0x18 +;; 4e: 5d pop rbp +;; 4f: c3 ret +;; 50: 0f0b ud2 +;; +;; 0: 55 push rbp +;; 1: 4889e5 mov rbp, rsp +;; 4: 4883ec10 sub rsp, 0x10 +;; 8: 897c240c mov dword ptr [rsp + 0xc], edi +;; c: 89742408 mov dword ptr [rsp + 8], esi +;; 10: 4c893424 mov qword ptr [rsp], r14 +;; 14: 448b5c240c mov r11d, dword ptr [rsp + 0xc] +;; 19: 4153 push r11 +;; 1b: 448b5c2410 mov r11d, dword ptr [rsp + 0x10] +;; 20: 4153 push r11 +;; 22: 4d8b5e38 mov r11, qword ptr [r14 + 0x38] +;; 26: 498b4b48 mov rcx, qword ptr [r11 + 0x48] +;; 2a: 5b pop rbx +;; 2b: 4d89f1 mov r9, r14 +;; 2e: 458b5150 mov r10d, dword ptr [r9 + 0x50] +;; 32: 4439d3 cmp ebx, r10d +;; 35: 0f8377000000 jae 0xb2 +;; 3b: 4189db mov r11d, ebx +;; 3e: 4d6bdb08 imul r11, r11, 8 +;; 42: 4d8b4948 mov r9, qword ptr [r9 + 0x48] +;; 46: 4d89cc mov r12, r9 +;; 49: 4d01d9 add r9, r11 +;; 4c: 4439d3 cmp ebx, r10d +;; 4f: 4d0f43cc cmovae r9, r12 +;; 53: 4d8b01 mov r8, qword ptr [r9] +;; 56: 4c89c0 mov rax, r8 +;; 59: 4d85c0 test r8, r8 +;; 5c: 0f8519000000 jne 0x7b +;; 62: 4883ec08 sub rsp, 8 +;; 66: 4c89f7 mov rdi, r14 +;; 69: be00000000 mov esi, 0 +;; 6e: 89da mov edx, ebx +;; 70: ffd1 call rcx +;; 72: 4883c408 add rsp, 8 +;; 76: e904000000 jmp 0x7f +;; 7b: 4883e0fe and rax, 0xfffffffffffffffe +;; 7f: 59 pop rcx +;; 80: 4c89f2 mov rdx, r14 +;; 83: 8b5a50 mov ebx, dword ptr [rdx + 0x50] +;; 86: 39d9 cmp ecx, ebx +;; 88: 0f8326000000 jae 0xb4 +;; 8e: 4189cb mov r11d, ecx +;; 91: 4d6bdb08 imul r11, r11, 8 +;; 95: 488b5248 mov rdx, qword ptr [rdx + 0x48] +;; 99: 4889d6 mov rsi, rdx +;; 9c: 4c01da add rdx, r11 +;; 9f: 39d9 cmp ecx, ebx +;; a1: 480f43d6 cmovae rdx, rsi +;; a5: 4883c801 or rax, 1 +;; a9: 488902 mov qword ptr [rdx], rax +;; ac: 4883c410 add rsp, 0x10 +;; b0: 5d pop rbp +;; b1: c3 ret +;; b2: 0f0b ud2 +;; b4: 0f0b ud2 diff --git a/winch/filetests/filetests/x64/table/size.wat b/winch/filetests/filetests/x64/table/size.wat new file mode 100644 index 000000000000..8c0f47f0ace2 --- /dev/null +++ b/winch/filetests/filetests/x64/table/size.wat @@ -0,0 +1,15 @@ +;;! target = "x86_64" +(module + (table $t1 0 funcref) + (func (export "size") (result i32) + (table.size $t1)) +) +;; 0: 55 push rbp +;; 1: 4889e5 mov rbp, rsp +;; 4: 4883ec08 sub rsp, 8 +;; 8: 4c893424 mov qword ptr [rsp], r14 +;; c: 4d89f3 mov r11, r14 +;; f: 418b4350 mov eax, dword ptr [r11 + 0x50] +;; 13: 4883c408 add rsp, 8 +;; 17: 5d pop rbp +;; 18: c3 ret