Files
wazero-wazero/internal/engine/interpreter/signature.go
T
Edoardo Vacchi 2c14bbff3d feat: typed function references spec (#2497)
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>
2026-05-28 15:57:29 +02:00

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")
}