mirror of
https://github.com/wazero/wazero
synced 2026-06-21 14:12:37 +00:00
2c14bbff3d
Add experimental support for the [Typed Function References](https://github.com/WebAssembly/function-references) proposal. Closes https://github.com/wazero/wazero/issues/2483, follows up to the refactoring in https://github.com/wazero/wazero/pull/2495 and prepares to WasmGC. Typed function references extend WebAssembly's type system with non-nullable reference types and concrete function type indices (`(ref $t)`, `(ref null $t)`), enabling direct calls through typed references (`call_ref`, `return_call_ref`) and null-aware branching (`br_on_null`, `br_on_non_null`, `ref.as_non_null`). Excluding tests and spec suites, the feature amounts to roughly 1,900 lines of code. Feature flag: `experimental.CoreFeaturesTypedFunctionReferences` ## What's the use for this? Sadly, very little. This proposal is a pretty much just a prerequisite for the GC proposal. On the flip side, it completes the exception handling spec (https://github.com/wazero/wazero/pull/2489): 1. two EH spec tests were previously skipped because they required distinguishing nullable from non-nullable references. Those tests now pass. 2. The fuzzer is now enabled for both exception handling and typed function references. It could not be enabled for EH because the fuzzer would generate func refs. So I guess, technically, it has a use :D ## Type System `ValueType` (already `uint64` introduced in https://github.com/wazero/wazero/pull/2495) is now extended with bit flags to encode nullability and concrete type indices: | Bits | Purpose | |-------|---------------------------------| | 0-7 | Base type byte (same as before) | | 8 | Non-nullable flag | | 9 | Concrete ref flag | | 32-63 | Type index (for `(ref $t)`) | This encoding should be fine for WasmGC too and should not require further changes in the near future. Subtyping rules: non-nullable is a subtype of nullable (same kind/index); concrete function refs `(ref $t)` are subtypes of `funcref`. ## New Instructions | Opcode | Hex | Description | |-------------------|--------|----------------------------------------------------------| | `call_ref` | `0x14` | Indirect call through a typed function reference | | `return_call_ref` | `0x15` | Tail-call variant of `call_ref` | | `ref.as_non_null` | `0xd4` | Assert ref is non-null, trap otherwise | | `br_on_null` | `0xd5` | Branch if null, push non-null ref on fall-through | | `br_on_non_null` | `0xd6` | Branch if non-null (carrying the ref), fall-through on null | ## Validation Tricky bits: - Non-nullable local initialization tracking: `local.get` on a non-nullable ref local is rejected unless a `local.set`/`local.tee` has been executed in the same or enclosing block scope. State is saved/restored at block boundaries per the spec (needed an additional field to keep track of init'd values) - `ref.func` now pushes `(ref $t)` (the concrete non-nullable type of the referenced function) onto the validation type stack instead of plain `funcref`, so that passing it to `call_ref` type-checks without an upcast. - Block types, element segments, table types, and const expressions all support concrete ref types. - The type section now validates forward references (standalone types can only reference previously defined types; rec group members can reference each other). ## Interpreter The five new opcodes are compiled to new IR operations and executed in the interpreter loop. `call_ref` / `return_call_ref` load the function instance from the opaque reference pointer, null-check, and dispatch. `br_on_null` / `br_on_non_null` pop the reference, check nullity, and either branch or fall through with the appropriate stack state. ## Compiler (wazevo) Implemented entirely as SSA-level lowering with no backend-specific code: - `call_ref` / `return_call_ref`: load executable and module context pointers from the function instance, null-check via `ExitIfTrueWithCode(ExitCodeNullReference)`, then dispatch as an indirect call. - `br_on_null` / `br_on_non_null`: compare against zero, branch with trampoline blocks for try-table exits and listener support. - `ref.as_non_null`: null-check with trap. ## Binary Decoding - `decodeRefType` helper extracted and shared across `value.go`, `element.go`, `table.go`, and `code.go` for consistent handling of `(ref null ht)` / `(ref ht)` prefixes. - Tables support the `0x40 0x00` prefix for initializer expressions (required for non-nullable table element types). - `DecodeBlockType` handles concrete ref types as block results. ## Cross-Module Linking `call_indirect` uses `FunctionTypeID` for fast runtime type checks. The existing `FunctionType.key()` method builds the key from raw `ValueType` bytes, but with concrete refs `(ref $0)` in module A and `(ref $0)` in module B may refer to structurally identical types at different local indices. `structuralTypeKey` fixes this by replacing local type indices with the already-assigned `FunctionTypeID` of the referenced type, so two modules with the same structural signature share a single `FunctionTypeID`. ## Spec Suite The spec test suite uses `wasm-tools json-from-wast` (same as exception handling). All 22 test files pass. ## Fuzzing The fuzzer (`nodiff`) now enables both `CoreFeaturesExceptionHandling` and `CoreFeaturesTypedFunctionReferences`. Dummy import generation handles `exnref` and concrete ref types. --------- Signed-off-by: Edoardo Vacchi <evacchi@users.noreply.github.com>
826 lines
33 KiB
Go
826 lines
33 KiB
Go
package interpreter
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"github.com/tetratelabs/wazero/internal/wasm"
|
|
)
|
|
|
|
// signature represents how a Wasm opcode
|
|
// manipulates the value stacks in terms of value types.
|
|
type signature struct {
|
|
in, out []unsignedType
|
|
}
|
|
|
|
var (
|
|
signature_None_None = &signature{}
|
|
signature_Unknown_None = &signature{
|
|
in: []unsignedType{unsignedTypeUnknown},
|
|
}
|
|
signature_None_I32 = &signature{
|
|
out: []unsignedType{unsignedTypeI32},
|
|
}
|
|
signature_None_I64 = &signature{
|
|
out: []unsignedType{unsignedTypeI64},
|
|
}
|
|
signature_None_V128 = &signature{
|
|
out: []unsignedType{unsignedTypeV128},
|
|
}
|
|
signature_None_F32 = &signature{
|
|
out: []unsignedType{unsignedTypeF32},
|
|
}
|
|
signature_None_F64 = &signature{
|
|
out: []unsignedType{unsignedTypeF64},
|
|
}
|
|
signature_I32_None = &signature{
|
|
in: []unsignedType{unsignedTypeI32},
|
|
}
|
|
signature_I64_None = &signature{
|
|
in: []unsignedType{unsignedTypeI64},
|
|
}
|
|
signature_F32_None = &signature{
|
|
in: []unsignedType{unsignedTypeF32},
|
|
}
|
|
signature_F64_None = &signature{
|
|
in: []unsignedType{unsignedTypeF64},
|
|
}
|
|
signature_V128_None = &signature{
|
|
in: []unsignedType{unsignedTypeV128},
|
|
}
|
|
signature_I32_I32 = &signature{
|
|
in: []unsignedType{unsignedTypeI32},
|
|
out: []unsignedType{unsignedTypeI32},
|
|
}
|
|
signature_I32_I64 = &signature{
|
|
in: []unsignedType{unsignedTypeI32},
|
|
out: []unsignedType{unsignedTypeI64},
|
|
}
|
|
signature_I64_I64 = &signature{
|
|
in: []unsignedType{unsignedTypeI64},
|
|
out: []unsignedType{unsignedTypeI64},
|
|
}
|
|
signature_I32_F32 = &signature{
|
|
in: []unsignedType{unsignedTypeI32},
|
|
out: []unsignedType{unsignedTypeF32},
|
|
}
|
|
signature_I32_F64 = &signature{
|
|
in: []unsignedType{unsignedTypeI32},
|
|
out: []unsignedType{unsignedTypeF64},
|
|
}
|
|
signature_I64_I32 = &signature{
|
|
in: []unsignedType{unsignedTypeI64},
|
|
out: []unsignedType{unsignedTypeI32},
|
|
}
|
|
signature_I64_F32 = &signature{
|
|
in: []unsignedType{unsignedTypeI64},
|
|
out: []unsignedType{unsignedTypeF32},
|
|
}
|
|
signature_I64_F64 = &signature{
|
|
in: []unsignedType{unsignedTypeI64},
|
|
out: []unsignedType{unsignedTypeF64},
|
|
}
|
|
signature_F32_I32 = &signature{
|
|
in: []unsignedType{unsignedTypeF32},
|
|
out: []unsignedType{unsignedTypeI32},
|
|
}
|
|
signature_F32_I64 = &signature{
|
|
in: []unsignedType{unsignedTypeF32},
|
|
out: []unsignedType{unsignedTypeI64},
|
|
}
|
|
signature_F32_F64 = &signature{
|
|
in: []unsignedType{unsignedTypeF32},
|
|
out: []unsignedType{unsignedTypeF64},
|
|
}
|
|
signature_F32_F32 = &signature{
|
|
in: []unsignedType{unsignedTypeF32},
|
|
out: []unsignedType{unsignedTypeF32},
|
|
}
|
|
signature_F64_I32 = &signature{
|
|
in: []unsignedType{unsignedTypeF64},
|
|
out: []unsignedType{unsignedTypeI32},
|
|
}
|
|
signature_F64_F32 = &signature{
|
|
in: []unsignedType{unsignedTypeF64},
|
|
out: []unsignedType{unsignedTypeF32},
|
|
}
|
|
signature_F64_I64 = &signature{
|
|
in: []unsignedType{unsignedTypeF64},
|
|
out: []unsignedType{unsignedTypeI64},
|
|
}
|
|
signature_F64_F64 = &signature{
|
|
in: []unsignedType{unsignedTypeF64},
|
|
out: []unsignedType{unsignedTypeF64},
|
|
}
|
|
signature_I32I32_None = &signature{
|
|
in: []unsignedType{unsignedTypeI32, unsignedTypeI32},
|
|
}
|
|
|
|
signature_I32I32_I32 = &signature{
|
|
in: []unsignedType{unsignedTypeI32, unsignedTypeI32},
|
|
out: []unsignedType{unsignedTypeI32},
|
|
}
|
|
signature_I32I64_None = &signature{
|
|
in: []unsignedType{unsignedTypeI32, unsignedTypeI64},
|
|
}
|
|
signature_I32F32_None = &signature{
|
|
in: []unsignedType{unsignedTypeI32, unsignedTypeF32},
|
|
}
|
|
signature_I32F64_None = &signature{
|
|
in: []unsignedType{unsignedTypeI32, unsignedTypeF64},
|
|
}
|
|
signature_I64I32_I32 = &signature{
|
|
in: []unsignedType{unsignedTypeI64, unsignedTypeI32},
|
|
out: []unsignedType{unsignedTypeI32},
|
|
}
|
|
signature_I64I64_I32 = &signature{
|
|
in: []unsignedType{unsignedTypeI64, unsignedTypeI64},
|
|
out: []unsignedType{unsignedTypeI32},
|
|
}
|
|
signature_I64I64_I64 = &signature{
|
|
in: []unsignedType{unsignedTypeI64, unsignedTypeI64},
|
|
out: []unsignedType{unsignedTypeI64},
|
|
}
|
|
signature_F32F32_I32 = &signature{
|
|
in: []unsignedType{unsignedTypeF32, unsignedTypeF32},
|
|
out: []unsignedType{unsignedTypeI32},
|
|
}
|
|
signature_F32F32_F32 = &signature{
|
|
in: []unsignedType{unsignedTypeF32, unsignedTypeF32},
|
|
out: []unsignedType{unsignedTypeF32},
|
|
}
|
|
signature_F64F64_I32 = &signature{
|
|
in: []unsignedType{unsignedTypeF64, unsignedTypeF64},
|
|
out: []unsignedType{unsignedTypeI32},
|
|
}
|
|
signature_F64F64_F64 = &signature{
|
|
in: []unsignedType{unsignedTypeF64, unsignedTypeF64},
|
|
out: []unsignedType{unsignedTypeF64},
|
|
}
|
|
signature_I32I32I32_None = &signature{
|
|
in: []unsignedType{unsignedTypeI32, unsignedTypeI32, unsignedTypeI32},
|
|
}
|
|
signature_I32I64I32_None = &signature{
|
|
in: []unsignedType{unsignedTypeI32, unsignedTypeI64, unsignedTypeI32},
|
|
}
|
|
signature_UnknownUnknownI32_Unknown = &signature{
|
|
in: []unsignedType{unsignedTypeUnknown, unsignedTypeUnknown, unsignedTypeI32},
|
|
out: []unsignedType{unsignedTypeUnknown},
|
|
}
|
|
signature_V128V128_V128 = &signature{
|
|
in: []unsignedType{unsignedTypeV128, unsignedTypeV128},
|
|
out: []unsignedType{unsignedTypeV128},
|
|
}
|
|
signature_V128V128V128_V32 = &signature{
|
|
in: []unsignedType{unsignedTypeV128, unsignedTypeV128, unsignedTypeV128},
|
|
out: []unsignedType{unsignedTypeV128},
|
|
}
|
|
signature_I32_V128 = &signature{
|
|
in: []unsignedType{unsignedTypeI32},
|
|
out: []unsignedType{unsignedTypeV128},
|
|
}
|
|
signature_I32V128_None = &signature{
|
|
in: []unsignedType{unsignedTypeI32, unsignedTypeV128},
|
|
}
|
|
signature_I32V128_V128 = &signature{
|
|
in: []unsignedType{unsignedTypeI32, unsignedTypeV128},
|
|
out: []unsignedType{unsignedTypeV128},
|
|
}
|
|
signature_V128I32_V128 = &signature{
|
|
in: []unsignedType{unsignedTypeV128, unsignedTypeI32},
|
|
out: []unsignedType{unsignedTypeV128},
|
|
}
|
|
signature_V128I64_V128 = &signature{
|
|
in: []unsignedType{unsignedTypeV128, unsignedTypeI64},
|
|
out: []unsignedType{unsignedTypeV128},
|
|
}
|
|
signature_V128F32_V128 = &signature{
|
|
in: []unsignedType{unsignedTypeV128, unsignedTypeF32},
|
|
out: []unsignedType{unsignedTypeV128},
|
|
}
|
|
signature_V128F64_V128 = &signature{
|
|
in: []unsignedType{unsignedTypeV128, unsignedTypeF64},
|
|
out: []unsignedType{unsignedTypeV128},
|
|
}
|
|
signature_V128_I32 = &signature{
|
|
in: []unsignedType{unsignedTypeV128},
|
|
out: []unsignedType{unsignedTypeI32},
|
|
}
|
|
signature_V128_I64 = &signature{
|
|
in: []unsignedType{unsignedTypeV128},
|
|
out: []unsignedType{unsignedTypeI64},
|
|
}
|
|
signature_V128_F32 = &signature{
|
|
in: []unsignedType{unsignedTypeV128},
|
|
out: []unsignedType{unsignedTypeF32},
|
|
}
|
|
signature_V128_F64 = &signature{
|
|
in: []unsignedType{unsignedTypeV128},
|
|
out: []unsignedType{unsignedTypeF64},
|
|
}
|
|
signature_V128_V128 = &signature{
|
|
in: []unsignedType{unsignedTypeV128},
|
|
out: []unsignedType{unsignedTypeV128},
|
|
}
|
|
signature_I64_V128 = &signature{
|
|
in: []unsignedType{unsignedTypeI64},
|
|
out: []unsignedType{unsignedTypeV128},
|
|
}
|
|
signature_F32_V128 = &signature{
|
|
in: []unsignedType{unsignedTypeF32},
|
|
out: []unsignedType{unsignedTypeV128},
|
|
}
|
|
signature_F64_V128 = &signature{
|
|
in: []unsignedType{unsignedTypeF64},
|
|
out: []unsignedType{unsignedTypeV128},
|
|
}
|
|
signature_I32I64_I64 = &signature{
|
|
in: []unsignedType{unsignedTypeI32, unsignedTypeI64},
|
|
out: []unsignedType{unsignedTypeI64},
|
|
}
|
|
signature_I32I32I64_I32 = &signature{
|
|
in: []unsignedType{unsignedTypeI32, unsignedTypeI32, unsignedTypeI64},
|
|
out: []unsignedType{unsignedTypeI32},
|
|
}
|
|
signature_I32I64I64_I32 = &signature{
|
|
in: []unsignedType{unsignedTypeI32, unsignedTypeI64, unsignedTypeI64},
|
|
out: []unsignedType{unsignedTypeI32},
|
|
}
|
|
signature_I32I32I32_I32 = &signature{
|
|
in: []unsignedType{unsignedTypeI32, unsignedTypeI32, unsignedTypeI32},
|
|
out: []unsignedType{unsignedTypeI32},
|
|
}
|
|
signature_I32I64I64_I64 = &signature{
|
|
in: []unsignedType{unsignedTypeI32, unsignedTypeI64, unsignedTypeI64},
|
|
out: []unsignedType{unsignedTypeI64},
|
|
}
|
|
)
|
|
|
|
// wasmOpcodeSignature returns the signature of given Wasm opcode.
|
|
// Note that some of opcodes' signature vary depending on
|
|
// the function instance (for example, local types).
|
|
// "index" parameter is not used by most of opcodes.
|
|
// The returned signature is used for stack validation when lowering Wasm's opcodes to interpreterir.
|
|
func (c *compiler) wasmOpcodeSignature(op wasm.Opcode, index uint32) (*signature, error) {
|
|
switch op {
|
|
case wasm.OpcodeUnreachable, wasm.OpcodeNop, wasm.OpcodeBlock, wasm.OpcodeLoop:
|
|
return signature_None_None, nil
|
|
case wasm.OpcodeIf:
|
|
return signature_I32_None, nil
|
|
case wasm.OpcodeElse, wasm.OpcodeEnd, wasm.OpcodeBr:
|
|
return signature_None_None, nil
|
|
case wasm.OpcodeThrow, wasm.OpcodeThrowRef, wasm.OpcodeTryTable:
|
|
// Stack manipulation handled dynamically by the compiler.
|
|
return signature_None_None, nil
|
|
case wasm.OpcodeBrIf, wasm.OpcodeBrTable:
|
|
return signature_I32_None, nil
|
|
case wasm.OpcodeReturn:
|
|
return signature_None_None, nil
|
|
case wasm.OpcodeCall, wasm.OpcodeTailCallReturnCall:
|
|
return c.funcTypeToSigs.get(c.funcs[index], false /* direct */), nil
|
|
case wasm.OpcodeCallIndirect, wasm.OpcodeTailCallReturnCallIndirect:
|
|
return c.funcTypeToSigs.get(index, true /* call_indirect */), nil
|
|
case wasm.OpcodeCallRef, wasm.OpcodeReturnCallRef:
|
|
return c.funcTypeToSigs.getCallRef(index), nil
|
|
case wasm.OpcodeRefAsNonNull:
|
|
// Pop a ref (i64), push it back (i64). Traps if null at runtime.
|
|
return signature_I64_I64, nil
|
|
case wasm.OpcodeBrOnNull:
|
|
// Pop a ref (i64). If null, branch; if non-null, push ref back.
|
|
return signature_I64_None, nil
|
|
case wasm.OpcodeBrOnNonNull:
|
|
// Pop a ref (i64). If non-null, push and branch; if null, fall through.
|
|
return signature_I64_None, nil
|
|
case wasm.OpcodeDrop:
|
|
return signature_Unknown_None, nil
|
|
case wasm.OpcodeSelect, wasm.OpcodeTypedSelect:
|
|
return signature_UnknownUnknownI32_Unknown, nil
|
|
case wasm.OpcodeLocalGet:
|
|
inputLen := uint32(len(c.sig.Params))
|
|
if l := uint32(len(c.localTypes)) + inputLen; index >= l {
|
|
return nil, fmt.Errorf("invalid local index for local.get %d >= %d", index, l)
|
|
}
|
|
var t wasm.ValueType
|
|
if index < inputLen {
|
|
t = c.sig.Params[index]
|
|
} else {
|
|
t = c.localTypes[index-inputLen]
|
|
}
|
|
return wasmValueTypeToUnsignedOutSignature(t), nil
|
|
case wasm.OpcodeLocalSet:
|
|
inputLen := uint32(len(c.sig.Params))
|
|
if l := uint32(len(c.localTypes)) + inputLen; index >= l {
|
|
return nil, fmt.Errorf("invalid local index for local.get %d >= %d", index, l)
|
|
}
|
|
var t wasm.ValueType
|
|
if index < inputLen {
|
|
t = c.sig.Params[index]
|
|
} else {
|
|
t = c.localTypes[index-inputLen]
|
|
}
|
|
return wasmValueTypeToUnsignedInSignature(t), nil
|
|
case wasm.OpcodeLocalTee:
|
|
inputLen := uint32(len(c.sig.Params))
|
|
if l := uint32(len(c.localTypes)) + inputLen; index >= l {
|
|
return nil, fmt.Errorf("invalid local index for local.get %d >= %d", index, l)
|
|
}
|
|
var t wasm.ValueType
|
|
if index < inputLen {
|
|
t = c.sig.Params[index]
|
|
} else {
|
|
t = c.localTypes[index-inputLen]
|
|
}
|
|
return wasmValueTypeToUnsignedInOutSignature(t), nil
|
|
case wasm.OpcodeGlobalGet:
|
|
if len(c.globals) <= int(index) {
|
|
return nil, fmt.Errorf("invalid global index for global.get %d >= %d", index, len(c.globals))
|
|
}
|
|
return wasmValueTypeToUnsignedOutSignature(c.globals[index].ValType), nil
|
|
case wasm.OpcodeGlobalSet:
|
|
if len(c.globals) <= int(index) {
|
|
return nil, fmt.Errorf("invalid global index for global.get %d >= %d", index, len(c.globals))
|
|
}
|
|
return wasmValueTypeToUnsignedInSignature(c.globals[index].ValType), nil
|
|
case wasm.OpcodeI32Load:
|
|
return signature_I32_I32, nil
|
|
case wasm.OpcodeI64Load:
|
|
return signature_I32_I64, nil
|
|
case wasm.OpcodeF32Load:
|
|
return signature_I32_F32, nil
|
|
case wasm.OpcodeF64Load:
|
|
return signature_I32_F64, nil
|
|
case wasm.OpcodeI32Load8S, wasm.OpcodeI32Load8U, wasm.OpcodeI32Load16S, wasm.OpcodeI32Load16U:
|
|
return signature_I32_I32, nil
|
|
case wasm.OpcodeI64Load8S, wasm.OpcodeI64Load8U, wasm.OpcodeI64Load16S, wasm.OpcodeI64Load16U,
|
|
wasm.OpcodeI64Load32S, wasm.OpcodeI64Load32U:
|
|
return signature_I32_I64, nil
|
|
case wasm.OpcodeI32Store:
|
|
return signature_I32I32_None, nil
|
|
case wasm.OpcodeI64Store:
|
|
return signature_I32I64_None, nil
|
|
case wasm.OpcodeF32Store:
|
|
return signature_I32F32_None, nil
|
|
case wasm.OpcodeF64Store:
|
|
return signature_I32F64_None, nil
|
|
case wasm.OpcodeI32Store8:
|
|
return signature_I32I32_None, nil
|
|
case wasm.OpcodeI32Store16:
|
|
return signature_I32I32_None, nil
|
|
case wasm.OpcodeI64Store8:
|
|
return signature_I32I64_None, nil
|
|
case wasm.OpcodeI64Store16:
|
|
return signature_I32I64_None, nil
|
|
case wasm.OpcodeI64Store32:
|
|
return signature_I32I64_None, nil
|
|
case wasm.OpcodeMemorySize:
|
|
return signature_None_I32, nil
|
|
case wasm.OpcodeMemoryGrow:
|
|
return signature_I32_I32, nil
|
|
case wasm.OpcodeI32Const:
|
|
return signature_None_I32, nil
|
|
case wasm.OpcodeI64Const:
|
|
return signature_None_I64, nil
|
|
case wasm.OpcodeF32Const:
|
|
return signature_None_F32, nil
|
|
case wasm.OpcodeF64Const:
|
|
return signature_None_F64, nil
|
|
case wasm.OpcodeI32Eqz:
|
|
return signature_I32_I32, nil
|
|
case wasm.OpcodeI32Eq, wasm.OpcodeI32Ne, wasm.OpcodeI32LtS,
|
|
wasm.OpcodeI32LtU, wasm.OpcodeI32GtS, wasm.OpcodeI32GtU,
|
|
wasm.OpcodeI32LeS, wasm.OpcodeI32LeU, wasm.OpcodeI32GeS,
|
|
wasm.OpcodeI32GeU:
|
|
return signature_I32I32_I32, nil
|
|
case wasm.OpcodeI64Eqz:
|
|
return signature_I64_I32, nil
|
|
case wasm.OpcodeI64Eq, wasm.OpcodeI64Ne, wasm.OpcodeI64LtS,
|
|
wasm.OpcodeI64LtU, wasm.OpcodeI64GtS, wasm.OpcodeI64GtU,
|
|
wasm.OpcodeI64LeS, wasm.OpcodeI64LeU, wasm.OpcodeI64GeS,
|
|
wasm.OpcodeI64GeU:
|
|
return signature_I64I64_I32, nil
|
|
case wasm.OpcodeF32Eq, wasm.OpcodeF32Ne, wasm.OpcodeF32Lt,
|
|
wasm.OpcodeF32Gt, wasm.OpcodeF32Le, wasm.OpcodeF32Ge:
|
|
return signature_F32F32_I32, nil
|
|
case wasm.OpcodeF64Eq, wasm.OpcodeF64Ne, wasm.OpcodeF64Lt,
|
|
wasm.OpcodeF64Gt, wasm.OpcodeF64Le, wasm.OpcodeF64Ge:
|
|
return signature_F64F64_I32, nil
|
|
case wasm.OpcodeI32Clz, wasm.OpcodeI32Ctz, wasm.OpcodeI32Popcnt:
|
|
return signature_I32_I32, nil
|
|
case wasm.OpcodeI32Add, wasm.OpcodeI32Sub, wasm.OpcodeI32Mul,
|
|
wasm.OpcodeI32DivS, wasm.OpcodeI32DivU, wasm.OpcodeI32RemS,
|
|
wasm.OpcodeI32RemU, wasm.OpcodeI32And, wasm.OpcodeI32Or,
|
|
wasm.OpcodeI32Xor, wasm.OpcodeI32Shl, wasm.OpcodeI32ShrS,
|
|
wasm.OpcodeI32ShrU, wasm.OpcodeI32Rotl, wasm.OpcodeI32Rotr:
|
|
return signature_I32I32_I32, nil
|
|
case wasm.OpcodeI64Clz, wasm.OpcodeI64Ctz, wasm.OpcodeI64Popcnt:
|
|
return signature_I64_I64, nil
|
|
case wasm.OpcodeI64Add, wasm.OpcodeI64Sub, wasm.OpcodeI64Mul,
|
|
wasm.OpcodeI64DivS, wasm.OpcodeI64DivU, wasm.OpcodeI64RemS,
|
|
wasm.OpcodeI64RemU, wasm.OpcodeI64And, wasm.OpcodeI64Or,
|
|
wasm.OpcodeI64Xor, wasm.OpcodeI64Shl, wasm.OpcodeI64ShrS,
|
|
wasm.OpcodeI64ShrU, wasm.OpcodeI64Rotl, wasm.OpcodeI64Rotr:
|
|
return signature_I64I64_I64, nil
|
|
case wasm.OpcodeF32Abs, wasm.OpcodeF32Neg, wasm.OpcodeF32Ceil,
|
|
wasm.OpcodeF32Floor, wasm.OpcodeF32Trunc, wasm.OpcodeF32Nearest,
|
|
wasm.OpcodeF32Sqrt:
|
|
return signature_F32_F32, nil
|
|
case wasm.OpcodeF32Add, wasm.OpcodeF32Sub, wasm.OpcodeF32Mul,
|
|
wasm.OpcodeF32Div, wasm.OpcodeF32Min, wasm.OpcodeF32Max,
|
|
wasm.OpcodeF32Copysign:
|
|
return signature_F32F32_F32, nil
|
|
case wasm.OpcodeF64Abs, wasm.OpcodeF64Neg, wasm.OpcodeF64Ceil,
|
|
wasm.OpcodeF64Floor, wasm.OpcodeF64Trunc, wasm.OpcodeF64Nearest,
|
|
wasm.OpcodeF64Sqrt:
|
|
return signature_F64_F64, nil
|
|
case wasm.OpcodeF64Add, wasm.OpcodeF64Sub, wasm.OpcodeF64Mul,
|
|
wasm.OpcodeF64Div, wasm.OpcodeF64Min, wasm.OpcodeF64Max,
|
|
wasm.OpcodeF64Copysign:
|
|
return signature_F64F64_F64, nil
|
|
case wasm.OpcodeI32WrapI64:
|
|
return signature_I64_I32, nil
|
|
case wasm.OpcodeI32TruncF32S, wasm.OpcodeI32TruncF32U:
|
|
return signature_F32_I32, nil
|
|
case wasm.OpcodeI32TruncF64S, wasm.OpcodeI32TruncF64U:
|
|
return signature_F64_I32, nil
|
|
case wasm.OpcodeI64ExtendI32S, wasm.OpcodeI64ExtendI32U:
|
|
return signature_I32_I64, nil
|
|
case wasm.OpcodeI64TruncF32S, wasm.OpcodeI64TruncF32U:
|
|
return signature_F32_I64, nil
|
|
case wasm.OpcodeI64TruncF64S, wasm.OpcodeI64TruncF64U:
|
|
return signature_F64_I64, nil
|
|
case wasm.OpcodeF32ConvertI32S, wasm.OpcodeF32ConvertI32U:
|
|
return signature_I32_F32, nil
|
|
case wasm.OpcodeF32ConvertI64S, wasm.OpcodeF32ConvertI64U:
|
|
return signature_I64_F32, nil
|
|
case wasm.OpcodeF32DemoteF64:
|
|
return signature_F64_F32, nil
|
|
case wasm.OpcodeF64ConvertI32S, wasm.OpcodeF64ConvertI32U:
|
|
return signature_I32_F64, nil
|
|
case wasm.OpcodeF64ConvertI64S, wasm.OpcodeF64ConvertI64U:
|
|
return signature_I64_F64, nil
|
|
case wasm.OpcodeF64PromoteF32:
|
|
return signature_F32_F64, nil
|
|
case wasm.OpcodeI32ReinterpretF32:
|
|
return signature_F32_I32, nil
|
|
case wasm.OpcodeI64ReinterpretF64:
|
|
return signature_F64_I64, nil
|
|
case wasm.OpcodeF32ReinterpretI32:
|
|
return signature_I32_F32, nil
|
|
case wasm.OpcodeF64ReinterpretI64:
|
|
return signature_I64_F64, nil
|
|
case wasm.OpcodeI32Extend8S, wasm.OpcodeI32Extend16S:
|
|
return signature_I32_I32, nil
|
|
case wasm.OpcodeI64Extend8S, wasm.OpcodeI64Extend16S, wasm.OpcodeI64Extend32S:
|
|
return signature_I64_I64, nil
|
|
case wasm.OpcodeTableGet:
|
|
// table.get takes table's offset and pushes the ref type value of opaque pointer as i64 value onto the stack.
|
|
return signature_I32_I64, nil
|
|
case wasm.OpcodeTableSet:
|
|
// table.set takes table's offset and the ref type value of opaque pointer as i64 value.
|
|
return signature_I32I64_None, nil
|
|
case wasm.OpcodeRefFunc:
|
|
// ref.func is translated as pushing the compiled function's opaque pointer (uint64) at interpreterir layer.
|
|
return signature_None_I64, nil
|
|
case wasm.OpcodeRefIsNull:
|
|
// ref.is_null is translated as checking if the uint64 on the top of the stack (opaque pointer) is zero or not.
|
|
return signature_I64_I32, nil
|
|
case wasm.OpcodeRefNull:
|
|
// ref.null is translated as i64.const 0.
|
|
return signature_None_I64, nil
|
|
case wasm.OpcodeMiscPrefix:
|
|
switch miscOp := c.body[c.pc+1]; miscOp {
|
|
case wasm.OpcodeMiscI32TruncSatF32S, wasm.OpcodeMiscI32TruncSatF32U:
|
|
return signature_F32_I32, nil
|
|
case wasm.OpcodeMiscI32TruncSatF64S, wasm.OpcodeMiscI32TruncSatF64U:
|
|
return signature_F64_I32, nil
|
|
case wasm.OpcodeMiscI64TruncSatF32S, wasm.OpcodeMiscI64TruncSatF32U:
|
|
return signature_F32_I64, nil
|
|
case wasm.OpcodeMiscI64TruncSatF64S, wasm.OpcodeMiscI64TruncSatF64U:
|
|
return signature_F64_I64, nil
|
|
case wasm.OpcodeMiscMemoryInit, wasm.OpcodeMiscMemoryCopy, wasm.OpcodeMiscMemoryFill,
|
|
wasm.OpcodeMiscTableInit, wasm.OpcodeMiscTableCopy:
|
|
return signature_I32I32I32_None, nil
|
|
case wasm.OpcodeMiscDataDrop, wasm.OpcodeMiscElemDrop:
|
|
return signature_None_None, nil
|
|
case wasm.OpcodeMiscTableGrow:
|
|
return signature_I64I32_I32, nil
|
|
case wasm.OpcodeMiscTableSize:
|
|
return signature_None_I32, nil
|
|
case wasm.OpcodeMiscTableFill:
|
|
return signature_I32I64I32_None, nil
|
|
default:
|
|
return nil, fmt.Errorf("unsupported misc instruction in interpreterir: 0x%x", op)
|
|
}
|
|
case wasm.OpcodeVecPrefix:
|
|
switch vecOp := c.body[c.pc+1]; vecOp {
|
|
case wasm.OpcodeVecV128Const:
|
|
return signature_None_V128, nil
|
|
case wasm.OpcodeVecV128Load, wasm.OpcodeVecV128Load8x8s, wasm.OpcodeVecV128Load8x8u,
|
|
wasm.OpcodeVecV128Load16x4s, wasm.OpcodeVecV128Load16x4u, wasm.OpcodeVecV128Load32x2s,
|
|
wasm.OpcodeVecV128Load32x2u, wasm.OpcodeVecV128Load8Splat, wasm.OpcodeVecV128Load16Splat,
|
|
wasm.OpcodeVecV128Load32Splat, wasm.OpcodeVecV128Load64Splat, wasm.OpcodeVecV128Load32zero,
|
|
wasm.OpcodeVecV128Load64zero:
|
|
return signature_I32_V128, nil
|
|
case wasm.OpcodeVecV128Load8Lane, wasm.OpcodeVecV128Load16Lane,
|
|
wasm.OpcodeVecV128Load32Lane, wasm.OpcodeVecV128Load64Lane:
|
|
return signature_I32V128_V128, nil
|
|
case wasm.OpcodeVecV128Store,
|
|
wasm.OpcodeVecV128Store8Lane,
|
|
wasm.OpcodeVecV128Store16Lane,
|
|
wasm.OpcodeVecV128Store32Lane,
|
|
wasm.OpcodeVecV128Store64Lane:
|
|
return signature_I32V128_None, nil
|
|
case wasm.OpcodeVecI8x16ExtractLaneS,
|
|
wasm.OpcodeVecI8x16ExtractLaneU,
|
|
wasm.OpcodeVecI16x8ExtractLaneS,
|
|
wasm.OpcodeVecI16x8ExtractLaneU,
|
|
wasm.OpcodeVecI32x4ExtractLane:
|
|
return signature_V128_I32, nil
|
|
case wasm.OpcodeVecI64x2ExtractLane:
|
|
return signature_V128_I64, nil
|
|
case wasm.OpcodeVecF32x4ExtractLane:
|
|
return signature_V128_F32, nil
|
|
case wasm.OpcodeVecF64x2ExtractLane:
|
|
return signature_V128_F64, nil
|
|
case wasm.OpcodeVecI8x16ReplaceLane, wasm.OpcodeVecI16x8ReplaceLane, wasm.OpcodeVecI32x4ReplaceLane,
|
|
wasm.OpcodeVecI8x16Shl, wasm.OpcodeVecI8x16ShrS, wasm.OpcodeVecI8x16ShrU,
|
|
wasm.OpcodeVecI16x8Shl, wasm.OpcodeVecI16x8ShrS, wasm.OpcodeVecI16x8ShrU,
|
|
wasm.OpcodeVecI32x4Shl, wasm.OpcodeVecI32x4ShrS, wasm.OpcodeVecI32x4ShrU,
|
|
wasm.OpcodeVecI64x2Shl, wasm.OpcodeVecI64x2ShrS, wasm.OpcodeVecI64x2ShrU:
|
|
return signature_V128I32_V128, nil
|
|
case wasm.OpcodeVecI64x2ReplaceLane:
|
|
return signature_V128I64_V128, nil
|
|
case wasm.OpcodeVecF32x4ReplaceLane:
|
|
return signature_V128F32_V128, nil
|
|
case wasm.OpcodeVecF64x2ReplaceLane:
|
|
return signature_V128F64_V128, nil
|
|
case wasm.OpcodeVecI8x16Splat,
|
|
wasm.OpcodeVecI16x8Splat,
|
|
wasm.OpcodeVecI32x4Splat:
|
|
return signature_I32_V128, nil
|
|
case wasm.OpcodeVecI64x2Splat:
|
|
return signature_I64_V128, nil
|
|
case wasm.OpcodeVecF32x4Splat:
|
|
return signature_F32_V128, nil
|
|
case wasm.OpcodeVecF64x2Splat:
|
|
return signature_F64_V128, nil
|
|
case wasm.OpcodeVecV128i8x16Shuffle, wasm.OpcodeVecI8x16Swizzle, wasm.OpcodeVecV128And, wasm.OpcodeVecV128Or, wasm.OpcodeVecV128Xor, wasm.OpcodeVecV128AndNot:
|
|
return signature_V128V128_V128, nil
|
|
case wasm.OpcodeVecI8x16AllTrue, wasm.OpcodeVecI16x8AllTrue, wasm.OpcodeVecI32x4AllTrue, wasm.OpcodeVecI64x2AllTrue,
|
|
wasm.OpcodeVecV128AnyTrue,
|
|
wasm.OpcodeVecI8x16BitMask, wasm.OpcodeVecI16x8BitMask, wasm.OpcodeVecI32x4BitMask, wasm.OpcodeVecI64x2BitMask:
|
|
return signature_V128_I32, nil
|
|
case wasm.OpcodeVecV128Not, wasm.OpcodeVecI8x16Neg, wasm.OpcodeVecI16x8Neg, wasm.OpcodeVecI32x4Neg, wasm.OpcodeVecI64x2Neg,
|
|
wasm.OpcodeVecF32x4Neg, wasm.OpcodeVecF64x2Neg, wasm.OpcodeVecF32x4Sqrt, wasm.OpcodeVecF64x2Sqrt,
|
|
wasm.OpcodeVecI8x16Abs, wasm.OpcodeVecI8x16Popcnt, wasm.OpcodeVecI16x8Abs, wasm.OpcodeVecI32x4Abs, wasm.OpcodeVecI64x2Abs,
|
|
wasm.OpcodeVecF32x4Abs, wasm.OpcodeVecF64x2Abs,
|
|
wasm.OpcodeVecF32x4Ceil, wasm.OpcodeVecF32x4Floor, wasm.OpcodeVecF32x4Trunc, wasm.OpcodeVecF32x4Nearest,
|
|
wasm.OpcodeVecF64x2Ceil, wasm.OpcodeVecF64x2Floor, wasm.OpcodeVecF64x2Trunc, wasm.OpcodeVecF64x2Nearest,
|
|
wasm.OpcodeVecI16x8ExtendLowI8x16S, wasm.OpcodeVecI16x8ExtendHighI8x16S, wasm.OpcodeVecI16x8ExtendLowI8x16U, wasm.OpcodeVecI16x8ExtendHighI8x16U,
|
|
wasm.OpcodeVecI32x4ExtendLowI16x8S, wasm.OpcodeVecI32x4ExtendHighI16x8S, wasm.OpcodeVecI32x4ExtendLowI16x8U, wasm.OpcodeVecI32x4ExtendHighI16x8U,
|
|
wasm.OpcodeVecI64x2ExtendLowI32x4S, wasm.OpcodeVecI64x2ExtendHighI32x4S, wasm.OpcodeVecI64x2ExtendLowI32x4U, wasm.OpcodeVecI64x2ExtendHighI32x4U,
|
|
wasm.OpcodeVecI16x8ExtaddPairwiseI8x16S, wasm.OpcodeVecI16x8ExtaddPairwiseI8x16U, wasm.OpcodeVecI32x4ExtaddPairwiseI16x8S, wasm.OpcodeVecI32x4ExtaddPairwiseI16x8U,
|
|
wasm.OpcodeVecF64x2PromoteLowF32x4Zero, wasm.OpcodeVecF32x4DemoteF64x2Zero,
|
|
wasm.OpcodeVecF32x4ConvertI32x4S, wasm.OpcodeVecF32x4ConvertI32x4U,
|
|
wasm.OpcodeVecF64x2ConvertLowI32x4S, wasm.OpcodeVecF64x2ConvertLowI32x4U,
|
|
wasm.OpcodeVecI32x4TruncSatF32x4S, wasm.OpcodeVecI32x4TruncSatF32x4U,
|
|
wasm.OpcodeVecI32x4TruncSatF64x2SZero, wasm.OpcodeVecI32x4TruncSatF64x2UZero:
|
|
return signature_V128_V128, nil
|
|
case wasm.OpcodeVecV128Bitselect:
|
|
return signature_V128V128V128_V32, nil
|
|
case wasm.OpcodeVecI8x16Eq, wasm.OpcodeVecI8x16Ne, wasm.OpcodeVecI8x16LtS, wasm.OpcodeVecI8x16LtU, wasm.OpcodeVecI8x16GtS,
|
|
wasm.OpcodeVecI8x16GtU, wasm.OpcodeVecI8x16LeS, wasm.OpcodeVecI8x16LeU, wasm.OpcodeVecI8x16GeS, wasm.OpcodeVecI8x16GeU,
|
|
wasm.OpcodeVecI16x8Eq, wasm.OpcodeVecI16x8Ne, wasm.OpcodeVecI16x8LtS, wasm.OpcodeVecI16x8LtU, wasm.OpcodeVecI16x8GtS,
|
|
wasm.OpcodeVecI16x8GtU, wasm.OpcodeVecI16x8LeS, wasm.OpcodeVecI16x8LeU, wasm.OpcodeVecI16x8GeS, wasm.OpcodeVecI16x8GeU,
|
|
wasm.OpcodeVecI32x4Eq, wasm.OpcodeVecI32x4Ne, wasm.OpcodeVecI32x4LtS, wasm.OpcodeVecI32x4LtU, wasm.OpcodeVecI32x4GtS,
|
|
wasm.OpcodeVecI32x4GtU, wasm.OpcodeVecI32x4LeS, wasm.OpcodeVecI32x4LeU, wasm.OpcodeVecI32x4GeS, wasm.OpcodeVecI32x4GeU,
|
|
wasm.OpcodeVecI64x2Eq, wasm.OpcodeVecI64x2Ne, wasm.OpcodeVecI64x2LtS, wasm.OpcodeVecI64x2GtS, wasm.OpcodeVecI64x2LeS,
|
|
wasm.OpcodeVecI64x2GeS, wasm.OpcodeVecF32x4Eq, wasm.OpcodeVecF32x4Ne, wasm.OpcodeVecF32x4Lt, wasm.OpcodeVecF32x4Gt,
|
|
wasm.OpcodeVecF32x4Le, wasm.OpcodeVecF32x4Ge, wasm.OpcodeVecF64x2Eq, wasm.OpcodeVecF64x2Ne, wasm.OpcodeVecF64x2Lt,
|
|
wasm.OpcodeVecF64x2Gt, wasm.OpcodeVecF64x2Le, wasm.OpcodeVecF64x2Ge,
|
|
wasm.OpcodeVecI8x16Add, wasm.OpcodeVecI8x16AddSatS, wasm.OpcodeVecI8x16AddSatU, wasm.OpcodeVecI8x16Sub,
|
|
wasm.OpcodeVecI8x16SubSatS, wasm.OpcodeVecI8x16SubSatU,
|
|
wasm.OpcodeVecI16x8Add, wasm.OpcodeVecI16x8AddSatS, wasm.OpcodeVecI16x8AddSatU, wasm.OpcodeVecI16x8Sub,
|
|
wasm.OpcodeVecI16x8SubSatS, wasm.OpcodeVecI16x8SubSatU, wasm.OpcodeVecI16x8Mul,
|
|
wasm.OpcodeVecI32x4Add, wasm.OpcodeVecI32x4Sub, wasm.OpcodeVecI32x4Mul,
|
|
wasm.OpcodeVecI64x2Add, wasm.OpcodeVecI64x2Sub, wasm.OpcodeVecI64x2Mul,
|
|
wasm.OpcodeVecF32x4Add, wasm.OpcodeVecF32x4Sub, wasm.OpcodeVecF32x4Mul, wasm.OpcodeVecF32x4Div,
|
|
wasm.OpcodeVecF64x2Add, wasm.OpcodeVecF64x2Sub, wasm.OpcodeVecF64x2Mul, wasm.OpcodeVecF64x2Div,
|
|
wasm.OpcodeVecI8x16MinS, wasm.OpcodeVecI8x16MinU, wasm.OpcodeVecI8x16MaxS, wasm.OpcodeVecI8x16MaxU, wasm.OpcodeVecI8x16AvgrU,
|
|
wasm.OpcodeVecI16x8MinS, wasm.OpcodeVecI16x8MinU, wasm.OpcodeVecI16x8MaxS, wasm.OpcodeVecI16x8MaxU, wasm.OpcodeVecI16x8AvgrU,
|
|
wasm.OpcodeVecI32x4MinS, wasm.OpcodeVecI32x4MinU, wasm.OpcodeVecI32x4MaxS, wasm.OpcodeVecI32x4MaxU,
|
|
wasm.OpcodeVecF32x4Min, wasm.OpcodeVecF32x4Max, wasm.OpcodeVecF64x2Min, wasm.OpcodeVecF64x2Max,
|
|
wasm.OpcodeVecF32x4Pmin, wasm.OpcodeVecF32x4Pmax, wasm.OpcodeVecF64x2Pmin, wasm.OpcodeVecF64x2Pmax,
|
|
wasm.OpcodeVecI16x8Q15mulrSatS,
|
|
wasm.OpcodeVecI16x8ExtMulLowI8x16S, wasm.OpcodeVecI16x8ExtMulHighI8x16S, wasm.OpcodeVecI16x8ExtMulLowI8x16U, wasm.OpcodeVecI16x8ExtMulHighI8x16U,
|
|
wasm.OpcodeVecI32x4ExtMulLowI16x8S, wasm.OpcodeVecI32x4ExtMulHighI16x8S, wasm.OpcodeVecI32x4ExtMulLowI16x8U, wasm.OpcodeVecI32x4ExtMulHighI16x8U,
|
|
wasm.OpcodeVecI64x2ExtMulLowI32x4S, wasm.OpcodeVecI64x2ExtMulHighI32x4S, wasm.OpcodeVecI64x2ExtMulLowI32x4U, wasm.OpcodeVecI64x2ExtMulHighI32x4U,
|
|
wasm.OpcodeVecI32x4DotI16x8S,
|
|
wasm.OpcodeVecI8x16NarrowI16x8S, wasm.OpcodeVecI8x16NarrowI16x8U, wasm.OpcodeVecI16x8NarrowI32x4S, wasm.OpcodeVecI16x8NarrowI32x4U:
|
|
return signature_V128V128_V128, nil
|
|
default:
|
|
return nil, fmt.Errorf("unsupported vector instruction in interpreterir: %s", wasm.VectorInstructionName(vecOp))
|
|
}
|
|
case wasm.OpcodeAtomicPrefix:
|
|
switch atomicOp := c.body[c.pc+1]; atomicOp {
|
|
case wasm.OpcodeAtomicMemoryNotify:
|
|
return signature_I32I32_I32, nil
|
|
case wasm.OpcodeAtomicMemoryWait32:
|
|
return signature_I32I32I64_I32, nil
|
|
case wasm.OpcodeAtomicMemoryWait64:
|
|
return signature_I32I64I64_I32, nil
|
|
case wasm.OpcodeAtomicFence:
|
|
return signature_None_None, nil
|
|
case wasm.OpcodeAtomicI32Load, wasm.OpcodeAtomicI32Load8U, wasm.OpcodeAtomicI32Load16U:
|
|
return signature_I32_I32, nil
|
|
case wasm.OpcodeAtomicI64Load, wasm.OpcodeAtomicI64Load8U, wasm.OpcodeAtomicI64Load16U, wasm.OpcodeAtomicI64Load32U:
|
|
return signature_I32_I64, nil
|
|
case wasm.OpcodeAtomicI32Store, wasm.OpcodeAtomicI32Store8, wasm.OpcodeAtomicI32Store16:
|
|
return signature_I32I32_None, nil
|
|
case wasm.OpcodeAtomicI64Store, wasm.OpcodeAtomicI64Store8, wasm.OpcodeAtomicI64Store16, wasm.OpcodeAtomicI64Store32:
|
|
return signature_I32I64_None, nil
|
|
case wasm.OpcodeAtomicI32RmwAdd, wasm.OpcodeAtomicI32RmwSub, wasm.OpcodeAtomicI32RmwAnd, wasm.OpcodeAtomicI32RmwOr, wasm.OpcodeAtomicI32RmwXor, wasm.OpcodeAtomicI32RmwXchg,
|
|
wasm.OpcodeAtomicI32Rmw8AddU, wasm.OpcodeAtomicI32Rmw8SubU, wasm.OpcodeAtomicI32Rmw8AndU, wasm.OpcodeAtomicI32Rmw8OrU, wasm.OpcodeAtomicI32Rmw8XorU, wasm.OpcodeAtomicI32Rmw8XchgU,
|
|
wasm.OpcodeAtomicI32Rmw16AddU, wasm.OpcodeAtomicI32Rmw16SubU, wasm.OpcodeAtomicI32Rmw16AndU, wasm.OpcodeAtomicI32Rmw16OrU, wasm.OpcodeAtomicI32Rmw16XorU, wasm.OpcodeAtomicI32Rmw16XchgU:
|
|
return signature_I32I32_I32, nil
|
|
case wasm.OpcodeAtomicI64RmwAdd, wasm.OpcodeAtomicI64RmwSub, wasm.OpcodeAtomicI64RmwAnd, wasm.OpcodeAtomicI64RmwOr, wasm.OpcodeAtomicI64RmwXor, wasm.OpcodeAtomicI64RmwXchg,
|
|
wasm.OpcodeAtomicI64Rmw8AddU, wasm.OpcodeAtomicI64Rmw8SubU, wasm.OpcodeAtomicI64Rmw8AndU, wasm.OpcodeAtomicI64Rmw8OrU, wasm.OpcodeAtomicI64Rmw8XorU, wasm.OpcodeAtomicI64Rmw8XchgU,
|
|
wasm.OpcodeAtomicI64Rmw16AddU, wasm.OpcodeAtomicI64Rmw16SubU, wasm.OpcodeAtomicI64Rmw16AndU, wasm.OpcodeAtomicI64Rmw16OrU, wasm.OpcodeAtomicI64Rmw16XorU, wasm.OpcodeAtomicI64Rmw16XchgU,
|
|
wasm.OpcodeAtomicI64Rmw32AddU, wasm.OpcodeAtomicI64Rmw32SubU, wasm.OpcodeAtomicI64Rmw32AndU, wasm.OpcodeAtomicI64Rmw32OrU, wasm.OpcodeAtomicI64Rmw32XorU, wasm.OpcodeAtomicI64Rmw32XchgU:
|
|
return signature_I32I64_I64, nil
|
|
case wasm.OpcodeAtomicI32RmwCmpxchg, wasm.OpcodeAtomicI32Rmw8CmpxchgU, wasm.OpcodeAtomicI32Rmw16CmpxchgU:
|
|
return signature_I32I32I32_I32, nil
|
|
case wasm.OpcodeAtomicI64RmwCmpxchg, wasm.OpcodeAtomicI64Rmw8CmpxchgU, wasm.OpcodeAtomicI64Rmw16CmpxchgU, wasm.OpcodeAtomicI64Rmw32CmpxchgU:
|
|
return signature_I32I64I64_I64, nil
|
|
default:
|
|
return nil, fmt.Errorf("unsupported atomic instruction in interpreterir: %s", wasm.AtomicInstructionName(atomicOp))
|
|
}
|
|
default:
|
|
return nil, fmt.Errorf("unsupported instruction in interpreterir: 0x%x", op)
|
|
}
|
|
}
|
|
|
|
// funcTypeToIRSignatures is the central cache for a module to get the *signature
|
|
// for function calls.
|
|
type funcTypeToIRSignatures struct {
|
|
directCalls []*signature
|
|
indirectCalls []*signature
|
|
callRefCalls []*signature
|
|
wasmTypes []wasm.FunctionType
|
|
}
|
|
|
|
// get returns the *signature for the direct or indirect function call against functions whose type is at `typeIndex`.
|
|
func (f *funcTypeToIRSignatures) get(typeIndex wasm.Index, indirect bool) *signature {
|
|
var sig *signature
|
|
if indirect {
|
|
sig = f.indirectCalls[typeIndex]
|
|
} else {
|
|
sig = f.directCalls[typeIndex]
|
|
}
|
|
if sig != nil {
|
|
return sig
|
|
}
|
|
|
|
tp := &f.wasmTypes[typeIndex]
|
|
if indirect {
|
|
sig = &signature{
|
|
in: make([]unsignedType, 0, len(tp.Params)+1), // +1 to reserve space for call indirect index.
|
|
out: make([]unsignedType, 0, len(tp.Results)),
|
|
}
|
|
} else {
|
|
sig = &signature{
|
|
in: make([]unsignedType, 0, len(tp.Params)),
|
|
out: make([]unsignedType, 0, len(tp.Results)),
|
|
}
|
|
}
|
|
|
|
for _, vt := range tp.Params {
|
|
sig.in = append(sig.in, wasmValueTypeTounsignedType(vt))
|
|
}
|
|
for _, vt := range tp.Results {
|
|
sig.out = append(sig.out, wasmValueTypeTounsignedType(vt))
|
|
}
|
|
|
|
if indirect {
|
|
sig.in = append(sig.in, unsignedTypeI32)
|
|
f.indirectCalls[typeIndex] = sig
|
|
} else {
|
|
f.directCalls[typeIndex] = sig
|
|
}
|
|
return sig
|
|
}
|
|
|
|
// getCallRef returns the *signature for call_ref, which is like a direct call
|
|
// but with an extra i64 input for the funcref operand.
|
|
func (f *funcTypeToIRSignatures) getCallRef(typeIndex wasm.Index) *signature {
|
|
if sig := f.callRefCalls[typeIndex]; sig != nil {
|
|
return sig
|
|
}
|
|
tp := &f.wasmTypes[typeIndex]
|
|
sig := &signature{
|
|
in: make([]unsignedType, 0, len(tp.Params)+1),
|
|
out: make([]unsignedType, 0, len(tp.Results)),
|
|
}
|
|
for _, vt := range tp.Params {
|
|
sig.in = append(sig.in, wasmValueTypeTounsignedType(vt))
|
|
}
|
|
sig.in = append(sig.in, unsignedTypeI64) // funcref operand
|
|
for _, vt := range tp.Results {
|
|
sig.out = append(sig.out, wasmValueTypeTounsignedType(vt))
|
|
}
|
|
f.callRefCalls[typeIndex] = sig
|
|
return sig
|
|
}
|
|
|
|
func wasmValueTypeTounsignedType(vt wasm.ValueType) unsignedType {
|
|
switch vt {
|
|
case wasm.ValueTypeI32:
|
|
return unsignedTypeI32
|
|
case wasm.ValueTypeI64,
|
|
// From interpreterir layer, ref type values are opaque 64-bit pointers.
|
|
wasm.ValueTypeExternref, wasm.ValueTypeFuncref,
|
|
wasm.ValueTypeExnref:
|
|
return unsignedTypeI64
|
|
case wasm.ValueTypeF32:
|
|
return unsignedTypeF32
|
|
case wasm.ValueTypeF64:
|
|
return unsignedTypeF64
|
|
case wasm.ValueTypeV128:
|
|
return unsignedTypeV128
|
|
default:
|
|
// Concrete ref types (ref $t) have variable bit patterns.
|
|
if vt.IsRef() {
|
|
return unsignedTypeI64
|
|
}
|
|
}
|
|
panic("unreachable")
|
|
}
|
|
|
|
func wasmValueTypeToUnsignedOutSignature(vt wasm.ValueType) *signature {
|
|
switch vt {
|
|
case wasm.ValueTypeI32:
|
|
return signature_None_I32
|
|
case wasm.ValueTypeI64,
|
|
// From interpreterir layer, ref type values are opaque 64-bit pointers.
|
|
wasm.ValueTypeExternref, wasm.ValueTypeFuncref,
|
|
wasm.ValueTypeExnref:
|
|
return signature_None_I64
|
|
case wasm.ValueTypeF32:
|
|
return signature_None_F32
|
|
case wasm.ValueTypeF64:
|
|
return signature_None_F64
|
|
case wasm.ValueTypeV128:
|
|
return signature_None_V128
|
|
default:
|
|
if vt.IsRef() {
|
|
return signature_None_I64
|
|
}
|
|
}
|
|
panic("unreachable")
|
|
}
|
|
|
|
func wasmValueTypeToUnsignedInSignature(vt wasm.ValueType) *signature {
|
|
switch vt {
|
|
case wasm.ValueTypeI32:
|
|
return signature_I32_None
|
|
case wasm.ValueTypeI64,
|
|
// From interpreterir layer, ref type values are opaque 64-bit pointers.
|
|
wasm.ValueTypeExternref, wasm.ValueTypeFuncref,
|
|
wasm.ValueTypeExnref:
|
|
return signature_I64_None
|
|
case wasm.ValueTypeF32:
|
|
return signature_F32_None
|
|
case wasm.ValueTypeF64:
|
|
return signature_F64_None
|
|
case wasm.ValueTypeV128:
|
|
return signature_V128_None
|
|
default:
|
|
if vt.IsRef() {
|
|
return signature_I64_None
|
|
}
|
|
}
|
|
panic("unreachable")
|
|
}
|
|
|
|
func wasmValueTypeToUnsignedInOutSignature(vt wasm.ValueType) *signature {
|
|
switch vt {
|
|
case wasm.ValueTypeI32:
|
|
return signature_I32_I32
|
|
case wasm.ValueTypeI64,
|
|
// At interpreterir layer, ref type values are opaque 64-bit pointers.
|
|
wasm.ValueTypeExternref, wasm.ValueTypeFuncref,
|
|
wasm.ValueTypeExnref:
|
|
return signature_I64_I64
|
|
case wasm.ValueTypeF32:
|
|
return signature_F32_F32
|
|
case wasm.ValueTypeF64:
|
|
return signature_F64_F64
|
|
case wasm.ValueTypeV128:
|
|
return signature_V128_V128
|
|
default:
|
|
if vt.IsRef() {
|
|
return signature_I64_I64
|
|
}
|
|
}
|
|
panic("unreachable")
|
|
}
|