diff --git a/src/mono/mono/mini/interp/interp.c b/src/mono/mono/mini/interp/interp.c index d85d306142e99b..a6ee9e1f3eeaf5 100644 --- a/src/mono/mono/mini/interp/interp.c +++ b/src/mono/mono/mini/interp/interp.c @@ -8717,6 +8717,7 @@ mono_jiterp_frame_data_allocator_alloc (FrameDataAllocator *stack, InterpFrame * return frame_data_allocator_alloc(stack, frame, size); } +// NOTE: This does not perform a null check and passing a null object or klass is an error! MONO_ALWAYS_INLINE gboolean mono_jiterp_isinst (MonoObject* object, MonoClass* klass) { diff --git a/src/mono/mono/mini/interp/jiterpreter.c b/src/mono/mono/mini/interp/jiterpreter.c index 5133210f744577..2d2275af17c901 100644 --- a/src/mono/mono/mini/interp/jiterpreter.c +++ b/src/mono/mono/mini/interp/jiterpreter.c @@ -163,29 +163,6 @@ mono_jiterp_object_unbox (MonoObject *obj) { return mono_object_unbox_internal(obj); } -EMSCRIPTEN_KEEPALIVE int -mono_jiterp_try_unbox_ref ( - MonoClass *klass, void **dest, MonoObject **src -) { - if (!klass) - return 0; - - MonoObject *o = *src; - if (!o) - return 0; - - if ( - !( - (m_class_get_rank (o->vtable->klass) == 0) && - (m_class_get_element_class (o->vtable->klass) == m_class_get_element_class (klass)) - ) - ) - return 0; - - *dest = mono_object_unbox_internal(o); - return 1; -} - EMSCRIPTEN_KEEPALIVE int mono_jiterp_type_is_byref (MonoType *type) { if (!type) @@ -234,82 +211,63 @@ mono_jiterp_gettype_ref ( } EMSCRIPTEN_KEEPALIVE int -mono_jiterp_cast_ref ( - MonoObject **destination, MonoObject **source, - MonoClass *klass, MintOpcode opcode +mono_jiterp_has_parent_fast ( + MonoClass *klass, MonoClass *parent ) { - if (!klass) - return 0; + // klass may be 0 if null check fusion is active, but that's fine: + // (m_class_get_idepth (0) >= m_class_get_idepth (parent)) in the fast check + // will fail since the idepth of the null ptr is going to be 0, and + // we know parent->idepth >= 1 due to the [m_class_get_idepth (parent) - 1] + return mono_class_has_parent_fast (klass, parent); +} - MonoObject *obj = *source; - if (!obj) { - *destination = 0; - return 1; - } +EMSCRIPTEN_KEEPALIVE int +mono_jiterp_implements_interface ( + MonoVTable *vtable, MonoClass *klass +) { + // If null check fusion is active, vtable->max_interface_id will be 0 + return MONO_VTABLE_IMPLEMENTS_INTERFACE (vtable, m_class_get_interface_id (klass)); +} - switch (opcode) { - case MINT_CASTCLASS: - case MINT_ISINST: { - if (obj) { - // FIXME push/pop LMF - if (!mono_jiterp_isinst (obj, klass)) { // FIXME: do not swallow the error - if (opcode == MINT_ISINST) - *destination = NULL; - else - return 0; // bailout - } else { - *destination = obj; - } - } else { - *destination = NULL; - } - return 1; - } - case MINT_CASTCLASS_INTERFACE: - case MINT_ISINST_INTERFACE: { - gboolean isinst; - // FIXME: Perform some of this work at JIT time - if (MONO_VTABLE_IMPLEMENTS_INTERFACE (obj->vtable, m_class_get_interface_id (klass))) { - isinst = TRUE; - } else if (m_class_is_array_special_interface (klass)) { - /* slow path */ - // FIXME push/pop LMF - isinst = mono_jiterp_isinst (obj, klass); // FIXME: do not swallow the error - } else { - isinst = FALSE; - } +EMSCRIPTEN_KEEPALIVE int +mono_jiterp_is_special_interface (MonoClass *klass) +{ + return m_class_is_array_special_interface (klass); +} - if (!isinst) { - if (opcode == MINT_ISINST_INTERFACE) - *destination = NULL; - else - return 0; // bailout - } else { - *destination = obj; - } - return 1; - } - case MINT_CASTCLASS_COMMON: - case MINT_ISINST_COMMON: { - if (obj) { - gboolean isinst = mono_class_has_parent_fast (obj->vtable->klass, klass); - - if (!isinst) { - if (opcode == MINT_ISINST_COMMON) - *destination = NULL; - else - return 0; // bailout - } else { - *destination = obj; - } - } else { - *destination = NULL; - } +EMSCRIPTEN_KEEPALIVE int +mono_jiterp_implements_special_interface ( + MonoObject *obj, MonoVTable *vtable, MonoClass *klass +) { + // If null check fusion is active, vtable->max_interface_id will be 0 + return MONO_VTABLE_IMPLEMENTS_INTERFACE (vtable, m_class_get_interface_id (klass)) || + // For special interfaces we need to do a more complex check to see whether the + // cast to the interface is valid in case obj is an array. + // mono_jiterp_isinst will *not* handle nulls for us, and we don't want + // to waste time running the full isinst machinery on nulls anyway, so nullcheck + (obj && mono_jiterp_isinst (obj, klass)); +} + +EMSCRIPTEN_KEEPALIVE int +mono_jiterp_cast_v2 ( + MonoObject **destination, MonoObject *obj, + MonoClass *klass, MintOpcode opcode +) { + if (!obj) { + *destination = NULL; + return 1; + // FIXME push/pop LMF + } else if (!mono_jiterp_isinst (obj, klass)) { + // FIXME: do not swallow the error + if (opcode == MINT_ISINST) { + *destination = NULL; return 1; - } + } else + return 0; // bailout + } else { + *destination = obj; + return 1; } - - return 0; } EMSCRIPTEN_KEEPALIVE void @@ -1170,6 +1128,11 @@ mono_jiterp_trace_transfer ( #define JITERP_MEMBER_BACKWARD_BRANCH_OFFSETS_COUNT 11 #define JITERP_MEMBER_CLAUSE_DATA_OFFSETS 12 #define JITERP_MEMBER_PARAMS_COUNT 13 +#define JITERP_MEMBER_VTABLE 14 +#define JITERP_MEMBER_VTABLE_KLASS 15 +#define JITERP_MEMBER_CLASS_RANK 16 +#define JITERP_MEMBER_CLASS_ELEMENT_CLASS 17 +#define JITERP_MEMBER_BOXED_VALUE_DATA 18 // we use these helpers at JIT time to figure out where to do memory loads and stores EMSCRIPTEN_KEEPALIVE size_t @@ -1203,6 +1166,17 @@ mono_jiterp_get_member_offset (int member) { return offsetof (MonoSpanOfVoid, _length); case JITERP_MEMBER_SPAN_DATA: return offsetof (MonoSpanOfVoid, _reference); + case JITERP_MEMBER_VTABLE: + return offsetof (MonoObject, vtable); + case JITERP_MEMBER_VTABLE_KLASS: + return offsetof (MonoVTable, klass); + case JITERP_MEMBER_CLASS_RANK: + return offsetof (MonoClass, rank); + case JITERP_MEMBER_CLASS_ELEMENT_CLASS: + return offsetof (MonoClass, element_class); + // see mono_object_get_data + case JITERP_MEMBER_BOXED_VALUE_DATA: + return MONO_ABI_SIZEOF (MonoObject); default: g_assert_not_reached(); } diff --git a/src/mono/wasm/runtime/cwraps.ts b/src/mono/wasm/runtime/cwraps.ts index c8f33e2a55166c..aa27334f0d3809 100644 --- a/src/mono/wasm/runtime/cwraps.ts +++ b/src/mono/wasm/runtime/cwraps.ts @@ -131,6 +131,7 @@ const fn_signatures: SigLine[] = [ [true, "mono_jiterp_get_arg_offset", "number", ["number", "number", "number"]], [true, "mono_jiterp_get_opcode_info", "number", ["number", "number"]], [true, "mono_wasm_is_zero_page_reserved", "number", []], + [true, "mono_jiterp_is_special_interface", "number", ["number"]], ...legacy_interop_cwraps ]; @@ -257,6 +258,7 @@ export interface t_Cwraps { mono_jiterp_get_arg_offset(imethod: number, sig: number, index: number): number; mono_jiterp_get_opcode_info(opcode: number, type: number): number; mono_wasm_is_zero_page_reserved(): number; + mono_jiterp_is_special_interface(klass: number): number; } const wrapped_c_functions: t_Cwraps = {}; diff --git a/src/mono/wasm/runtime/jiterpreter-support.ts b/src/mono/wasm/runtime/jiterpreter-support.ts index 2cfeab6e2b4b32..65b248573ae3ee 100644 --- a/src/mono/wasm/runtime/jiterpreter-support.ts +++ b/src/mono/wasm/runtime/jiterpreter-support.ts @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +import MonoWasmThreads from "consts:monoWasmThreads"; import { NativePointer, ManagedPointer, VoidPtr } from "./types/emscripten"; import { Module, runtimeHelpers } from "./globals"; import { WasmOpcode, WasmSimdOpcode } from "./jiterpreter-opcodes"; @@ -1739,6 +1740,11 @@ export const enum JiterpMember { BackwardBranchOffsetsCount = 11, ClauseDataOffsets = 12, ParamsCount = 13, + VTable = 14, + VTableKlass = 15, + ClassRank = 16, + ClassElementClass = 17, + BoxedValueData = 18, } const memberOffsets: { [index: number]: number } = {}; @@ -1781,6 +1787,9 @@ export function bytesFromHex(hex: string): Uint8Array { export function isZeroPageReserved(): boolean { // FIXME: This check will always return true on worker threads. // Right now the jiterpreter is disabled when threading is active, so that's not an issue. + if (MonoWasmThreads) + return false; + if (!cwraps.mono_wasm_is_zero_page_reserved()) return false; diff --git a/src/mono/wasm/runtime/jiterpreter-trace-generator.ts b/src/mono/wasm/runtime/jiterpreter-trace-generator.ts index e28643581ca974..1140dfbbfd4a4d 100644 --- a/src/mono/wasm/runtime/jiterpreter-trace-generator.ts +++ b/src/mono/wasm/runtime/jiterpreter-trace-generator.ts @@ -186,6 +186,10 @@ export function generateWasmBody( builder.cfg.entry(ip); while (ip) { + // This means some code went 'ip = abort; continue' + if (!ip) + break; + builder.cfg.ip = ip; if (ip >= endOfBody) { @@ -842,26 +846,208 @@ export function generateWasmBody( break; } - case MintOpcode.MINT_CASTCLASS: - case MintOpcode.MINT_ISINST: - case MintOpcode.MINT_CASTCLASS_COMMON: - case MintOpcode.MINT_ISINST_COMMON: case MintOpcode.MINT_CASTCLASS_INTERFACE: case MintOpcode.MINT_ISINST_INTERFACE: { - builder.block(); - // dest, src - append_ldloca(builder, getArgU16(ip, 1), 4); - append_ldloca(builder, getArgU16(ip, 2), 0); - // klass - builder.ptr_const(get_imethod_data(frame, getArgU16(ip, 3))); - // opcode - builder.i32_const(opcode); - builder.callImport("cast"); - // if cast operation succeeded, skip the bailout - builder.appendU8(WasmOpcode.br_if); - builder.appendULeb(0); - append_bailout(builder, ip, BailoutReason.CastFailed); - builder.endBlock(); + const klass = get_imethod_data(frame, getArgU16(ip, 3)), + isSpecialInterface = cwraps.mono_jiterp_is_special_interface(klass), + bailoutOnFailure = (opcode === MintOpcode.MINT_CASTCLASS_INTERFACE), + destOffset = getArgU16(ip, 1); + if (!klass) { + record_abort(traceIp, ip, traceName, "null-klass"); + ip = abort; + continue; + } + + builder.block(); // depth x -> 0 (opcode block) + + if (builder.options.zeroPageOptimization && isZeroPageReserved()) { + // Null check fusion is possible, so (obj->vtable) will be 0 for !obj + append_ldloc(builder, getArgU16(ip, 2), WasmOpcode.i32_load); + builder.local("temp_ptr", WasmOpcode.tee_local); + counters.nullChecksFused++; + } else { + builder.block(); // depth 0 -> 1 (null check block) + // src + append_ldloc(builder, getArgU16(ip, 2), WasmOpcode.i32_load); + builder.local("temp_ptr", WasmOpcode.tee_local); + // Null ptr check: If the ptr is non-null, skip this block + builder.appendU8(WasmOpcode.br_if); + builder.appendULeb(0); + builder.local("pLocals"); + builder.i32_const(0); + append_stloc_tail(builder, destOffset, WasmOpcode.i32_store); + // at the end of this block (depth 0) we skip to the end of the opcode block (depth 1) + // because we successfully zeroed the destination register + builder.appendU8(WasmOpcode.br); + builder.appendULeb(1); + builder.endBlock(); // depth 1 -> 0 (end null check block) + // Put ptr back on the stack + builder.local("temp_ptr"); + } + + // the special interface version signature is (obj, vtable, klass), but + // the fast signature is (vtable, klass) + if (isSpecialInterface) { + // load a second copy of obj to build the helper arglist (obj, vtable, klass) + builder.local("temp_ptr"); + } + + builder.appendU8(WasmOpcode.i32_load); // obj->vtable + builder.appendMemarg(getMemberOffset(JiterpMember.VTable), 0); // fixme: alignment + + builder.ptr_const(klass); + builder.callImport(isSpecialInterface ? "imp_iface_s" : "imp_iface"); + + if (bailoutOnFailure) { + // generate a 1 for null ptrs so we don't bail out and instead write the 0 + // to the destination + builder.local("temp_ptr"); + builder.appendU8(WasmOpcode.i32_eqz); + builder.appendU8(WasmOpcode.i32_or); + } + + builder.block(WasmValtype.void, WasmOpcode.if_); // if cast succeeded + builder.local("pLocals"); + builder.local("temp_ptr"); + append_stloc_tail(builder, destOffset, WasmOpcode.i32_store); + builder.appendU8(WasmOpcode.else_); // else cast failed + if (bailoutOnFailure) { + // so bailout + append_bailout(builder, ip, BailoutReason.CastFailed); + } else { + // this is isinst, so write 0 to destination instead + builder.local("pLocals"); + builder.i32_const(0); + append_stloc_tail(builder, destOffset, WasmOpcode.i32_store); + } + builder.endBlock(); // endif + + builder.endBlock(); // depth 0 -> x (end opcode block) + + break; + } + + case MintOpcode.MINT_CASTCLASS_COMMON: + case MintOpcode.MINT_ISINST_COMMON: + case MintOpcode.MINT_CASTCLASS: + case MintOpcode.MINT_ISINST: { + const klass = get_imethod_data(frame, getArgU16(ip, 3)), + canDoFastCheck = (opcode === MintOpcode.MINT_CASTCLASS_COMMON) || + (opcode === MintOpcode.MINT_ISINST_COMMON), + bailoutOnFailure = (opcode === MintOpcode.MINT_CASTCLASS) || + (opcode === MintOpcode.MINT_CASTCLASS_COMMON), + destOffset = getArgU16(ip, 1); + if (!klass) { + record_abort(traceIp, ip, traceName, "null-klass"); + ip = abort; + continue; + } + + builder.block(); // depth x -> 0 (opcode block) + + if (builder.options.zeroPageOptimization && isZeroPageReserved()) { + // Null check fusion is possible, so (obj->vtable)->klass will be 0 for !obj + append_ldloc(builder, getArgU16(ip, 2), WasmOpcode.i32_load); + builder.local("temp_ptr", WasmOpcode.tee_local); + counters.nullChecksFused++; + } else { + builder.block(); // depth 0 -> 1 (null check block) + // src + append_ldloc(builder, getArgU16(ip, 2), WasmOpcode.i32_load); + builder.local("temp_ptr", WasmOpcode.tee_local); + // Null ptr check: If the ptr is non-null, skip this block + builder.appendU8(WasmOpcode.br_if); + builder.appendULeb(0); + builder.local("pLocals"); + builder.i32_const(0); + append_stloc_tail(builder, destOffset, WasmOpcode.i32_store); + // at the end of this block (depth 0) we skip to the end of the opcode block (depth 1) + // because we successfully zeroed the destination register + builder.appendU8(WasmOpcode.br); + builder.appendULeb(1); + builder.endBlock(); // depth 1 -> 0 (end null check block) + // Put ptr back on the stack + builder.local("temp_ptr"); + } + + // If we're here the null check passed and we now need to type-check + builder.appendU8(WasmOpcode.i32_load); // obj->vtable + builder.appendMemarg(getMemberOffset(JiterpMember.VTable), 0); // fixme: alignment + builder.appendU8(WasmOpcode.i32_load); // (obj->vtable)->klass + builder.appendMemarg(getMemberOffset(JiterpMember.VTableKlass), 0); // fixme: alignment + // Stash obj->vtable->klass so we can do a fast has_parent check later + if (canDoFastCheck) + builder.local("temp_ptr2", WasmOpcode.tee_local); + builder.i32_const(klass); + builder.appendU8(WasmOpcode.i32_eq); + builder.block(WasmValtype.void, WasmOpcode.if_); // if A + + // Fast type-check passed (exact match), so store the ptr and continue + builder.local("pLocals"); + builder.local("temp_ptr"); + append_stloc_tail(builder, destOffset, WasmOpcode.i32_store); + + // Fast type-check failed, so call the helper function + builder.appendU8(WasmOpcode.else_); // else A + + if (canDoFastCheck) { + // Fast path for ISINST_COMMON/CASTCLASS_COMMON. We know klass is a simple type + // so all we need to do is a parentage check. + builder.local("temp_ptr2"); // obj->vtable->klass + builder.ptr_const(klass); + builder.callImport("hasparent"); + + if (bailoutOnFailure) { + // generate a 1 for null ptrs so we don't bail out and instead write the 0 + // to the destination + builder.local("temp_ptr"); + builder.appendU8(WasmOpcode.i32_eqz); + builder.appendU8(WasmOpcode.i32_or); + } + + builder.block(WasmValtype.void, WasmOpcode.if_); // if B + // mono_class_has_parent_fast returned 1 so *destination = obj + builder.local("pLocals"); + builder.local("temp_ptr"); + append_stloc_tail(builder, destOffset, WasmOpcode.i32_store); + builder.appendU8(WasmOpcode.else_); // else B + // mono_class_has_parent_fast returned 0 + if (bailoutOnFailure) { + // so bailout + append_bailout(builder, ip, BailoutReason.CastFailed); + } else { + // this is isinst, so write 0 to destination instead + builder.local("pLocals"); + builder.i32_const(0); + append_stloc_tail(builder, destOffset, WasmOpcode.i32_store); + } + builder.endBlock(); // endif B + } else { + // Slow path for ISINST/CASTCLASS, handles things like generics and nullable. + // &dest + append_ldloca(builder, getArgU16(ip, 1), 4); + // src + builder.local("temp_ptr"); + // klass + builder.ptr_const(klass); + // opcode + builder.i32_const(opcode); + builder.callImport("castv2"); + + // We don't need to do an explicit null check because mono_jiterp_cast_v2 does it + + // Check whether the cast operation failed + builder.appendU8(WasmOpcode.i32_eqz); + builder.block(WasmValtype.void, WasmOpcode.if_); // if B + // Cast failed so bail out + append_bailout(builder, ip, BailoutReason.CastFailed); + builder.endBlock(); // endif B + } + + builder.endBlock(); // endif A + + builder.endBlock(); // depth 0 -> x (end opcode block) + break; } @@ -876,19 +1062,70 @@ export function generateWasmBody( builder.callImport("box"); break; } + case MintOpcode.MINT_UNBOX: { - builder.block(); - // MonoClass *c = (MonoClass*)frame->imethod->data_items [ip [3]]; - builder.ptr_const(get_imethod_data(frame, getArgU16(ip, 3))); - // dest, src - append_ldloca(builder, getArgU16(ip, 1), 4); - append_ldloca(builder, getArgU16(ip, 2), 0); - builder.callImport("try_unbox"); - // If the unbox operation succeeded, continue, otherwise bailout - builder.appendU8(WasmOpcode.br_if); - builder.appendULeb(0); + const klass = get_imethod_data(frame, getArgU16(ip, 3)), + // The type check needs to examine the boxed value's rank and element class + elementClassOffset = getMemberOffset(JiterpMember.ClassElementClass), + destOffset = getArgU16(ip, 1), + // Get the class's element class, which is what we will actually type-check against + elementClass = getU32_unaligned(klass + elementClassOffset); + + if (!klass || !elementClass) { + record_abort(traceIp, ip, traceName, "null-klass"); + ip = abort; + continue; + } + + if (builder.options.zeroPageOptimization && isZeroPageReserved()) { + // Null check fusion is possible, so (obj->vtable)->klass will be 0 for !obj + append_ldloc(builder, getArgU16(ip, 2), WasmOpcode.i32_load); + builder.local("temp_ptr", WasmOpcode.tee_local); + counters.nullChecksFused++; + } else { + append_ldloc_cknull(builder, getArgU16(ip, 2), ip, true); + builder.local("temp_ptr", WasmOpcode.tee_local); + } + + // Fetch the object's klass so we can perform a type check + builder.appendU8(WasmOpcode.i32_load); // obj->vtable + builder.appendMemarg(getMemberOffset(JiterpMember.VTable), 0); // fixme: alignment + builder.appendU8(WasmOpcode.i32_load); // (obj->vtable)->klass + builder.appendMemarg(getMemberOffset(JiterpMember.VTableKlass), 0); // fixme: alignment + + // Stash obj->vtable->klass, then check klass->element_class == expected + builder.local("temp_ptr2", WasmOpcode.tee_local); + builder.appendU8(WasmOpcode.i32_load); + builder.appendMemarg(elementClassOffset, 0); + builder.i32_const(elementClass); + builder.appendU8(WasmOpcode.i32_eq); + + // Check klass->rank == 0 + builder.local("temp_ptr2"); + builder.appendU8(WasmOpcode.i32_load8_u); // rank is a uint8 + builder.appendMemarg(getMemberOffset(JiterpMember.ClassRank), 0); + builder.appendU8(WasmOpcode.i32_eqz); + + // (element_class == expected) && (rank == 0) + builder.appendU8(WasmOpcode.i32_and); + + builder.block(WasmValtype.void, WasmOpcode.if_); // if type check passed + + // Type-check passed, so now compute the address of the object's data + // and store the address + builder.local("pLocals"); + builder.local("temp_ptr"); + builder.i32_const(getMemberOffset(JiterpMember.BoxedValueData)); + builder.appendU8(WasmOpcode.i32_add); + append_stloc_tail(builder, destOffset, WasmOpcode.i32_store); + + builder.appendU8(WasmOpcode.else_); // else type check failed + + // append_bailout(builder, ip, BailoutReason.UnboxFailed); - builder.endBlock(); + + builder.endBlock(); // endif A + break; } @@ -1490,6 +1727,7 @@ function append_branch_target_block(builder: WasmBuilder, ip: MintOpcodePtr, isB function append_ldloc(builder: WasmBuilder, offset: number, opcodeOrPrefix: WasmOpcode, simdOpcode?: WasmSimdOpcode) { builder.local("pLocals"); + mono_assert(opcodeOrPrefix >= WasmOpcode.i32_load, () => `Expected load opcode but got ${opcodeOrPrefix}`); builder.appendU8(opcodeOrPrefix); if (simdOpcode !== undefined) { // This looks wrong but I assure you it's correct. @@ -1507,6 +1745,7 @@ function append_ldloc(builder: WasmBuilder, offset: number, opcodeOrPrefix: Wasm // The actual store operation is equivalent to `pBase[offset] = value` (alignment has no // observable impact on behavior, other than causing compilation failures if out of range) function append_stloc_tail(builder: WasmBuilder, offset: number, opcodeOrPrefix: WasmOpcode, simdOpcode?: WasmSimdOpcode) { + mono_assert(opcodeOrPrefix >= WasmOpcode.i32_store, () => `Expected store opcode but got ${opcodeOrPrefix}`); builder.appendU8(opcodeOrPrefix); if (simdOpcode !== undefined) { // This looks wrong but I assure you it's correct. diff --git a/src/mono/wasm/runtime/jiterpreter.ts b/src/mono/wasm/runtime/jiterpreter.ts index 8b7986088ca520..ebec1f2caa1eef 100644 --- a/src/mono/wasm/runtime/jiterpreter.ts +++ b/src/mono/wasm/runtime/jiterpreter.ts @@ -263,8 +263,10 @@ function getTraceImports() { importDef("entry", getRawCwrap("mono_jiterp_increase_entry_count")), importDef("value_copy", getRawCwrap("mono_jiterp_value_copy")), importDef("gettype", getRawCwrap("mono_jiterp_gettype_ref")), - importDef("cast", getRawCwrap("mono_jiterp_cast_ref")), - importDef("try_unbox", getRawCwrap("mono_jiterp_try_unbox_ref")), + importDef("castv2", getRawCwrap("mono_jiterp_cast_v2")), + importDef("hasparent", getRawCwrap("mono_jiterp_has_parent_fast")), + importDef("imp_iface", getRawCwrap("mono_jiterp_implements_interface")), + importDef("imp_iface_s", getRawCwrap("mono_jiterp_implements_special_interface")), importDef("box", getRawCwrap("mono_jiterp_box_ref")), importDef("localloc", getRawCwrap("mono_jiterp_localloc")), ["ckovr_i4", "overflow_check_i4", getRawCwrap("mono_jiterp_overflow_check_i4")], @@ -500,7 +502,7 @@ function initialize_builder(builder: WasmBuilder) { WasmValtype.i32, true ); builder.defineType( - "cast", + "castv2", { "destination": WasmValtype.i32, "source": WasmValtype.i32, @@ -510,11 +512,27 @@ function initialize_builder(builder: WasmBuilder) { WasmValtype.i32, true ); builder.defineType( - "try_unbox", + "hasparent", { "klass": WasmValtype.i32, - "destination": WasmValtype.i32, - "source": WasmValtype.i32, + "parent": WasmValtype.i32, + }, + WasmValtype.i32, true + ); + builder.defineType( + "imp_iface", + { + "vtable": WasmValtype.i32, + "klass": WasmValtype.i32, + }, + WasmValtype.i32, true + ); + builder.defineType( + "imp_iface_s", + { + "obj": WasmValtype.i32, + "vtable": WasmValtype.i32, + "klass": WasmValtype.i32, }, WasmValtype.i32, true ); @@ -766,6 +784,7 @@ function generate_wasm( locals: { "disp": WasmValtype.i32, "temp_ptr": WasmValtype.i32, + "temp_ptr2": WasmValtype.i32, "cknull_ptr": WasmValtype.i32, "math_lhs32": WasmValtype.i32, "math_rhs32": WasmValtype.i32,