blob: 24a9556d83990a991539a80ccdf30f92a0073ca9 [file] [log] [blame]
// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import 'module.dart';
import 'serialize.dart';
import 'types.dart';
/// Thrown when Wasm bytecode validation fails.
class ValidationError {
final String trace;
final String error;
ValidationError(this.trace, this.error);
@override
String toString() => "$trace\n$error";
}
/// Label to use as target for branch instructions.
abstract class Label {
final List<ValueType> inputs;
final List<ValueType> outputs;
late final int? ordinal;
late final int depth;
late final int baseStackHeight;
late final bool reachable;
Label._(this.inputs, this.outputs);
List<ValueType> get targetTypes;
bool get hasOrdinal => ordinal != null;
@override
String toString() => "L$ordinal";
}
class Expression extends Label {
Expression(List<ValueType> inputs, List<ValueType> outputs)
: super._(inputs, outputs) {
ordinal = null;
depth = 0;
baseStackHeight = 0;
reachable = true;
}
List<ValueType> get targetTypes => outputs;
}
class Block extends Label {
Block(List<ValueType> inputs, List<ValueType> outputs)
: super._(inputs, outputs);
List<ValueType> get targetTypes => outputs;
}
class Loop extends Label {
Loop(List<ValueType> inputs, List<ValueType> outputs)
: super._(inputs, outputs);
List<ValueType> get targetTypes => inputs;
}
class If extends Label {
bool hasElse = false;
If(List<ValueType> inputs, List<ValueType> outputs)
: super._(inputs, outputs);
List<ValueType> get targetTypes => outputs;
}
/// A sequence of Wasm instructions.
///
/// Instructions can be added to the sequence by calling the corresponding
/// instruction methods.
///
/// If asserts are enabled, the instruction methods will perform on-the-fly
/// validation and throw a [ValidationError] if validation fails.
class Instructions with SerializerMixin {
/// The module containing these instructions.
final Module module;
/// Locals declared in this body, including parameters.
final List<Local> locals;
/// Is this the initializer of a global variable?
final bool isGlobalInitializer;
/// Whether a textual trace of the instruction stream should be recorded when
/// emitting instructions (provided asserts are enabled).
///
/// This trace can be accessed via the [trace] property and will be part of
/// the exception text if a validation error occurs.
bool traceEnabled = true;
/// Whether to print a byte offset for each instruction in the textual trace.
bool byteOffsetEnabled = false;
/// Column width for the instruction byte offset.
int byteOffsetWidth = 7;
/// Column width for the instructions.
int instructionColumnWidth = 50;
int _indent = 1;
final List<String> _traceLines = [];
int _labelCount = 0;
final List<Label> _labelStack = [];
final List<ValueType> _stackTypes = [];
bool _reachable = true;
/// Create a new instruction sequence.
Instructions(this.module, List<ValueType> outputs,
{this.locals = const [], this.isGlobalInitializer = false}) {
_labelStack.add(Expression(const [], outputs));
}
/// Whether the current point in the instruction stream is reachable.
bool get reachable => _reachable;
/// Whether the instruction sequence has been completed by the final `end`.
bool get isComplete => _labelStack.isEmpty;
/// Textual trace of the instructions.
String get trace => _traceLines.join();
bool _debugTrace(List<Object>? trace,
{required bool reachableAfter,
int indentBefore = 0,
int indentAfter = 0}) {
if (traceEnabled && trace != null) {
_indent += indentBefore;
String byteOffset =
byteOffsetEnabled ? "${data.length}".padLeft(byteOffsetWidth) : "";
String instr = " " * _indent + " " + trace.join(" ");
instr = instr.length > instructionColumnWidth - 2
? instr.substring(0, instructionColumnWidth - 4) + "... "
: instr.padRight(instructionColumnWidth);
final String stack = reachableAfter ? _stackTypes.join(', ') : "-";
final String line = "$byteOffset$instr$stack\n";
_indent += indentAfter;
_traceLines.add(line);
}
return true;
}
bool _comment(String text) {
if (traceEnabled) {
final String line = " " * (byteOffsetEnabled ? byteOffsetWidth : 0) +
" " * _indent +
" ;; $text\n";
_traceLines.add(line);
}
return true;
}
Never _reportError(String error) {
throw ValidationError(trace, error);
}
ValueType get _topOfStack {
if (!reachable) return RefType.any();
if (_stackTypes.isEmpty) _reportError("Stack underflow");
return _stackTypes.last;
}
Label get _topOfLabelStack {
if (_labelStack.isEmpty) _reportError("Label stack underflow");
return _labelStack.last;
}
List<ValueType> _stack(int n) {
if (_stackTypes.length < n) _reportError("Stack underflow");
return _stackTypes.sublist(_stackTypes.length - n);
}
List<ValueType> _checkStackTypes(List<ValueType> inputs,
[List<ValueType>? stack]) {
stack ??= _stack(inputs.length);
bool typesMatch = true;
for (int i = 0; i < inputs.length; i++) {
if (!stack[i].isSubtypeOf(inputs[i])) {
typesMatch = false;
break;
}
}
if (!typesMatch) {
final String expected = inputs.join(', ');
final String got = stack.join(', ');
_reportError("Expected [$expected], but stack contained [$got]");
}
return stack;
}
bool _verifyTypes(List<ValueType> inputs, List<ValueType> outputs,
{List<Object>? trace, bool reachableAfter = true}) {
return _verifyTypesFun(inputs, (_) => outputs,
trace: trace, reachableAfter: reachableAfter);
}
bool _verifyTypesFun(List<ValueType> inputs,
List<ValueType> Function(List<ValueType>) outputsFun,
{List<Object>? trace, bool reachableAfter = true}) {
if (!reachable) {
return _debugTrace(trace, reachableAfter: false);
}
if (_stackTypes.length - inputs.length < _topOfLabelStack.baseStackHeight) {
_reportError("Underflowing base stack of innermost block");
}
final List<ValueType> stack = _checkStackTypes(inputs);
_stackTypes.length -= inputs.length;
_stackTypes.addAll(outputsFun(stack));
return _debugTrace(trace, reachableAfter: reachableAfter);
}
bool _verifyBranchTypes(Label label,
[int popped = 0, List<ValueType> pushed = const []]) {
if (!reachable) {
return true;
}
final List<ValueType> inputs = label.targetTypes;
if (_stackTypes.length - popped + pushed.length - inputs.length <
label.baseStackHeight) {
_reportError("Underflowing base stack of target label");
}
final List<ValueType> stack = inputs.length <= pushed.length
? pushed.sublist(pushed.length - inputs.length)
: [
..._stackTypes.sublist(
_stackTypes.length - popped + pushed.length - inputs.length,
_stackTypes.length - popped),
...pushed
];
_checkStackTypes(inputs, stack);
return true;
}
bool _verifyStartOfBlock(Label label, {required List<Object> trace}) {
return _debugTrace(
["$label:", ...trace, FunctionType(label.inputs, label.outputs)],
reachableAfter: reachable, indentAfter: 1);
}
bool _verifyEndOfBlock(List<ValueType> outputs,
{required List<Object> trace,
required bool reachableAfter,
required bool reindent}) {
final Label label = _topOfLabelStack;
if (reachable) {
final int expectedHeight = label.baseStackHeight + label.outputs.length;
if (_stackTypes.length != expectedHeight) {
_reportError("Incorrect stack height at end of block"
" (expected $expectedHeight, actual ${_stackTypes.length})");
}
_checkStackTypes(label.outputs);
}
if (label.reachable) {
assert(_stackTypes.length >= label.baseStackHeight);
_stackTypes.length = label.baseStackHeight;
_stackTypes.addAll(outputs);
}
return _debugTrace([if (label.hasOrdinal) "$label:", ...trace],
reachableAfter: reachableAfter,
indentBefore: -1,
indentAfter: reindent ? 1 : 0);
}
// Meta
/// Emit a comment.
void comment(String text) {
assert(_comment(text));
}
// Control instructions
/// Emit an `unreachable` instruction.
void unreachable() {
assert(_verifyTypes(const [], const [],
trace: const ['unreachable'], reachableAfter: false));
_reachable = false;
writeByte(0x00);
}
/// Emit a `nop` instruction.
void nop() {
assert(_verifyTypes(const [], const [], trace: const ['nop']));
writeByte(0x01);
}
Label _beginBlock(int encoding, Label label, {required List<Object> trace}) {
assert(_verifyTypes(label.inputs, label.inputs));
label.ordinal = ++_labelCount;
label.depth = _labelStack.length;
label.baseStackHeight = _stackTypes.length - label.inputs.length;
label.reachable = reachable;
_labelStack.add(label);
assert(_verifyStartOfBlock(label, trace: trace));
writeByte(encoding);
if (label.inputs.isEmpty && label.outputs.isEmpty) {
writeByte(0x40);
} else if (label.inputs.isEmpty && label.outputs.length == 1) {
write(label.outputs.single);
} else {
final type = module.addFunctionType(label.inputs, label.outputs);
writeSigned(type.index);
}
return label;
}
/// Emit a `block` instruction.
/// Branching to the returned label will branch to the matching `end`.
Label block(
[List<ValueType> inputs = const [], List<ValueType> outputs = const []]) {
return _beginBlock(0x02, Block(inputs, outputs), trace: const ['block']);
}
/// Emit a `loop` instruction.
/// Branching to the returned label will branch to the `loop`.
Label loop(
[List<ValueType> inputs = const [], List<ValueType> outputs = const []]) {
return _beginBlock(0x03, Loop(inputs, outputs), trace: const ['loop']);
}
/// Emit an `if` instruction.
/// Branching to the returned label will branch to the matching `end`.
Label if_(
[List<ValueType> inputs = const [], List<ValueType> outputs = const []]) {
assert(_verifyTypes(const [NumType.i32], const []));
return _beginBlock(0x04, If(inputs, outputs), trace: const ['if']);
}
/// Emit an `else` instruction.
void else_() {
assert(_topOfLabelStack is If ||
_reportError("Unexpected 'else' (not in 'if' block)"));
final If label = _topOfLabelStack as If;
assert(!label.hasElse || _reportError("Duplicate 'else' in 'if' block"));
assert(_verifyEndOfBlock(label.inputs,
trace: const ['else'],
reachableAfter: _topOfLabelStack.reachable,
reindent: true));
label.hasElse = true;
_reachable = _topOfLabelStack.reachable;
writeByte(0x05);
}
/// Emit an `end` instruction.
void end() {
assert(_verifyEndOfBlock(_topOfLabelStack.outputs,
trace: const ['end'],
reachableAfter: _topOfLabelStack.reachable,
reindent: false));
_reachable = _topOfLabelStack.reachable;
_labelStack.removeLast();
writeByte(0x0B);
}
int _labelIndex(Label label) {
final int index = _labelStack.length - label.depth - 1;
assert(_labelStack[label.depth] == label);
return index;
}
void _writeLabel(Label label) {
writeUnsigned(_labelIndex(label));
}
/// Emit a `br` instruction.
void br(Label label) {
assert(_verifyTypes(const [], const [],
trace: ['br', label], reachableAfter: false));
assert(_verifyBranchTypes(label));
_reachable = false;
writeByte(0x0C);
_writeLabel(label);
}
/// Emit a `br_if` instruction.
void br_if(Label label) {
assert(
_verifyTypes(const [NumType.i32], const [], trace: ['br_if', label]));
assert(_verifyBranchTypes(label));
writeByte(0x0D);
_writeLabel(label);
}
/// Emit a `br_table` instruction.
void br_table(List<Label> labels, Label defaultLabel) {
assert(_verifyTypes(const [NumType.i32], const [],
trace: ['br_table', ...labels, defaultLabel], reachableAfter: false));
for (var label in labels) {
assert(_verifyBranchTypes(label));
}
assert(_verifyBranchTypes(defaultLabel));
_reachable = false;
writeByte(0x0E);
writeUnsigned(labels.length);
for (Label label in labels) {
_writeLabel(label);
}
_writeLabel(defaultLabel);
}
/// Emit a `return` instruction.
void return_() {
assert(_verifyTypes(_labelStack[0].outputs, const [],
trace: const ['return'], reachableAfter: false));
_reachable = false;
writeByte(0x0F);
}
/// Emit a `call` instruction.
void call(BaseFunction function) {
assert(_verifyTypes(function.type.inputs, function.type.outputs,
trace: ['call', function]));
writeByte(0x10);
writeUnsigned(function.index);
}
/// Emit a `call_indirect` instruction.
void call_indirect(FunctionType type, [Table? table]) {
assert(_verifyTypes([...type.inputs, NumType.i32], type.outputs,
trace: ['call_indirect', type, if (table != null) table.index]));
writeByte(0x11);
writeUnsigned(type.index);
writeUnsigned(table?.index ?? 0);
}
bool _verifyCallRef() {
if (!reachable) {
return _debugTrace(const ['call_ref'], reachableAfter: false);
}
ValueType fun = _topOfStack;
if (fun is RefType) {
var heapType = fun.heapType;
if (heapType is FunctionType) {
return _verifyTypes([...heapType.inputs, fun], heapType.outputs,
trace: const ['call_ref']);
}
}
_reportError("Expected function type, got $fun");
}
/// Emit a `call_ref` instruction.
void call_ref() {
assert(_verifyCallRef());
writeByte(0x14);
}
// Parametric instructions
/// Emit a `drop` instruction.
void drop() {
assert(_verifyTypes([_topOfStack], const [], trace: const ['drop']));
writeByte(0x1A);
}
/// Emit a `select` instruction.
void select(ValueType type) {
assert(_verifyTypes([type, type, NumType.i32], [type],
trace: ['select', type]));
if (type is NumType) {
writeByte(0x1B);
} else {
writeByte(0x1C);
writeUnsigned(1);
write(type);
}
}
// Variable instructions
/// Emit a `local.get` instruction.
void local_get(Local local) {
assert(locals[local.index] == local);
assert(_verifyTypes(const [], [local.type], trace: ['local.get', local]));
writeByte(0x20);
writeUnsigned(local.index);
}
/// Emit a `local.set` instruction.
void local_set(Local local) {
assert(locals[local.index] == local);
assert(_verifyTypes([local.type], const [], trace: ['local.set', local]));
writeByte(0x21);
writeUnsigned(local.index);
}
/// Emit a `local.tee` instruction.
void local_tee(Local local) {
assert(locals[local.index] == local);
assert(
_verifyTypes([local.type], [local.type], trace: ['local.tee', local]));
writeByte(0x22);
writeUnsigned(local.index);
}
/// Emit a `global.get` instruction.
void global_get(Global global) {
assert(_verifyTypes(const [], [global.type.type],
trace: ['global.get', global]));
writeByte(0x23);
writeUnsigned(global.index);
}
/// Emit a `global.set` instruction.
void global_set(Global global) {
assert(global.type.mutable);
assert(_verifyTypes([global.type.type], const [],
trace: ['global.set', global]));
writeByte(0x24);
writeUnsigned(global.index);
}
// Memory instructions
void _writeMemArg(Memory memory, int offset, int align) {
assert(align >= 0 && align < 64);
if (memory.index == 0) {
writeByte(align);
writeUnsigned(offset);
} else {
writeByte(64 + align);
writeUnsigned(offset);
writeUnsigned(memory.index);
}
}
/// Emit an `i32.load` instruction.
void i32_load(Memory memory, int offset, [int align = 2]) {
assert(align >= 0 && align <= 2);
assert(_verifyTypes(const [NumType.i32], const [NumType.i32],
trace: ['i32.load', memory.index, offset, align]));
writeByte(0x28);
_writeMemArg(memory, offset, align);
}
/// Emit an `i64.load` instruction.
void i64_load(Memory memory, int offset, [int align = 3]) {
assert(align >= 0 && align <= 3);
assert(_verifyTypes(const [NumType.i32], const [NumType.i64],
trace: ['i64.load', memory.index, offset, align]));
writeByte(0x29);
_writeMemArg(memory, offset, align);
}
/// Emit an `f32.load` instruction.
void f32_load(Memory memory, int offset, [int align = 2]) {
assert(align >= 0 && align <= 2);
assert(_verifyTypes(const [NumType.i32], const [NumType.f32],
trace: ['f32.load', memory.index, offset, align]));
writeByte(0x2A);
_writeMemArg(memory, offset, align);
}
/// Emit an `f64.load` instruction.
void f64_load(Memory memory, int offset, [int align = 3]) {
assert(align >= 0 && align <= 3);
assert(_verifyTypes(const [NumType.i32], const [NumType.f64],
trace: ['f64.load', memory.index, offset, align]));
writeByte(0x2B);
_writeMemArg(memory, offset, align);
}
/// Emit an `i32.load8_s` instruction.
void i32_load8_s(Memory memory, int offset, [int align = 0]) {
assert(align == 0);
assert(_verifyTypes(const [NumType.i32], const [NumType.i32],
trace: ['i32.load8_s', memory.index, offset, align]));
writeByte(0x2C);
_writeMemArg(memory, offset, align);
}
/// Emit an `i32.load8_u` instruction.
void i32_load8_u(Memory memory, int offset, [int align = 0]) {
assert(align == 0);
assert(_verifyTypes(const [NumType.i32], const [NumType.i32],
trace: ['i32.load8_u', memory.index, offset, align]));
writeByte(0x2D);
_writeMemArg(memory, offset, align);
}
/// Emit an `i32.load16_s` instruction.
void i32_load16_s(Memory memory, int offset, [int align = 1]) {
assert(align >= 0 && align <= 1);
assert(_verifyTypes(const [NumType.i32], const [NumType.i32],
trace: ['i32.load16_s', memory.index, offset, align]));
writeByte(0x2E);
_writeMemArg(memory, offset, align);
}
/// Emit an `i32.load16_u` instruction.
void i32_load16_u(Memory memory, int offset, [int align = 1]) {
assert(align >= 0 && align <= 1);
assert(_verifyTypes(const [NumType.i32], const [NumType.i32],
trace: ['i32.load16_u', memory.index, offset, align]));
writeByte(0x2F);
_writeMemArg(memory, offset, align);
}
/// Emit an `i64.load8_s` instruction.
void i64_load8_s(Memory memory, int offset, [int align = 0]) {
assert(align == 0);
assert(_verifyTypes(const [NumType.i32], const [NumType.i64],
trace: ['i64.load8_s', memory.index, offset, align]));
writeByte(0x30);
_writeMemArg(memory, offset, align);
}
/// Emit an `i64.load8_u` instruction.
void i64_load8_u(Memory memory, int offset, [int align = 0]) {
assert(align == 0);
assert(_verifyTypes(const [NumType.i32], const [NumType.i64],
trace: ['i64.load8_u', memory.index, offset, align]));
writeByte(0x31);
_writeMemArg(memory, offset, align);
}
/// Emit an `i64.load16_s` instruction.
void i64_load16_s(Memory memory, int offset, [int align = 1]) {
assert(align >= 0 && align <= 1);
assert(_verifyTypes(const [NumType.i32], const [NumType.i64],
trace: ['i64.load16_s', memory.index, offset, align]));
writeByte(0x32);
_writeMemArg(memory, offset, align);
}
/// Emit an `i64.load16_u` instruction.
void i64_load16_u(Memory memory, int offset, [int align = 1]) {
assert(align >= 0 && align <= 1);
assert(_verifyTypes(const [NumType.i32], const [NumType.i64],
trace: ['i64.load16_u', memory.index, offset, align]));
writeByte(0x33);
_writeMemArg(memory, offset, align);
}
/// Emit an `i64.load32_s` instruction.
void i64_load32_s(Memory memory, int offset, [int align = 2]) {
assert(align >= 0 && align <= 2);
assert(_verifyTypes(const [NumType.i32], const [NumType.i64],
trace: ['i64.load32_s', memory.index, offset, align]));
writeByte(0x34);
_writeMemArg(memory, offset, align);
}
/// Emit an `i64.load32_u` instruction.
void i64_load32_u(Memory memory, int offset, [int align = 2]) {
assert(align >= 0 && align <= 2);
assert(_verifyTypes(const [NumType.i32], const [NumType.i64],
trace: ['i64.load32_u', memory.index, offset, align]));
writeByte(0x35);
_writeMemArg(memory, offset, align);
}
/// Emit an `i32.store` instruction.
void i32_store(Memory memory, int offset, [int align = 2]) {
assert(align >= 0 && align <= 2);
assert(_verifyTypes(const [NumType.i32, NumType.i32], const [],
trace: ['i32.store', memory.index, offset, align]));
writeByte(0x36);
_writeMemArg(memory, offset, align);
}
/// Emit an `i64.store` instruction.
void i64_store(Memory memory, int offset, [int align = 3]) {
assert(align >= 0 && align <= 3);
assert(_verifyTypes(const [NumType.i32, NumType.i64], const [],
trace: ['i64.store', memory.index, offset, align]));
writeByte(0x37);
_writeMemArg(memory, offset, align);
}
/// Emit an `f32.store` instruction.
void f32_store(Memory memory, int offset, [int align = 2]) {
assert(align >= 0 && align <= 2);
assert(_verifyTypes(const [NumType.i32, NumType.f32], const [],
trace: ['f32.store', memory.index, offset, align]));
writeByte(0x38);
_writeMemArg(memory, offset, align);
}
/// Emit an `f64.store` instruction.
void f64_store(Memory memory, int offset, [int align = 3]) {
assert(align >= 0 && align <= 3);
assert(_verifyTypes(const [NumType.i32, NumType.f64], const [],
trace: ['f64.store', memory.index, offset, align]));
writeByte(0x39);
_writeMemArg(memory, offset, align);
}
/// Emit an `i32.store8` instruction.
void i32_store8(Memory memory, int offset, [int align = 0]) {
assert(align == 0);
assert(_verifyTypes(const [NumType.i32, NumType.i32], const [],
trace: ['i32.store8', memory.index, offset, align]));
writeByte(0x3A);
_writeMemArg(memory, offset, align);
}
/// Emit an `i32.store16` instruction.
void i32_store16(Memory memory, int offset, [int align = 1]) {
assert(align >= 0 && align <= 1);
assert(_verifyTypes(const [NumType.i32, NumType.i32], const [],
trace: ['i32.store16', memory.index, offset, align]));
writeByte(0x3B);
_writeMemArg(memory, offset, align);
}
/// Emit an `i64.store8` instruction.
void i64_store8(Memory memory, int offset, [int align = 0]) {
assert(align == 0);
assert(_verifyTypes(const [NumType.i32, NumType.i64], const [],
trace: ['i64.store8', memory.index, offset, align]));
writeByte(0x3C);
_writeMemArg(memory, offset, align);
}
/// Emit an `i64.store16` instruction.
void i64_store16(Memory memory, int offset, [int align = 1]) {
assert(align >= 0 && align <= 1);
assert(_verifyTypes(const [NumType.i32, NumType.i64], const [],
trace: ['i64.store16', memory.index, offset, align]));
writeByte(0x3D);
_writeMemArg(memory, offset, align);
}
/// Emit an `i64.store32` instruction.
void i64_store32(Memory memory, int offset, [int align = 2]) {
assert(align >= 0 && align <= 2);
assert(_verifyTypes(const [NumType.i32, NumType.i64], const [],
trace: ['i64.store32', memory.index, offset, align]));
writeByte(0x3E);
_writeMemArg(memory, offset, align);
}
/// Emit a `memory.size` instruction.
void memory_size(Memory memory) {
assert(_verifyTypes(const [], const [NumType.i32]));
writeByte(0x3F);
writeUnsigned(memory.index);
}
/// Emit a `memory.grow` instruction.
void memory_grow(Memory memory) {
assert(_verifyTypes(const [NumType.i32], const [NumType.i32]));
writeByte(0x40);
writeUnsigned(memory.index);
}
// Reference instructions
/// Emit a `ref.null` instruction.
void ref_null(HeapType heapType) {
assert(_verifyTypes(const [], [RefType(heapType, nullable: true)],
trace: ['ref.null', heapType]));
writeByte(0xD0);
write(heapType);
}
/// Emit a `ref.is_null` instruction.
void ref_is_null() {
assert(_verifyTypes(const [RefType.any()], const [NumType.i32],
trace: const ['ref.is_null']));
writeByte(0xD1);
}
/// Emit a `ref.func` instruction.
void ref_func(BaseFunction function) {
assert(_verifyTypes(const [], [RefType.def(function.type, nullable: false)],
trace: ['ref.func', function]));
writeByte(0xD2);
writeUnsigned(function.index);
}
/// Emit a `ref.as_non_null` instruction.
void ref_as_non_null() {
assert(_verifyTypes(
const [RefType.any()], [_topOfStack.withNullability(false)],
trace: const ['ref.as_non_null']));
writeByte(0xD3);
}
/// Emit a `br_on_null` instruction.
void br_on_null(Label label) {
assert(_verifyTypes(
const [RefType.any()], [_topOfStack.withNullability(false)],
trace: ['br_on_null', label]));
assert(_verifyBranchTypes(label, 1));
writeByte(0xD4);
_writeLabel(label);
}
/// Emit a `ref.eq` instruction.
void ref_eq() {
assert(_verifyTypes(const [RefType.eq(), RefType.eq()], const [NumType.i32],
trace: const ['ref.eq']));
writeByte(0xD5);
}
/// Emit a `br_on_non_null` instruction.
void br_on_non_null(Label label) {
assert(_verifyBranchTypes(label, 1, [_topOfStack.withNullability(false)]));
assert(_verifyTypes(const [RefType.any()], const [],
trace: ['br_on_non_null', label]));
writeByte(0xD6);
_writeLabel(label);
}
/// Emit a `struct.new_with_rtt` instruction.
void struct_new_with_rtt(StructType structType) {
assert(_verifyTypes(
[...structType.fields.map((f) => f.type.unpacked), Rtt(structType)],
[RefType.def(structType, nullable: false)],
trace: ['struct.new_with_rtt', structType]));
writeBytes(const [0xFB, 0x01]);
writeUnsigned(structType.index);
}
/// Emit a `struct.new_default_with_rtt` instruction.
void struct_new_default_with_rtt(StructType structType) {
assert(_verifyTypes(
[Rtt(structType)], [RefType.def(structType, nullable: false)],
trace: ['struct.new_default_with_rtt', structType]));
writeBytes(const [0xFB, 0x02]);
writeUnsigned(structType.index);
}
/// Emit a `struct.get` instruction.
void struct_get(StructType structType, int fieldIndex) {
assert(structType.fields[fieldIndex].type is ValueType);
assert(_verifyTypes([RefType.def(structType, nullable: true)],
[structType.fields[fieldIndex].type.unpacked],
trace: ['struct.get', structType, fieldIndex]));
writeBytes(const [0xFB, 0x03]);
writeUnsigned(structType.index);
writeUnsigned(fieldIndex);
}
/// Emit a `struct.get_s` instruction.
void struct_get_s(StructType structType, int fieldIndex) {
assert(structType.fields[fieldIndex].type is PackedType);
assert(_verifyTypes([RefType.def(structType, nullable: true)],
[structType.fields[fieldIndex].type.unpacked],
trace: ['struct.get_s', structType, fieldIndex]));
writeBytes(const [0xFB, 0x04]);
writeUnsigned(structType.index);
writeUnsigned(fieldIndex);
}
/// Emit a `struct.get_u` instruction.
void struct_get_u(StructType structType, int fieldIndex) {
assert(structType.fields[fieldIndex].type is PackedType);
assert(_verifyTypes([RefType.def(structType, nullable: true)],
[structType.fields[fieldIndex].type.unpacked],
trace: ['struct.get_u', structType, fieldIndex]));
writeBytes(const [0xFB, 0x05]);
writeUnsigned(structType.index);
writeUnsigned(fieldIndex);
}
/// Emit a `struct.set` instruction.
void struct_set(StructType structType, int fieldIndex) {
assert(_verifyTypes([
RefType.def(structType, nullable: true),
structType.fields[fieldIndex].type.unpacked
], const [], trace: [
'struct.set',
structType,
fieldIndex
]));
writeBytes(const [0xFB, 0x06]);
writeUnsigned(structType.index);
writeUnsigned(fieldIndex);
}
/// Emit a `struct.new` instruction.
void struct_new(StructType structType) {
assert(_verifyTypes([...structType.fields.map((f) => f.type.unpacked)],
[RefType.def(structType, nullable: false)],
trace: ['struct.new', structType]));
writeBytes(const [0xFB, 0x07]);
writeUnsigned(structType.index);
}
/// Emit a `struct.new_default` instruction.
void struct_new_default(StructType structType) {
assert(_verifyTypes(const [], [RefType.def(structType, nullable: false)],
trace: ['struct.new_default', structType]));
writeBytes(const [0xFB, 0x08]);
writeUnsigned(structType.index);
}
/// Emit an `array.new_with_rtt` instruction.
void array_new_with_rtt(ArrayType arrayType) {
assert(_verifyTypes(
[arrayType.elementType.type.unpacked, NumType.i32, Rtt(arrayType)],
[RefType.def(arrayType, nullable: false)],
trace: ['array.new_with_rtt', arrayType]));
writeBytes(const [0xFB, 0x11]);
writeUnsigned(arrayType.index);
}
/// Emit an `array.new_default_with_rtt` instruction.
void array_new_default_with_rtt(ArrayType arrayType) {
assert(_verifyTypes([NumType.i32, Rtt(arrayType)],
[RefType.def(arrayType, nullable: false)],
trace: ['array.new_default_with_rtt', arrayType]));
writeBytes(const [0xFB, 0x12]);
writeUnsigned(arrayType.index);
}
/// Emit an `array.get` instruction.
void array_get(ArrayType arrayType) {
assert(arrayType.elementType.type is ValueType);
assert(_verifyTypes([RefType.def(arrayType, nullable: true), NumType.i32],
[arrayType.elementType.type.unpacked],
trace: ['array.get', arrayType]));
writeBytes(const [0xFB, 0x13]);
writeUnsigned(arrayType.index);
}
/// Emit an `array.get_s` instruction.
void array_get_s(ArrayType arrayType) {
assert(arrayType.elementType.type is PackedType);
assert(_verifyTypes([RefType.def(arrayType, nullable: true), NumType.i32],
[arrayType.elementType.type.unpacked],
trace: ['array.get_s', arrayType]));
writeBytes(const [0xFB, 0x14]);
writeUnsigned(arrayType.index);
}
/// Emit an `array.get_u` instruction.
void array_get_u(ArrayType arrayType) {
assert(arrayType.elementType.type is PackedType);
assert(_verifyTypes([RefType.def(arrayType, nullable: true), NumType.i32],
[arrayType.elementType.type.unpacked],
trace: ['array.get_u', arrayType]));
writeBytes(const [0xFB, 0x15]);
writeUnsigned(arrayType.index);
}
/// Emit an `array.set` instruction.
void array_set(ArrayType arrayType) {
assert(_verifyTypes([
RefType.def(arrayType, nullable: true),
NumType.i32,
arrayType.elementType.type.unpacked
], const [], trace: [
'array.set',
arrayType
]));
writeBytes(const [0xFB, 0x16]);
writeUnsigned(arrayType.index);
}
/// Emit an `array.len` instruction.
void array_len(ArrayType arrayType) {
assert(_verifyTypes(
[RefType.def(arrayType, nullable: true)], const [NumType.i32],
trace: ['array.len', arrayType]));
writeBytes(const [0xFB, 0x17]);
writeUnsigned(arrayType.index);
}
/// Emit an `array.init` instruction.
void array_init(ArrayType arrayType, int length) {
ValueType elementType = arrayType.elementType.type.unpacked;
assert(_verifyTypes([...List.filled(length, elementType), Rtt(arrayType)],
[RefType.def(arrayType, nullable: false)],
trace: ['array.init', arrayType, length]));
writeBytes(const [0xFB, 0x19]);
writeUnsigned(arrayType.index);
writeUnsigned(length);
}
/// Emit an `array.init_static` instruction.
void array_init_static(ArrayType arrayType, int length) {
ValueType elementType = arrayType.elementType.type.unpacked;
assert(_verifyTypes([...List.filled(length, elementType)],
[RefType.def(arrayType, nullable: false)],
trace: ['array.init_static', arrayType, length]));
writeBytes(const [0xFB, 0x1a]);
writeUnsigned(arrayType.index);
writeUnsigned(length);
}
/// Emit an `array.new` instruction.
void array_new(ArrayType arrayType) {
assert(_verifyTypes([arrayType.elementType.type.unpacked, NumType.i32],
[RefType.def(arrayType, nullable: false)],
trace: ['array.new', arrayType]));
writeBytes(const [0xFB, 0x1b]);
writeUnsigned(arrayType.index);
}
/// Emit an `array.new_default` instruction.
void array_new_default(ArrayType arrayType) {
assert(_verifyTypes(
[NumType.i32], [RefType.def(arrayType, nullable: false)],
trace: ['array.new_default', arrayType]));
writeBytes(const [0xFB, 0x1c]);
writeUnsigned(arrayType.index);
}
/// Emit an `array.init_from_data_static` instruction.
void array_init_from_data_static(ArrayType arrayType, DataSegment data) {
assert(arrayType.elementType.type.isPrimitive);
assert(_verifyTypes(
[NumType.i32, NumType.i32], [RefType.def(arrayType, nullable: false)],
trace: ['array.init_from_data_static', arrayType, data.index]));
writeBytes(const [0xFB, 0x1d]);
writeUnsigned(arrayType.index);
writeUnsigned(data.index);
if (isGlobalInitializer) module.dataReferencedFromGlobalInitializer = true;
}
/// Emit an `array.init_from_data` instruction.
void array_init_from_data(ArrayType arrayType, DataSegment data) {
assert(arrayType.elementType.type.isPrimitive);
assert(_verifyTypes([NumType.i32, NumType.i32, Rtt(arrayType)],
[RefType.def(arrayType, nullable: false)],
trace: ['array.init_from_data', arrayType, data.index]));
writeBytes(const [0xFB, 0x1e]);
writeUnsigned(arrayType.index);
writeUnsigned(data.index);
if (isGlobalInitializer) module.dataReferencedFromGlobalInitializer = true;
}
/// Emit an `i31.new` instruction.
void i31_new() {
assert(_verifyTypes(const [NumType.i32], const [RefType.i31()],
trace: const ['i31.new']));
writeBytes(const [0xFB, 0x20]);
}
/// Emit an `i31.get_s` instruction.
void i31_get_s() {
assert(_verifyTypes(const [RefType.i31()], const [NumType.i32],
trace: const ['i31.get_s']));
writeBytes(const [0xFB, 0x21]);
}
/// Emit an `i31.get_u` instruction.
void i31_get_u() {
assert(_verifyTypes(const [RefType.i31()], const [NumType.i32],
trace: const ['i31.get_u']));
writeBytes(const [0xFB, 0x22]);
}
/// Emit an `rtt.canon` instruction.
void rtt_canon(DefType defType) {
assert(_verifyTypes(const [], [Rtt(defType, defType.depth)],
trace: ['rtt.canon', defType]));
writeBytes(const [0xFB, 0x30]);
writeSigned(defType.index);
}
bool _verifyRttSub(DefType subType) {
if (!reachable) {
return _debugTrace(['rtt.sub', subType], reachableAfter: false);
}
final ValueType input = _topOfStack;
if (input is! Rtt) _reportError("Expected rtt, but stack contained $input");
final int? depth = input.depth;
if (depth == null) _reportError("Expected rtt with known depth");
final DefType superType = input.defType;
if (!subType.isSubtypeOf(superType)) {
_reportError("Expected supertype of $subType, but got $superType");
}
return _verifyTypes([input], [Rtt(subType, depth + 1)],
trace: ['rtt.sub', subType]);
}
/// Emit an `rtt.sub` instruction.
void rtt_sub(DefType defType) {
assert(_verifyRttSub(defType));
writeBytes(const [0xFB, 0x31]);
writeSigned(defType.index);
}
bool _verifyCast(List<ValueType> Function(List<ValueType>) outputsFun,
{List<Object>? trace}) {
if (!reachable) {
return _debugTrace(trace, reachableAfter: false);
}
final stack = _stack(2);
final ValueType value = stack[0];
final ValueType rtt = stack[1];
if (rtt is! Rtt ||
!value.isSubtypeOf(const RefType.data(nullable: true)) &&
!value.isSubtypeOf(const RefType.func(nullable: true))) {
_reportError("Expected [data or func, rtt], but stack contained $stack");
}
return _verifyTypesFun(stack, outputsFun, trace: trace);
}
/// Emit a `ref.test` instruction.
void ref_test() {
assert(_verifyCast((_) => const [NumType.i32], trace: const ['ref.test']));
writeBytes(const [0xFB, 0x40]);
}
/// Emit a `ref.cast` instruction.
void ref_cast() {
assert(_verifyCast(
(inputs) => [
RefType.def((inputs[1] as Rtt).defType,
nullable: inputs[0].nullable)
],
trace: const ['ref.cast']));
writeBytes(const [0xFB, 0x41]);
}
/// Emit a `br_on_cast` instruction.
void br_on_cast(Label label) {
late final DefType targetType;
assert(_verifyCast((inputs) {
targetType = (inputs[1] as Rtt).defType;
return [inputs[0]];
}, trace: ['br_on_cast', label]));
assert(_verifyBranchTypes(
label, 1, [RefType.def(targetType, nullable: false)]));
writeBytes(const [0xFB, 0x42]);
_writeLabel(label);
}
/// Emit a `br_on_cast_fail` instruction.
void br_on_cast_fail(Label label) {
assert(_verifyBranchTypes(label, 1, [_topOfStack]));
assert(_verifyCast(
(inputs) => [RefType.def((inputs[1] as Rtt).defType, nullable: false)],
trace: ['br_on_cast_fail', label]));
writeBytes(const [0xFB, 0x43]);
_writeLabel(label);
}
bool _verifyCastStatic(List<ValueType> Function(List<ValueType>) outputsFun,
{List<Object>? trace}) {
if (!reachable) {
return _debugTrace(trace, reachableAfter: false);
}
final ValueType value = _topOfStack;
if (!value.isSubtypeOf(const RefType.data(nullable: true)) &&
!value.isSubtypeOf(const RefType.func(nullable: true))) {
_reportError("Expected [data or func], but stack contained [$value]");
}
return _verifyTypesFun([value], outputsFun, trace: trace);
}
/// Emit a `ref.test_static` instruction.
void ref_test_static(DefType targetType) {
assert(_verifyCastStatic((_) => const [NumType.i32],
trace: ['ref.test_static', targetType]));
writeBytes(const [0xFB, 0x44]);
writeSigned(targetType.index);
}
/// Emit a `ref.cast_static` instruction.
void ref_cast_static(DefType targetType) {
assert(_verifyCastStatic(
(inputs) => [RefType.def(targetType, nullable: inputs[0].nullable)],
trace: ['ref.cast_static', targetType]));
writeBytes(const [0xFB, 0x45]);
writeSigned(targetType.index);
}
/// Emit a `br_on_cast_static` instruction.
void br_on_cast_static(Label label, DefType targetType) {
assert(_verifyCastStatic((inputs) {
return [inputs[0]];
}, trace: ['br_on_cast_static', label, targetType]));
assert(_verifyBranchTypes(
label, 1, [RefType.def(targetType, nullable: false)]));
writeBytes(const [0xFB, 0x46]);
_writeLabel(label);
writeSigned(targetType.index);
}
/// Emit a `br_on_cast_static_fail` instruction.
void br_on_cast_static_fail(Label label, DefType targetType) {
assert(_verifyBranchTypes(label, 1, [_topOfStack]));
assert(_verifyCast((inputs) => [RefType.def(targetType, nullable: false)],
trace: ['br_on_cast_static_fail', label, targetType]));
writeBytes(const [0xFB, 0x47]);
_writeLabel(label);
writeSigned(targetType.index);
}
/// Emit a `ref.is_func` instruction.
void ref_is_func() {
assert(_verifyTypes(const [RefType.any()], const [NumType.i32],
trace: const ['ref.is_func']));
writeBytes(const [0xFB, 0x50]);
}
/// Emit a `ref.is_data` instruction.
void ref_is_data() {
assert(_verifyTypes(const [RefType.any()], const [NumType.i32],
trace: const ['ref.is_data']));
writeBytes(const [0xFB, 0x51]);
}
/// Emit a `ref.is_i31` instruction.
void ref_is_i31() {
assert(_verifyTypes(const [RefType.any()], const [NumType.i32],
trace: const ['ref.is_i31']));
writeBytes(const [0xFB, 0x52]);
}
/// Emit a `ref.as_func` instruction.
void ref_as_func() {
assert(_verifyTypes(
const [RefType.any()], const [RefType.func(nullable: false)],
trace: const ['ref.as_func']));
writeBytes(const [0xFB, 0x58]);
}
/// Emit a `ref.as_data` instruction.
void ref_as_data() {
assert(_verifyTypes(
const [RefType.any()], const [RefType.data(nullable: false)],
trace: const ['ref.as_data']));
writeBytes(const [0xFB, 0x59]);
}
/// Emit a `ref.as_i31` instruction.
void ref_as_i31() {
assert(_verifyTypes(
const [RefType.any()], const [RefType.i31(nullable: false)],
trace: const ['ref.as_i31']));
writeBytes(const [0xFB, 0x5A]);
}
/// Emit a `br_on_func` instruction.
void br_on_func(Label label) {
assert(_verifyTypes(const [RefType.any()], [_topOfStack],
trace: ['br_on_func', label]));
assert(_verifyBranchTypes(label, 1, const [RefType.func(nullable: false)]));
writeBytes(const [0xFB, 0x60]);
_writeLabel(label);
}
/// Emit a `br_on_data` instruction.
void br_on_data(Label label) {
assert(_verifyTypes(const [RefType.any()], [_topOfStack],
trace: ['br_on_data', label]));
assert(_verifyBranchTypes(label, 1, const [RefType.data(nullable: false)]));
writeBytes(const [0xFB, 0x61]);
_writeLabel(label);
}
/// Emit a `br_on_i31` instruction.
void br_on_i31(Label label) {
assert(_verifyTypes(const [RefType.any()], [_topOfStack],
trace: ['br_on_i31', label]));
assert(_verifyBranchTypes(label, 1, const [RefType.i31(nullable: false)]));
writeBytes(const [0xFB, 0x62]);
_writeLabel(label);
}
/// Emit a `br_on_non_func` instruction.
void br_on_non_func(Label label) {
assert(_verifyBranchTypes(label, 1, [_topOfStack]));
assert(_verifyTypes(
const [RefType.any()], const [RefType.func(nullable: false)],
trace: ['br_on_non_func', label]));
writeBytes(const [0xFB, 0x63]);
_writeLabel(label);
}
/// Emit a `br_on_non_data` instruction.
void br_on_non_data(Label label) {
assert(_verifyBranchTypes(label, 1, [_topOfStack]));
assert(_verifyTypes(
const [RefType.any()], const [RefType.data(nullable: false)],
trace: ['br_on_non_data', label]));
writeBytes(const [0xFB, 0x64]);
_writeLabel(label);
}
/// Emit a `br_on_non_i31` instruction.
void br_on_non_i31(Label label) {
assert(_verifyBranchTypes(label, 1, [_topOfStack]));
assert(_verifyTypes(
const [RefType.any()], const [RefType.i31(nullable: false)],
trace: ['br_on_non_i31', label]));
writeBytes(const [0xFB, 0x65]);
_writeLabel(label);
}
// Numeric instructions
/// Emit an `i32.const` instruction.
void i32_const(int value) {
assert(_verifyTypes(const [], const [NumType.i32],
trace: ['i32.const', value]));
assert(-1 << 31 <= value && value < 1 << 31);
writeByte(0x41);
writeSigned(value);
}
/// Emit an `i64.const` instruction.
void i64_const(int value) {
assert(_verifyTypes(const [], const [NumType.i64],
trace: ['i64.const', value]));
writeByte(0x42);
writeSigned(value);
}
/// Emit an `f32.const` instruction.
void f32_const(double value) {
assert(_verifyTypes(const [], const [NumType.f32],
trace: ['f32.const', value]));
writeByte(0x43);
writeF32(value);
}
/// Emit an `f64.const` instruction.
void f64_const(double value) {
assert(_verifyTypes(const [], const [NumType.f64],
trace: ['f64.const', value]));
writeByte(0x44);
writeF64(value);
}
/// Emit an `i32.eqz` instruction.
void i32_eqz() {
assert(_verifyTypes(const [NumType.i32], const [NumType.i32],
trace: const ['i32.eqz']));
writeByte(0x45);
}
/// Emit an `i32.eq` instruction.
void i32_eq() {
assert(_verifyTypes(const [NumType.i32, NumType.i32], const [NumType.i32],
trace: const ['i32.eq']));
writeByte(0x46);
}
/// Emit an `i32.ne` instruction.
void i32_ne() {
assert(_verifyTypes(const [NumType.i32, NumType.i32], const [NumType.i32],
trace: const ['i32.ne']));
writeByte(0x47);
}
/// Emit an `i32.lt_s` instruction.
void i32_lt_s() {
assert(_verifyTypes(const [NumType.i32, NumType.i32], const [NumType.i32],
trace: const ['i32.lt_s']));
writeByte(0x48);
}
/// Emit an `i32.lt_u` instruction.
void i32_lt_u() {
assert(_verifyTypes(const [NumType.i32, NumType.i32], const [NumType.i32],
trace: const ['i32.lt_u']));
writeByte(0x49);
}
/// Emit an `i32.gt_s` instruction.
void i32_gt_s() {
assert(_verifyTypes(const [NumType.i32, NumType.i32], const [NumType.i32],
trace: const ['i32.gt_s']));
writeByte(0x4A);
}
/// Emit an `i32.gt_u` instruction.
void i32_gt_u() {
assert(_verifyTypes(const [NumType.i32, NumType.i32], const [NumType.i32],
trace: const ['i32.gt_u']));
writeByte(0x4B);
}
/// Emit an `i32.le_s` instruction.
void i32_le_s() {
assert(_verifyTypes(const [NumType.i32, NumType.i32], const [NumType.i32],
trace: const ['i32.le_s']));
writeByte(0x4C);
}
/// Emit an `i32.le_u` instruction.
void i32_le_u() {
assert(_verifyTypes(const [NumType.i32, NumType.i32], const [NumType.i32],
trace: const ['i32.le_u']));
writeByte(0x4D);
}
/// Emit an `i32.ge_s` instruction.
void i32_ge_s() {
assert(_verifyTypes(const [NumType.i32, NumType.i32], const [NumType.i32],
trace: const ['i32.ge_s']));
writeByte(0x4E);
}
/// Emit an `i32.ge_u` instruction.
void i32_ge_u() {
assert(_verifyTypes(const [NumType.i32, NumType.i32], const [NumType.i32],
trace: const ['i32.ge_u']));
writeByte(0x4F);
}
/// Emit an `i64.eqz` instruction.
void i64_eqz() {
assert(_verifyTypes(const [NumType.i64], const [NumType.i32],
trace: const ['i64.eqz']));
writeByte(0x50);
}
/// Emit an `i64.eq` instruction.
void i64_eq() {
assert(_verifyTypes(const [NumType.i64, NumType.i64], const [NumType.i32],
trace: const ['i64.eq']));
writeByte(0x51);
}
/// Emit an `i64.ne` instruction.
void i64_ne() {
assert(_verifyTypes(const [NumType.i64, NumType.i64], const [NumType.i32],
trace: const ['i64.ne']));
writeByte(0x52);
}
/// Emit an `i64.lt_s` instruction.
void i64_lt_s() {
assert(_verifyTypes(const [NumType.i64, NumType.i64], const [NumType.i32],
trace: const ['i64.lt_s']));
writeByte(0x53);
}
/// Emit an `i64.lt_u` instruction.
void i64_lt_u() {
assert(_verifyTypes(const [NumType.i64, NumType.i64], const [NumType.i32],
trace: const ['i64.lt_u']));
writeByte(0x54);
}
/// Emit an `i64.gt_s` instruction.
void i64_gt_s() {
assert(_verifyTypes(const [NumType.i64, NumType.i64], const [NumType.i32],
trace: const ['i64.gt_s']));
writeByte(0x55);
}
/// Emit an `i64.gt_u` instruction.
void i64_gt_u() {
assert(_verifyTypes(const [NumType.i64, NumType.i64], const [NumType.i32],
trace: const ['i64.gt_u']));
writeByte(0x56);
}
/// Emit an `i64.le_s` instruction.
void i64_le_s() {
assert(_verifyTypes(const [NumType.i64, NumType.i64], const [NumType.i32],
trace: const ['i64.le_s']));
writeByte(0x57);
}
/// Emit an `i64.le_u` instruction.
void i64_le_u() {
assert(_verifyTypes(const [NumType.i64, NumType.i64], const [NumType.i32],
trace: const ['i64.le_u']));
writeByte(0x58);
}
/// Emit an `i64.ge_s` instruction.
void i64_ge_s() {
assert(_verifyTypes(const [NumType.i64, NumType.i64], const [NumType.i32],
trace: const ['i64.ge_s']));
writeByte(0x59);
}
/// Emit an `i64.ge_u` instruction.
void i64_ge_u() {
assert(_verifyTypes(const [NumType.i64, NumType.i64], const [NumType.i32],
trace: const ['i64.ge_u']));
writeByte(0x5A);
}
/// Emit an `f32.eq` instruction.
void f32_eq() {
assert(_verifyTypes(const [NumType.f32, NumType.f32], const [NumType.i32],
trace: const ['f32.eq']));
writeByte(0x5B);
}
/// Emit an `f32.ne` instruction.
void f32_ne() {
assert(_verifyTypes(const [NumType.f32, NumType.f32], const [NumType.i32],
trace: const ['f32.ne']));
writeByte(0x5C);
}
/// Emit an `f32.lt` instruction.
void f32_lt() {
assert(_verifyTypes(const [NumType.f32, NumType.f32], const [NumType.i32],
trace: const ['f32.lt']));
writeByte(0x5D);
}
/// Emit an `f32.gt` instruction.
void f32_gt() {
assert(_verifyTypes(const [NumType.f32, NumType.f32], const [NumType.i32],
trace: const ['f32.gt']));
writeByte(0x5E);
}
/// Emit an `f32.le` instruction.
void f32_le() {
assert(_verifyTypes(const [NumType.f32, NumType.f32], const [NumType.i32],
trace: const ['f32.le']));
writeByte(0x5F);
}
/// Emit an `f32.ge` instruction.
void f32_ge() {
assert(_verifyTypes(const [NumType.f32, NumType.f32], const [NumType.i32],
trace: const ['f32.ge']));
writeByte(0x60);
}
/// Emit an `f64.eq` instruction.
void f64_eq() {
assert(_verifyTypes(const [NumType.f64, NumType.f64], const [NumType.i32],
trace: const ['f64.eq']));
writeByte(0x61);
}
/// Emit an `f64.ne` instruction.
void f64_ne() {
assert(_verifyTypes(const [NumType.f64, NumType.f64], const [NumType.i32],
trace: const ['f64.ne']));
writeByte(0x62);
}
/// Emit an `f64.lt` instruction.
void f64_lt() {
assert(_verifyTypes(const [NumType.f64, NumType.f64], const [NumType.i32],
trace: const ['f64.lt']));
writeByte(0x63);
}
/// Emit an `f64.gt` instruction.
void f64_gt() {
assert(_verifyTypes(const [NumType.f64, NumType.f64], const [NumType.i32],
trace: const ['f64.gt']));
writeByte(0x64);
}
/// Emit an `f64.le` instruction.
void f64_le() {
assert(_verifyTypes(const [NumType.f64, NumType.f64], const [NumType.i32],
trace: const ['f64.le']));
writeByte(0x65);
}
/// Emit an `f64.ge` instruction.
void f64_ge() {
assert(_verifyTypes(const [NumType.f64, NumType.f64], const [NumType.i32],
trace: const ['f64.ge']));
writeByte(0x66);
}
/// Emit an `i32.clz` instruction.
void i32_clz() {
assert(_verifyTypes(const [NumType.i32], const [NumType.i32],
trace: const ['i32.clz']));
writeByte(0x67);
}
/// Emit an `i32.ctz` instruction.
void i32_ctz() {
assert(_verifyTypes(const [NumType.i32], const [NumType.i32],
trace: const ['i32.ctz']));
writeByte(0x68);
}
/// Emit an `i32.popcnt` instruction.
void i32_popcnt() {
assert(_verifyTypes(const [NumType.i32], const [NumType.i32],
trace: const ['i32.popcnt']));
writeByte(0x69);
}
/// Emit an `i32.add` instruction.
void i32_add() {
assert(_verifyTypes(const [NumType.i32, NumType.i32], const [NumType.i32],
trace: const ['i32.add']));
writeByte(0x6A);
}
/// Emit an `i32.sub` instruction.
void i32_sub() {
assert(_verifyTypes(const [NumType.i32, NumType.i32], const [NumType.i32],
trace: const ['i32.sub']));
writeByte(0x6B);
}
/// Emit an `i32.mul` instruction.
void i32_mul() {
assert(_verifyTypes(const [NumType.i32, NumType.i32], const [NumType.i32],
trace: const ['i32.mul']));
writeByte(0x6C);
}
/// Emit an `i32.div_s` instruction.
void i32_div_s() {
assert(_verifyTypes(const [NumType.i32, NumType.i32], const [NumType.i32],
trace: const ['i32.div_s']));
writeByte(0x6D);
}
/// Emit an `i32.div_u` instruction.
void i32_div_u() {
assert(_verifyTypes(const [NumType.i32, NumType.i32], const [NumType.i32],
trace: const ['i32.div_u']));
writeByte(0x6E);
}
/// Emit an `i32.rem_s` instruction.
void i32_rem_s() {
assert(_verifyTypes(const [NumType.i32, NumType.i32], const [NumType.i32],
trace: const ['i32.rem_s']));
writeByte(0x6F);
}
/// Emit an `i32.rem_u` instruction.
void i32_rem_u() {
assert(_verifyTypes(const [NumType.i32, NumType.i32], const [NumType.i32],
trace: const ['i32.rem_u']));
writeByte(0x70);
}
/// Emit an `i32.and` instruction.
void i32_and() {
assert(_verifyTypes(const [NumType.i32, NumType.i32], const [NumType.i32],
trace: const ['i32.and']));
writeByte(0x71);
}
/// Emit an `i32.or` instruction.
void i32_or() {
assert(_verifyTypes(const [NumType.i32, NumType.i32], const [NumType.i32],
trace: const ['i32.or']));
writeByte(0x72);
}
/// Emit an `i32.xor` instruction.
void i32_xor() {
assert(_verifyTypes(const [NumType.i32, NumType.i32], const [NumType.i32],
trace: const ['i32.xor']));
writeByte(0x73);
}
/// Emit an `i32.shl` instruction.
void i32_shl() {
assert(_verifyTypes(const [NumType.i32, NumType.i32], const [NumType.i32],
trace: const ['i32.shl']));
writeByte(0x74);
}
/// Emit an `i32.shr_s` instruction.
void i32_shr_s() {
assert(_verifyTypes(const [NumType.i32, NumType.i32], const [NumType.i32],
trace: const ['i32.shr_s']));
writeByte(0x75);
}
/// Emit an `i32.shr_u` instruction.
void i32_shr_u() {
assert(_verifyTypes(const [NumType.i32, NumType.i32], const [NumType.i32],
trace: const ['i32.shr_u']));
writeByte(0x76);
}
/// Emit an `i32.rotl` instruction.
void i32_rotl() {
assert(_verifyTypes(const [NumType.i32, NumType.i32], const [NumType.i32],
trace: const ['i32.rotl']));
writeByte(0x77);
}
/// Emit an `i32.rotr` instruction.
void i32_rotr() {
assert(_verifyTypes(const [NumType.i32, NumType.i32], const [NumType.i32],
trace: const ['i32.rotr']));
writeByte(0x78);
}
/// Emit an `i64.clz` instruction.
void i64_clz() {
assert(_verifyTypes(const [NumType.i64], const [NumType.i64],
trace: const ['i64.clz']));
writeByte(0x79);
}
/// Emit an `i64.ctz` instruction.
void i64_ctz() {
assert(_verifyTypes(const [NumType.i64], const [NumType.i64],
trace: const ['i64.ctz']));
writeByte(0x7A);
}
/// Emit an `i64.popcnt` instruction.
void i64_popcnt() {
assert(_verifyTypes(const [NumType.i64], const [NumType.i64],
trace: const ['i64.popcnt']));
writeByte(0x7B);
}
/// Emit an `i64.add` instruction.
void i64_add() {
assert(_verifyTypes(const [NumType.i64, NumType.i64], const [NumType.i64],
trace: const ['i64.add']));
writeByte(0x7C);
}
/// Emit an `i64.sub` instruction.
void i64_sub() {
assert(_verifyTypes(const [NumType.i64, NumType.i64], const [NumType.i64],
trace: const ['i64.sub']));
writeByte(0x7D);
}
/// Emit an `i64.mul` instruction.
void i64_mul() {
assert(_verifyTypes(const [NumType.i64, NumType.i64], const [NumType.i64],
trace: const ['i64.mul']));
writeByte(0x7E);
}
/// Emit an `i64.div_s` instruction.
void i64_div_s() {
assert(_verifyTypes(const [NumType.i64, NumType.i64], const [NumType.i64],
trace: const ['i64.div_s']));
writeByte(0x7F);
}
/// Emit an `i64.div_u` instruction.
void i64_div_u() {
assert(_verifyTypes(const [NumType.i64, NumType.i64], const [NumType.i64],
trace: const ['i64.div_u']));
writeByte(0x80);
}
/// Emit an `i64.rem_s` instruction.
void i64_rem_s() {
assert(_verifyTypes(const [NumType.i64, NumType.i64], const [NumType.i64],
trace: const ['i64.rem_s']));
writeByte(0x81);
}
/// Emit an `i64.rem_u` instruction.
void i64_rem_u() {
assert(_verifyTypes(const [NumType.i64, NumType.i64], const [NumType.i64],
trace: const ['i64.rem_u']));
writeByte(0x82);
}
/// Emit an `i64.and` instruction.
void i64_and() {
assert(_verifyTypes(const [NumType.i64, NumType.i64], const [NumType.i64],
trace: const ['i64.and']));
writeByte(0x83);
}
/// Emit an `i64.or` instruction.
void i64_or() {
assert(_verifyTypes(const [NumType.i64, NumType.i64], const [NumType.i64],
trace: const ['i64.or']));
writeByte(0x84);
}
/// Emit an `i64.xor` instruction.
void i64_xor() {
assert(_verifyTypes(const [NumType.i64, NumType.i64], const [NumType.i64],
trace: const ['i64.xor']));
writeByte(0x85);
}
/// Emit an `i64.shl` instruction.
void i64_shl() {
assert(_verifyTypes(const [NumType.i64, NumType.i64], const [NumType.i64],
trace: const ['i64.shl']));
writeByte(0x86);
}
/// Emit an `i64.shr_s` instruction.
void i64_shr_s() {
assert(_verifyTypes(const [NumType.i64, NumType.i64], const [NumType.i64],
trace: const ['i64.shr_s']));
writeByte(0x87);
}
/// Emit an `i64.shr_u` instruction.
void i64_shr_u() {
assert(_verifyTypes(const [NumType.i64, NumType.i64], const [NumType.i64],
trace: const ['i64.shr_u']));
writeByte(0x88);
}
/// Emit an `i64.rotl` instruction.
void i64_rotl() {
assert(_verifyTypes(const [NumType.i64, NumType.i64], const [NumType.i64],
trace: const ['i64.rotl']));
writeByte(0x89);
}
/// Emit an `i64.rotr` instruction.
void i64_rotr() {
assert(_verifyTypes(const [NumType.i64, NumType.i64], const [NumType.i64],
trace: const ['i64.rotr']));
writeByte(0x8A);
}
/// Emit an `f32.abs` instruction.
void f32_abs() {
assert(_verifyTypes(const [NumType.f32], const [NumType.f32],
trace: const ['f32.abs']));
writeByte(0x8B);
}
/// Emit an `f32.neg` instruction.
void f32_neg() {
assert(_verifyTypes(const [NumType.f32], const [NumType.f32],
trace: const ['f32.neg']));
writeByte(0x8C);
}
/// Emit an `f32.ceil` instruction.
void f32_ceil() {
assert(_verifyTypes(const [NumType.f32], const [NumType.f32],
trace: const ['f32.ceil']));
writeByte(0x8D);
}
/// Emit an `f32.floor` instruction.
void f32_floor() {
assert(_verifyTypes(const [NumType.f32], const [NumType.f32],
trace: const ['f32.floor']));
writeByte(0x8E);
}
/// Emit an `f32.trunc` instruction.
void f32_trunc() {
assert(_verifyTypes(const [NumType.f32], const [NumType.f32],
trace: const ['f32.trunc']));
writeByte(0x8F);
}
/// Emit an `f32.nearest` instruction.
void f32_nearest() {
assert(_verifyTypes(const [NumType.f32], const [NumType.f32],
trace: const ['f32.nearest']));
writeByte(0x90);
}
/// Emit an `f32.sqrt` instruction.
void f32_sqrt() {
assert(_verifyTypes(const [NumType.f32], const [NumType.f32],
trace: const ['f32.sqrt']));
writeByte(0x91);
}
/// Emit an `f32.add` instruction.
void f32_add() {
assert(_verifyTypes(const [NumType.f32, NumType.f32], const [NumType.f32],
trace: const ['f32.add']));
writeByte(0x92);
}
/// Emit an `f32.sub` instruction.
void f32_sub() {
assert(_verifyTypes(const [NumType.f32, NumType.f32], const [NumType.f32],
trace: const ['f32.sub']));
writeByte(0x93);
}
/// Emit an `f32.mul` instruction.
void f32_mul() {
assert(_verifyTypes(const [NumType.f32, NumType.f32], const [NumType.f32],
trace: const ['f32.mul']));
writeByte(0x94);
}
/// Emit an `f32.div` instruction.
void f32_div() {
assert(_verifyTypes(const [NumType.f32, NumType.f32], const [NumType.f32],
trace: const ['f32.div']));
writeByte(0x95);
}
/// Emit an `f32.min` instruction.
void f32_min() {
assert(_verifyTypes(const [NumType.f32, NumType.f32], const [NumType.f32],
trace: const ['f32.min']));
writeByte(0x96);
}
/// Emit an `f32.max` instruction.
void f32_max() {
assert(_verifyTypes(const [NumType.f32, NumType.f32], const [NumType.f32],
trace: const ['f32.max']));
writeByte(0x97);
}
/// Emit an `f32.copysign` instruction.
void f32_copysign() {
assert(_verifyTypes(const [NumType.f32, NumType.f32], const [NumType.f32],
trace: const ['f32.copysign']));
writeByte(0x98);
}
/// Emit an `f64.abs` instruction.
void f64_abs() {
assert(_verifyTypes(const [NumType.f64], const [NumType.f64],
trace: const ['f64.abs']));
writeByte(0x99);
}
/// Emit an `f64.neg` instruction.
void f64_neg() {
assert(_verifyTypes(const [NumType.f64], const [NumType.f64],
trace: const ['f64.neg']));
writeByte(0x9A);
}
/// Emit an `f64.ceil` instruction.
void f64_ceil() {
assert(_verifyTypes(const [NumType.f64], const [NumType.f64],
trace: const ['f64.ceil']));
writeByte(0x9B);
}
/// Emit an `f64.floor` instruction.
void f64_floor() {
assert(_verifyTypes(const [NumType.f64], const [NumType.f64],
trace: const ['f64.floor']));
writeByte(0x9C);
}
/// Emit an `f64.trunc` instruction.
void f64_trunc() {
assert(_verifyTypes(const [NumType.f64], const [NumType.f64],
trace: const ['f64.trunc']));
writeByte(0x9D);
}
/// Emit an `f64.nearest` instruction.
void f64_nearest() {
assert(_verifyTypes(const [NumType.f64], const [NumType.f64],
trace: const ['f64.nearest']));
writeByte(0x9E);
}
/// Emit an `f64.sqrt` instruction.
void f64_sqrt() {
assert(_verifyTypes(const [NumType.f64], const [NumType.f64],
trace: const ['f64.sqrt']));
writeByte(0x9F);
}
/// Emit an `f64.add` instruction.
void f64_add() {
assert(_verifyTypes(const [NumType.f64, NumType.f64], const [NumType.f64],
trace: const ['f64.add']));
writeByte(0xA0);
}
/// Emit an `f64.sub` instruction.
void f64_sub() {
assert(_verifyTypes(const [NumType.f64, NumType.f64], const [NumType.f64],
trace: const ['f64.sub']));
writeByte(0xA1);
}
/// Emit an `f64.mul` instruction.
void f64_mul() {
assert(_verifyTypes(const [NumType.f64, NumType.f64], const [NumType.f64],
trace: const ['f64.mul']));
writeByte(0xA2);
}
/// Emit an `f64.div` instruction.
void f64_div() {
assert(_verifyTypes(const [NumType.f64, NumType.f64], const [NumType.f64],
trace: const ['f64.div']));
writeByte(0xA3);
}
/// Emit an `f64.min` instruction.
void f64_min() {
assert(_verifyTypes(const [NumType.f64, NumType.f64], const [NumType.f64],
trace: const ['f64.min']));
writeByte(0xA4);
}
/// Emit an `f64.max` instruction.
void f64_max() {
assert(_verifyTypes(const [NumType.f64, NumType.f64], const [NumType.f64],
trace: const ['f64.max']));
writeByte(0xA5);
}
/// Emit an `f64.copysign` instruction.
void f64_copysign() {
assert(_verifyTypes(const [NumType.f64, NumType.f64], const [NumType.f64],
trace: const ['f64.copysign']));
writeByte(0xA6);
}
/// Emit an `i32.wrap_i64` instruction.
void i32_wrap_i64() {
assert(_verifyTypes(const [NumType.i64], const [NumType.i32],
trace: const ['i32.wrap_i64']));
writeByte(0xA7);
}
/// Emit an `i32.trunc_f32_s` instruction.
void i32_trunc_f32_s() {
assert(_verifyTypes(const [NumType.f32], const [NumType.i32],
trace: const ['i32.trunc_f32_s']));
writeByte(0xA8);
}
/// Emit an `i32.trunc_f32_u` instruction.
void i32_trunc_f32_u() {
assert(_verifyTypes(const [NumType.f32], const [NumType.i32],
trace: const ['i32.trunc_f32_u']));
writeByte(0xA9);
}
/// Emit an `i32.trunc_f64_s` instruction.
void i32_trunc_f64_s() {
assert(_verifyTypes(const [NumType.f64], const [NumType.i32],
trace: const ['i32.trunc_f64_s']));
writeByte(0xAA);
}
/// Emit an `i32.trunc_f64_u` instruction.
void i32_trunc_f64_u() {
assert(_verifyTypes(const [NumType.f64], const [NumType.i32],
trace: const ['i32.trunc_f64_u']));
writeByte(0xAB);
}
/// Emit an `i64.extend_i32_s` instruction.
void i64_extend_i32_s() {
assert(_verifyTypes(const [NumType.i32], const [NumType.i64],
trace: const ['i64.extend_i32_s']));
writeByte(0xAC);
}
/// Emit an `i64.extend_i32_u` instruction.
void i64_extend_i32_u() {
assert(_verifyTypes(const [NumType.i32], const [NumType.i64],
trace: const ['i64.extend_i32_u']));
writeByte(0xAD);
}
/// Emit an `i64.trunc_f32_s` instruction.
void i64_trunc_f32_s() {
assert(_verifyTypes(const [NumType.f32], const [NumType.i64],
trace: const ['i64.trunc_f32_s']));
writeByte(0xAE);
}
/// Emit an `i64.trunc_f32_u` instruction.
void i64_trunc_f32_u() {
assert(_verifyTypes(const [NumType.f32], const [NumType.i64],
trace: const ['i64.trunc_f32_u']));
writeByte(0xAF);
}
/// Emit an `i64.trunc_f64_s` instruction.
void i64_trunc_f64_s() {
assert(_verifyTypes(const [NumType.f64], const [NumType.i64],
trace: const ['i64.trunc_f64_s']));
writeByte(0xB0);
}
/// Emit an `i64.trunc_f64_u` instruction.
void i64_trunc_f64_u() {
assert(_verifyTypes(const [NumType.f64], const [NumType.i64],
trace: const ['i64.trunc_f64_u']));
writeByte(0xB1);
}
/// Emit an `f32.convert_i32_s` instruction.
void f32_convert_i32_s() {
assert(_verifyTypes(const [NumType.i32], const [NumType.f32],
trace: const ['f32.convert_i32_s']));
writeByte(0xB2);
}
/// Emit an `f32.convert_i32_u` instruction.
void f32_convert_i32_u() {
assert(_verifyTypes(const [NumType.i32], const [NumType.f32],
trace: const ['f32.convert_i32_u']));
writeByte(0xB3);
}
/// Emit an `f32.convert_i64_s` instruction.
void f32_convert_i64_s() {
assert(_verifyTypes(const [NumType.i64], const [NumType.f32],
trace: const ['f32.convert_i64_s']));
writeByte(0xB4);
}
/// Emit an `f32.convert_i64_u` instruction.
void f32_convert_i64_u() {
assert(_verifyTypes(const [NumType.i64], const [NumType.f32],
trace: const ['f32.convert_i64_u']));
writeByte(0xB5);
}
/// Emit an `f32.demote_f64` instruction.
void f32_demote_f64() {
assert(_verifyTypes(const [NumType.f64], const [NumType.f32],
trace: const ['f32.demote_f64']));
writeByte(0xB6);
}
/// Emit an `f64.convert_i32_s` instruction.
void f64_convert_i32_s() {
assert(_verifyTypes(const [NumType.i32], const [NumType.f64],
trace: const ['f64.convert_i32_s']));
writeByte(0xB7);
}
/// Emit an `f64.convert_i32_u` instruction.
void f64_convert_i32_u() {
assert(_verifyTypes(const [NumType.i32], const [NumType.f64],
trace: const ['f64.convert_i32_u']));
writeByte(0xB8);
}
/// Emit an `f64.convert_i64_s` instruction.
void f64_convert_i64_s() {
assert(_verifyTypes(const [NumType.i64], const [NumType.f64],
trace: const ['f64.convert_i64_s']));
writeByte(0xB9);
}
/// Emit an `f64.convert_i64_u` instruction.
void f64_convert_i64_u() {
assert(_verifyTypes(const [NumType.i64], const [NumType.f64],
trace: const ['f64.convert_i64_u']));
writeByte(0xBA);
}
/// Emit an `f64.promote_f32` instruction.
void f64_promote_f32() {
assert(_verifyTypes(const [NumType.f32], const [NumType.f64],
trace: const ['f64.promote_f32']));
writeByte(0xBB);
}
/// Emit an `i32.reinterpret_f32` instruction.
void i32_reinterpret_f32() {
assert(_verifyTypes(const [NumType.f32], const [NumType.i32],
trace: const ['i32.reinterpret_f32']));
writeByte(0xBC);
}
/// Emit an `i64.reinterpret_f64` instruction.
void i64_reinterpret_f64() {
assert(_verifyTypes(const [NumType.f64], const [NumType.i64],
trace: const ['i64.reinterpret_f64']));
writeByte(0xBD);
}
/// Emit an `f32.reinterpret_i32` instruction.
void f32_reinterpret_i32() {
assert(_verifyTypes(const [NumType.i32], const [NumType.f32],
trace: const ['f32.reinterpret_i32']));
writeByte(0xBE);
}
/// Emit an `f64.reinterpret_i64` instruction.
void f64_reinterpret_i64() {
assert(_verifyTypes(const [NumType.i64], const [NumType.f64],
trace: const ['f64.reinterpret_i64']));
writeByte(0xBF);
}
/// Emit an `i32.extend8_s` instruction.
void i32_extend8_s() {
assert(_verifyTypes(const [NumType.i32], const [NumType.i32],
trace: const ['i32.extend8_s']));
writeByte(0xC0);
}
/// Emit an `i32.extend16_s` instruction.
void i32_extend16_s() {
assert(_verifyTypes(const [NumType.i32], const [NumType.i32],
trace: const ['i32.extend16_s']));
writeByte(0xC1);
}
/// Emit an `i64.extend8_s` instruction.
void i64_extend8_s() {
assert(_verifyTypes(const [NumType.i64], const [NumType.i64],
trace: const ['i64.extend8_s']));
writeByte(0xC2);
}
/// Emit an `i64.extend16_s` instruction.
void i64_extend16_s() {
assert(_verifyTypes(const [NumType.i64], const [NumType.i64],
trace: const ['i64.extend16_s']));
writeByte(0xC3);
}
/// Emit an `i64.extend32_s` instruction.
void i64_extend32_s() {
assert(_verifyTypes(const [NumType.i64], const [NumType.i64],
trace: const ['i64.extend32_s']));
writeByte(0xC4);
}
/// Emit an `i32.trunc_sat_f32_s` instruction.
void i32_trunc_sat_f32_s() {
assert(_verifyTypes(const [NumType.f32], const [NumType.i32],
trace: const ['i32.trunc_sat_f32_s']));
writeBytes(const [0xFC, 0x00]);
}
/// Emit an `i32.trunc_sat_f32_u` instruction.
void i32_trunc_sat_f32_u() {
assert(_verifyTypes(const [NumType.f32], const [NumType.i32],
trace: const ['i32.trunc_sat_f32_u']));
writeBytes(const [0xFC, 0x01]);
}
/// Emit an `i32.trunc_sat_f64_s` instruction.
void i32_trunc_sat_f64_s() {
assert(_verifyTypes(const [NumType.f64], const [NumType.i32],
trace: const ['i32.trunc_sat_f64_s']));
writeBytes(const [0xFC, 0x02]);
}
/// Emit an `i32.trunc_sat_f64_u` instruction.
void i32_trunc_sat_f64_u() {
assert(_verifyTypes(const [NumType.f64], const [NumType.i32],
trace: const ['i32.trunc_sat_f64_u']));
writeBytes(const [0xFC, 0x03]);
}
/// Emit an `i64.trunc_sat_f32_s` instruction.
void i64_trunc_sat_f32_s() {
assert(_verifyTypes(const [NumType.f32], const [NumType.i64],
trace: const ['i64.trunc_sat_f32_s']));
writeBytes(const [0xFC, 0x04]);
}
/// Emit an `i64.trunc_sat_f32_u` instruction.
void i64_trunc_sat_f32_u() {
assert(_verifyTypes(const [NumType.f32], const [NumType.i64],
trace: const ['i64.trunc_sat_f32_u']));
writeBytes(const [0xFC, 0x05]);
}
/// Emit an `i64.trunc_sat_f64_s` instruction.
void i64_trunc_sat_f64_s() {
assert(_verifyTypes(const [NumType.f64], const [NumType.i64],
trace: const ['i64.trunc_sat_f64_s']));
writeBytes(const [0xFC, 0x06]);
}
/// Emit an `i64.trunc_sat_f64_u` instruction.
void i64_trunc_sat_f64_u() {
assert(_verifyTypes(const [NumType.f64], const [NumType.i64],
trace: const ['i64.trunc_sat_f64_u']));
writeBytes(const [0xFC, 0x07]);
}
}