author | Benjamin Bouvier <benj@benj.me> |
Fri, 13 Dec 2019 19:35:58 +0000 | |
changeset 507051 | b0fb09545a0128c4ffdc4806e9f97cb3281f133c |
parent 507050 | fbeac1746d504ac63ad910d9c103ca2d65377d73 |
child 507052 | d3918dafc6a8695e8003d7cf15ba8a4f1e974943 |
push id | 36922 |
push user | ncsoregi@mozilla.com |
push date | Mon, 16 Dec 2019 17:21:47 +0000 |
treeherder | mozilla-central@27d0d6cc2131 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | rhunt |
bugs | 1603772 |
milestone | 73.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
js/src/wasm/cranelift/src/compile.rs | file | annotate | diff | comparison | revisions | |
js/src/wasm/cranelift/src/wasm2clif.rs | file | annotate | diff | comparison | revisions |
--- a/js/src/wasm/cranelift/src/compile.rs +++ b/js/src/wasm/cranelift/src/compile.rs @@ -29,17 +29,17 @@ use cranelift_codegen::ir::{self, consta use cranelift_codegen::isa::TargetIsa; use cranelift_codegen::CodegenResult; use cranelift_codegen::Context; use cranelift_wasm::{FuncIndex, FuncTranslator, ModuleTranslationState, WasmResult}; use crate::bindings; use crate::isa::make_isa; use crate::utils::DashResult; -use crate::wasm2clif::{init_sig, native_pointer_size, TransEnv, TRAP_THROW_REPORTED}; +use crate::wasm2clif::{init_sig, TransEnv, POINTER_SIZE, TRAP_THROW_REPORTED}; // Namespace for user-defined functions. const USER_FUNCTION_NAMESPACE: u32 = 0; // Namespace for builtins functions that are translated to symbolic accesses in Spidermonkey. const SYMBOLIC_FUNCTION_NAMESPACE: u32 = 1; /// The result of a function's compilation: code + metadata. @@ -517,17 +517,17 @@ impl<'a> RelocSink for EmitEnv<'a> { ir::ExternalName::User { namespace: SYMBOLIC_FUNCTION_NAMESPACE, index, } => { // This is a symbolic function reference encoded by `symbolic_function_name()`. let sym = index.into(); // The symbolic access patch address points *after* the stored pointer. - let offset = offset + native_pointer_size() as u32; + let offset = offset + POINTER_SIZE as u32; self.metadata .push(bindings::MetadataEntry::symbolic_access(offset, sym)); } ir::ExternalName::LibCall(call) => { let sym = match call { ir::LibCall::CeilF32 => bindings::SymbolicAddress::CeilF32, ir::LibCall::CeilF64 => bindings::SymbolicAddress::CeilF64, @@ -538,17 +538,17 @@ impl<'a> RelocSink for EmitEnv<'a> { ir::LibCall::TruncF32 => bindings::SymbolicAddress::TruncF32, ir::LibCall::TruncF64 => bindings::SymbolicAddress::TruncF64, _ => { panic!("Don't understand external {}", name); } }; // The symbolic access patch address points *after* the stored pointer. - let offset = offset + native_pointer_size() as u32; + let offset = offset + POINTER_SIZE as u32; self.metadata .push(bindings::MetadataEntry::symbolic_access(offset, sym)); } _ => { panic!("Don't understand external {}", name); } }
--- a/js/src/wasm/cranelift/src/wasm2clif.rs +++ b/js/src/wasm/cranelift/src/wasm2clif.rs @@ -30,39 +30,26 @@ use cranelift_codegen::packed_option::Pa use cranelift_wasm::{ FuncEnvironment, FuncIndex, GlobalIndex, GlobalVariable, MemoryIndex, ReturnMode, SignatureIndex, TableIndex, TargetEnvironment, WasmError, WasmResult, }; use crate::bindings::{self, SymbolicAddress}; use crate::compile::{symbolic_function_name, wasm_function_name}; -/// Get the integer type used for representing pointers on this platform. -fn native_pointer_type() -> ir::Type { - if cfg!(target_pointer_width = "64") { - ir::types::I64 - } else { - ir::types::I32 - } -} - -/// Number of bytes in a native pointer. -pub fn native_pointer_size() -> i32 { - if cfg!(target_pointer_width = "64") { - 8 - } else { - 4 - } -} - #[cfg(target_pointer_width = "64")] const POINTER_TYPE: ir::Type = ir::types::I64; #[cfg(target_pointer_width = "32")] const POINTER_TYPE: ir::Type = ir::types::I32; +#[cfg(target_pointer_width = "64")] +pub const POINTER_SIZE: i32 = 8; +#[cfg(target_pointer_width = "32")] +pub const POINTER_SIZE: i32 = 4; + /// Convert a TlsData offset into a `Offset32` for a global decl. fn offset32(offset: usize) -> ir::immediates::Offset32 { assert!(offset <= i32::max_value() as usize); (offset as i32).into() } /// Convert a usize offset into a `Imm64` for an iadd_imm. fn imm64(offset: usize) -> ir::immediates::Imm64 { @@ -88,17 +75,17 @@ fn init_sig_from_wsig( _ => ir::AbiParam::new(ret_type), }; sig.returns.push(ret); } // Add a VM context pointer argument. // This corresponds to SpiderMonkey's `WasmTlsReg` hidden argument. sig.params.push(ir::AbiParam::special( - native_pointer_type(), + POINTER_TYPE, ir::ArgumentPurpose::VMContext, )); Ok(sig) } /// Initialize the signature `sig` to match the function with `index` in `env`. pub fn init_sig( @@ -323,77 +310,76 @@ impl<'a, 'b, 'c> TransEnv<'a, 'b, 'c> { /// Get the global variable storing the ID of the given signature. fn sig_global(&mut self, func: &mut ir::Function, offset: usize) -> ir::GlobalValue { let vmctx = self.get_vmctx_gv(func); *self.signatures.entry(offset as i32).or_insert_with(|| { func.create_global_value(ir::GlobalValueData::IAddImm { base: vmctx, offset: imm64(offset), - global_type: native_pointer_type(), + global_type: POINTER_TYPE, }) }) } /// Get the global variable storing the `FuncImportTls` struct for an imported function. fn func_import_global(&mut self, func: &mut ir::Function, index: FuncIndex) -> ir::GlobalValue { // See if we already allocated a global for this import. if let Some(gv) = self.func_gvs.get(index).and_then(|gv| gv.expand()) { return gv; } // We need to create a global variable for `import_index`. let vmctx = self.get_vmctx_gv(func); let gv = func.create_global_value(ir::GlobalValueData::IAddImm { base: vmctx, offset: imm64(self.env.func_import_tls_offset(index)), - global_type: native_pointer_type(), + global_type: POINTER_TYPE, }); // Save it for next time. self.func_gvs[index] = gv.into(); gv } /// Generate code that loads the current instance pointer. fn load_instance(&mut self, pos: &mut FuncCursor) -> ir::Value { let gv = match self.instance_gv.expand() { Some(gv) => gv, None => { // We need to allocate the global variable. let vmctx = self.get_vmctx_gv(pos.func); let gv = pos.func.create_global_value(ir::GlobalValueData::IAddImm { base: vmctx, offset: imm64(self.static_env.instanceTlsOffset), - global_type: native_pointer_type(), + global_type: POINTER_TYPE, }); self.instance_gv = gv.into(); gv } }; - let ga = pos.ins().global_value(native_pointer_type(), gv); - pos.ins() - .load(native_pointer_type(), ir::MemFlags::trusted(), ga, 0) + let ga = pos.ins().global_value(POINTER_TYPE, gv); + pos.ins().load(POINTER_TYPE, ir::MemFlags::trusted(), ga, 0) } /// Generate code that loads the current instance pointer. fn load_interrupt_flag(&mut self, pos: &mut FuncCursor) -> ir::Value { let gv = match self.interrupt_gv.expand() { Some(gv) => gv, None => { // We need to allocate the global variable. let vmctx = self.get_vmctx_gv(pos.func); let gv = pos.func.create_global_value(ir::GlobalValueData::IAddImm { base: vmctx, offset: imm64(self.static_env.interruptTlsOffset), - global_type: native_pointer_type(), + global_type: POINTER_TYPE, }); self.interrupt_gv = gv.into(); gv } }; - let ga = pos.ins().global_value(native_pointer_type(), gv); + let ga = pos.ins().global_value(POINTER_TYPE, gv); pos.ins() .load(ir::types::I32, ir::MemFlags::trusted(), ga, 0) } /// Get a `FuncRef` for the given symbolic address. /// Uses the closure to create the signature if necessary. fn symbolic_funcref<MKSIG: FnOnce() -> ir::Signature>( &mut self, @@ -423,47 +409,47 @@ impl<'a, 'b, 'c> TransEnv<'a, 'b, 'c> { fn switch_to_wasm_tls_realm(&mut self, pos: &mut FuncCursor) { if self.cx_addr.is_none() { let vmctx = self.get_vmctx_gv(&mut pos.func); self.cx_addr = pos .func .create_global_value(ir::GlobalValueData::IAddImm { base: vmctx, offset: imm64(self.static_env.cxTlsOffset), - global_type: native_pointer_type(), + global_type: POINTER_TYPE, }) .into(); } if self.realm_addr.is_none() { let vmctx = self.get_vmctx_gv(&mut pos.func); self.realm_addr = pos .func .create_global_value(ir::GlobalValueData::IAddImm { base: vmctx, offset: imm64(self.static_env.realmTlsOffset), - global_type: native_pointer_type(), + global_type: POINTER_TYPE, }) .into(); } - let ptr = native_pointer_type(); + let ptr = POINTER_TYPE; let flags = ir::MemFlags::trusted(); let cx_addr_val = pos.ins().global_value(ptr, self.cx_addr.unwrap()); let cx = pos.ins().load(ptr, flags, cx_addr_val, 0); let realm_addr_val = pos.ins().global_value(ptr, self.realm_addr.unwrap()); let realm = pos.ins().load(ptr, flags, realm_addr_val, 0); pos.ins() .store(flags, realm, cx, offset32(self.static_env.realmCxOffset)); } /// Update the JSContext's realm value in preparation for making an indirect call through /// an external table. fn switch_to_indirect_callee_realm(&mut self, pos: &mut FuncCursor, vmctx: ir::Value) { - let ptr = native_pointer_type(); + let ptr = POINTER_TYPE; let flags = ir::MemFlags::trusted(); let cx = pos .ins() .load(ptr, flags, vmctx, offset32(self.static_env.cxTlsOffset)); let realm = pos .ins() .load(ptr, flags, vmctx, offset32(self.static_env.realmTlsOffset)); pos.ins() @@ -473,17 +459,17 @@ impl<'a, 'b, 'c> TransEnv<'a, 'b, 'c> { /// Update the JSContext's realm value in preparation for making a call to an imported /// function. fn switch_to_import_realm( &mut self, pos: &mut FuncCursor, vmctx: ir::Value, gv_addr: ir::Value, ) { - let ptr = native_pointer_type(); + let ptr = POINTER_TYPE; let flags = ir::MemFlags::trusted(); let cx = pos .ins() .load(ptr, flags, vmctx, offset32(self.static_env.cxTlsOffset)); let realm = pos.ins().load( ptr, flags, gv_addr, @@ -491,48 +477,48 @@ impl<'a, 'b, 'c> TransEnv<'a, 'b, 'c> { ); pos.ins() .store(flags, realm, cx, offset32(self.static_env.realmCxOffset)); } fn load_pinned_reg(&self, pos: &mut FuncCursor, vmctx: ir::Value) { if cfg!(feature = "cranelift_x86") && cfg!(target_pointer_width = "64") { let heap_base = pos.ins().load( - native_pointer_type(), + POINTER_TYPE, ir::MemFlags::trusted(), vmctx, self.static_env.memoryBaseTlsOffset as i32, ); pos.ins().set_pinned_reg(heap_base); } } fn reload_tls_and_pinned_regs(&mut self, pos: &mut FuncCursor) { let vmctx_gv = self.get_vmctx_gv(&mut pos.func); - let vmctx = pos.ins().global_value(native_pointer_type(), vmctx_gv); + let vmctx = pos.ins().global_value(POINTER_TYPE, vmctx_gv); self.load_pinned_reg(pos, vmctx); } fn instance_call( &mut self, pos: &mut FuncCursor, call: &InstanceCall, arguments: &[ir::Value], ) -> Option<ir::Value> { debug_assert!(call.arguments.len() == arguments.len()); let call_conv = self.static_env.call_conv(); let (fnref, sigref) = self.symbolic_funcref(pos.func, call.address, || { let mut sig = ir::Signature::new(call_conv); - sig.params.push(ir::AbiParam::new(native_pointer_type())); + sig.params.push(ir::AbiParam::new(POINTER_TYPE)); for argument in call.arguments { sig.params.push(ir::AbiParam::new(*argument).uext()); } sig.params.push(ir::AbiParam::special( - native_pointer_type(), + POINTER_TYPE, ir::ArgumentPurpose::VMContext, )); if let Some(ret) = &call.ret { sig.returns.push(ir::AbiParam::new(*ret).uext()); } sig }); @@ -540,17 +526,17 @@ impl<'a, 'b, 'c> TransEnv<'a, 'b, 'c> { let vmctx = pos .func .special_param(ir::ArgumentPurpose::VMContext) .expect("Missing vmctx arg"); // We must use `func_addr` for symbolic references since the stubs can be far away, and the // C++ `SymbolicAccess` linker expects it. - let func_addr = pos.ins().func_addr(native_pointer_type(), fnref); + let func_addr = pos.ins().func_addr(POINTER_TYPE, fnref); let call_ins = pos.ins().call_indirect(sigref, func_addr, &[]); let mut built_arguments = pos.func.dfg[call_ins].take_value_list().unwrap(); built_arguments.push(instance, &mut pos.func.dfg.value_lists); built_arguments.extend(arguments.iter().cloned(), &mut pos.func.dfg.value_lists); built_arguments.push(vmctx, &mut pos.func.dfg.value_lists); pos.func.dfg[call_ins].put_value_list(built_arguments); self.switch_to_wasm_tls_realm(pos); @@ -577,17 +563,17 @@ impl<'a, 'b, 'c> TransEnv<'a, 'b, 'c> { } impl<'a, 'b, 'c> TargetEnvironment for TransEnv<'a, 'b, 'c> { fn target_config(&self) -> TargetFrontendConfig { self.isa.frontend_config() } fn pointer_type(&self) -> ir::Type { - native_pointer_type() + POINTER_TYPE } } impl<'a, 'b, 'c> FuncEnvironment for TransEnv<'a, 'b, 'c> { fn make_global( &mut self, func: &mut ir::Function, index: GlobalIndex, @@ -609,17 +595,17 @@ impl<'a, 'b, 'c> FuncEnvironment for Tra // Some globals are represented as a pointer to the actual data, in which case we // must do an extra dereference to get to them. Also, in that case, the pointer // itself is immutable, so we mark it `readonly` here to assist Cranelift in commoning // up what would otherwise be multiple adjacent reads of the value. let (base_gv, offset) = if global.is_indirect() { let gv = func.create_global_value(ir::GlobalValueData::Load { base: vmctx_gv, offset: offset32(offset), - global_type: native_pointer_type(), + global_type: POINTER_TYPE, readonly: true, }); (gv, 0.into()) } else { (vmctx_gv, offset32(offset)) }; let mem_ty = global.value_type()?; @@ -643,29 +629,29 @@ impl<'a, 'b, 'c> FuncEnvironment for Tra let bound = self.static_env.staticMemoryBound as u64; let is_static = bound > 0; // Get the `TlsData::memoryBase` field. let base = func.create_global_value(ir::GlobalValueData::Load { base: vcmtx, offset: offset32(0), - global_type: native_pointer_type(), + global_type: POINTER_TYPE, readonly: is_static, }); let style = if is_static { // We have a static heap. let bound = bound.into(); ir::HeapStyle::Static { bound } } else { // Get the `TlsData::boundsCheckLimit` field. let bound_gv = func.create_global_value(ir::GlobalValueData::Load { base: vcmtx, - offset: native_pointer_size().into(), + offset: POINTER_SIZE.into(), global_type: ir::types::I32, readonly: false, }); ir::HeapStyle::Dynamic { bound_gv } }; let min_size = (self.env.min_memory_length() as u64).into(); let offset_guard_size = (self.static_env.memoryGuardSize as u64).into(); @@ -685,17 +671,17 @@ impl<'a, 'b, 'c> FuncEnvironment for Tra index: SignatureIndex, ) -> WasmResult<ir::SigRef> { let wsig = self.env.signature(index); let mut sigdata = init_sig_from_wsig(self.static_env.call_conv(), wsig)?; if wsig.id_kind() != bindings::FuncTypeIdDescKind::None { // A signature to be used for an indirect call also takes a signature id. sigdata.params.push(ir::AbiParam::special( - native_pointer_type(), + POINTER_TYPE, ir::ArgumentPurpose::SignatureId, )); } Ok(func.import_signature(sigdata)) } fn make_table(&mut self, func: &mut ir::Function, index: TableIndex) -> WasmResult<ir::Table> { @@ -714,18 +700,18 @@ impl<'a, 'b, 'c> FuncEnvironment for Tra base: table_desc.global, offset: 0.into(), global_type: ir::types::I32, readonly: false, }); let base_gv = func.create_global_value(ir::GlobalValueData::Load { base: table_desc.global, - offset: offset32(native_pointer_size() as usize), - global_type: native_pointer_type(), + offset: offset32(POINTER_SIZE as usize), + global_type: POINTER_TYPE, readonly: false, }); Ok(func.create_table(ir::TableData { base_gv, min_size: 0.into(), bound_gv, element_size: (u64::from(self.pointer_bytes()) * 2).into(), @@ -772,24 +758,24 @@ impl<'a, 'b, 'c> FuncEnvironment for Tra // Follows `MacroAssembler::wasmCallIndirect`: // 1. Materialize the signature ID. let sigid_value = match wsig.id_kind() { bindings::FuncTypeIdDescKind::None => None, bindings::FuncTypeIdDescKind::Immediate => { // The signature is represented as an immediate pointer-sized value. let imm = wsig.id_immediate() as i64; - Some(pos.ins().iconst(native_pointer_type(), imm)) + Some(pos.ins().iconst(POINTER_TYPE, imm)) } bindings::FuncTypeIdDescKind::Global => { let gv = self.sig_global(pos.func, wsig.id_tls_offset()); - let addr = pos.ins().global_value(native_pointer_type(), gv); + let addr = pos.ins().global_value(POINTER_TYPE, gv); Some( pos.ins() - .load(native_pointer_type(), ir::MemFlags::trusted(), addr, 0), + .load(POINTER_TYPE, ir::MemFlags::trusted(), addr, 0), ) } }; // 2. Bounds check the callee against the table length. let (bound_gv, base_gv) = { let table_data = &pos.func.tables[table]; (table_data.bound_gv, table_data.base_gv) @@ -798,44 +784,41 @@ impl<'a, 'b, 'c> FuncEnvironment for Tra let tlength = pos.ins().global_value(ir::types::I32, bound_gv); let oob = pos .ins() .icmp(IntCC::UnsignedGreaterThanOrEqual, callee, tlength); pos.ins().trapnz(oob, ir::TrapCode::OutOfBounds); // 3. Load the wtable base pointer from a global. - let tbase = pos.ins().global_value(native_pointer_type(), base_gv); + let tbase = pos.ins().global_value(POINTER_TYPE, base_gv); // 4. Load callee pointer from wtable. - let callee_x = if native_pointer_type() != ir::types::I32 { - pos.ins().uextend(native_pointer_type(), callee) + let callee_x = if POINTER_TYPE != ir::types::I32 { + pos.ins().uextend(POINTER_TYPE, callee) } else { callee }; let callee_scaled = pos.ins().imul_imm(callee_x, wtable.entry_size()); let entry = pos.ins().iadd(tbase, callee_scaled); let callee_func = pos .ins() - .load(native_pointer_type(), ir::MemFlags::trusted(), entry, 0); + .load(POINTER_TYPE, ir::MemFlags::trusted(), entry, 0); // Check for a null callee. pos.ins() .trapz(callee_func, ir::TrapCode::IndirectCallToNull); // Handle external tables, set up environment. // A function table call could redirect execution to another module with a different realm, // so switch to this realm just in case. - let callee_vmctx = pos.ins().load( - native_pointer_type(), - ir::MemFlags::trusted(), - entry, - native_pointer_size(), - ); + let callee_vmctx = + pos.ins() + .load(POINTER_TYPE, ir::MemFlags::trusted(), entry, POINTER_SIZE); self.switch_to_indirect_callee_realm(&mut pos, callee_vmctx); self.load_pinned_reg(&mut pos, callee_vmctx); // First the wasm args. let mut args = ir::ValueList::default(); args.push(callee_func, &mut pos.func.dfg.value_lists); args.extend(call_args.iter().cloned(), &mut pos.func.dfg.value_lists); args.push(callee_vmctx, &mut pos.func.dfg.value_lists); @@ -865,29 +848,26 @@ impl<'a, 'b, 'c> FuncEnvironment for Tra let mut args = ir::ValueList::default(); args.extend(call_args.iter().cloned(), &mut pos.func.dfg.value_lists); // Is this an imported function in a different instance, or a local function? if self.env.func_is_import(callee_index) { // This is a call to an imported function. We need to load the callee address and vmctx // from the associated `FuncImportTls` struct in a global. let gv = self.func_import_global(pos.func, callee_index); - let gv_addr = pos.ins().global_value(native_pointer_type(), gv); + let gv_addr = pos.ins().global_value(POINTER_TYPE, gv); // We need the first two pointer-sized fields from the `FuncImportTls` struct: `code` // and `tls`. - let fit_code = + let fit_code = pos + .ins() + .load(POINTER_TYPE, ir::MemFlags::trusted(), gv_addr, 0); + let fit_tls = pos.ins() - .load(native_pointer_type(), ir::MemFlags::trusted(), gv_addr, 0); - let fit_tls = pos.ins().load( - native_pointer_type(), - ir::MemFlags::trusted(), - gv_addr, - native_pointer_size(), - ); + .load(POINTER_TYPE, ir::MemFlags::trusted(), gv_addr, POINTER_SIZE); // Switch to the callee's realm. self.switch_to_import_realm(&mut pos, fit_tls, gv_addr); self.load_pinned_reg(&mut pos, fit_tls); // The `tls` field is the VM context pointer for the callee. args.push(fit_tls, &mut pos.func.dfg.value_lists); @@ -947,17 +927,17 @@ impl<'a, 'b, 'c> FuncEnvironment for Tra mut pos: FuncCursor, _index: MemoryIndex, heap: ir::Heap, dst: ir::Value, src: ir::Value, len: ir::Value, ) -> WasmResult<()> { let heap_gv = pos.func.heaps[heap].base; - let mem_base = pos.ins().global_value(native_pointer_type(), heap_gv); + let mem_base = pos.ins().global_value(POINTER_TYPE, heap_gv); // We have a specialized version of `memory.copy` when we are using // shared memory or not. let ret = if self.env.uses_shared_memory() { self.instance_call(&mut pos, &FN_MEMORY_COPY_SHARED, &[dst, src, len, mem_base]) } else { self.instance_call(&mut pos, &FN_MEMORY_COPY, &[dst, src, len, mem_base]) }; @@ -970,17 +950,17 @@ impl<'a, 'b, 'c> FuncEnvironment for Tra mut pos: FuncCursor, _index: MemoryIndex, heap: ir::Heap, dst: ir::Value, val: ir::Value, len: ir::Value, ) -> WasmResult<()> { let mem_base_gv = pos.func.heaps[heap].base; - let mem_base = pos.ins().global_value(native_pointer_type(), mem_base_gv); + let mem_base = pos.ins().global_value(POINTER_TYPE, mem_base_gv); // We have a specialized version of `memory.fill` when we are using // shared memory or not. let ret = if self.env.uses_shared_memory() { self.instance_call(&mut pos, &FN_MEMORY_FILL_SHARED, &[dst, val, len, mem_base]) } else { self.instance_call(&mut pos, &FN_MEMORY_FILL, &[dst, val, len, mem_base]) }; @@ -1107,21 +1087,21 @@ impl TableInfo { ) -> TableInfo { // Create the global variable. let offset = wtab.tls_offset(); assert!(offset < i32::max_value() as usize); let offset = imm64(offset); let global = func.create_global_value(ir::GlobalValueData::IAddImm { base: vmctx, offset, - global_type: native_pointer_type(), + global_type: POINTER_TYPE, }); TableInfo { global } } /// Get the size in bytes of each table entry. pub fn entry_size(&self) -> i64 { // Each entry is an `wasm::FunctionTableElem` which consists of the code pointer and a new // VM context pointer. - i64::from(native_pointer_size()) * 2 + i64::from(POINTER_SIZE) * 2 } }