From 3b9b78cae072fc2ab9a748047eb599885ca50218 Mon Sep 17 00:00:00 2001 From: Katelyn Gadd Date: Mon, 15 May 2023 16:02:06 -0700 Subject: [PATCH] Move jiterpreter tables to new file; Add draft jiterpreter overview document (#86270) Add draft jiterpreter overview document Move jiterpreter tables out of jiterpreter-trace-generator --- docs/design/mono/jiterpreter.md | 103 ++++++ src/mono/wasm/runtime/jiterpreter-opcodes.ts | 9 + src/mono/wasm/runtime/jiterpreter-tables.ts | 358 ++++++++++++++++++++ .../wasm/runtime/jiterpreter-trace-generator.ts | 372 +-------------------- 4 files changed, 479 insertions(+), 363 deletions(-) create mode 100644 docs/design/mono/jiterpreter.md create mode 100644 src/mono/wasm/runtime/jiterpreter-tables.ts diff --git a/docs/design/mono/jiterpreter.md b/docs/design/mono/jiterpreter.md new file mode 100644 index 0000000..32c4463d --- /dev/null +++ b/docs/design/mono/jiterpreter.md @@ -0,0 +1,103 @@ +# The Jiterpreter + +## Introduction +The jiterpreter is a just-in-time compiler for the Mono Interpreter component of the .NET WASM Runtime. It's split into three components: +* The trace compiler, which converts "traces" of sequential Mono Interpreter opcodes into WebAssembly functions +* The interp_entry thunk generator, which generates optimized versions of the wrappers that normally handle transitions from AOT code into the interpreter +* The jit_call thunk generator, which generates optimized versions of the wrapper that normally handles transitions from the interpreter into native code + +## Configuring the Jiterpreter +The jiterpreter is configured by mono runtime options, defined in options-def.h. The key ones to configure the jiterpreter are as follows: +* `jiterpreter-traces-enabled`: Enables trace compiler. On by default for single-threaded builds; disabled if threading is active. +* `jiterpreter-interp-entry-enabled`: Enables interp_entry thunks. Only relevant for AOT. +* `jiterpreter-jit-call-enabled`: Enables jit_call thunks. Only relevant for AOT. +* `jiterpreter-stats-enabled`: Enables collecting and printing statistics like number of traces compiled, number of bytes generated, and which interpreter opcodes caused failures. Statistic printing may need to be manually triggered using `INTERNAL.jiterpreter_dump_stats()`. +* `jiterpreter-backward-branches-enabled`: Configures whether backward branches will be handled in traces. If disabled, backward branches will be handled by the interpreter. +* `jiterpreter-eliminate-null-checks`: Configures whether null check elimination is enabled. This optimization may cause correctness issues in rare cases if you hit a bug, especially if your code throws NullReferenceExceptions during normal execution. +* `jiterpreter-wasm-bytes-limit`: Limits the total number of wasm bytes the jiterpreter will generate. Once this limit is hit, it shuts off in order to avoid exhausting browser memory. + +To set runtime options when using the .NET runtime directly, use the `withRuntimeOptions` configuration method: +```javascript + const runtime = await dotnet + .withRuntimeOptions(["--jiterpreter-stats-enabled"]) +``` +When using Blazor, you can use the Msbuild property `BlazorWebAssemblyJiterpreter` as a convenient shorthand to configure whether the Jiterpreter is enabled. You can also use `BlazorWebAssemblyRuntimeOptions` to set specific options directly. At present, the Jiterpreter only functions in Blazor applications that have been published. When running with debugging enabled, it will be inactive. + +## Trace lifecycle +The trace compiler operates in multiple phases, starting first during code generation for "tiered" (optimized) interpreter methods and then finishing during actual execution of interpreted code. + +### Prepare point insertion +While the interpreter is generating code, we select candidate locations that may be good places to create a trace, using multiple heuristics to eliminate locations that are less likely to be valuable. At each candidate location, we insert a "prepare point" opcode that will maintain statistics during execution and eventually trigger compilation of the trace. The heuristics are important because each prepare point opcode adds a small amount of overhead to the method and a large method may have many prepare points. Each prepare point has a unique index with an associated entry in the "trace info" table maintained by the jiterpreter. + +### Prepare point execution +During execution, each time a prepare point is hit, we increment its hit counter. If the hit counter reaches a set threshold we will invoke the trace compiler, and if trace compilation is successful we transform the prepare point into a "monitor point". If compilation fails or the trace is rejected for some reason, we patch the opcode to become a NOP, reducing its cost slightly. + +### Trace compilation +Trace compilation occurs synchronously as soon as the threshold is hit for a given prepare point and produces a function pointer that can be used to enter the trace directly. Compilation will fail and "reject" a trace if it doesn't meet key criteria - for example, traces that are too short are rejected because the overhead of entering and exiting the trace makes them harmful to performance. Upon successful compilation, the `MINT_TIER_PREPARE_JITERPRETER trace_index` opcode is patched to become `MINT_TIER_MONITOR_JITERPRETER trace_index`. + +### Monitor point execution +The trace info table contains a hit counter and "penalty total" for each unique candidate location. During execution when the interpreter hits a monitor point it executes the trace in a special monitoring mode by passing a JiterpreterCallInfo structure into the trace. All traces have special monitoring support compiled in that will update this structure when exiting early, either due to an abnormal condition (like an exception being thrown) or due to a trace compilation failure (an unsupported opcode, etc.) These abnormal exits store an estimated number of opcodes executed along with a backward branch flag, and the combination of these two values are used to compute a "penalty value" for the current trace execution. We maintain a running hit counter for monitoring mode along with a running sum of the penalty values. + +After a trace is executed a set number of times in monitoring mode, we compare the average penalty value against a threshold and decide whether to disable the trace (turning the entry point into a NOP) or keep it permanently. This allows us to cull traces that have undesirable behavior we couldn't detect at compile time (for example, conditionally performing a function call 99% of the time right at the beginning of the trace, which forces us to return control to the interpreter.) If the trace survives the monitoring phase, we patch the `MINT_TIER_MONITOR_JITERPRETER trace_index` opcode into a `MINT_TIER_ENTER_JITERPRETER function_pointer` opcode to allow more efficient execution of the trace. + +### Normal trace execution +For traces which survive the monitoring phase, interpreter execution will hit a MINT_TIER_ENTER_JITERPRETER opcode, read the function pointer out of it, and directly invoke the function pointer. The trace returns a displacement (in bytes) from the current location, which allows returning control to any location in the method. + +## Trace compilation model +The trace compiler is designed to operate sequentially in two simple passes in order to reduce the amount of time and memory consumed by JIT compilation. + +### Opcode translation pass +The first pass scans sequentially through the interpreter opcodes for a method, starting from the prepare point, transforming each encountered opcode into one of: +* A native WASM implementation of the opcode +* A call out to a helper that implements the opcode, written in C +* A call out to a libc function that implements the opcode +* A "bailout" which returns control to the interpreter at the opcode's location in order to execute it +During this first pass the compiler also records control flow information, keeping track of branches and branch targets in its "CFG" which will be used later to construct the WASM structures necessary for loops and other control flow. +During compilation a running estimate is maintained of the trace's size, because web browsers impose an arbitrary 4KB limit on the total size of a synchronously compiled module, including the size of things like function names and type information. If we get too close to the 4KB limit, trace compilation will end at the current location. During this phase all of the generated Webassembly code is appended into a scratch buffer, with buffer offsets recorded in the CFG. + +### Control flow pass +The second pass generates the final WebAssembly module including metadata like types, imported and exported functions, etc. The actual executable WebAssembly code is stitched together from segments of the scratch buffer based on the "segments" recorded in the CFG. Segments come in three types: +* Blob segments, containing one or more WASM opcodes that execute sequentially. These can be copied directly into the result module +* Branch block header segments, which represent a location targeted by forward or backward branches elsewhere in the code. We generate WebAssembly flow control structures at these locations based on the information we have about the entire trace. +* Branch segments, which represent a conditional or unconditional branch that occurs after a blob. Conditional branch segments are surrounded by header and footer blobs, used to implement opcodes like conditional branches or null checks. These are translated into WebAssembly branch opcodes targeting a specific branch block header, and for backward branches we also set a dispatch index. +For traces containing backward branches, each trace begins with a small "dispatch table" which performs a forward branch to a specific destination determined by a dispatch index. Upon trace entry the dispatch index points to the top of the trace, but when a backwards branch occurs we set a specific dispatch index and always jump to the dispatch table. This is necessary due to WebAssembly's heavily constrained flow control model that does not allow arbitrary jumps and encodes jumps based on nesting depths instead of as branches targeting specific code offsets. + +### Opcode translation patterns +Opcodes fall into a few large categories: +* Direct translation - where we generate WebAssembly code matching the behavior of the original interpreter C exactly or almost exactly (perhaps patching out a data_items access to be a constant instead). This describes the vast majority of opcodes. In many cases, this is driven by tables - for example, most of the arithmetic operators are just looked up in a table and generated by a generic emitter function instead of being specific cases in a switch statement. +* Conditional translation - where we can use information available to us at trace compile time to decide whether to omit some or all of the code that would be generated for direct translation. The main example of this is null checks - we are able to remove redundant null checks from sequences of operations that all read from the same local, which results in smaller/faster traces while the interpreter must perform those checks every time. +* Helper translation - some opcodes do a small amount of setup and then call a C helper in the runtime that performs the hard work. This is typically done when translating the opcode directly would generate a large amount of code or the opcode is generally unlikely to be hit. In some cases the interpreter's implementation of an opcode is a thin layer over libc, so for those cases we try to invoke the libc function directly. +* Native translation - some opcodes can be represented by a handful of native WebAssembly opcodes instead of using the C implementation from the interpreter. The main example of this is SIMD operations, but there are a small number of others. This kind of translation needs to be done carefully by consulting the spec because in some cases the semantics are intentionally underspecified. +* Abort - opcodes that can't be implemented in the jiterpreter or haven't been implemented yet will translate into a bailout that terminates trace execution, returning control to the interpreter at the location of the opcode. Many of these bailouts are skipped over by branches, so a given trace might contain multiple bailouts of various types. If an abort is determined to execute unconditionally, we will fully abort trace compilation at that point. + +### Opcode values +When determining whether to keep or reject a trace, we do this based on 'opcode values', where each opcode is assigned a value that approximates how much productive work the resulting trace will perform. These opcodes are defined in `jiterpreter-opcode-values.h`. For opcodes like branches we assign them a low value due to the overhead associated with running them in a trace (sometimes worse than the interpreter for reasons described below), while for opcodes like SIMD instructions or field accesses with eliminated null checks, we assign them a high value to represent how they are significantly faster in traces than in the interpreter. The sum of a trace's opcode values is compared against a threshold at the end of trace compilation to decide whether to keep it. + +### Trace performance considerations +Transforming interpreter opcodes into WebAssembly opcodes comes with a few key performance considerations: +* Translating each branch opcode into a unique branch in WebAssembly changes branch prediction characteristics (usually for the worse), which without additional optimization may cause performance to decrease. This is because a significant percentage of the branches in interpreter code are for handling error conditions, so in the interpreter they predict accurately ~99% of the time. +* When the interpreter is executing tight loops all of the relevant code and data may remain in cache, while the generated trace for that same code may be much bigger and end up evicting critical information from cache. This increases the importance of generating small, efficient WebAssembly code since the native code generated from it may contain security checks and error handlers. +* Many interpreter opcodes determine their behavior based on information from the method's "data_items" table or opcode arguments, containing things like MonoType* pointers or struct sizes. For optimal performance it is critical to encode this information as constants or where possible use the information to statically determine the correct behavior at compile time. The main example of this is that the jiterpreter unrolls memory sets and moves of known-small sizes, avoiding an expensive call into libc. +* Arbitrary stack or heap access (via pointers) comes with additional overhead in WebAssembly compared to leaving values on the native WASM stack or in WASM locals, due to the need to bounds-check all memory operations. This means that while the C implementation of a given opcode might dereference a pointer multiple times, it can be critical to instead dereference it once and store it into a temporary local. This can cause new problems, however, so careful measurement is necessary to determine whether doing this actually improves performance for a given scenario. +* All major browsers have tiering compilers for WebAssembly, so it is important to ensure that the code we generate will not cause significant performance issues in a given compiler's fast/naive tier(s) (for example, creating an enormous stack frame due to too many locals - this scenario caused stack overflows on iOS at one point.). We should also keep in mind that in some corner cases, our WebAssembly code may itself run in an interpreter. +* Function pointers also have considerably higher overhead in WebAssembly (due to indirection and type checks), so we should take steps where possible to minimize the amount of indirection through vtables and function pointers, calling functions directly (as WebAssembly imports) where possible. + +## Interpreter entry thunks +Interpreter entry points that meet some basic criteria (number of arguments, etc) are instrumented to notify the jiterpreter each time they are hit. After a certain number of hits, they are added to the "jit queue" and will be compiled asynchronously in small batches. If a specific entry point is hit an even larger number of times, the queue will immediately be flushed to compile it. + +The compiled thunks are simple functions that imitate the behavior of the generic interp_entry wrapper for a given target method, eliminating some of the indirect function calls and branching on types that would normally happen. Arguments are mapped from the native WASM calling convention to the interpreter stack so that execution can begin. The behavior of the thunks is meant to match the semantics of `interp_entry` 1:1. + +There is also a small optimization in this path that attempts to detect whether the interp_entry thunk is being used to call a delegate, and in cases where the same delegate is invoked repeatedly we are able to cache the target method instead of performing an expensive delegate invoke lookup every call. + +## JIT call thunks +The `do_jit_call` interpreter operation is instrumented to record hit counts for each call site. After a certain number of hits, a call site is added to the "jit queue" and the queue will be flushed asynchronously in small batches. If a specific call site is hit a large number of times, the queue will immediately be flushed. + +The compiled jit call thunks are simple functions that load function arguments from the interpreter stack and pass them directly to native code with the WASM calling convention. The thunks also perform exception handling, using native WebAssembly Exceptions if possible for improved performance. The behavior of the thunks is meant to match the semantics of `mini_get_gsharedvt_out_sig_wrapper` 1:1. + +Where possible jit call thunks will be "direct", bypassing the by-reference wrappers typically used for AOT->native transitions and calling the target function directly with arguments passed by-value. This is possible when all of the arguments are simple types and the wrapper is not needed for some special reason. + +## Feature detection and fallbacks +At startup the jiterpreter will attempt to compile two small webassembly modules in order to detect support for WebAssembly Exception Handling and WebAssembly SIMD. While both of these features are widely supported, they were not available in the WebAssembly MVP so we need to detect their availability before trying to use them. If they are unavailable, code generation will adapt to use fallbacks (for exception handling, a JavaScript helper, and for SIMD, scalar implementations in C or C#.) + +## Error handling +The jiterpreter's compiler detects various errors during compilation and reports them to the browser console. If more than a small number of compile errors occur during execution, the jiterpreter will automatically be disabled to avoid having serious impacts on application performance or stability. diff --git a/src/mono/wasm/runtime/jiterpreter-opcodes.ts b/src/mono/wasm/runtime/jiterpreter-opcodes.ts index fdc5dd2..8f6668a 100644 --- a/src/mono/wasm/runtime/jiterpreter-opcodes.ts +++ b/src/mono/wasm/runtime/jiterpreter-opcodes.ts @@ -29,6 +29,15 @@ export const enum MintOpArgType { MintOpPair4 } +export const enum JiterpSpecialOpcode { + CNE_UN_R4 = 0xFFFF + 0, + CGE_UN_R4 = 0xFFFF + 1, + CLE_UN_R4 = 0xFFFF + 2, + CNE_UN_R8 = 0xFFFF + 3, + CGE_UN_R8 = 0xFFFF + 4, + CLE_UN_R8 = 0xFFFF + 5, +} + export const enum OpcodeInfoType { Name = 0, Length, diff --git a/src/mono/wasm/runtime/jiterpreter-tables.ts b/src/mono/wasm/runtime/jiterpreter-tables.ts new file mode 100644 index 0000000..ebaa91f --- /dev/null +++ b/src/mono/wasm/runtime/jiterpreter-tables.ts @@ -0,0 +1,358 @@ +import { + WasmOpcode, JiterpSpecialOpcode +} from "./jiterpreter-opcodes"; +import { + MintOpcode, SimdIntrinsic3 +} from "./mintops"; + +export const ldcTable: { [opcode: number]: [WasmOpcode, number] } = { + [MintOpcode.MINT_LDC_I4_M1]: [WasmOpcode.i32_const, -1], + [MintOpcode.MINT_LDC_I4_0]: [WasmOpcode.i32_const, 0], + [MintOpcode.MINT_LDC_I4_1]: [WasmOpcode.i32_const, 1], + [MintOpcode.MINT_LDC_I4_2]: [WasmOpcode.i32_const, 2], + [MintOpcode.MINT_LDC_I4_3]: [WasmOpcode.i32_const, 3], + [MintOpcode.MINT_LDC_I4_4]: [WasmOpcode.i32_const, 4], + [MintOpcode.MINT_LDC_I4_5]: [WasmOpcode.i32_const, 5], + [MintOpcode.MINT_LDC_I4_6]: [WasmOpcode.i32_const, 6], + [MintOpcode.MINT_LDC_I4_7]: [WasmOpcode.i32_const, 7], + [MintOpcode.MINT_LDC_I4_8]: [WasmOpcode.i32_const, 8], +}; + +// operator, loadOperator, storeOperator +export type OpRec3 = [WasmOpcode, WasmOpcode, WasmOpcode]; +// operator, lhsLoadOperator, rhsLoadOperator, storeOperator +export type OpRec4 = [WasmOpcode, WasmOpcode, WasmOpcode, WasmOpcode]; + +export const floatToIntTable: { [opcode: number]: WasmOpcode } = { + [MintOpcode.MINT_CONV_I4_R4]: WasmOpcode.i32_trunc_s_f32, + [MintOpcode.MINT_CONV_I8_R4]: WasmOpcode.i64_trunc_s_f32, + [MintOpcode.MINT_CONV_I4_R8]: WasmOpcode.i32_trunc_s_f64, + [MintOpcode.MINT_CONV_I8_R8]: WasmOpcode.i64_trunc_s_f64, +}; + +export const unopTable: { [opcode: number]: OpRec3 | undefined } = { + [MintOpcode.MINT_CEQ0_I4]: [WasmOpcode.i32_eqz, WasmOpcode.i32_load, WasmOpcode.i32_store], + [MintOpcode.MINT_ADD1_I4]: [WasmOpcode.i32_add, WasmOpcode.i32_load, WasmOpcode.i32_store], + [MintOpcode.MINT_SUB1_I4]: [WasmOpcode.i32_sub, WasmOpcode.i32_load, WasmOpcode.i32_store], + [MintOpcode.MINT_NEG_I4]: [WasmOpcode.i32_sub, WasmOpcode.i32_load, WasmOpcode.i32_store], + [MintOpcode.MINT_NOT_I4]: [WasmOpcode.i32_xor, WasmOpcode.i32_load, WasmOpcode.i32_store], + + [MintOpcode.MINT_ADD1_I8]: [WasmOpcode.i64_add, WasmOpcode.i64_load, WasmOpcode.i64_store], + [MintOpcode.MINT_SUB1_I8]: [WasmOpcode.i64_sub, WasmOpcode.i64_load, WasmOpcode.i64_store], + [MintOpcode.MINT_NEG_I8]: [WasmOpcode.i64_sub, WasmOpcode.i64_load, WasmOpcode.i64_store], + [MintOpcode.MINT_NOT_I8]: [WasmOpcode.i64_xor, WasmOpcode.i64_load, WasmOpcode.i64_store], + + [MintOpcode.MINT_ADD_I4_IMM]: [WasmOpcode.i32_add, WasmOpcode.i32_load, WasmOpcode.i32_store], + [MintOpcode.MINT_MUL_I4_IMM]: [WasmOpcode.i32_mul, WasmOpcode.i32_load, WasmOpcode.i32_store], + [MintOpcode.MINT_ADD_I8_IMM]: [WasmOpcode.i64_add, WasmOpcode.i64_load, WasmOpcode.i64_store], + [MintOpcode.MINT_MUL_I8_IMM]: [WasmOpcode.i64_mul, WasmOpcode.i64_load, WasmOpcode.i64_store], + + [MintOpcode.MINT_NEG_R4]: [WasmOpcode.f32_neg, WasmOpcode.f32_load, WasmOpcode.f32_store], + [MintOpcode.MINT_NEG_R8]: [WasmOpcode.f64_neg, WasmOpcode.f64_load, WasmOpcode.f64_store], + + [MintOpcode.MINT_CONV_R4_I4]: [WasmOpcode.f32_convert_s_i32, WasmOpcode.i32_load, WasmOpcode.f32_store], + [MintOpcode.MINT_CONV_R8_I4]: [WasmOpcode.f64_convert_s_i32, WasmOpcode.i32_load, WasmOpcode.f64_store], + [MintOpcode.MINT_CONV_R_UN_I4]: [WasmOpcode.f64_convert_u_i32, WasmOpcode.i32_load, WasmOpcode.f64_store], + [MintOpcode.MINT_CONV_R4_I8]: [WasmOpcode.f32_convert_s_i64, WasmOpcode.i64_load, WasmOpcode.f32_store], + [MintOpcode.MINT_CONV_R8_I8]: [WasmOpcode.f64_convert_s_i64, WasmOpcode.i64_load, WasmOpcode.f64_store], + [MintOpcode.MINT_CONV_R_UN_I8]: [WasmOpcode.f64_convert_u_i64, WasmOpcode.i64_load, WasmOpcode.f64_store], + [MintOpcode.MINT_CONV_R8_R4]: [WasmOpcode.f64_promote_f32, WasmOpcode.f32_load, WasmOpcode.f64_store], + [MintOpcode.MINT_CONV_R4_R8]: [WasmOpcode.f32_demote_f64, WasmOpcode.f64_load, WasmOpcode.f32_store], + + [MintOpcode.MINT_CONV_I8_I4]: [WasmOpcode.nop, WasmOpcode.i64_load32_s, WasmOpcode.i64_store], + [MintOpcode.MINT_CONV_I8_U4]: [WasmOpcode.nop, WasmOpcode.i64_load32_u, WasmOpcode.i64_store], + + [MintOpcode.MINT_CONV_U1_I4]: [WasmOpcode.i32_and, WasmOpcode.i32_load, WasmOpcode.i32_store], + [MintOpcode.MINT_CONV_U2_I4]: [WasmOpcode.i32_and, WasmOpcode.i32_load, WasmOpcode.i32_store], + [MintOpcode.MINT_CONV_I1_I4]: [WasmOpcode.i32_shr_s, WasmOpcode.i32_load, WasmOpcode.i32_store], + [MintOpcode.MINT_CONV_I2_I4]: [WasmOpcode.i32_shr_s, WasmOpcode.i32_load, WasmOpcode.i32_store], + + [MintOpcode.MINT_CONV_U1_I8]: [WasmOpcode.i32_and, WasmOpcode.i64_load, WasmOpcode.i32_store], + [MintOpcode.MINT_CONV_U2_I8]: [WasmOpcode.i32_and, WasmOpcode.i64_load, WasmOpcode.i32_store], + [MintOpcode.MINT_CONV_I1_I8]: [WasmOpcode.i32_shr_s, WasmOpcode.i64_load, WasmOpcode.i32_store], + [MintOpcode.MINT_CONV_I2_I8]: [WasmOpcode.i32_shr_s, WasmOpcode.i64_load, WasmOpcode.i32_store], + + [MintOpcode.MINT_SHL_I4_IMM]: [WasmOpcode.i32_shl, WasmOpcode.i32_load, WasmOpcode.i32_store], + [MintOpcode.MINT_SHL_I8_IMM]: [WasmOpcode.i64_shl, WasmOpcode.i64_load, WasmOpcode.i64_store], + [MintOpcode.MINT_SHR_I4_IMM]: [WasmOpcode.i32_shr_s, WasmOpcode.i32_load, WasmOpcode.i32_store], + [MintOpcode.MINT_SHR_I8_IMM]: [WasmOpcode.i64_shr_s, WasmOpcode.i64_load, WasmOpcode.i64_store], + [MintOpcode.MINT_SHR_UN_I4_IMM]: [WasmOpcode.i32_shr_u, WasmOpcode.i32_load, WasmOpcode.i32_store], + [MintOpcode.MINT_SHR_UN_I8_IMM]: [WasmOpcode.i64_shr_u, WasmOpcode.i64_load, WasmOpcode.i64_store], + + [MintOpcode.MINT_ROL_I4_IMM]: [WasmOpcode.i32_rotl, WasmOpcode.i32_load, WasmOpcode.i32_store], + [MintOpcode.MINT_ROL_I8_IMM]: [WasmOpcode.i64_rotl, WasmOpcode.i64_load, WasmOpcode.i64_store], + [MintOpcode.MINT_ROR_I4_IMM]: [WasmOpcode.i32_rotr, WasmOpcode.i32_load, WasmOpcode.i32_store], + [MintOpcode.MINT_ROR_I8_IMM]: [WasmOpcode.i64_rotr, WasmOpcode.i64_load, WasmOpcode.i64_store], + + [MintOpcode.MINT_CLZ_I4]: [WasmOpcode.i32_clz, WasmOpcode.i32_load, WasmOpcode.i32_store], + [MintOpcode.MINT_CTZ_I4]: [WasmOpcode.i32_ctz, WasmOpcode.i32_load, WasmOpcode.i32_store], + [MintOpcode.MINT_POPCNT_I4]: [WasmOpcode.i32_popcnt, WasmOpcode.i32_load, WasmOpcode.i32_store], + [MintOpcode.MINT_CLZ_I8]: [WasmOpcode.i64_clz, WasmOpcode.i64_load, WasmOpcode.i64_store], + [MintOpcode.MINT_CTZ_I8]: [WasmOpcode.i64_ctz, WasmOpcode.i64_load, WasmOpcode.i64_store], + [MintOpcode.MINT_POPCNT_I8]: [WasmOpcode.i64_popcnt, WasmOpcode.i64_load, WasmOpcode.i64_store], +}; + +// HACK: Generating correct wasm for these is non-trivial so we hand them off to C. +// The opcode specifies whether the operands need to be promoted first. +export const intrinsicFpBinops: { [opcode: number]: WasmOpcode } = { + [MintOpcode.MINT_CEQ_R4]: WasmOpcode.f64_promote_f32, + [MintOpcode.MINT_CEQ_R8]: WasmOpcode.nop, + [MintOpcode.MINT_CNE_R4]: WasmOpcode.f64_promote_f32, + [MintOpcode.MINT_CNE_R8]: WasmOpcode.nop, + [MintOpcode.MINT_CGT_R4]: WasmOpcode.f64_promote_f32, + [MintOpcode.MINT_CGT_R8]: WasmOpcode.nop, + [MintOpcode.MINT_CGE_R4]: WasmOpcode.f64_promote_f32, + [MintOpcode.MINT_CGE_R8]: WasmOpcode.nop, + [MintOpcode.MINT_CGT_UN_R4]: WasmOpcode.f64_promote_f32, + [MintOpcode.MINT_CGT_UN_R8]: WasmOpcode.nop, + [MintOpcode.MINT_CLT_R4]: WasmOpcode.f64_promote_f32, + [MintOpcode.MINT_CLT_R8]: WasmOpcode.nop, + [MintOpcode.MINT_CLT_UN_R4]: WasmOpcode.f64_promote_f32, + [MintOpcode.MINT_CLT_UN_R8]: WasmOpcode.nop, + [MintOpcode.MINT_CLE_R4]: WasmOpcode.f64_promote_f32, + [MintOpcode.MINT_CLE_R8]: WasmOpcode.nop, + [JiterpSpecialOpcode.CGE_UN_R4]: WasmOpcode.f64_promote_f32, + [JiterpSpecialOpcode.CLE_UN_R4]: WasmOpcode.f64_promote_f32, + [JiterpSpecialOpcode.CNE_UN_R4]: WasmOpcode.f64_promote_f32, + [JiterpSpecialOpcode.CGE_UN_R8]: WasmOpcode.nop, + [JiterpSpecialOpcode.CLE_UN_R8]: WasmOpcode.nop, + [JiterpSpecialOpcode.CNE_UN_R8]: WasmOpcode.nop, +}; + +export const binopTable: { [opcode: number]: OpRec3 | OpRec4 | undefined } = { + [MintOpcode.MINT_ADD_I4]: [WasmOpcode.i32_add, WasmOpcode.i32_load, WasmOpcode.i32_store], + [MintOpcode.MINT_ADD_OVF_I4]: [WasmOpcode.i32_add, WasmOpcode.i32_load, WasmOpcode.i32_store], + [MintOpcode.MINT_ADD_OVF_UN_I4]: [WasmOpcode.i32_add, WasmOpcode.i32_load, WasmOpcode.i32_store], + [MintOpcode.MINT_SUB_I4]: [WasmOpcode.i32_sub, WasmOpcode.i32_load, WasmOpcode.i32_store], + [MintOpcode.MINT_MUL_I4]: [WasmOpcode.i32_mul, WasmOpcode.i32_load, WasmOpcode.i32_store], + [MintOpcode.MINT_MUL_OVF_I4]: [WasmOpcode.i32_mul, WasmOpcode.i32_load, WasmOpcode.i32_store], + [MintOpcode.MINT_MUL_OVF_UN_I4]: [WasmOpcode.i32_mul, WasmOpcode.i32_load, WasmOpcode.i32_store], + [MintOpcode.MINT_DIV_I4]: [WasmOpcode.i32_div_s, WasmOpcode.i32_load, WasmOpcode.i32_store], + [MintOpcode.MINT_DIV_UN_I4]: [WasmOpcode.i32_div_u, WasmOpcode.i32_load, WasmOpcode.i32_store], + [MintOpcode.MINT_REM_I4]: [WasmOpcode.i32_rem_s, WasmOpcode.i32_load, WasmOpcode.i32_store], + [MintOpcode.MINT_REM_UN_I4]: [WasmOpcode.i32_rem_u, WasmOpcode.i32_load, WasmOpcode.i32_store], + [MintOpcode.MINT_AND_I4]: [WasmOpcode.i32_and, WasmOpcode.i32_load, WasmOpcode.i32_store], + [MintOpcode.MINT_OR_I4]: [WasmOpcode.i32_or, WasmOpcode.i32_load, WasmOpcode.i32_store], + [MintOpcode.MINT_XOR_I4]: [WasmOpcode.i32_xor, WasmOpcode.i32_load, WasmOpcode.i32_store], + [MintOpcode.MINT_SHL_I4]: [WasmOpcode.i32_shl, WasmOpcode.i32_load, WasmOpcode.i32_store], + [MintOpcode.MINT_SHR_I4]: [WasmOpcode.i32_shr_s, WasmOpcode.i32_load, WasmOpcode.i32_store], + [MintOpcode.MINT_SHR_UN_I4]: [WasmOpcode.i32_shr_u, WasmOpcode.i32_load, WasmOpcode.i32_store], + + [MintOpcode.MINT_ADD_I8]: [WasmOpcode.i64_add, WasmOpcode.i64_load, WasmOpcode.i64_store], + [MintOpcode.MINT_SUB_I8]: [WasmOpcode.i64_sub, WasmOpcode.i64_load, WasmOpcode.i64_store], + [MintOpcode.MINT_MUL_I8]: [WasmOpcode.i64_mul, WasmOpcode.i64_load, WasmOpcode.i64_store], + [MintOpcode.MINT_DIV_I8]: [WasmOpcode.i64_div_s, WasmOpcode.i64_load, WasmOpcode.i64_store], + [MintOpcode.MINT_REM_I8]: [WasmOpcode.i64_rem_s, WasmOpcode.i64_load, WasmOpcode.i64_store], + [MintOpcode.MINT_DIV_UN_I8]: [WasmOpcode.i64_div_u, WasmOpcode.i64_load, WasmOpcode.i64_store], + [MintOpcode.MINT_REM_UN_I8]: [WasmOpcode.i64_rem_u, WasmOpcode.i64_load, WasmOpcode.i64_store], + [MintOpcode.MINT_AND_I8]: [WasmOpcode.i64_and, WasmOpcode.i64_load, WasmOpcode.i64_store], + [MintOpcode.MINT_OR_I8]: [WasmOpcode.i64_or, WasmOpcode.i64_load, WasmOpcode.i64_store], + [MintOpcode.MINT_XOR_I8]: [WasmOpcode.i64_xor, WasmOpcode.i64_load, WasmOpcode.i64_store], + [MintOpcode.MINT_SHL_I8]: [WasmOpcode.i64_shl, WasmOpcode.i64_load, WasmOpcode.i64_store], + [MintOpcode.MINT_SHR_I8]: [WasmOpcode.i64_shr_s, WasmOpcode.i64_load, WasmOpcode.i64_store], + [MintOpcode.MINT_SHR_UN_I8]: [WasmOpcode.i64_shr_u, WasmOpcode.i64_load, WasmOpcode.i64_store], + + [MintOpcode.MINT_ADD_R4]: [WasmOpcode.f32_add, WasmOpcode.f32_load, WasmOpcode.f32_store], + [MintOpcode.MINT_SUB_R4]: [WasmOpcode.f32_sub, WasmOpcode.f32_load, WasmOpcode.f32_store], + [MintOpcode.MINT_MUL_R4]: [WasmOpcode.f32_mul, WasmOpcode.f32_load, WasmOpcode.f32_store], + [MintOpcode.MINT_DIV_R4]: [WasmOpcode.f32_div, WasmOpcode.f32_load, WasmOpcode.f32_store], + + [MintOpcode.MINT_ADD_R8]: [WasmOpcode.f64_add, WasmOpcode.f64_load, WasmOpcode.f64_store], + [MintOpcode.MINT_SUB_R8]: [WasmOpcode.f64_sub, WasmOpcode.f64_load, WasmOpcode.f64_store], + [MintOpcode.MINT_MUL_R8]: [WasmOpcode.f64_mul, WasmOpcode.f64_load, WasmOpcode.f64_store], + [MintOpcode.MINT_DIV_R8]: [WasmOpcode.f64_div, WasmOpcode.f64_load, WasmOpcode.f64_store], + + [MintOpcode.MINT_CEQ_I4]: [WasmOpcode.i32_eq, WasmOpcode.i32_load, WasmOpcode.i32_store], + [MintOpcode.MINT_CNE_I4]: [WasmOpcode.i32_ne, WasmOpcode.i32_load, WasmOpcode.i32_store], + [MintOpcode.MINT_CLT_I4]: [WasmOpcode.i32_lt_s, WasmOpcode.i32_load, WasmOpcode.i32_store], + [MintOpcode.MINT_CGT_I4]: [WasmOpcode.i32_gt_s, WasmOpcode.i32_load, WasmOpcode.i32_store], + [MintOpcode.MINT_CLE_I4]: [WasmOpcode.i32_le_s, WasmOpcode.i32_load, WasmOpcode.i32_store], + [MintOpcode.MINT_CGE_I4]: [WasmOpcode.i32_ge_s, WasmOpcode.i32_load, WasmOpcode.i32_store], + + [MintOpcode.MINT_CLT_UN_I4]: [WasmOpcode.i32_lt_u, WasmOpcode.i32_load, WasmOpcode.i32_store], + [MintOpcode.MINT_CGT_UN_I4]: [WasmOpcode.i32_gt_u, WasmOpcode.i32_load, WasmOpcode.i32_store], + [MintOpcode.MINT_CLE_UN_I4]: [WasmOpcode.i32_le_u, WasmOpcode.i32_load, WasmOpcode.i32_store], + [MintOpcode.MINT_CGE_UN_I4]: [WasmOpcode.i32_ge_u, WasmOpcode.i32_load, WasmOpcode.i32_store], + + [MintOpcode.MINT_CEQ_I8]: [WasmOpcode.i64_eq, WasmOpcode.i64_load, WasmOpcode.i32_store], + [MintOpcode.MINT_CNE_I8]: [WasmOpcode.i64_ne, WasmOpcode.i64_load, WasmOpcode.i32_store], + [MintOpcode.MINT_CLT_I8]: [WasmOpcode.i64_lt_s, WasmOpcode.i64_load, WasmOpcode.i32_store], + [MintOpcode.MINT_CGT_I8]: [WasmOpcode.i64_gt_s, WasmOpcode.i64_load, WasmOpcode.i32_store], + [MintOpcode.MINT_CLE_I8]: [WasmOpcode.i64_le_s, WasmOpcode.i64_load, WasmOpcode.i32_store], + [MintOpcode.MINT_CGE_I8]: [WasmOpcode.i64_ge_s, WasmOpcode.i64_load, WasmOpcode.i32_store], + + [MintOpcode.MINT_CLT_UN_I8]: [WasmOpcode.i64_lt_u, WasmOpcode.i64_load, WasmOpcode.i32_store], + [MintOpcode.MINT_CGT_UN_I8]: [WasmOpcode.i64_gt_u, WasmOpcode.i64_load, WasmOpcode.i32_store], + [MintOpcode.MINT_CLE_UN_I8]: [WasmOpcode.i64_le_u, WasmOpcode.i64_load, WasmOpcode.i32_store], + [MintOpcode.MINT_CGE_UN_I8]: [WasmOpcode.i64_ge_u, WasmOpcode.i64_load, WasmOpcode.i32_store], + +}; + +export const relopbranchTable: { [opcode: number]: [comparisonOpcode: MintOpcode, immediateOpcode: WasmOpcode | false, isSafepoint: boolean] | MintOpcode | undefined } = { + [MintOpcode.MINT_BEQ_I4_S]: MintOpcode.MINT_CEQ_I4, + [MintOpcode.MINT_BNE_UN_I4_S]: MintOpcode.MINT_CNE_I4, + [MintOpcode.MINT_BGT_I4_S]: MintOpcode.MINT_CGT_I4, + [MintOpcode.MINT_BGT_UN_I4_S]: MintOpcode.MINT_CGT_UN_I4, + [MintOpcode.MINT_BLT_I4_S]: MintOpcode.MINT_CLT_I4, + [MintOpcode.MINT_BLT_UN_I4_S]: MintOpcode.MINT_CLT_UN_I4, + [MintOpcode.MINT_BGE_I4_S]: MintOpcode.MINT_CGE_I4, + [MintOpcode.MINT_BGE_UN_I4_S]: MintOpcode.MINT_CGE_UN_I4, + [MintOpcode.MINT_BLE_I4_S]: MintOpcode.MINT_CLE_I4, + [MintOpcode.MINT_BLE_UN_I4_S]: MintOpcode.MINT_CLE_UN_I4, + + [MintOpcode.MINT_BEQ_I4_SP]: [MintOpcode.MINT_CEQ_I4, false, true], + [MintOpcode.MINT_BNE_UN_I4_SP]: [MintOpcode.MINT_CNE_I4, false, true], + [MintOpcode.MINT_BGT_I4_SP]: [MintOpcode.MINT_CGT_I4, false, true], + [MintOpcode.MINT_BGT_UN_I4_SP]: [MintOpcode.MINT_CGT_UN_I4, false, true], + [MintOpcode.MINT_BLT_I4_SP]: [MintOpcode.MINT_CLT_I4, false, true], + [MintOpcode.MINT_BLT_UN_I4_SP]: [MintOpcode.MINT_CLT_UN_I4, false, true], + [MintOpcode.MINT_BGE_I4_SP]: [MintOpcode.MINT_CGE_I4, false, true], + [MintOpcode.MINT_BGE_UN_I4_SP]: [MintOpcode.MINT_CGE_UN_I4, false, true], + [MintOpcode.MINT_BLE_I4_SP]: [MintOpcode.MINT_CLE_I4, false, true], + [MintOpcode.MINT_BLE_UN_I4_SP]: [MintOpcode.MINT_CLE_UN_I4, false, true], + + [MintOpcode.MINT_BEQ_I4_IMM_SP]: [MintOpcode.MINT_CEQ_I4, WasmOpcode.i32_const, true], + [MintOpcode.MINT_BNE_UN_I4_IMM_SP]: [MintOpcode.MINT_CNE_I4, WasmOpcode.i32_const, true], + [MintOpcode.MINT_BGT_I4_IMM_SP]: [MintOpcode.MINT_CGT_I4, WasmOpcode.i32_const, true], + [MintOpcode.MINT_BGT_UN_I4_IMM_SP]: [MintOpcode.MINT_CGT_UN_I4, WasmOpcode.i32_const, true], + [MintOpcode.MINT_BLT_I4_IMM_SP]: [MintOpcode.MINT_CLT_I4, WasmOpcode.i32_const, true], + [MintOpcode.MINT_BLT_UN_I4_IMM_SP]: [MintOpcode.MINT_CLT_UN_I4, WasmOpcode.i32_const, true], + [MintOpcode.MINT_BGE_I4_IMM_SP]: [MintOpcode.MINT_CGE_I4, WasmOpcode.i32_const, true], + [MintOpcode.MINT_BGE_UN_I4_IMM_SP]: [MintOpcode.MINT_CGE_UN_I4, WasmOpcode.i32_const, true], + [MintOpcode.MINT_BLE_I4_IMM_SP]: [MintOpcode.MINT_CLE_I4, WasmOpcode.i32_const, true], + [MintOpcode.MINT_BLE_UN_I4_IMM_SP]: [MintOpcode.MINT_CLE_UN_I4, WasmOpcode.i32_const, true], + + [MintOpcode.MINT_BEQ_I8_S]: MintOpcode.MINT_CEQ_I8, + [MintOpcode.MINT_BNE_UN_I8_S]: MintOpcode.MINT_CNE_I8, + [MintOpcode.MINT_BGT_I8_S]: MintOpcode.MINT_CGT_I8, + [MintOpcode.MINT_BGT_UN_I8_S]: MintOpcode.MINT_CGT_UN_I8, + [MintOpcode.MINT_BLT_I8_S]: MintOpcode.MINT_CLT_I8, + [MintOpcode.MINT_BLT_UN_I8_S]: MintOpcode.MINT_CLT_UN_I8, + [MintOpcode.MINT_BGE_I8_S]: MintOpcode.MINT_CGE_I8, + [MintOpcode.MINT_BGE_UN_I8_S]: MintOpcode.MINT_CGE_UN_I8, + [MintOpcode.MINT_BLE_I8_S]: MintOpcode.MINT_CLE_I8, + [MintOpcode.MINT_BLE_UN_I8_S]: MintOpcode.MINT_CLE_UN_I8, + + [MintOpcode.MINT_BEQ_I8_IMM_SP]: [MintOpcode.MINT_CEQ_I8, WasmOpcode.i64_const, true], + // FIXME: Missing compare opcode + // [MintOpcode.MINT_BNE_UN_I8_IMM_SP]: [MintOpcode.MINT_CNE_UN_I8, WasmOpcode.i64_const, true], + [MintOpcode.MINT_BGT_I8_IMM_SP]: [MintOpcode.MINT_CGT_I8, WasmOpcode.i64_const, true], + [MintOpcode.MINT_BGT_UN_I8_IMM_SP]: [MintOpcode.MINT_CGT_UN_I8, WasmOpcode.i64_const, true], + [MintOpcode.MINT_BLT_I8_IMM_SP]: [MintOpcode.MINT_CLT_I8, WasmOpcode.i64_const, true], + [MintOpcode.MINT_BLT_UN_I8_IMM_SP]: [MintOpcode.MINT_CLT_UN_I8, WasmOpcode.i64_const, true], + [MintOpcode.MINT_BGE_I8_IMM_SP]: [MintOpcode.MINT_CGE_I8, WasmOpcode.i64_const, true], + [MintOpcode.MINT_BGE_UN_I8_IMM_SP]: [MintOpcode.MINT_CGE_UN_I8, WasmOpcode.i64_const, true], + [MintOpcode.MINT_BLE_I8_IMM_SP]: [MintOpcode.MINT_CLE_I8, WasmOpcode.i64_const, true], + [MintOpcode.MINT_BLE_UN_I8_IMM_SP]: [MintOpcode.MINT_CLE_UN_I8, WasmOpcode.i64_const, true], + + [MintOpcode.MINT_BEQ_R4_S]: MintOpcode.MINT_CEQ_R4, + [MintOpcode.MINT_BNE_UN_R4_S]: JiterpSpecialOpcode.CNE_UN_R4, + [MintOpcode.MINT_BGT_R4_S]: MintOpcode.MINT_CGT_R4, + [MintOpcode.MINT_BGT_UN_R4_S]: MintOpcode.MINT_CGT_UN_R4, + [MintOpcode.MINT_BLT_R4_S]: MintOpcode.MINT_CLT_R4, + [MintOpcode.MINT_BLT_UN_R4_S]: MintOpcode.MINT_CLT_UN_R4, + [MintOpcode.MINT_BGE_R4_S]: MintOpcode.MINT_CGE_R4, + [MintOpcode.MINT_BGE_UN_R4_S]: JiterpSpecialOpcode.CGE_UN_R4, + [MintOpcode.MINT_BLE_R4_S]: MintOpcode.MINT_CLE_R4, + [MintOpcode.MINT_BLE_UN_R4_S]: JiterpSpecialOpcode.CLE_UN_R4, + + [MintOpcode.MINT_BEQ_R8_S]: MintOpcode.MINT_CEQ_R8, + [MintOpcode.MINT_BNE_UN_R8_S]: JiterpSpecialOpcode.CNE_UN_R8, + [MintOpcode.MINT_BGT_R8_S]: MintOpcode.MINT_CGT_R8, + [MintOpcode.MINT_BGT_UN_R8_S]: MintOpcode.MINT_CGT_UN_R8, + [MintOpcode.MINT_BLT_R8_S]: MintOpcode.MINT_CLT_R8, + [MintOpcode.MINT_BLT_UN_R8_S]: MintOpcode.MINT_CLT_UN_R8, + [MintOpcode.MINT_BGE_R8_S]: MintOpcode.MINT_CGE_R8, + [MintOpcode.MINT_BGE_UN_R8_S]: JiterpSpecialOpcode.CGE_UN_R8, + [MintOpcode.MINT_BLE_R8_S]: MintOpcode.MINT_CLE_R8, + [MintOpcode.MINT_BLE_UN_R8_S]: JiterpSpecialOpcode.CLE_UN_R8, +}; + +export const mathIntrinsicTable: { [opcode: number]: [isUnary: boolean, isF32: boolean, opcodeOrFuncName: WasmOpcode | string] } = { + [MintOpcode.MINT_SQRT]: [true, false, WasmOpcode.f64_sqrt], + [MintOpcode.MINT_SQRTF]: [true, true, WasmOpcode.f32_sqrt], + [MintOpcode.MINT_CEILING]: [true, false, WasmOpcode.f64_ceil], + [MintOpcode.MINT_CEILINGF]: [true, true, WasmOpcode.f32_ceil], + [MintOpcode.MINT_FLOOR]: [true, false, WasmOpcode.f64_floor], + [MintOpcode.MINT_FLOORF]: [true, true, WasmOpcode.f32_floor], + [MintOpcode.MINT_ABS]: [true, false, WasmOpcode.f64_abs], + [MintOpcode.MINT_ABSF]: [true, true, WasmOpcode.f32_abs], + + [MintOpcode.MINT_ACOS]: [true, false, "acos"], + [MintOpcode.MINT_ACOSF]: [true, true, "acosf"], + [MintOpcode.MINT_ACOSH]: [true, false, "acosh"], + [MintOpcode.MINT_ACOSHF]: [true, true, "acoshf"], + [MintOpcode.MINT_COS]: [true, false, "cos"], + [MintOpcode.MINT_COSF]: [true, true, "cosf"], + [MintOpcode.MINT_ASIN]: [true, false, "asin"], + [MintOpcode.MINT_ASINF]: [true, true, "asinf"], + [MintOpcode.MINT_ASINH]: [true, false, "asinh"], + [MintOpcode.MINT_ASINHF]: [true, true, "asinhf"], + [MintOpcode.MINT_SIN]: [true, false, "sin"], + [MintOpcode.MINT_SINF]: [true, true, "sinf"], + [MintOpcode.MINT_ATAN]: [true, false, "atan"], + [MintOpcode.MINT_ATANF]: [true, true, "atanf"], + [MintOpcode.MINT_ATANH]: [true, false, "atanh"], + [MintOpcode.MINT_ATANHF]: [true, true, "atanhf"], + [MintOpcode.MINT_TAN]: [true, false, "tan"], + [MintOpcode.MINT_TANF]: [true, true, "tanf"], + [MintOpcode.MINT_CBRT]: [true, false, "cbrt"], + [MintOpcode.MINT_CBRTF]: [true, true, "cbrtf"], + [MintOpcode.MINT_EXP]: [true, false, "exp"], + [MintOpcode.MINT_EXPF]: [true, true, "expf"], + [MintOpcode.MINT_LOG]: [true, false, "log"], + [MintOpcode.MINT_LOGF]: [true, true, "logf"], + [MintOpcode.MINT_LOG2]: [true, false, "log2"], + [MintOpcode.MINT_LOG2F]: [true, true, "log2f"], + [MintOpcode.MINT_LOG10]: [true, false, "log10"], + [MintOpcode.MINT_LOG10F]: [true, true, "log10f"], + + [MintOpcode.MINT_MIN]: [false, false, WasmOpcode.f64_min], + [MintOpcode.MINT_MINF]: [false, true, WasmOpcode.f32_min], + [MintOpcode.MINT_MAX]: [false, false, WasmOpcode.f64_max], + [MintOpcode.MINT_MAXF]: [false, true, WasmOpcode.f32_max], + + [MintOpcode.MINT_ATAN2]: [false, false, "atan2"], + [MintOpcode.MINT_ATAN2F]: [false, true, "atan2f"], + [MintOpcode.MINT_POW]: [false, false, "pow"], + [MintOpcode.MINT_POWF]: [false, true, "powf"], + [MintOpcode.MINT_REM_R8]: [false, false, "fmod"], + [MintOpcode.MINT_REM_R4]: [false, true, "fmodf"], +}; + +export const simdCreateSizes = { + [MintOpcode.MINT_SIMD_V128_I1_CREATE]: 1, + [MintOpcode.MINT_SIMD_V128_I2_CREATE]: 2, + [MintOpcode.MINT_SIMD_V128_I4_CREATE]: 4, + [MintOpcode.MINT_SIMD_V128_I8_CREATE]: 8, +}; + +export const simdCreateLoadOps = { + [MintOpcode.MINT_SIMD_V128_I1_CREATE]: WasmOpcode.i32_load8_s, + [MintOpcode.MINT_SIMD_V128_I2_CREATE]: WasmOpcode.i32_load16_s, + [MintOpcode.MINT_SIMD_V128_I4_CREATE]: WasmOpcode.i32_load, + [MintOpcode.MINT_SIMD_V128_I8_CREATE]: WasmOpcode.i64_load, +}; + +export const simdCreateStoreOps = { + [MintOpcode.MINT_SIMD_V128_I1_CREATE]: WasmOpcode.i32_store8, + [MintOpcode.MINT_SIMD_V128_I2_CREATE]: WasmOpcode.i32_store16, + [MintOpcode.MINT_SIMD_V128_I4_CREATE]: WasmOpcode.i32_store, + [MintOpcode.MINT_SIMD_V128_I8_CREATE]: WasmOpcode.i64_store, +}; + +export const simdShiftTable = new Set([ + SimdIntrinsic3.V128_I1_LEFT_SHIFT, + SimdIntrinsic3.V128_I2_LEFT_SHIFT, + SimdIntrinsic3.V128_I4_LEFT_SHIFT, + SimdIntrinsic3.V128_I8_LEFT_SHIFT, + + SimdIntrinsic3.V128_I1_RIGHT_SHIFT, + SimdIntrinsic3.V128_I2_RIGHT_SHIFT, + SimdIntrinsic3.V128_I4_RIGHT_SHIFT, + + SimdIntrinsic3.V128_I1_URIGHT_SHIFT, + SimdIntrinsic3.V128_I2_URIGHT_SHIFT, + SimdIntrinsic3.V128_I4_URIGHT_SHIFT, + SimdIntrinsic3.V128_I8_URIGHT_SHIFT, +]); diff --git a/src/mono/wasm/runtime/jiterpreter-trace-generator.ts b/src/mono/wasm/runtime/jiterpreter-trace-generator.ts index 9731bf3..6c9bc03 100644 --- a/src/mono/wasm/runtime/jiterpreter-trace-generator.ts +++ b/src/mono/wasm/runtime/jiterpreter-trace-generator.ts @@ -10,7 +10,7 @@ import { } from "./memory"; import { WasmOpcode, WasmSimdOpcode, - getOpcodeName, OpcodeInfoType + getOpcodeName, OpcodeInfoType, } from "./jiterpreter-opcodes"; import { MintOpcode, SimdInfo, @@ -41,6 +41,14 @@ import { record_abort, } from "./jiterpreter"; +import { + ldcTable, OpRec3, OpRec4, + floatToIntTable, unopTable, + binopTable, intrinsicFpBinops, + relopbranchTable, mathIntrinsicTable, + simdCreateLoadOps, simdCreateSizes, + simdCreateStoreOps, simdShiftTable, +} from "./jiterpreter-tables"; /* struct MonoVTable { @@ -77,15 +85,6 @@ struct InterpMethod { void **data_items; */ -const enum JiterpSpecialOpcode { - CNE_UN_R4 = 0xFFFF + 0, - CGE_UN_R4 = 0xFFFF + 1, - CLE_UN_R4 = 0xFFFF + 2, - CNE_UN_R8 = 0xFFFF + 3, - CGE_UN_R8 = 0xFFFF + 4, - CLE_UN_R8 = 0xFFFF + 5, -} - // indexPlusOne so that ip[1] in the interpreter becomes getArgU16(ip, 1) function getArgU16(ip: MintOpcodePtr, indexPlusOne: number) { return getU16(ip + (2 * indexPlusOne)); @@ -1597,19 +1596,6 @@ function append_ldloc_cknull(builder: WasmBuilder, localOffset: number, ip: Mint cknullOffset = -1; } -const ldcTable: { [opcode: number]: [WasmOpcode, number] } = { - [MintOpcode.MINT_LDC_I4_M1]: [WasmOpcode.i32_const, -1], - [MintOpcode.MINT_LDC_I4_0]: [WasmOpcode.i32_const, 0], - [MintOpcode.MINT_LDC_I4_1]: [WasmOpcode.i32_const, 1], - [MintOpcode.MINT_LDC_I4_2]: [WasmOpcode.i32_const, 2], - [MintOpcode.MINT_LDC_I4_3]: [WasmOpcode.i32_const, 3], - [MintOpcode.MINT_LDC_I4_4]: [WasmOpcode.i32_const, 4], - [MintOpcode.MINT_LDC_I4_5]: [WasmOpcode.i32_const, 5], - [MintOpcode.MINT_LDC_I4_6]: [WasmOpcode.i32_const, 6], - [MintOpcode.MINT_LDC_I4_7]: [WasmOpcode.i32_const, 7], - [MintOpcode.MINT_LDC_I4_8]: [WasmOpcode.i32_const, 8], -}; - function emit_ldc(builder: WasmBuilder, ip: MintOpcodePtr, opcode: MintOpcode): boolean { let storeType = WasmOpcode.i32_store; let value: number | undefined; @@ -2053,257 +2039,6 @@ function emit_sfieldop( } } -// operator, loadOperator, storeOperator -type OpRec3 = [WasmOpcode, WasmOpcode, WasmOpcode]; -// operator, lhsLoadOperator, rhsLoadOperator, storeOperator -type OpRec4 = [WasmOpcode, WasmOpcode, WasmOpcode, WasmOpcode]; - -const floatToIntTable: { [opcode: number]: WasmOpcode } = { - [MintOpcode.MINT_CONV_I4_R4]: WasmOpcode.i32_trunc_s_f32, - [MintOpcode.MINT_CONV_I8_R4]: WasmOpcode.i64_trunc_s_f32, - [MintOpcode.MINT_CONV_I4_R8]: WasmOpcode.i32_trunc_s_f64, - [MintOpcode.MINT_CONV_I8_R8]: WasmOpcode.i64_trunc_s_f64, -}; - -// thanks for making this as complex as possible, typescript -const unopTable: { [opcode: number]: OpRec3 | undefined } = { - [MintOpcode.MINT_CEQ0_I4]: [WasmOpcode.i32_eqz, WasmOpcode.i32_load, WasmOpcode.i32_store], - [MintOpcode.MINT_ADD1_I4]: [WasmOpcode.i32_add, WasmOpcode.i32_load, WasmOpcode.i32_store], - [MintOpcode.MINT_SUB1_I4]: [WasmOpcode.i32_sub, WasmOpcode.i32_load, WasmOpcode.i32_store], - [MintOpcode.MINT_NEG_I4]: [WasmOpcode.i32_sub, WasmOpcode.i32_load, WasmOpcode.i32_store], - [MintOpcode.MINT_NOT_I4]: [WasmOpcode.i32_xor, WasmOpcode.i32_load, WasmOpcode.i32_store], - - [MintOpcode.MINT_ADD1_I8]: [WasmOpcode.i64_add, WasmOpcode.i64_load, WasmOpcode.i64_store], - [MintOpcode.MINT_SUB1_I8]: [WasmOpcode.i64_sub, WasmOpcode.i64_load, WasmOpcode.i64_store], - [MintOpcode.MINT_NEG_I8]: [WasmOpcode.i64_sub, WasmOpcode.i64_load, WasmOpcode.i64_store], - [MintOpcode.MINT_NOT_I8]: [WasmOpcode.i64_xor, WasmOpcode.i64_load, WasmOpcode.i64_store], - - [MintOpcode.MINT_ADD_I4_IMM]: [WasmOpcode.i32_add, WasmOpcode.i32_load, WasmOpcode.i32_store], - [MintOpcode.MINT_MUL_I4_IMM]: [WasmOpcode.i32_mul, WasmOpcode.i32_load, WasmOpcode.i32_store], - [MintOpcode.MINT_ADD_I8_IMM]: [WasmOpcode.i64_add, WasmOpcode.i64_load, WasmOpcode.i64_store], - [MintOpcode.MINT_MUL_I8_IMM]: [WasmOpcode.i64_mul, WasmOpcode.i64_load, WasmOpcode.i64_store], - - [MintOpcode.MINT_NEG_R4]: [WasmOpcode.f32_neg, WasmOpcode.f32_load, WasmOpcode.f32_store], - [MintOpcode.MINT_NEG_R8]: [WasmOpcode.f64_neg, WasmOpcode.f64_load, WasmOpcode.f64_store], - - [MintOpcode.MINT_CONV_R4_I4]: [WasmOpcode.f32_convert_s_i32, WasmOpcode.i32_load, WasmOpcode.f32_store], - [MintOpcode.MINT_CONV_R8_I4]: [WasmOpcode.f64_convert_s_i32, WasmOpcode.i32_load, WasmOpcode.f64_store], - [MintOpcode.MINT_CONV_R_UN_I4]: [WasmOpcode.f64_convert_u_i32, WasmOpcode.i32_load, WasmOpcode.f64_store], - [MintOpcode.MINT_CONV_R4_I8]: [WasmOpcode.f32_convert_s_i64, WasmOpcode.i64_load, WasmOpcode.f32_store], - [MintOpcode.MINT_CONV_R8_I8]: [WasmOpcode.f64_convert_s_i64, WasmOpcode.i64_load, WasmOpcode.f64_store], - [MintOpcode.MINT_CONV_R_UN_I8]: [WasmOpcode.f64_convert_u_i64, WasmOpcode.i64_load, WasmOpcode.f64_store], - [MintOpcode.MINT_CONV_R8_R4]: [WasmOpcode.f64_promote_f32, WasmOpcode.f32_load, WasmOpcode.f64_store], - [MintOpcode.MINT_CONV_R4_R8]: [WasmOpcode.f32_demote_f64, WasmOpcode.f64_load, WasmOpcode.f32_store], - - [MintOpcode.MINT_CONV_I8_I4]: [WasmOpcode.nop, WasmOpcode.i64_load32_s, WasmOpcode.i64_store], - [MintOpcode.MINT_CONV_I8_U4]: [WasmOpcode.nop, WasmOpcode.i64_load32_u, WasmOpcode.i64_store], - - [MintOpcode.MINT_CONV_U1_I4]: [WasmOpcode.i32_and, WasmOpcode.i32_load, WasmOpcode.i32_store], - [MintOpcode.MINT_CONV_U2_I4]: [WasmOpcode.i32_and, WasmOpcode.i32_load, WasmOpcode.i32_store], - [MintOpcode.MINT_CONV_I1_I4]: [WasmOpcode.i32_shr_s, WasmOpcode.i32_load, WasmOpcode.i32_store], - [MintOpcode.MINT_CONV_I2_I4]: [WasmOpcode.i32_shr_s, WasmOpcode.i32_load, WasmOpcode.i32_store], - - [MintOpcode.MINT_CONV_U1_I8]: [WasmOpcode.i32_and, WasmOpcode.i64_load, WasmOpcode.i32_store], - [MintOpcode.MINT_CONV_U2_I8]: [WasmOpcode.i32_and, WasmOpcode.i64_load, WasmOpcode.i32_store], - [MintOpcode.MINT_CONV_I1_I8]: [WasmOpcode.i32_shr_s, WasmOpcode.i64_load, WasmOpcode.i32_store], - [MintOpcode.MINT_CONV_I2_I8]: [WasmOpcode.i32_shr_s, WasmOpcode.i64_load, WasmOpcode.i32_store], - - [MintOpcode.MINT_SHL_I4_IMM]: [WasmOpcode.i32_shl, WasmOpcode.i32_load, WasmOpcode.i32_store], - [MintOpcode.MINT_SHL_I8_IMM]: [WasmOpcode.i64_shl, WasmOpcode.i64_load, WasmOpcode.i64_store], - [MintOpcode.MINT_SHR_I4_IMM]: [WasmOpcode.i32_shr_s, WasmOpcode.i32_load, WasmOpcode.i32_store], - [MintOpcode.MINT_SHR_I8_IMM]: [WasmOpcode.i64_shr_s, WasmOpcode.i64_load, WasmOpcode.i64_store], - [MintOpcode.MINT_SHR_UN_I4_IMM]: [WasmOpcode.i32_shr_u, WasmOpcode.i32_load, WasmOpcode.i32_store], - [MintOpcode.MINT_SHR_UN_I8_IMM]: [WasmOpcode.i64_shr_u, WasmOpcode.i64_load, WasmOpcode.i64_store], - - [MintOpcode.MINT_ROL_I4_IMM]: [WasmOpcode.i32_rotl, WasmOpcode.i32_load, WasmOpcode.i32_store], - [MintOpcode.MINT_ROL_I8_IMM]: [WasmOpcode.i64_rotl, WasmOpcode.i64_load, WasmOpcode.i64_store], - [MintOpcode.MINT_ROR_I4_IMM]: [WasmOpcode.i32_rotr, WasmOpcode.i32_load, WasmOpcode.i32_store], - [MintOpcode.MINT_ROR_I8_IMM]: [WasmOpcode.i64_rotr, WasmOpcode.i64_load, WasmOpcode.i64_store], - - [MintOpcode.MINT_CLZ_I4]: [WasmOpcode.i32_clz, WasmOpcode.i32_load, WasmOpcode.i32_store], - [MintOpcode.MINT_CTZ_I4]: [WasmOpcode.i32_ctz, WasmOpcode.i32_load, WasmOpcode.i32_store], - [MintOpcode.MINT_POPCNT_I4]: [WasmOpcode.i32_popcnt, WasmOpcode.i32_load, WasmOpcode.i32_store], - [MintOpcode.MINT_CLZ_I8]: [WasmOpcode.i64_clz, WasmOpcode.i64_load, WasmOpcode.i64_store], - [MintOpcode.MINT_CTZ_I8]: [WasmOpcode.i64_ctz, WasmOpcode.i64_load, WasmOpcode.i64_store], - [MintOpcode.MINT_POPCNT_I8]: [WasmOpcode.i64_popcnt, WasmOpcode.i64_load, WasmOpcode.i64_store], -}; - -// HACK: Generating correct wasm for these is non-trivial so we hand them off to C. -// The opcode specifies whether the operands need to be promoted first. -const intrinsicFpBinops: { [opcode: number]: WasmOpcode } = { - [MintOpcode.MINT_CEQ_R4]: WasmOpcode.f64_promote_f32, - [MintOpcode.MINT_CEQ_R8]: WasmOpcode.nop, - [MintOpcode.MINT_CNE_R4]: WasmOpcode.f64_promote_f32, - [MintOpcode.MINT_CNE_R8]: WasmOpcode.nop, - [MintOpcode.MINT_CGT_R4]: WasmOpcode.f64_promote_f32, - [MintOpcode.MINT_CGT_R8]: WasmOpcode.nop, - [MintOpcode.MINT_CGE_R4]: WasmOpcode.f64_promote_f32, - [MintOpcode.MINT_CGE_R8]: WasmOpcode.nop, - [MintOpcode.MINT_CGT_UN_R4]: WasmOpcode.f64_promote_f32, - [MintOpcode.MINT_CGT_UN_R8]: WasmOpcode.nop, - [MintOpcode.MINT_CLT_R4]: WasmOpcode.f64_promote_f32, - [MintOpcode.MINT_CLT_R8]: WasmOpcode.nop, - [MintOpcode.MINT_CLT_UN_R4]: WasmOpcode.f64_promote_f32, - [MintOpcode.MINT_CLT_UN_R8]: WasmOpcode.nop, - [MintOpcode.MINT_CLE_R4]: WasmOpcode.f64_promote_f32, - [MintOpcode.MINT_CLE_R8]: WasmOpcode.nop, - [JiterpSpecialOpcode.CGE_UN_R4]: WasmOpcode.f64_promote_f32, - [JiterpSpecialOpcode.CLE_UN_R4]: WasmOpcode.f64_promote_f32, - [JiterpSpecialOpcode.CNE_UN_R4]: WasmOpcode.f64_promote_f32, - [JiterpSpecialOpcode.CGE_UN_R8]: WasmOpcode.nop, - [JiterpSpecialOpcode.CLE_UN_R8]: WasmOpcode.nop, - [JiterpSpecialOpcode.CNE_UN_R8]: WasmOpcode.nop, -}; - -const binopTable: { [opcode: number]: OpRec3 | OpRec4 | undefined } = { - [MintOpcode.MINT_ADD_I4]: [WasmOpcode.i32_add, WasmOpcode.i32_load, WasmOpcode.i32_store], - [MintOpcode.MINT_ADD_OVF_I4]: [WasmOpcode.i32_add, WasmOpcode.i32_load, WasmOpcode.i32_store], - [MintOpcode.MINT_ADD_OVF_UN_I4]: [WasmOpcode.i32_add, WasmOpcode.i32_load, WasmOpcode.i32_store], - [MintOpcode.MINT_SUB_I4]: [WasmOpcode.i32_sub, WasmOpcode.i32_load, WasmOpcode.i32_store], - [MintOpcode.MINT_MUL_I4]: [WasmOpcode.i32_mul, WasmOpcode.i32_load, WasmOpcode.i32_store], - [MintOpcode.MINT_MUL_OVF_I4]: [WasmOpcode.i32_mul, WasmOpcode.i32_load, WasmOpcode.i32_store], - [MintOpcode.MINT_MUL_OVF_UN_I4]: [WasmOpcode.i32_mul, WasmOpcode.i32_load, WasmOpcode.i32_store], - [MintOpcode.MINT_DIV_I4]: [WasmOpcode.i32_div_s, WasmOpcode.i32_load, WasmOpcode.i32_store], - [MintOpcode.MINT_DIV_UN_I4]: [WasmOpcode.i32_div_u, WasmOpcode.i32_load, WasmOpcode.i32_store], - [MintOpcode.MINT_REM_I4]: [WasmOpcode.i32_rem_s, WasmOpcode.i32_load, WasmOpcode.i32_store], - [MintOpcode.MINT_REM_UN_I4]: [WasmOpcode.i32_rem_u, WasmOpcode.i32_load, WasmOpcode.i32_store], - [MintOpcode.MINT_AND_I4]: [WasmOpcode.i32_and, WasmOpcode.i32_load, WasmOpcode.i32_store], - [MintOpcode.MINT_OR_I4]: [WasmOpcode.i32_or, WasmOpcode.i32_load, WasmOpcode.i32_store], - [MintOpcode.MINT_XOR_I4]: [WasmOpcode.i32_xor, WasmOpcode.i32_load, WasmOpcode.i32_store], - [MintOpcode.MINT_SHL_I4]: [WasmOpcode.i32_shl, WasmOpcode.i32_load, WasmOpcode.i32_store], - [MintOpcode.MINT_SHR_I4]: [WasmOpcode.i32_shr_s, WasmOpcode.i32_load, WasmOpcode.i32_store], - [MintOpcode.MINT_SHR_UN_I4]: [WasmOpcode.i32_shr_u, WasmOpcode.i32_load, WasmOpcode.i32_store], - - [MintOpcode.MINT_ADD_I8]: [WasmOpcode.i64_add, WasmOpcode.i64_load, WasmOpcode.i64_store], - [MintOpcode.MINT_SUB_I8]: [WasmOpcode.i64_sub, WasmOpcode.i64_load, WasmOpcode.i64_store], - [MintOpcode.MINT_MUL_I8]: [WasmOpcode.i64_mul, WasmOpcode.i64_load, WasmOpcode.i64_store], - [MintOpcode.MINT_DIV_I8]: [WasmOpcode.i64_div_s, WasmOpcode.i64_load, WasmOpcode.i64_store], - [MintOpcode.MINT_REM_I8]: [WasmOpcode.i64_rem_s, WasmOpcode.i64_load, WasmOpcode.i64_store], - [MintOpcode.MINT_DIV_UN_I8]: [WasmOpcode.i64_div_u, WasmOpcode.i64_load, WasmOpcode.i64_store], - [MintOpcode.MINT_REM_UN_I8]: [WasmOpcode.i64_rem_u, WasmOpcode.i64_load, WasmOpcode.i64_store], - [MintOpcode.MINT_AND_I8]: [WasmOpcode.i64_and, WasmOpcode.i64_load, WasmOpcode.i64_store], - [MintOpcode.MINT_OR_I8]: [WasmOpcode.i64_or, WasmOpcode.i64_load, WasmOpcode.i64_store], - [MintOpcode.MINT_XOR_I8]: [WasmOpcode.i64_xor, WasmOpcode.i64_load, WasmOpcode.i64_store], - [MintOpcode.MINT_SHL_I8]: [WasmOpcode.i64_shl, WasmOpcode.i64_load, WasmOpcode.i64_store], - [MintOpcode.MINT_SHR_I8]: [WasmOpcode.i64_shr_s, WasmOpcode.i64_load, WasmOpcode.i64_store], - [MintOpcode.MINT_SHR_UN_I8]: [WasmOpcode.i64_shr_u, WasmOpcode.i64_load, WasmOpcode.i64_store], - - [MintOpcode.MINT_ADD_R4]: [WasmOpcode.f32_add, WasmOpcode.f32_load, WasmOpcode.f32_store], - [MintOpcode.MINT_SUB_R4]: [WasmOpcode.f32_sub, WasmOpcode.f32_load, WasmOpcode.f32_store], - [MintOpcode.MINT_MUL_R4]: [WasmOpcode.f32_mul, WasmOpcode.f32_load, WasmOpcode.f32_store], - [MintOpcode.MINT_DIV_R4]: [WasmOpcode.f32_div, WasmOpcode.f32_load, WasmOpcode.f32_store], - - [MintOpcode.MINT_ADD_R8]: [WasmOpcode.f64_add, WasmOpcode.f64_load, WasmOpcode.f64_store], - [MintOpcode.MINT_SUB_R8]: [WasmOpcode.f64_sub, WasmOpcode.f64_load, WasmOpcode.f64_store], - [MintOpcode.MINT_MUL_R8]: [WasmOpcode.f64_mul, WasmOpcode.f64_load, WasmOpcode.f64_store], - [MintOpcode.MINT_DIV_R8]: [WasmOpcode.f64_div, WasmOpcode.f64_load, WasmOpcode.f64_store], - - [MintOpcode.MINT_CEQ_I4]: [WasmOpcode.i32_eq, WasmOpcode.i32_load, WasmOpcode.i32_store], - [MintOpcode.MINT_CNE_I4]: [WasmOpcode.i32_ne, WasmOpcode.i32_load, WasmOpcode.i32_store], - [MintOpcode.MINT_CLT_I4]: [WasmOpcode.i32_lt_s, WasmOpcode.i32_load, WasmOpcode.i32_store], - [MintOpcode.MINT_CGT_I4]: [WasmOpcode.i32_gt_s, WasmOpcode.i32_load, WasmOpcode.i32_store], - [MintOpcode.MINT_CLE_I4]: [WasmOpcode.i32_le_s, WasmOpcode.i32_load, WasmOpcode.i32_store], - [MintOpcode.MINT_CGE_I4]: [WasmOpcode.i32_ge_s, WasmOpcode.i32_load, WasmOpcode.i32_store], - - [MintOpcode.MINT_CLT_UN_I4]: [WasmOpcode.i32_lt_u, WasmOpcode.i32_load, WasmOpcode.i32_store], - [MintOpcode.MINT_CGT_UN_I4]: [WasmOpcode.i32_gt_u, WasmOpcode.i32_load, WasmOpcode.i32_store], - [MintOpcode.MINT_CLE_UN_I4]: [WasmOpcode.i32_le_u, WasmOpcode.i32_load, WasmOpcode.i32_store], - [MintOpcode.MINT_CGE_UN_I4]: [WasmOpcode.i32_ge_u, WasmOpcode.i32_load, WasmOpcode.i32_store], - - [MintOpcode.MINT_CEQ_I8]: [WasmOpcode.i64_eq, WasmOpcode.i64_load, WasmOpcode.i32_store], - [MintOpcode.MINT_CNE_I8]: [WasmOpcode.i64_ne, WasmOpcode.i64_load, WasmOpcode.i32_store], - [MintOpcode.MINT_CLT_I8]: [WasmOpcode.i64_lt_s, WasmOpcode.i64_load, WasmOpcode.i32_store], - [MintOpcode.MINT_CGT_I8]: [WasmOpcode.i64_gt_s, WasmOpcode.i64_load, WasmOpcode.i32_store], - [MintOpcode.MINT_CLE_I8]: [WasmOpcode.i64_le_s, WasmOpcode.i64_load, WasmOpcode.i32_store], - [MintOpcode.MINT_CGE_I8]: [WasmOpcode.i64_ge_s, WasmOpcode.i64_load, WasmOpcode.i32_store], - - [MintOpcode.MINT_CLT_UN_I8]: [WasmOpcode.i64_lt_u, WasmOpcode.i64_load, WasmOpcode.i32_store], - [MintOpcode.MINT_CGT_UN_I8]: [WasmOpcode.i64_gt_u, WasmOpcode.i64_load, WasmOpcode.i32_store], - [MintOpcode.MINT_CLE_UN_I8]: [WasmOpcode.i64_le_u, WasmOpcode.i64_load, WasmOpcode.i32_store], - [MintOpcode.MINT_CGE_UN_I8]: [WasmOpcode.i64_ge_u, WasmOpcode.i64_load, WasmOpcode.i32_store], - -}; - -const relopbranchTable: { [opcode: number]: [comparisonOpcode: MintOpcode, immediateOpcode: WasmOpcode | false, isSafepoint: boolean] | MintOpcode | undefined } = { - [MintOpcode.MINT_BEQ_I4_S]: MintOpcode.MINT_CEQ_I4, - [MintOpcode.MINT_BNE_UN_I4_S]: MintOpcode.MINT_CNE_I4, - [MintOpcode.MINT_BGT_I4_S]: MintOpcode.MINT_CGT_I4, - [MintOpcode.MINT_BGT_UN_I4_S]: MintOpcode.MINT_CGT_UN_I4, - [MintOpcode.MINT_BLT_I4_S]: MintOpcode.MINT_CLT_I4, - [MintOpcode.MINT_BLT_UN_I4_S]: MintOpcode.MINT_CLT_UN_I4, - [MintOpcode.MINT_BGE_I4_S]: MintOpcode.MINT_CGE_I4, - [MintOpcode.MINT_BGE_UN_I4_S]: MintOpcode.MINT_CGE_UN_I4, - [MintOpcode.MINT_BLE_I4_S]: MintOpcode.MINT_CLE_I4, - [MintOpcode.MINT_BLE_UN_I4_S]: MintOpcode.MINT_CLE_UN_I4, - - [MintOpcode.MINT_BEQ_I4_SP]: [MintOpcode.MINT_CEQ_I4, false, true], - [MintOpcode.MINT_BNE_UN_I4_SP]: [MintOpcode.MINT_CNE_I4, false, true], - [MintOpcode.MINT_BGT_I4_SP]: [MintOpcode.MINT_CGT_I4, false, true], - [MintOpcode.MINT_BGT_UN_I4_SP]: [MintOpcode.MINT_CGT_UN_I4, false, true], - [MintOpcode.MINT_BLT_I4_SP]: [MintOpcode.MINT_CLT_I4, false, true], - [MintOpcode.MINT_BLT_UN_I4_SP]: [MintOpcode.MINT_CLT_UN_I4, false, true], - [MintOpcode.MINT_BGE_I4_SP]: [MintOpcode.MINT_CGE_I4, false, true], - [MintOpcode.MINT_BGE_UN_I4_SP]: [MintOpcode.MINT_CGE_UN_I4, false, true], - [MintOpcode.MINT_BLE_I4_SP]: [MintOpcode.MINT_CLE_I4, false, true], - [MintOpcode.MINT_BLE_UN_I4_SP]: [MintOpcode.MINT_CLE_UN_I4, false, true], - - [MintOpcode.MINT_BEQ_I4_IMM_SP]: [MintOpcode.MINT_CEQ_I4, WasmOpcode.i32_const, true], - [MintOpcode.MINT_BNE_UN_I4_IMM_SP]: [MintOpcode.MINT_CNE_I4, WasmOpcode.i32_const, true], - [MintOpcode.MINT_BGT_I4_IMM_SP]: [MintOpcode.MINT_CGT_I4, WasmOpcode.i32_const, true], - [MintOpcode.MINT_BGT_UN_I4_IMM_SP]: [MintOpcode.MINT_CGT_UN_I4, WasmOpcode.i32_const, true], - [MintOpcode.MINT_BLT_I4_IMM_SP]: [MintOpcode.MINT_CLT_I4, WasmOpcode.i32_const, true], - [MintOpcode.MINT_BLT_UN_I4_IMM_SP]: [MintOpcode.MINT_CLT_UN_I4, WasmOpcode.i32_const, true], - [MintOpcode.MINT_BGE_I4_IMM_SP]: [MintOpcode.MINT_CGE_I4, WasmOpcode.i32_const, true], - [MintOpcode.MINT_BGE_UN_I4_IMM_SP]: [MintOpcode.MINT_CGE_UN_I4, WasmOpcode.i32_const, true], - [MintOpcode.MINT_BLE_I4_IMM_SP]: [MintOpcode.MINT_CLE_I4, WasmOpcode.i32_const, true], - [MintOpcode.MINT_BLE_UN_I4_IMM_SP]: [MintOpcode.MINT_CLE_UN_I4, WasmOpcode.i32_const, true], - - [MintOpcode.MINT_BEQ_I8_S]: MintOpcode.MINT_CEQ_I8, - [MintOpcode.MINT_BNE_UN_I8_S]: MintOpcode.MINT_CNE_I8, - [MintOpcode.MINT_BGT_I8_S]: MintOpcode.MINT_CGT_I8, - [MintOpcode.MINT_BGT_UN_I8_S]: MintOpcode.MINT_CGT_UN_I8, - [MintOpcode.MINT_BLT_I8_S]: MintOpcode.MINT_CLT_I8, - [MintOpcode.MINT_BLT_UN_I8_S]: MintOpcode.MINT_CLT_UN_I8, - [MintOpcode.MINT_BGE_I8_S]: MintOpcode.MINT_CGE_I8, - [MintOpcode.MINT_BGE_UN_I8_S]: MintOpcode.MINT_CGE_UN_I8, - [MintOpcode.MINT_BLE_I8_S]: MintOpcode.MINT_CLE_I8, - [MintOpcode.MINT_BLE_UN_I8_S]: MintOpcode.MINT_CLE_UN_I8, - - [MintOpcode.MINT_BEQ_I8_IMM_SP]: [MintOpcode.MINT_CEQ_I8, WasmOpcode.i64_const, true], - // FIXME: Missing compare opcode - // [MintOpcode.MINT_BNE_UN_I8_IMM_SP]: [MintOpcode.MINT_CNE_UN_I8, WasmOpcode.i64_const, true], - [MintOpcode.MINT_BGT_I8_IMM_SP]: [MintOpcode.MINT_CGT_I8, WasmOpcode.i64_const, true], - [MintOpcode.MINT_BGT_UN_I8_IMM_SP]: [MintOpcode.MINT_CGT_UN_I8, WasmOpcode.i64_const, true], - [MintOpcode.MINT_BLT_I8_IMM_SP]: [MintOpcode.MINT_CLT_I8, WasmOpcode.i64_const, true], - [MintOpcode.MINT_BLT_UN_I8_IMM_SP]: [MintOpcode.MINT_CLT_UN_I8, WasmOpcode.i64_const, true], - [MintOpcode.MINT_BGE_I8_IMM_SP]: [MintOpcode.MINT_CGE_I8, WasmOpcode.i64_const, true], - [MintOpcode.MINT_BGE_UN_I8_IMM_SP]: [MintOpcode.MINT_CGE_UN_I8, WasmOpcode.i64_const, true], - [MintOpcode.MINT_BLE_I8_IMM_SP]: [MintOpcode.MINT_CLE_I8, WasmOpcode.i64_const, true], - [MintOpcode.MINT_BLE_UN_I8_IMM_SP]: [MintOpcode.MINT_CLE_UN_I8, WasmOpcode.i64_const, true], - - [MintOpcode.MINT_BEQ_R4_S]: MintOpcode.MINT_CEQ_R4, - [MintOpcode.MINT_BNE_UN_R4_S]: JiterpSpecialOpcode.CNE_UN_R4, - [MintOpcode.MINT_BGT_R4_S]: MintOpcode.MINT_CGT_R4, - [MintOpcode.MINT_BGT_UN_R4_S]: MintOpcode.MINT_CGT_UN_R4, - [MintOpcode.MINT_BLT_R4_S]: MintOpcode.MINT_CLT_R4, - [MintOpcode.MINT_BLT_UN_R4_S]: MintOpcode.MINT_CLT_UN_R4, - [MintOpcode.MINT_BGE_R4_S]: MintOpcode.MINT_CGE_R4, - [MintOpcode.MINT_BGE_UN_R4_S]: JiterpSpecialOpcode.CGE_UN_R4, - [MintOpcode.MINT_BLE_R4_S]: MintOpcode.MINT_CLE_R4, - [MintOpcode.MINT_BLE_UN_R4_S]: JiterpSpecialOpcode.CLE_UN_R4, - - [MintOpcode.MINT_BEQ_R8_S]: MintOpcode.MINT_CEQ_R8, - [MintOpcode.MINT_BNE_UN_R8_S]: JiterpSpecialOpcode.CNE_UN_R8, - [MintOpcode.MINT_BGT_R8_S]: MintOpcode.MINT_CGT_R8, - [MintOpcode.MINT_BGT_UN_R8_S]: MintOpcode.MINT_CGT_UN_R8, - [MintOpcode.MINT_BLT_R8_S]: MintOpcode.MINT_CLT_R8, - [MintOpcode.MINT_BLT_UN_R8_S]: MintOpcode.MINT_CLT_UN_R8, - [MintOpcode.MINT_BGE_R8_S]: MintOpcode.MINT_CGE_R8, - [MintOpcode.MINT_BGE_UN_R8_S]: JiterpSpecialOpcode.CGE_UN_R8, - [MintOpcode.MINT_BLE_R8_S]: MintOpcode.MINT_CLE_R8, - [MintOpcode.MINT_BLE_UN_R8_S]: JiterpSpecialOpcode.CLE_UN_R8, -}; - function emit_binop(builder: WasmBuilder, ip: MintOpcodePtr, opcode: MintOpcode): boolean { // operands are popped right to left, which means you build the arg list left to right let lhsLoadOp: WasmOpcode, rhsLoadOp: WasmOpcode, storeOp: WasmOpcode, @@ -2826,58 +2561,6 @@ function emit_relop_branch( return emit_branch(builder, ip, frame, opcode, displacement); } -const mathIntrinsicTable: { [opcode: number]: [isUnary: boolean, isF32: boolean, opcodeOrFuncName: WasmOpcode | string] } = { - [MintOpcode.MINT_SQRT]: [true, false, WasmOpcode.f64_sqrt], - [MintOpcode.MINT_SQRTF]: [true, true, WasmOpcode.f32_sqrt], - [MintOpcode.MINT_CEILING]: [true, false, WasmOpcode.f64_ceil], - [MintOpcode.MINT_CEILINGF]: [true, true, WasmOpcode.f32_ceil], - [MintOpcode.MINT_FLOOR]: [true, false, WasmOpcode.f64_floor], - [MintOpcode.MINT_FLOORF]: [true, true, WasmOpcode.f32_floor], - [MintOpcode.MINT_ABS]: [true, false, WasmOpcode.f64_abs], - [MintOpcode.MINT_ABSF]: [true, true, WasmOpcode.f32_abs], - - [MintOpcode.MINT_ACOS]: [true, false, "acos"], - [MintOpcode.MINT_ACOSF]: [true, true, "acosf"], - [MintOpcode.MINT_ACOSH]: [true, false, "acosh"], - [MintOpcode.MINT_ACOSHF]: [true, true, "acoshf"], - [MintOpcode.MINT_COS]: [true, false, "cos"], - [MintOpcode.MINT_COSF]: [true, true, "cosf"], - [MintOpcode.MINT_ASIN]: [true, false, "asin"], - [MintOpcode.MINT_ASINF]: [true, true, "asinf"], - [MintOpcode.MINT_ASINH]: [true, false, "asinh"], - [MintOpcode.MINT_ASINHF]: [true, true, "asinhf"], - [MintOpcode.MINT_SIN]: [true, false, "sin"], - [MintOpcode.MINT_SINF]: [true, true, "sinf"], - [MintOpcode.MINT_ATAN]: [true, false, "atan"], - [MintOpcode.MINT_ATANF]: [true, true, "atanf"], - [MintOpcode.MINT_ATANH]: [true, false, "atanh"], - [MintOpcode.MINT_ATANHF]: [true, true, "atanhf"], - [MintOpcode.MINT_TAN]: [true, false, "tan"], - [MintOpcode.MINT_TANF]: [true, true, "tanf"], - [MintOpcode.MINT_CBRT]: [true, false, "cbrt"], - [MintOpcode.MINT_CBRTF]: [true, true, "cbrtf"], - [MintOpcode.MINT_EXP]: [true, false, "exp"], - [MintOpcode.MINT_EXPF]: [true, true, "expf"], - [MintOpcode.MINT_LOG]: [true, false, "log"], - [MintOpcode.MINT_LOGF]: [true, true, "logf"], - [MintOpcode.MINT_LOG2]: [true, false, "log2"], - [MintOpcode.MINT_LOG2F]: [true, true, "log2f"], - [MintOpcode.MINT_LOG10]: [true, false, "log10"], - [MintOpcode.MINT_LOG10F]: [true, true, "log10f"], - - [MintOpcode.MINT_MIN]: [false, false, WasmOpcode.f64_min], - [MintOpcode.MINT_MINF]: [false, true, WasmOpcode.f32_min], - [MintOpcode.MINT_MAX]: [false, false, WasmOpcode.f64_max], - [MintOpcode.MINT_MAXF]: [false, true, WasmOpcode.f32_max], - - [MintOpcode.MINT_ATAN2]: [false, false, "atan2"], - [MintOpcode.MINT_ATAN2F]: [false, true, "atan2f"], - [MintOpcode.MINT_POW]: [false, false, "pow"], - [MintOpcode.MINT_POWF]: [false, true, "powf"], - [MintOpcode.MINT_REM_R8]: [false, false, "fmod"], - [MintOpcode.MINT_REM_R4]: [false, true, "fmodf"], -}; - function emit_math_intrinsic(builder: WasmBuilder, ip: MintOpcodePtr, opcode: MintOpcode): boolean { let isUnary: boolean, isF32: boolean, name: string | undefined; let wasmOp: WasmOpcode | undefined; @@ -3347,27 +3030,6 @@ function get_import_name( return name; } -const simdCreateSizes = { - [MintOpcode.MINT_SIMD_V128_I1_CREATE]: 1, - [MintOpcode.MINT_SIMD_V128_I2_CREATE]: 2, - [MintOpcode.MINT_SIMD_V128_I4_CREATE]: 4, - [MintOpcode.MINT_SIMD_V128_I8_CREATE]: 8, -}; - -const simdCreateLoadOps = { - [MintOpcode.MINT_SIMD_V128_I1_CREATE]: WasmOpcode.i32_load8_s, - [MintOpcode.MINT_SIMD_V128_I2_CREATE]: WasmOpcode.i32_load16_s, - [MintOpcode.MINT_SIMD_V128_I4_CREATE]: WasmOpcode.i32_load, - [MintOpcode.MINT_SIMD_V128_I8_CREATE]: WasmOpcode.i64_load, -}; - -const simdCreateStoreOps = { - [MintOpcode.MINT_SIMD_V128_I1_CREATE]: WasmOpcode.i32_store8, - [MintOpcode.MINT_SIMD_V128_I2_CREATE]: WasmOpcode.i32_store16, - [MintOpcode.MINT_SIMD_V128_I4_CREATE]: WasmOpcode.i32_store, - [MintOpcode.MINT_SIMD_V128_I8_CREATE]: WasmOpcode.i64_store, -}; - function emit_simd( builder: WasmBuilder, ip: MintOpcodePtr, opcode: MintOpcode, opname: string, @@ -3493,22 +3155,6 @@ function append_simd_4_load(builder: WasmBuilder, ip: MintOpcodePtr) { append_ldloc(builder, getArgU16(ip, 4), WasmOpcode.PREFIX_simd, WasmSimdOpcode.v128_load); } -const simdShiftTable = new Set([ - SimdIntrinsic3.V128_I1_LEFT_SHIFT, - SimdIntrinsic3.V128_I2_LEFT_SHIFT, - SimdIntrinsic3.V128_I4_LEFT_SHIFT, - SimdIntrinsic3.V128_I8_LEFT_SHIFT, - - SimdIntrinsic3.V128_I1_RIGHT_SHIFT, - SimdIntrinsic3.V128_I2_RIGHT_SHIFT, - SimdIntrinsic3.V128_I4_RIGHT_SHIFT, - - SimdIntrinsic3.V128_I1_URIGHT_SHIFT, - SimdIntrinsic3.V128_I2_URIGHT_SHIFT, - SimdIntrinsic3.V128_I4_URIGHT_SHIFT, - SimdIntrinsic3.V128_I8_URIGHT_SHIFT, -]); - function append_stloc_simd_zero(builder: WasmBuilder, offset: number) { builder.local("pLocals"); builder.appendSimd(WasmSimdOpcode.v128_const); -- 2.7.4