blob: 30dcf9d8e5c87bbad53f786d63c1760c0acdb8ec [file] [edit]
// Copyright (c) 2023, 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.
// ignore_for_file: non_constant_identifier_names
import 'dart:typed_data';
import '../../source_map.dart';
import '../ir/ir.dart' as ir;
import 'builder.dart';
// TODO(joshualitt): Suggested further optimizations:
// 1) Add size estimates to `_Instruction`, and then remove logic where we
// need to serialize instructions to get their size.
// 2) Emit binary directly to a filestream, instead of buffering with a
// Uint8List.
/// 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<ir.ValueType> inputs;
final List<ir.ValueType> outputs;
late final int? ordinal;
late final int depth;
late final int baseStackHeight;
late final bool reachable;
late final int localInitializationStackHeight;
Label._(this.inputs, this.outputs);
List<ir.ValueType> get targetTypes;
bool get hasOrdinal => ordinal != null;
@override
String toString() => "L$ordinal";
}
class Expression extends Label {
Expression(super.inputs, super.outputs) : super._() {
ordinal = null;
depth = 0;
baseStackHeight = 0;
reachable = true;
localInitializationStackHeight = 0;
}
@override
List<ir.ValueType> get targetTypes => outputs;
}
class Block extends Label {
Block(super.inputs, super.outputs) : super._();
@override
List<ir.ValueType> get targetTypes => outputs;
}
class Loop extends Label {
Loop(super.inputs, super.outputs) : super._();
@override
List<ir.ValueType> get targetTypes => inputs;
}
class If extends Label {
bool hasElse = false;
If(super.inputs, super.outputs) : super._();
@override
List<ir.ValueType> get targetTypes => outputs;
}
class Try extends Label {
bool hasCatch = false;
Try(super.inputs, super.outputs) : super._();
@override
List<ir.ValueType> get targetTypes => outputs;
}
class TryTable extends Label {
final List<TryTableCatch> catches;
TryTable(super.inputs, super.outputs, this.catches) : super._();
@override
List<ir.ValueType> get targetTypes => outputs;
}
abstract class TryTableCatch {
final Label label;
TryTableCatch(this.label);
ir.TryTableCatch toIr(int labelIndex);
/// Values that the catch block catches, i.e. pushes as outputs.
List<ir.ValueType> caughtValues();
}
class Catch extends TryTableCatch {
final ir.Tag tag;
Catch(this.tag, super.label);
@override
ir.TryTableCatch toIr(int labelIndex) => ir.Catch(tag, labelIndex);
@override
List<ir.ValueType> caughtValues() => tag.type.inputs;
}
class CatchRef extends TryTableCatch {
final ir.Tag tag;
CatchRef(this.tag, super.label);
@override
ir.TryTableCatch toIr(int labelIndex) => ir.CatchRef(tag, labelIndex);
@override
List<ir.ValueType> caughtValues() => <ir.ValueType>[
...tag.type.inputs,
ir.RefType.exn(nullable: false),
];
}
class CatchAll extends TryTableCatch {
CatchAll(super.label);
@override
ir.TryTableCatch toIr(int labelIndex) => ir.CatchAll(labelIndex);
@override
List<ir.ValueType> caughtValues() => <ir.ValueType>[];
}
class CatchAllRef extends TryTableCatch {
CatchAllRef(super.label);
@override
ir.TryTableCatch toIr(int labelIndex) => ir.CatchAllRef(labelIndex);
@override
List<ir.ValueType> caughtValues() => <ir.ValueType>[
ir.RefType.exn(nullable: false),
];
}
/// 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 InstructionsBuilder with Builder<ir.Instructions> {
/// The module containing these instructions.
final ModuleBuilder moduleBuilder;
/// Locals declared in this body, including parameters.
final List<ir.Local> locals = [];
/// Names of the locals in `locals`.
///
/// Most of the locals won't have names, so this is a [Map] instead of [List]
/// like [locals], with local indices as keys and names as values.
final Map<int, String> localNames = {};
/// 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;
/// Column width for the instructions.
int instructionColumnWidth = 50;
/// The maximum number of stack slots for which to print the types after each
/// instruction. When the stack is higher than this, some elements in the
/// middle of the stack are left out.
int maxStackShown = 10;
/// Mappings for the instructions in [_instructions] to their source code.
///
/// Since we add mappings as we generate instructions, this will be sorted
/// based on [SourceMapping.instructionOffset].
final List<SourceMapping>? _sourceMappings;
int _indent = 1;
final List<String> _inlinedFrames = [];
final List<String> _traceLines = [];
int _labelCount = 0;
final List<Label> _labelStack = [];
final List<ir.ValueType> _stackTypes = [];
bool _reachable = true;
/// Whether each local is currently definitely initialized.
final List<bool> _localInitialized = [];
/// Stack of currently initialized non-defaultable locals.
final List<int> _localInitializationStack = [];
/// List of instructions.
final List<ir.Instruction> _instructions = [];
/// Stored stack traces leading to the instructions for watch points.
final Map<ir.Instruction, StackTrace>? _stackTraces;
final List<_PatchableRegion> _patchPoints = [];
/// Whether the instruction block is for a Wasm constant expression.
final bool constantExpression;
/// Create a new instruction sequence.
InstructionsBuilder(
this.moduleBuilder,
List<ir.ValueType> inputs,
List<ir.ValueType> outputs, {
this.constantExpression = false,
}) : _stackTraces = moduleBuilder.watchPoints.isNotEmpty ? {} : null,
_sourceMappings = moduleBuilder.sourceMapUrl == null ? null : [] {
_labelStack.add(Expression(const [], outputs));
for (ir.ValueType paramType in inputs) {
_addParameter(paramType);
}
}
ir.Module get module => moduleBuilder.module;
/// 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 get recordSourceMaps => _sourceMappings != null;
bool get isEmpty => _instructions.isEmpty;
void collectUsedTypes(Set<ir.DefType> usedTypes) {
for (final local in locals) {
final localDefType = local.type.containedDefType;
if (localDefType != null) usedTypes.add(localDefType);
}
for (final instruction in _instructions) {
usedTypes.addAll(instruction.usedDefTypes);
for (final valueType in instruction.usedValueTypes) {
final type = valueType.containedDefType;
if (type != null) usedTypes.add(type);
}
}
for (final patch in _patchPoints) {
patch.patchBuilder.collectUsedTypes(usedTypes);
}
}
@override
ir.Instructions forceBuild() {
if (_patchPoints.isEmpty) {
return ir.Instructions(
locals,
localNames,
_instructions,
_stackTraces,
_traceLines,
_sourceMappings,
);
}
// We have to fill in the patched instructions & update stack maps.
final instructions = _instructions;
final newInstructions = <ir.Instruction>[];
final sourceMappings = _sourceMappings;
final newSourceMappings = _sourceMappings == null
? null
: <SourceMapping>[];
// The number of additional patch instructions emitted.
int shift = 0;
int ini = 0;
int smi = _sourceMappings != null ? 0 : -1;
for (final patch in _patchPoints) {
// Add all instructions before the patch starts.
while (ini < patch.start) {
newInstructions.add(instructions[ini++]);
}
// Advance current source mapping to be the last that covers the start of
// patchable region.
if (sourceMappings != null && smi < sourceMappings.length) {
while (smi < (sourceMappings.length - 1) &&
sourceMappings[smi + 1].instructionOffset <= patch.start) {
newSourceMappings!.add(sourceMappings[smi].shiftBy(shift));
smi++;
}
}
// Add patched instructions & update shift.
final replacement = patch.patchBuilder._instructions;
newInstructions.addAll(replacement);
shift += replacement.length;
}
// Add remaining instructions & shift remaining source map entries.
for (; ini < instructions.length; ini++) {
newInstructions.add(instructions[ini]);
}
if (sourceMappings != null && shift != 0) {
for (; smi < sourceMappings.length; smi++) {
newSourceMappings!.add(sourceMappings[smi].shiftBy(shift));
}
}
return ir.Instructions(
locals,
localNames,
newInstructions,
_stackTraces,
_traceLines,
newSourceMappings,
);
}
/// Marks a region in the instruction stream (defined by instructions emitted
/// by `fun`) which will be updated in the link phase via the `linkFun`.
InstructionsBuilder? createPatchableRegion(
List<ir.ValueType> inputs,
List<ir.ValueType> outputs,
) {
assert(_verifyTypes(inputs, outputs, trace: ['<patchable region>']));
if (!_reachable) return null;
final patchBuilder = InstructionsBuilder(
moduleBuilder,
inputs,
outputs,
constantExpression: constantExpression,
);
_patchPoints.add(
_PatchableRegion._PatchPoint(_instructions.length, patchBuilder),
);
return patchBuilder;
}
void _add(ir.Instruction i) {
assert(
!constantExpression || i.isConstant,
"Non-constant instruction $i added to constant expression",
);
if (!_reachable) return;
_instructions.add(i);
if (moduleBuilder.watchPoints.isNotEmpty) {
_stackTraces![i] = StackTrace.current;
}
}
ir.Local _addParameter(ir.ValueType type) {
final local = ir.Local(locals.length, type);
locals.add(local);
_localInitialized.add(true);
return local;
}
ir.Local addLocal(ir.ValueType type, {String? name}) {
if (name != null) {
final index = locals.length;
localNames[index] = name;
}
final local = ir.Local(locals.length, type);
locals.add(local);
_localInitialized.add(type.defaultable);
return local;
}
bool _initializeLocal(ir.Local local) {
if (!_localInitialized[local.index]) {
_localInitialized[local.index] = true;
_localInitializationStack.add(local.index);
}
return true;
}
bool _localIsInitialized(ir.Local local) {
return _localInitialized[local.index];
}
void _resetLocalInitialization(Label label) {
while (_localInitializationStack.length >
label.localInitializationStackHeight) {
_localInitialized[_localInitializationStack.removeLast()] = false;
}
}
bool _debugTrace(
List<Object>? trace, {
required bool reachableAfter,
int indentBefore = 0,
int indentAfter = 0,
}) {
if (traceEnabled && trace != null) {
_indent += indentBefore;
String instr = "${" " * _indent} ${trace.join(" ")}";
instr = instr.length > instructionColumnWidth - 2
? "${instr.substring(0, instructionColumnWidth - 4)}... "
: instr.padRight(instructionColumnWidth);
final int stackHeight = _stackTypes.length;
final String stack = reachableAfter
? stackHeight <= maxStackShown
? _stackTypes.join(', ')
: [
..._stackTypes.sublist(0, maxStackShown ~/ 2),
"... ${stackHeight - maxStackShown} omitted ...",
..._stackTypes.sublist(
stackHeight - (maxStackShown + 1) ~/ 2,
),
].join(', ')
: "-";
final String line = "$instr$stack\n";
_indent += indentAfter;
_traceLines.add(line);
}
return true;
}
bool _comment(String text) {
if (traceEnabled) {
final String line = "${" " * _indent} ;; $text\n";
_traceLines.add(line);
}
return true;
}
Never _reportError(String error) {
throw ValidationError(trace, error);
}
ir.ValueType get _topOfStack {
if (!_reachable) return ir.RefType.common(nullable: true);
if (_stackTypes.isEmpty) _reportError("Stack underflow");
return _stackTypes.last;
}
Label get _topOfLabelStack {
if (_labelStack.isEmpty) _reportError("Label stack underflow");
return _labelStack.last;
}
List<ir.ValueType> _stack(int n) {
if (_stackTypes.length < n) _reportError("Stack underflow");
return _stackTypes.sublist(_stackTypes.length - n);
}
List<ir.ValueType> get stack => _stackTypes;
List<ir.ValueType> _checkStackTypes(
List<ir.ValueType> inputs, [
List<ir.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<ir.ValueType> inputs,
List<ir.ValueType> outputs, {
List<Object>? trace,
bool reachableAfter = true,
}) {
return _verifyTypesFun(
inputs,
(_) => outputs,
trace: trace,
reachableAfter: reachableAfter,
);
}
bool _verifyTypesFun(
List<ir.ValueType> inputs,
List<ir.ValueType> Function(List<ir.ValueType>) outputsFun, {
List<Object>? trace,
bool reachableAfter = true,
}) {
if (!_reachable) {
return _debugTrace(trace, reachableAfter: false);
}
final int baseStackHeight = _topOfLabelStack.baseStackHeight;
if (_stackTypes.length - inputs.length < baseStackHeight) {
final String expected = inputs.join(', ');
final String got = _stackTypes.sublist(baseStackHeight).join(', ');
_reportError(
"Underflowing base stack of innermost block: expected [$expected], "
"but stack contained [$got]",
);
}
final List<ir.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<ir.ValueType> pushed = const [],
]) {
if (!_reachable) {
return true;
}
final List<ir.ValueType> inputs = label.targetTypes;
if (_stackTypes.length - popped + pushed.length - inputs.length <
label.baseStackHeight) {
_reportError("Underflowing base stack of target label");
}
final List<ir.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, ir.FunctionType(label.inputs, label.outputs)],
reachableAfter: _reachable,
indentAfter: 1,
);
}
bool _verifyEndOfBlock(
List<ir.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);
}
_resetLocalInitialization(label);
return _debugTrace(
[if (label.hasOrdinal) "$label:", ...trace],
reachableAfter: reachableAfter,
indentBefore: -1,
indentAfter: reindent ? 1 : 0,
);
}
// Source maps
/// Start mapping added instructions to the source location given in
/// arguments.
///
/// This assumes [recordSourceMaps] is `true`.
void startSourceMapping(Uri fileUri, int line, int col, String? name) {
_addSourceMapping(
SourceMapping(_instructions.length, fileUri, line, col, name),
);
}
/// Stop mapping added instructions to the last source location given in
/// [startSourceMapping].
///
/// The instructions added after this won't have a mapping in the source map.
///
/// This assumes [recordSourceMaps] is `true`.
void stopSourceMapping() {
_addSourceMapping(SourceMapping.unmapped(_instructions.length));
}
void _addSourceMapping(SourceMapping mapping) {
final sourceMappings = _sourceMappings!;
if (sourceMappings.isNotEmpty) {
final lastMapping = sourceMappings.last;
// Check if we are overriding the current source location. This can
// happen when we restore the source location after a compiling a
// sub-tree, and the next node in the AST immediately updates the source
// location. The restored location is then never used.
if (lastMapping.instructionOffset == mapping.instructionOffset) {
sourceMappings.removeLast();
sourceMappings.add(mapping);
return;
}
// Check if we the new mapping maps to the same source as the old
// mapping. This happens when we have e.g. an instance field get like
// `length`, which gets transformed by the front-end as `this.length`. In
// this case `this` and `length` will have the same source location.
if (lastMapping.sourceInfo == mapping.sourceInfo) {
return;
}
}
sourceMappings.add(mapping);
}
// Meta
/// Emit a comment.
void comment(String text) {
assert(_comment(text));
}
/// Pushes `name` to inlining stack and emit the current inlining stack as
/// a comment.
T withInlinedFrame<T>(String name, T Function() fun) {
bool assertsEnabled = false;
assert(assertsEnabled = true);
if (!assertsEnabled) {
return fun();
}
_inlinedFrames.add(name);
try {
final inliningStack = _inlinedFrames.map((p) => '[$p]').join(' ');
comment(inliningStack);
return fun();
} finally {
_inlinedFrames.removeLast();
}
}
// Control instructions
/// Emit an `unreachable` instruction.
void unreachable() {
assert(
_verifyTypes(
const [],
const [],
trace: const ['unreachable'],
reachableAfter: false,
),
);
_add(const ir.Unreachable());
_reachable = false;
}
/// Emit a `nop` instruction.
void nop() {
assert(_verifyTypes(const [], const [], trace: const ['nop']));
_add(const ir.Nop());
}
Label _pushLabel(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;
label.localInitializationStackHeight = _localInitializationStack.length;
_labelStack.add(label);
assert(_verifyStartOfBlock(label, trace: trace));
return label;
}
Label _beginBlock(
Label label,
ir.Instruction Function() noEffect,
ir.Instruction Function(ir.ValueType type) oneOutput,
ir.Instruction Function(ir.FunctionType type) function,
) {
if (label.inputs.isEmpty && label.outputs.isEmpty) {
_add(noEffect());
} else if (label.inputs.isEmpty && label.outputs.length == 1) {
_add(oneOutput(label.outputs.single));
} else {
_add(
function(
moduleBuilder.types.defineFunction(label.inputs, label.outputs),
),
);
}
return label;
}
/// Emit a `block` instruction.
/// Branching to the returned label will branch to the matching `end`.
Label block([
List<ir.ValueType> inputs = const [],
List<ir.ValueType> outputs = const [],
]) => _beginBlock(
_pushLabel(Block(inputs, outputs), trace: const ['block']),
ir.BeginNoEffectBlock.new,
ir.BeginOneOutputBlock.new,
ir.BeginFunctionBlock.new,
);
/// Emit a `loop` instruction.
/// Branching to the returned label will branch to the `loop`.
Label loop([
List<ir.ValueType> inputs = const [],
List<ir.ValueType> outputs = const [],
]) => _beginBlock(
_pushLabel(Loop(inputs, outputs), trace: const ['loop']),
ir.BeginNoEffectLoop.new,
ir.BeginOneOutputLoop.new,
ir.BeginFunctionLoop.new,
);
/// Emit an `if` instruction.
/// Branching to the returned label will branch to the matching `end`.
Label if_([
List<ir.ValueType> inputs = const [],
List<ir.ValueType> outputs = const [],
]) {
assert(_verifyTypes(const [ir.NumType.i32], const []));
return _beginBlock(
_pushLabel(If(inputs, outputs), trace: const ['if']),
ir.BeginNoEffectIf.new,
ir.BeginOneOutputIf.new,
ir.BeginFunctionIf.new,
);
}
/// 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;
_add(const ir.Else());
}
/// Emit a legacy `try` instruction.
Label try_legacy([
List<ir.ValueType> inputs = const [],
List<ir.ValueType> outputs = const [],
]) => _beginBlock(
_pushLabel(Try(inputs, outputs), trace: const ['try']),
ir.BeginNoEffectTry.new,
ir.BeginOneOutputTry.new,
ir.BeginFunctionTry.new,
);
/// Emit a legacy `catch` instruction.
void catch_legacy(ir.Tag tag) {
assert(
_topOfLabelStack is Try ||
_reportError("Unexpected 'catch' (not in 'try' block)"),
);
final Try try_ = _topOfLabelStack as Try;
assert(
_verifyEndOfBlock(
tag.type.inputs,
trace: ['catch', tag],
reachableAfter: try_.reachable,
reindent: true,
),
);
assert(tag.enclosingModule == module);
try_.hasCatch = true;
_reachable = try_.reachable;
_add(ir.CatchLegacy(tag));
}
/// Emit a `throw` instruction.
void throw_(ir.Tag tag) {
assert(_verifyTypes(tag.type.inputs, const [], trace: ['throw', tag]));
assert(tag.enclosingModule == module);
_add(ir.Throw(tag));
_reachable = false;
}
/// Emit a `rethrow` instruction.
void rethrow_(Label label) {
assert(label is Try && label.hasCatch);
assert(_verifyTypes(const [], const [], trace: ['rethrow', label]));
_add(ir.Rethrow(_labelIndex(label)));
_reachable = false;
}
/// Emit a `throw_ref` instruction.
void throw_ref() {
_add(ir.ThrowRef());
_reachable = false;
}
/// Emit an `end` instruction.
void end() {
assert(
_verifyEndOfBlock(
_topOfLabelStack.outputs,
trace: const ['end'],
reachableAfter: _topOfLabelStack.reachable,
reindent: false,
),
);
_reachable = _topOfLabelStack.reachable;
_labelStack.removeLast();
_add(const ir.End());
}
int _labelIndex(Label label) {
final int index = _labelStack.length - label.depth - 1;
assert(_labelStack[label.depth] == label);
return index;
}
/// Emit a `br` instruction.
void br(Label label) {
assert(
_verifyTypes(
const [],
const [],
trace: ['br', label],
reachableAfter: false,
),
);
assert(_verifyBranchTypes(label));
_add(ir.Br(_labelIndex(label)));
_reachable = false;
}
/// Emit a `br_if` instruction.
void br_if(Label label) {
assert(
_verifyTypes(const [ir.NumType.i32], const [], trace: ['br_if', label]),
);
assert(_verifyBranchTypes(label));
_add(ir.BrIf(_labelIndex(label)));
}
/// Emit a `br_table` instruction.
void br_table(List<Label> labels, Label defaultLabel) {
assert(
_verifyTypes(
const [ir.NumType.i32],
const [],
trace: ['br_table', ...labels, defaultLabel],
reachableAfter: false,
),
);
for (var label in labels) {
assert(_verifyBranchTypes(label));
}
assert(_verifyBranchTypes(defaultLabel));
_add(
ir.BrTable(labels.map(_labelIndex).toList(), _labelIndex(defaultLabel)),
);
_reachable = false;
}
/// Emit a `try_table` instruction.
Label try_table(
List<TryTableCatch> catches, [
List<ir.ValueType> inputs = const [],
List<ir.ValueType> outputs = const [],
]) {
// Validation: blocks in the table should have the outputs based on the
// types of exceptions they catch.
for (TryTableCatch catch_ in catches) {
assert(_verifyBranchTypes(catch_.label, 0, catch_.caughtValues()));
}
final List<ir.TryTableCatch> irCatches = catches
.map((c) => c.toIr(_labelIndex(c.label)))
.toList();
final label = _pushLabel(
TryTable(inputs, outputs, catches),
trace: const ['try_table'],
);
return _beginBlock(
label,
() => ir.BeginNoEffectTryTable(irCatches),
(ty) => ir.BeginOneOutputTryTable(ty, irCatches),
(ty) => ir.BeginFunctionTryTable(ty, irCatches),
);
}
/// Emit a `return` instruction.
void return_() {
assert(
_verifyTypes(
_labelStack[0].outputs,
const [],
trace: const ['return'],
reachableAfter: false,
),
);
_add(const ir.Return());
_reachable = false;
}
/// Emit a `call` instruction.
void call(ir.BaseFunction function) {
assert(
_verifyTypes(
function.type.inputs,
function.type.outputs,
trace: ['call', function],
),
);
assert(function.enclosingModule == module);
_add(ir.Call(function));
}
/// Emit a `call_indirect` instruction.
void call_indirect(ir.FunctionType type, [ir.Table? table]) {
assert(
_verifyTypes(
[...type.inputs, ir.NumType.i32],
type.outputs,
trace: ['call_indirect', type, if (table != null) table.name],
),
);
assert(table == null || table.enclosingModule == module);
_add(ir.CallIndirect(type, table));
}
/// Emit a `call_ref` instruction.
void call_ref(ir.FunctionType type) {
assert(
(() {
if (!_reachable) return true;
final actualRefType = _topOfStack;
if (actualRefType is! ir.RefType) return false;
final actualFuncType = actualRefType.heapType;
if (actualFuncType is! ir.FunctionType) return false;
return actualFuncType.isStructurallyEqualTo(type);
})(),
'$_topOfStack != $type',
);
assert(
_verifyTypes(
[...type.inputs, ir.RefType.func(nullable: true)],
type.outputs,
trace: ['call_ref', type],
),
);
_add(ir.CallRef(type));
}
// Parametric instructions
/// Emit a `drop` instruction.
void drop() {
assert(_verifyTypes([_topOfStack], const [], trace: const ['drop']));
_add(const ir.Drop());
}
/// Emit a `select` instruction.
void select(ir.ValueType type) {
assert(
_verifyTypes(
[type, type, ir.NumType.i32],
[type],
trace: ['select', type],
),
);
_add(type is ir.NumType ? ir.Select() : ir.SelectWithType(type));
}
// Variable instructions
/// Emit a `local.get` instruction.
void local_get(ir.Local local) {
assert(locals[local.index] == local);
assert(
_verifyTypes(
const [],
[local.type],
trace: ['local.get', _localTraceString(local)],
),
);
assert(
_localIsInitialized(local) ||
_reportError("Uninitialized local with non-defaultable type"),
);
_add(ir.LocalGet(local));
}
/// Emit a `local.set` instruction.
void local_set(ir.Local local) {
assert(locals[local.index] == local);
assert(
_verifyTypes(
[local.type],
const [],
trace: ['local.set', _localTraceString(local)],
),
);
assert(_initializeLocal(local));
_add(ir.LocalSet(local));
}
/// Emit a `local.tee` instruction.
void local_tee(ir.Local local) {
assert(locals[local.index] == local);
assert(
_verifyTypes(
[local.type],
[local.type],
trace: ['local.tee', _localTraceString(local)],
),
);
assert(_initializeLocal(local));
_add(ir.LocalTee(local));
}
/// Emit a `global.get` instruction.
void global_get(ir.Global global) {
assert(
_verifyTypes(const [], [global.type.type], trace: ['global.get', global]),
);
assert(global.enclosingModule == module);
_add(ir.GlobalGet(global));
}
/// Emit a `global.set` instruction.
void global_set(ir.Global global) {
assert(global.type.mutable);
assert(
_verifyTypes([global.type.type], const [], trace: ['global.set', global]),
);
assert(global.enclosingModule == module);
_add(ir.GlobalSet(global));
}
// Table instructions
/// Emit a `table.get` instruction.
void table_get(ir.Table table) {
assert(
_verifyTypes(
const [ir.NumType.i32],
[table.type],
trace: ['table.get', table.name],
),
);
assert(table.enclosingModule == module);
_add(ir.TableGet(table));
}
/// Emit a `table.set` instruction.
void table_set(ir.Table table) {
assert(
_verifyTypes(
[ir.NumType.i32, table.type],
const [],
trace: ['table.set', table.name],
),
);
assert(table.enclosingModule == module);
_add(ir.TableSet(table));
}
/// Emit a `table.size` instruction.
void table_fill(ir.Table table) {
assert(
_verifyTypes(
[ir.NumType.i32, table.type, ir.NumType.i32],
const [],
trace: ['table.fill', table.name],
),
);
assert(table.enclosingModule == module);
_add(ir.TableFill(table));
}
/// Emit a `table.size` instruction.
void table_size(ir.Table table) {
assert(
_verifyTypes(
const [],
const [ir.NumType.i32],
trace: ['table.size', table.name],
),
);
assert(table.enclosingModule == module);
_add(ir.TableSize(table));
}
// Memory instructions
void _addMemoryInstruction(
ir.Instruction Function(ir.MemoryOffsetAlign memory) create,
ir.Memory memory, {
required int offset,
required int align,
}) {
assert(memory.enclosingModule == module);
_add(create(ir.MemoryOffsetAlign(memory, offset: offset, align: align)));
}
/// Emit an `i32.load` instruction.
void i32_load(ir.Memory memory, int offset, [int align = 2]) {
assert(align >= 0 && align <= 2);
assert(
_verifyTypes(
const [ir.NumType.i32],
const [ir.NumType.i32],
trace: ['i32.load', memory.name, offset, align],
),
);
_addMemoryInstruction(ir.I32Load.new, memory, offset: offset, align: align);
}
/// Emit an `i64.load` instruction.
void i64_load(ir.Memory memory, int offset, [int align = 3]) {
assert(align >= 0 && align <= 3);
assert(
_verifyTypes(
const [ir.NumType.i32],
const [ir.NumType.i64],
trace: ['i64.load', memory.name, offset, align],
),
);
_addMemoryInstruction(ir.I64Load.new, memory, offset: offset, align: align);
}
/// Emit an `f32.load` instruction.
void f32_load(ir.Memory memory, int offset, [int align = 2]) {
assert(align >= 0 && align <= 2);
assert(
_verifyTypes(
const [ir.NumType.i32],
const [ir.NumType.f32],
trace: ['f32.load', memory.name, offset, align],
),
);
_addMemoryInstruction(ir.F32Load.new, memory, offset: offset, align: align);
}
/// Emit an `f64.load` instruction.
void f64_load(ir.Memory memory, int offset, [int align = 3]) {
assert(align >= 0 && align <= 3);
assert(
_verifyTypes(
const [ir.NumType.i32],
const [ir.NumType.f64],
trace: ['f64.load', memory.name, offset, align],
),
);
_addMemoryInstruction(ir.F64Load.new, memory, offset: offset, align: align);
}
/// Emit an `i32.load8_s` instruction.
void i32_load8_s(ir.Memory memory, int offset, [int align = 0]) {
assert(align == 0);
assert(
_verifyTypes(
const [ir.NumType.i32],
const [ir.NumType.i32],
trace: ['i32.load8_s', memory.name, offset, align],
),
);
_addMemoryInstruction(
ir.I32Load8S.new,
memory,
offset: offset,
align: align,
);
}
/// Emit an `i32.load8_u` instruction.
void i32_load8_u(ir.Memory memory, int offset, [int align = 0]) {
assert(align == 0);
assert(
_verifyTypes(
const [ir.NumType.i32],
const [ir.NumType.i32],
trace: ['i32.load8_u', memory.name, offset, align],
),
);
_addMemoryInstruction(
ir.I32Load8U.new,
memory,
offset: offset,
align: align,
);
}
/// Emit an `i32.load16_s` instruction.
void i32_load16_s(ir.Memory memory, int offset, [int align = 1]) {
assert(align >= 0 && align <= 1);
assert(
_verifyTypes(
const [ir.NumType.i32],
const [ir.NumType.i32],
trace: ['i32.load16_s', memory.name, offset, align],
),
);
_addMemoryInstruction(
ir.I32Load16S.new,
memory,
offset: offset,
align: align,
);
}
/// Emit an `i32.load16_u` instruction.
void i32_load16_u(ir.Memory memory, int offset, [int align = 1]) {
assert(align >= 0 && align <= 1);
assert(
_verifyTypes(
const [ir.NumType.i32],
const [ir.NumType.i32],
trace: ['i32.load16_u', memory.name, offset, align],
),
);
_addMemoryInstruction(
ir.I32Load16U.new,
memory,
offset: offset,
align: align,
);
}
/// Emit an `i64.load8_s` instruction.
void i64_load8_s(ir.Memory memory, int offset, [int align = 0]) {
assert(align == 0);
assert(
_verifyTypes(
const [ir.NumType.i32],
const [ir.NumType.i64],
trace: ['i64.load8_s', memory.name, offset, align],
),
);
_addMemoryInstruction(
ir.I64Load8S.new,
memory,
offset: offset,
align: align,
);
}
/// Emit an `i64.load8_u` instruction.
void i64_load8_u(ir.Memory memory, int offset, [int align = 0]) {
assert(align == 0);
assert(
_verifyTypes(
const [ir.NumType.i32],
const [ir.NumType.i64],
trace: ['i64.load8_u', memory.name, offset, align],
),
);
_addMemoryInstruction(
ir.I64Load8U.new,
memory,
offset: offset,
align: align,
);
}
/// Emit an `i64.load16_s` instruction.
void i64_load16_s(ir.Memory memory, int offset, [int align = 1]) {
assert(align >= 0 && align <= 1);
assert(
_verifyTypes(
const [ir.NumType.i32],
const [ir.NumType.i64],
trace: ['i64.load16_s', memory.name, offset, align],
),
);
_addMemoryInstruction(
ir.I64Load16S.new,
memory,
offset: offset,
align: align,
);
}
/// Emit an `i64.load16_u` instruction.
void i64_load16_u(ir.Memory memory, int offset, [int align = 1]) {
assert(align >= 0 && align <= 1);
assert(
_verifyTypes(
const [ir.NumType.i32],
const [ir.NumType.i64],
trace: ['i64.load16_u', memory.name, offset, align],
),
);
_addMemoryInstruction(
ir.I64Load16U.new,
memory,
offset: offset,
align: align,
);
}
/// Emit an `i64.load32_s` instruction.
void i64_load32_s(ir.Memory memory, int offset, [int align = 2]) {
assert(align >= 0 && align <= 2);
assert(
_verifyTypes(
const [ir.NumType.i32],
const [ir.NumType.i64],
trace: ['i64.load32_s', memory.name, offset, align],
),
);
_addMemoryInstruction(
ir.I64Load32S.new,
memory,
offset: offset,
align: align,
);
}
/// Emit an `i64.load32_u` instruction.
void i64_load32_u(ir.Memory memory, int offset, [int align = 2]) {
assert(align >= 0 && align <= 2);
assert(
_verifyTypes(
const [ir.NumType.i32],
const [ir.NumType.i64],
trace: ['i64.load32_u', memory.name, offset, align],
),
);
_addMemoryInstruction(
ir.I64Load32U.new,
memory,
offset: offset,
align: align,
);
}
/// Emit an `i32.store` instruction.
void i32_store(ir.Memory memory, int offset, [int align = 2]) {
assert(align >= 0 && align <= 2);
assert(
_verifyTypes(
const [ir.NumType.i32, ir.NumType.i32],
const [],
trace: ['i32.store', memory.name, offset, align],
),
);
_addMemoryInstruction(
ir.I32Store.new,
memory,
offset: offset,
align: align,
);
}
/// Emit an `i64.store` instruction.
void i64_store(ir.Memory memory, int offset, [int align = 3]) {
assert(align >= 0 && align <= 3);
assert(
_verifyTypes(
const [ir.NumType.i32, ir.NumType.i64],
const [],
trace: ['i64.store', memory.name, offset, align],
),
);
_addMemoryInstruction(
ir.I64Store.new,
memory,
offset: offset,
align: align,
);
}
/// Emit an `f32.store` instruction.
void f32_store(ir.Memory memory, int offset, [int align = 2]) {
assert(align >= 0 && align <= 2);
assert(
_verifyTypes(
const [ir.NumType.i32, ir.NumType.f32],
const [],
trace: ['f32.store', memory.name, offset, align],
),
);
_addMemoryInstruction(
ir.F32Store.new,
memory,
offset: offset,
align: align,
);
}
/// Emit an `f64.store` instruction.
void f64_store(ir.Memory memory, int offset, [int align = 3]) {
assert(align >= 0 && align <= 3);
assert(
_verifyTypes(
const [ir.NumType.i32, ir.NumType.f64],
const [],
trace: ['f64.store', memory.name, offset, align],
),
);
_addMemoryInstruction(
ir.F64Store.new,
memory,
offset: offset,
align: align,
);
}
/// Emit an `i32.store8` instruction.
void i32_store8(ir.Memory memory, int offset, [int align = 0]) {
assert(align == 0);
assert(
_verifyTypes(
const [ir.NumType.i32, ir.NumType.i32],
const [],
trace: ['i32.store8', memory.name, offset, align],
),
);
_addMemoryInstruction(
ir.I32Store8.new,
memory,
offset: offset,
align: align,
);
}
/// Emit an `i32.store16` instruction.
void i32_store16(ir.Memory memory, int offset, [int align = 1]) {
assert(align >= 0 && align <= 1);
assert(
_verifyTypes(
const [ir.NumType.i32, ir.NumType.i32],
const [],
trace: ['i32.store16', memory.name, offset, align],
),
);
_addMemoryInstruction(
ir.I32Store16.new,
memory,
offset: offset,
align: align,
);
}
/// Emit an `i64.store8` instruction.
void i64_store8(ir.Memory memory, int offset, [int align = 0]) {
assert(align == 0);
assert(
_verifyTypes(
const [ir.NumType.i32, ir.NumType.i64],
const [],
trace: ['i64.store8', memory.name, offset, align],
),
);
_addMemoryInstruction(
ir.I64Store8.new,
memory,
offset: offset,
align: align,
);
}
/// Emit an `i64.store16` instruction.
void i64_store16(ir.Memory memory, int offset, [int align = 1]) {
assert(align >= 0 && align <= 1);
assert(
_verifyTypes(
const [ir.NumType.i32, ir.NumType.i64],
const [],
trace: ['i64.store16', memory.name, offset, align],
),
);
_addMemoryInstruction(
ir.I64Store16.new,
memory,
offset: offset,
align: align,
);
}
/// Emit an `i64.store32` instruction.
void i64_store32(ir.Memory memory, int offset, [int align = 2]) {
assert(align >= 0 && align <= 2);
assert(
_verifyTypes(
const [ir.NumType.i32, ir.NumType.i64],
const [],
trace: ['i64.store32', memory.name, offset, align],
),
);
_addMemoryInstruction(
ir.I64Store32.new,
memory,
offset: offset,
align: align,
);
}
/// Emit a `memory.size` instruction.
void memory_size(ir.Memory memory) {
assert(_verifyTypes(const [], const [ir.NumType.i32]));
assert(memory.enclosingModule == module);
_add(ir.MemorySize(memory));
}
/// Emit a `memory.grow` instruction.
void memory_grow(ir.Memory memory) {
assert(_verifyTypes(const [ir.NumType.i32], const [ir.NumType.i32]));
assert(memory.enclosingModule == module);
_add(ir.MemoryGrow(memory));
}
/// Emit a `memory.fill` instruction.
void memory_fill(ir.Memory memory) {
assert(
_verifyTypes(const [
ir.NumType.i32,
ir.NumType.i32,
ir.NumType.i32,
], const []),
);
assert(memory.enclosingModule == module);
_add(ir.MemoryFill(memory));
}
// Reference instructions
/// Emit a `ref.null` instruction.
void ref_null(ir.HeapType heapType) {
assert(
_verifyTypes(
const [],
[ir.RefType(heapType, nullable: true)],
trace: ['ref.null', heapType],
),
);
_add(ir.RefNull(heapType));
}
/// Emit a `ref.is_null` instruction.
void ref_is_null() {
assert(
_verifyTypes(
const [ir.RefType.common(nullable: true)],
const [ir.NumType.i32],
trace: const ['ref.is_null'],
),
);
_add(const ir.RefIsNull());
}
/// Emit a `ref.func` instruction.
void ref_func(ir.BaseFunction function) {
assert(
_verifyTypes(
const [],
[ir.RefType.def(function.type, nullable: false)],
trace: ['ref.func', function],
),
);
assert(function.enclosingModule == module);
_add(ir.RefFunc(function));
}
/// Emit a `ref.as_non_null` instruction.
void ref_as_non_null() {
assert(
_verifyTypes(
const [ir.RefType.common(nullable: true)],
[_topOfStack.withNullability(false)],
trace: const ['ref.as_non_null'],
),
);
_add(const ir.RefAsNonNull());
}
/// Emit a `br_on_null` instruction.
void br_on_null(Label label) {
assert(
_verifyTypes(
const [ir.RefType.common(nullable: true)],
[_topOfStack.withNullability(false)],
trace: ['br_on_null', label],
),
);
assert(_verifyBranchTypes(label, 1));
_add(ir.BrOnNull(_labelIndex(label)));
}
/// Emit a `ref.eq` instruction.
void ref_eq() {
assert(
_verifyTypes(
const [ir.RefType.eq(nullable: true), ir.RefType.eq(nullable: true)],
const [ir.NumType.i32],
trace: const ['ref.eq'],
),
);
_add(const ir.RefEq());
}
/// Emit a `br_on_non_null` instruction.
void br_on_non_null(Label label) {
assert(_verifyBranchTypes(label, 1, [_topOfStack.withNullability(false)]));
assert(
_verifyTypes(
const [ir.RefType.common(nullable: true)],
const [],
trace: ['br_on_non_null', label],
),
);
_add(ir.BrOnNonNull(_labelIndex(label)));
}
/// Emit a `struct.get` instruction.
void struct_get(ir.StructType structType, int fieldIndex) {
assert(structType.fields[fieldIndex].type is ir.ValueType);
assert(
_verifyTypes(
[ir.RefType.def(structType, nullable: true)],
[structType.fields[fieldIndex].type.unpacked],
trace: ['struct.get', structType, fieldIndex],
),
);
_add(ir.StructGet(structType, fieldIndex));
}
/// Emit a `struct.get_s` instruction.
void struct_get_s(ir.StructType structType, int fieldIndex) {
assert(structType.fields[fieldIndex].type is ir.PackedType);
assert(
_verifyTypes(
[ir.RefType.def(structType, nullable: true)],
[structType.fields[fieldIndex].type.unpacked],
trace: ['struct.get_s', structType, fieldIndex],
),
);
_add(ir.StructGetS(structType, fieldIndex));
}
/// Emit a `struct.get_u` instruction.
void struct_get_u(ir.StructType structType, int fieldIndex) {
assert(structType.fields[fieldIndex].type is ir.PackedType);
assert(
_verifyTypes(
[ir.RefType.def(structType, nullable: true)],
[structType.fields[fieldIndex].type.unpacked],
trace: ['struct.get_u', structType, fieldIndex],
),
);
_add(ir.StructGetU(structType, fieldIndex));
}
/// Emit a `struct.set` instruction.
void struct_set(ir.StructType structType, int fieldIndex) {
assert(
_verifyTypes(
[
ir.RefType.def(structType, nullable: true),
structType.fields[fieldIndex].type.unpacked,
],
const [],
trace: ['struct.set', structType, fieldIndex],
),
);
_add(ir.StructSet(structType, fieldIndex));
}
/// Emit a `struct.new` instruction.
void struct_new(ir.StructType structType) {
assert(
_verifyTypes(
[...structType.fields.map((f) => f.type.unpacked)],
[ir.RefType.def(structType, nullable: false)],
trace: ['struct.new', structType],
),
);
_add(ir.StructNew(structType));
}
/// Emit a `struct.new_default` instruction.
void struct_new_default(ir.StructType structType) {
assert(
_verifyTypes(
const [],
[ir.RefType.def(structType, nullable: false)],
trace: ['struct.new_default', structType],
),
);
_add(ir.StructNewDefault(structType));
}
/// Emit an `array.get` instruction.
void array_get(ir.ArrayType arrayType) {
assert(arrayType.elementType.type is ir.ValueType);
assert(
_verifyTypes(
[ir.RefType.def(arrayType, nullable: true), ir.NumType.i32],
[arrayType.elementType.type.unpacked],
trace: ['array.get', arrayType],
),
);
_add(ir.ArrayGet(arrayType));
}
/// Emit an `array.get_s` instruction.
void array_get_s(ir.ArrayType arrayType) {
assert(arrayType.elementType.type is ir.PackedType);
assert(
_verifyTypes(
[ir.RefType.def(arrayType, nullable: true), ir.NumType.i32],
[arrayType.elementType.type.unpacked],
trace: ['array.get_s', arrayType],
),
);
_add(ir.ArrayGetS(arrayType));
}
/// Emit an `array.get_u` instruction.
void array_get_u(ir.ArrayType arrayType) {
assert(arrayType.elementType.type is ir.PackedType);
assert(
_verifyTypes(
[ir.RefType.def(arrayType, nullable: true), ir.NumType.i32],
[arrayType.elementType.type.unpacked],
trace: ['array.get_u', arrayType],
),
);
_add(ir.ArrayGetU(arrayType));
}
/// Emit an `array.set` instruction.
void array_set(ir.ArrayType arrayType) {
assert(
_verifyTypes(
[
ir.RefType.def(arrayType, nullable: true),
ir.NumType.i32,
arrayType.elementType.type.unpacked,
],
const [],
trace: ['array.set', arrayType],
),
);
_add(ir.ArraySet(arrayType));
}
/// Emit an `array.len` instruction.
void array_len() {
assert(
_verifyTypes(
[ir.RefType.array(nullable: true)],
const [ir.NumType.i32],
trace: ['array.len'],
),
);
_add(const ir.ArrayLen());
}
/// Emit an `array.new_fixed` instruction.
void array_new_fixed(ir.ArrayType arrayType, int length) {
ir.ValueType elementType = arrayType.elementType.type.unpacked;
assert(
_verifyTypes(
[...List.filled(length, elementType)],
[ir.RefType.def(arrayType, nullable: false)],
trace: ['array.new_fixed', arrayType, length],
),
);
_add(ir.ArrayNewFixed(arrayType, length));
}
/// Emit an `array.new` instruction.
void array_new(ir.ArrayType arrayType) {
assert(
_verifyTypes(
[arrayType.elementType.type.unpacked, ir.NumType.i32],
[ir.RefType.def(arrayType, nullable: false)],
trace: ['array.new', arrayType],
),
);
_add(ir.ArrayNew(arrayType));
}
/// Emit an `array.new_default` instruction.
void array_new_default(ir.ArrayType arrayType) {
assert(
_verifyTypes(
[ir.NumType.i32],
[ir.RefType.def(arrayType, nullable: false)],
trace: ['array.new_default', arrayType],
),
);
_add(ir.ArrayNewDefault(arrayType));
}
/// Emit an `array.new_data` instruction.
void array_new_data(ir.ArrayType arrayType, ir.BaseDataSegment data) {
assert(arrayType.elementType.type.isPrimitive);
assert(
_verifyTypes(
[ir.NumType.i32, ir.NumType.i32],
[ir.RefType.def(arrayType, nullable: false)],
trace: ['array.new_data', arrayType, data.index],
),
);
_add(ir.ArrayNewData(arrayType, data));
}
/// Emit an `array.copy` instruction.
void array_copy(ir.ArrayType destArrayType, ir.ArrayType sourceArrayType) {
assert(
_verifyTypes(
[
ir.RefType.def(destArrayType, nullable: true), // dest
ir.NumType.i32, // dest_offset
ir.RefType.def(sourceArrayType, nullable: true), // source
ir.NumType.i32, // source_offset
ir.NumType.i32, // size
],
[],
trace: ['array.copy', destArrayType, sourceArrayType],
),
);
_add(
ir.ArrayCopy(
destArrayType: destArrayType,
sourceArrayType: sourceArrayType,
),
);
}
/// Emit an `array.fill` instruction.
void array_fill(ir.ArrayType arrayType) {
assert(
_verifyTypes(
[
ir.RefType.def(arrayType, nullable: true),
ir.NumType.i32, // offset
arrayType.elementType.type.unpacked, // fill value
ir.NumType.i32, // size
],
[],
trace: ['array.copy', arrayType],
),
);
_add(ir.ArrayFill(arrayType));
}
/// Emit an `i31.new` instruction.
void i31_new() {
assert(
_verifyTypes(
const [ir.NumType.i32],
const [ir.RefType.i31(nullable: false)],
trace: const ['i31.new'],
),
);
_add(const ir.I31New());
}
/// Emit an `i31.get_s` instruction.
void i31_get_s() {
assert(
_verifyTypes(
const [ir.RefType.i31(nullable: false)],
const [ir.NumType.i32],
trace: const ['i31.get_s'],
),
);
_add(const ir.I31GetS());
}
/// Emit an `i31.get_u` instruction.
void i31_get_u() {
assert(
_verifyTypes(
const [ir.RefType.i31(nullable: false)],
const [ir.NumType.i32],
trace: const ['i31.get_u'],
),
);
_add(const ir.I31GetU());
}
bool _verifyCast(
ir.RefType inputType,
ir.RefType targetType,
ir.ValueType outputType, {
List<Object>? trace,
}) {
_verifyTypes([inputType], [outputType], trace: trace);
if (!targetType.isSubtypeOf(inputType)) {
_reportError(
"Target type '$targetType' not a subtype of "
"input type '$inputType' in cast",
);
}
return true;
}
/// Emit a `ref.test` instruction.
void ref_test(ir.RefType targetType) {
assert(
_verifyCast(
ir.RefType(targetType.heapType.topType, nullable: true),
targetType,
ir.NumType.i32,
trace: [
'ref.test',
if (targetType.nullable) 'null',
targetType.heapType,
],
),
);
_add(ir.RefTest(targetType));
}
/// Emit a `ref.cast` instruction.
void ref_cast(ir.RefType targetType) {
assert(
_verifyCast(
ir.RefType(targetType.heapType.topType, nullable: true),
targetType,
targetType,
trace: [
'ref.cast',
if (targetType.nullable) 'null',
targetType.heapType,
],
),
);
_add(ir.RefCast(targetType));
}
/// Emit a `br_on_cast` instruction.
void br_on_cast(Label label, ir.RefType inputType, ir.RefType targetType) {
assert(
_verifyCast(
inputType,
targetType,
inputType.withNullability(inputType.nullable && !targetType.nullable),
trace: [
'br_on_cast',
label,
if (inputType.nullable) 'null',
inputType.heapType,
if (targetType.nullable) 'null',
targetType.heapType,
],
),
);
assert(_verifyBranchTypes(label, 1, [targetType]));
_add(ir.BrOnCast(_labelIndex(label), inputType, targetType));
}
/// Emit a `br_on_cast_fail` instruction.
void br_on_cast_fail(
Label label,
ir.RefType inputType,
ir.RefType targetType,
) {
assert(
_verifyCast(
inputType,
targetType,
targetType,
trace: [
'br_on_cast_fail',
label,
if (inputType.nullable) 'null',
inputType.heapType,
if (targetType.nullable) 'null',
targetType.heapType,
],
),
);
assert(
_verifyBranchTypes(label, 1, [
inputType.withNullability(inputType.nullable && !targetType.nullable),
]),
);
_add(ir.BrOnCastFail(_labelIndex(label), inputType, targetType));
}
/// Emit an `any.convert_extern` instruction.
void any_convert_extern() {
assert(
_verifyTypesFun(
const [ir.RefType.extern(nullable: true)],
(inputs) => [ir.RefType.any(nullable: inputs.single.nullable)],
trace: ['extern.internalize'],
),
);
_add(const ir.ExternInternalize());
}
/// Emit an `extern.convert_any` instruction.
void extern_convert_any() {
assert(
_verifyTypesFun(
const [ir.RefType.any(nullable: true)],
(inputs) => [ir.RefType.extern(nullable: inputs.single.nullable)],
trace: ['extern.externalize'],
),
);
_add(const ir.ExternExternalize());
}
// Numeric instructions
/// Emit an `i32.const` instruction.
void i32_const(int value) {
assert(
_verifyTypes(
const [],
const [ir.NumType.i32],
trace: ['i32.const', value],
),
);
assert(-1 << 31 <= value && value < 1 << 31);
_add(ir.I32Const(value));
}
/// Emit an `i64.const` instruction.
void i64_const(int value) {
assert(
_verifyTypes(
const [],
const [ir.NumType.i64],
trace: ['i64.const', value],
),
);
_add(ir.I64Const(value));
}
/// Emit an `f32.const` instruction.
void f32_const(double value) {
assert(
_verifyTypes(
const [],
const [ir.NumType.f32],
trace: ['f32.const', value],
),
);
_add(ir.F32Const(value));
}
/// Emit an `f64.const` instruction.
void f64_const(double value) {
assert(
_verifyTypes(
const [],
const [ir.NumType.f64],
trace: ['f64.const', value],
),
);
_add(ir.F64Const(value));
}
void v128_const(Uint8List bytes) {
assert(
_verifyTypes(
const [],
const [ir.NumType.v128],
trace: ['v128.const', bytes],
),
);
_add(ir.V128Const(bytes));
}
void v128_const_f64x2(double value0, double value1) {
assert(
_verifyTypes(
const [],
const [ir.NumType.v128],
trace: ['v128.const f64x2', value0, value1],
),
);
final bytes = Uint8List(16);
final data = ByteData.view(bytes.buffer);
data.setFloat64(0, value0, Endian.little);
data.setFloat64(8, value1, Endian.little);
_add(ir.V128Const(bytes));
}
void v128_const_i64x2(int value0, int value1) {
assert(
_verifyTypes(
const [],
const [ir.NumType.v128],
trace: ['v128.const i64x2', value0, value1],
),
);
final bytes = Uint8List(16);
final data = ByteData.view(bytes.buffer);
data.setInt64(0, value0, Endian.little);
data.setInt64(8, value1, Endian.little);
_add(ir.V128Const(bytes));
}
void v128_const_f32x4(double v0, double v1, double v2, double v3) {
assert(
_verifyTypes(
const [],
const [ir.NumType.v128],
trace: ['v128.const f32x4', v0, v1, v2, v3],
),
);
final bytes = Uint8List(16);
final data = ByteData.view(bytes.buffer);
data.setFloat32(0, v0, Endian.little);
data.setFloat32(4, v1, Endian.little);
data.setFloat32(8, v2, Endian.little);
data.setFloat32(12, v3, Endian.little);
_add(ir.V128Const(bytes));
}
void v128_const_i32x4(int v0, int v1, int v2, int v3) {
assert(
_verifyTypes(
const [],
const [ir.NumType.v128],
trace: ['v128.const i32x4', v0, v1, v2, v3],
),
);
final bytes = Uint8List(16);
final data = ByteData.view(bytes.buffer);
data.setInt32(0, v0, Endian.little);
data.setInt32(4, v1, Endian.little);
data.setInt32(8, v2, Endian.little);
data.setInt32(12, v3, Endian.little);
_add(ir.V128Const(bytes));
}
void v128_const_i16x8(
int v0,
int v1,
int v2,
int v3,
int v4,
int v5,
int v6,
int v7,
) {
assert(
_verifyTypes(
const [],
const [ir.NumType.v128],
trace: ['v128.const i16x8', v0, v1, v2, v3, v4, v5, v6, v7],
),
);
final bytes = Uint8List(16);
final data = ByteData.view(bytes.buffer);
data.setInt16(0, v0, Endian.little);
data.setInt16(2, v1, Endian.little);
data.setInt16(4, v2, Endian.little);
data.setInt16(6, v3, Endian.little);
data.setInt16(8, v4, Endian.little);
data.setInt16(10, v5, Endian.little);
data.setInt16(12, v6, Endian.little);
data.setInt16(14, v7, Endian.little);
_add(ir.V128Const(bytes));
}
void v128_const_i8x16(
int v0,
int v1,
int v2,
int v3,
int v4,
int v5,
int v6,
int v7,
int v8,
int v9,
int v10,
int v11,
int v12,
int v13,
int v14,
int v15,
) {
assert(
_verifyTypes(
const [],
const [ir.NumType.v128],
trace: [
'v128.const i8x16',
v0,
v1,
v2,
v3,
v4,
v5,
v6,
v7,
v8,
v9,
v10,
v11,
v12,
v13,
v14,
v15,
],
),
);
final bytes = Uint8List(16);
final data = ByteData.view(bytes.buffer);
data.setInt8(0, v0);
data.setInt8(1, v1);
data.setInt8(2, v2);
data.setInt8(3, v3);
data.setInt8(4, v4);
data.setInt8(5, v5);
data.setInt8(6, v6);
data.setInt8(7, v7);
data.setInt8(8, v8);
data.setInt8(9, v9);
data.setInt8(10, v10);
data.setInt8(11, v11);
data.setInt8(12, v12);
data.setInt8(13, v13);
data.setInt8(14, v14);
data.setInt8(15, v15);
_add(ir.V128Const(bytes));
}
/// Emit an `i32.eqz` instruction.
void i32_eqz() {
assert(
_verifyTypes(
const [ir.NumType.i32],
const [ir.NumType.i32],
trace: const ['i32.eqz'],
),
);
_add(const ir.I32Eqz());
}
/// Emit an `i32.eq` instruction.
void i32_eq() {
assert(
_verifyTypes(
const [ir.NumType.i32, ir.NumType.i32],
const [ir.NumType.i32],
trace: const ['i32.eq'],
),
);
_add(const ir.I32Eq());
}
/// Emit an `i32.ne` instruction.
void i32_ne() {
assert(
_verifyTypes(
const [ir.NumType.i32, ir.NumType.i32],
const [ir.NumType.i32],
trace: const ['i32.ne'],
),
);
_add(const ir.I32Ne());
}
/// Emit an `i32.lt_s` instruction.
void i32_lt_s() {
assert(
_verifyTypes(
const [ir.NumType.i32, ir.NumType.i32],
const [ir.NumType.i32],
trace: const ['i32.lt_s'],
),
);
_add(const ir.I32LtS());
}
/// Emit an `i32.lt_u` instruction.
void i32_lt_u() {
assert(
_verifyTypes(
const [ir.NumType.i32, ir.NumType.i32],
const [ir.NumType.i32],
trace: const ['i32.lt_u'],
),
);
_add(const ir.I32LtU());
}
/// Emit an `i32.gt_s` instruction.
void i32_gt_s() {
assert(
_verifyTypes(
const [ir.NumType.i32, ir.NumType.i32],
const [ir.NumType.i32],
trace: const ['i32.gt_s'],
),
);
_add(const ir.I32GtS());
}
/// Emit an `i32.gt_u` instruction.
void i32_gt_u() {
assert(
_verifyTypes(
const [ir.NumType.i32, ir.NumType.i32],
const [ir.NumType.i32],
trace: const ['i32.gt_u'],
),
);
_add(const ir.I32GtU());
}
/// Emit an `i32.le_s` instruction.
void i32_le_s() {
assert(
_verifyTypes(
const [ir.NumType.i32, ir.NumType.i32],
const [ir.NumType.i32],
trace: const ['i32.le_s'],
),
);
_add(const ir.I32LeS());
}
/// Emit an `i32.le_u` instruction.
void i32_le_u() {
assert(
_verifyTypes(
const [ir.NumType.i32, ir.NumType.i32],
const [ir.NumType.i32],
trace: const ['i32.le_u'],
),
);
_add(const ir.I32LeU());
}
/// Emit an `i32.ge_s` instruction.
void i32_ge_s() {
assert(
_verifyTypes(
const [ir.NumType.i32, ir.NumType.i32],
const [ir.NumType.i32],
trace: const ['i32.ge_s'],
),
);
_add(const ir.I32GeS());
}
/// Emit an `i32.ge_u` instruction.
void i32_ge_u() {
assert(
_verifyTypes(
const [ir.NumType.i32, ir.NumType.i32],
const [ir.NumType.i32],
trace: const ['i32.ge_u'],
),
);
_add(const ir.I32GeU());
}
/// Emit an `i64.eqz` instruction.
void i64_eqz() {
assert(
_verifyTypes(
const [ir.NumType.i64],
const [ir.NumType.i32],
trace: const ['i64.eqz'],
),
);
_add(const ir.I64Eqz());
}
/// Emit an `i64.eq` instruction.
void i64_eq() {
assert(
_verifyTypes(
const [ir.NumType.i64, ir.NumType.i64],
const [ir.NumType.i32],
trace: const ['i64.eq'],
),
);
_add(const ir.I64Eq());
}
/// Emit an `i64.ne` instruction.
void i64_ne() {
assert(
_verifyTypes(
const [ir.NumType.i64, ir.NumType.i64],
const [ir.NumType.i32],
trace: const ['i64.ne'],
),
);
_add(const ir.I64Ne());
}
/// Emit an `i64.lt_s` instruction.
void i64_lt_s() {
assert(
_verifyTypes(
const [ir.NumType.i64, ir.NumType.i64],
const [ir.NumType.i32],
trace: const ['i64.lt_s'],
),
);
_add(const ir.I64LtS());
}
/// Emit an `i64.lt_u` instruction.
void i64_lt_u() {
assert(
_verifyTypes(
const [ir.NumType.i64, ir.NumType.i64],
const [ir.NumType.i32],
trace: const ['i64.lt_u'],
),
);
_add(const ir.I64LtU());
}
/// Emit an `i64.gt_s` instruction.
void i64_gt_s() {
assert(
_verifyTypes(
const [ir.NumType.i64, ir.NumType.i64],
const [ir.NumType.i32],
trace: const ['i64.gt_s'],
),
);
_add(const ir.I64GtS());
}
/// Emit an `i64.gt_u` instruction.
void i64_gt_u() {
assert(
_verifyTypes(
const [ir.NumType.i64, ir.NumType.i64],
const [ir.NumType.i32],
trace: const ['i64.gt_u'],
),
);
_add(const ir.I64GtU());
}
/// Emit an `i64.le_s` instruction.
void i64_le_s() {
assert(
_verifyTypes(
const [ir.NumType.i64, ir.NumType.i64],
const [ir.NumType.i32],
trace: const ['i64.le_s'],
),
);
_add(const ir.I64LeS());
}
/// Emit an `i64.le_u` instruction.
void i64_le_u() {
assert(
_verifyTypes(
const [ir.NumType.i64, ir.NumType.i64],
const [ir.NumType.i32],
trace: const ['i64.le_u'],
),
);
_add(const ir.I64LeU());
}
/// Emit an `i64.ge_s` instruction.
void i64_ge_s() {
assert(
_verifyTypes(
const [ir.NumType.i64, ir.NumType.i64],
const [ir.NumType.i32],
trace: const ['i64.ge_s'],
),
);
_add(const ir.I64GeS());
}
/// Emit an `i64.ge_u` instruction.
void i64_ge_u() {
assert(
_verifyTypes(
const [ir.NumType.i64, ir.NumType.i64],
const [ir.NumType.i32],
trace: const ['i64.ge_u'],
),
);
_add(const ir.I64GeU());
}
/// Emit an `f32.eq` instruction.
void f32_eq() {
assert(
_verifyTypes(
const [ir.NumType.f32, ir.NumType.f32],
const [ir.NumType.i32],
trace: const ['f32.eq'],
),
);
_add(const ir.F32Eq());
}
/// Emit an `f32.ne` instruction.
void f32_ne() {
assert(
_verifyTypes(
const [ir.NumType.f32, ir.NumType.f32],
const [ir.NumType.i32],
trace: const ['f32.ne'],
),
);
_add(const ir.F32Ne());
}
/// Emit an `f32.lt` instruction.
void f32_lt() {
assert(
_verifyTypes(
const [ir.NumType.f32, ir.NumType.f32],
const [ir.NumType.i32],
trace: const ['f32.lt'],
),
);
_add(const ir.F32Lt());
}
/// Emit an `f32.gt` instruction.
void f32_gt() {
assert(
_verifyTypes(
const [ir.NumType.f32, ir.NumType.f32],
const [ir.NumType.i32],
trace: const ['f32.gt'],
),
);
_add(const ir.F32Gt());
}
/// Emit an `f32.le` instruction.
void f32_le() {
assert(
_verifyTypes(
const [ir.NumType.f32, ir.NumType.f32],
const [ir.NumType.i32],
trace: const ['f32.le'],
),
);
_add(const ir.F32Le());
}
/// Emit an `f32.ge` instruction.
void f32_ge() {
assert(
_verifyTypes(
const [ir.NumType.f32, ir.NumType.f32],
const [ir.NumType.i32],
trace: const ['f32.ge'],
),
);
_add(const ir.F32Ge());
}
/// Emit an `f64.eq` instruction.
void f64_eq() {
assert(
_verifyTypes(
const [ir.NumType.f64, ir.NumType.f64],
const [ir.NumType.i32],
trace: const ['f64.eq'],
),
);
_add(const ir.F64Eq());
}
/// Emit an `f64.ne` instruction.
void f64_ne() {
assert(
_verifyTypes(
const [ir.NumType.f64, ir.NumType.f64],
const [ir.NumType.i32],
trace: const ['f64.ne'],
),
);
_add(const ir.F64Ne());
}
/// Emit an `f64.lt` instruction.
void f64_lt() {
assert(
_verifyTypes(
const [ir.NumType.f64, ir.NumType.f64],
const [ir.NumType.i32],
trace: const ['f64.lt'],
),
);
_add(const ir.F64Lt());
}
/// Emit an `f64.gt` instruction.
void f64_gt() {
assert(
_verifyTypes(
const [ir.NumType.f64, ir.NumType.f64],
const [ir.NumType.i32],
trace: const ['f64.gt'],
),
);
_add(const ir.F64Gt());
}
/// Emit an `f64.le` instruction.
void f64_le() {
assert(
_verifyTypes(
const [ir.NumType.f64, ir.NumType.f64],
const [ir.NumType.i32],
trace: const ['f64.le'],
),
);
_add(const ir.F64Le());
}
/// Emit an `f64.ge` instruction.
void f64_ge() {
assert(
_verifyTypes(
const [ir.NumType.f64, ir.NumType.f64],
const [ir.NumType.i32],
trace: const ['f64.ge'],
),
);
_add(const ir.F64Ge());
}
/// Emit an `i32.clz` instruction.
void i32_clz() {
assert(
_verifyTypes(
const [ir.NumType.i32],
const [ir.NumType.i32],
trace: const ['i32.clz'],
),
);
_add(const ir.I32Clz());
}
/// Emit an `i32.ctz` instruction.
void i32_ctz() {
assert(
_verifyTypes(
const [ir.NumType.i32],
const [ir.NumType.i32],
trace: const ['i32.ctz'],
),
);
_add(const ir.I32Ctz());
}
/// Emit an `i32.popcnt` instruction.
void i32_popcnt() {
assert(
_verifyTypes(
const [ir.NumType.i32],
const [ir.NumType.i32],
trace: const ['i32.popcnt'],
),
);
_add(const ir.I32Popcnt());
}
/// Emit an `i32.add` instruction.
void i32_add() {
assert(
_verifyTypes(
const [ir.NumType.i32, ir.NumType.i32],
const [ir.NumType.i32],
trace: const ['i32.add'],
),
);
_add(const ir.I32Add());
}
/// Emit an `i32.sub` instruction.
void i32_sub() {
assert(
_verifyTypes(
const [ir.NumType.i32, ir.NumType.i32],
const [ir.NumType.i32],
trace: const ['i32.sub'],
),
);
_add(const ir.I32Sub());
}
/// Emit an `i32.mul` instruction.
void i32_mul() {
assert(
_verifyTypes(
const [ir.NumType.i32, ir.NumType.i32],
const [ir.NumType.i32],
trace: const ['i32.mul'],
),
);
_add(const ir.I32Mul());
}
/// Emit an `i32.div_s` instruction.
void i32_div_s() {
assert(
_verifyTypes(
const [ir.NumType.i32, ir.NumType.i32],
const [ir.NumType.i32],
trace: const ['i32.div_s'],
),
);
_add(const ir.I32DivS());
}
/// Emit an `i32.div_u` instruction.
void i32_div_u() {
assert(
_verifyTypes(
const [ir.NumType.i32, ir.NumType.i32],
const [ir.NumType.i32],
trace: const ['i32.div_u'],
),
);
_add(const ir.I32DivU());
}
/// Emit an `i32.rem_s` instruction.
void i32_rem_s() {
assert(
_verifyTypes(
const [ir.NumType.i32, ir.NumType.i32],
const [ir.NumType.i32],
trace: const ['i32.rem_s'],
),
);
_add(const ir.I32RemS());
}
/// Emit an `i32.rem_u` instruction.
void i32_rem_u() {
assert(
_verifyTypes(
const [ir.NumType.i32, ir.NumType.i32],
const [ir.NumType.i32],
trace: const ['i32.rem_u'],
),
);
_add(const ir.I32RemU());
}
/// Emit an `i32.and` instruction.
void i32_and() {
assert(
_verifyTypes(
const [ir.NumType.i32, ir.NumType.i32],
const [ir.NumType.i32],
trace: const ['i32.and'],
),
);
_add(const ir.I32And());
}
/// Emit an `i32.or` instruction.
void i32_or() {
assert(
_verifyTypes(
const [ir.NumType.i32, ir.NumType.i32],
const [ir.NumType.i32],
trace: const ['i32.or'],
),
);
_add(const ir.I32Or());
}
/// Emit an `i32.xor` instruction.
void i32_xor() {
assert(
_verifyTypes(
const [ir.NumType.i32, ir.NumType.i32],
const [ir.NumType.i32],
trace: const ['i32.xor'],
),
);
_add(const ir.I32Xor());
}
/// Emit an `i32.shl` instruction.
void i32_shl() {
assert(
_verifyTypes(
const [ir.NumType.i32, ir.NumType.i32],
const [ir.NumType.i32],
trace: const ['i32.shl'],
),
);
_add(const ir.I32Shl());
}
/// Emit an `i32.shr_s` instruction.
void i32_shr_s() {
assert(
_verifyTypes(
const [ir.NumType.i32, ir.NumType.i32],
const [ir.NumType.i32],
trace: const ['i32.shr_s'],
),
);
_add(const ir.I32ShrS());
}
/// Emit an `i32.shr_u` instruction.
void i32_shr_u() {
assert(
_verifyTypes(
const [ir.NumType.i32, ir.NumType.i32],
const [ir.NumType.i32],
trace: const ['i32.shr_u'],
),
);
_add(const ir.I32ShrU());
}
/// Emit an `i32.rotl` instruction.
void i32_rotl() {
assert(
_verifyTypes(
const [ir.NumType.i32, ir.NumType.i32],
const [ir.NumType.i32],
trace: const ['i32.rotl'],
),
);
_add(const ir.I32Rotl());
}
/// Emit an `i32.rotr` instruction.
void i32_rotr() {
assert(
_verifyTypes(
const [ir.NumType.i32, ir.NumType.i32],
const [ir.NumType.i32],
trace: const ['i32.rotr'],
),
);
_add(const ir.I32Rotr());
}
/// Emit an `i64.clz` instruction.
void i64_clz() {
assert(
_verifyTypes(
const [ir.NumType.i64],
const [ir.NumType.i64],
trace: const ['i64.clz'],
),
);
_add(const ir.I64Clz());
}
/// Emit an `i64.ctz` instruction.
void i64_ctz() {
assert(
_verifyTypes(
const [ir.NumType.i64],
const [ir.NumType.i64],
trace: const ['i64.ctz'],
),
);
_add(const ir.I64Ctz());
}
/// Emit an `i64.popcnt` instruction.
void i64_popcnt() {
assert(
_verifyTypes(
const [ir.NumType.i64],
const [ir.NumType.i64],
trace: const ['i64.popcnt'],
),
);
_add(const ir.I64Popcnt());
}
/// Emit an `i64.add` instruction.
void i64_add() {
assert(
_verifyTypes(
const [ir.NumType.i64, ir.NumType.i64],
const [ir.NumType.i64],
trace: const ['i64.add'],
),
);
_add(const ir.I64Add());
}
/// Emit an `i64.sub` instruction.
void i64_sub() {
assert(
_verifyTypes(
const [ir.NumType.i64, ir.NumType.i64],
const [ir.NumType.i64],
trace: const ['i64.sub'],
),
);
_add(const ir.I64Sub());
}
/// Emit an `i64.mul` instruction.
void i64_mul() {
assert(
_verifyTypes(
const [ir.NumType.i64, ir.NumType.i64],
const [ir.NumType.i64],
trace: const ['i64.mul'],
),
);
_add(const ir.I64Mul());
}
/// Emit an `i64.div_s` instruction.
void i64_div_s() {
assert(
_verifyTypes(
const [ir.NumType.i64, ir.NumType.i64],
const [ir.NumType.i64],
trace: const ['i64.div_s'],
),
);
_add(const ir.I64DivS());
}
/// Emit an `i64.div_u` instruction.
void i64_div_u() {
assert(
_verifyTypes(
const [ir.NumType.i64, ir.NumType.i64],
const [ir.NumType.i64],
trace: const ['i64.div_u'],
),
);
_add(const ir.I64DivU());
}
/// Emit an `i64.rem_s` instruction.
void i64_rem_s() {
assert(
_verifyTypes(
const [ir.NumType.i64, ir.NumType.i64],
const [ir.NumType.i64],
trace: const ['i64.rem_s'],
),
);
_add(const ir.I64RemS());
}
/// Emit an `i64.rem_u` instruction.
void i64_rem_u() {
assert(
_verifyTypes(
const [ir.NumType.i64, ir.NumType.i64],
const [ir.NumType.i64],
trace: const ['i64.rem_u'],
),
);
_add(const ir.I64RemU());
}
/// Emit an `i64.and` instruction.
void i64_and() {
assert(
_verifyTypes(
const [ir.NumType.i64, ir.NumType.i64],
const [ir.NumType.i64],
trace: const ['i64.and'],
),
);
_add(const ir.I64And());
}
/// Emit an `i64.or` instruction.
void i64_or() {
assert(
_verifyTypes(
const [ir.NumType.i64, ir.NumType.i64],
const [ir.NumType.i64],
trace: const ['i64.or'],
),
);
_add(const ir.I64Or());
}
/// Emit an `i64.xor` instruction.
void i64_xor() {
assert(
_verifyTypes(
const [ir.NumType.i64, ir.NumType.i64],
const [ir.NumType.i64],
trace: const ['i64.xor'],
),
);
_add(const ir.I64Xor());
}
/// Emit an `i64.shl` instruction.
void i64_shl() {
assert(
_verifyTypes(
const [ir.NumType.i64, ir.NumType.i64],
const [ir.NumType.i64],
trace: const ['i64.shl'],
),
);
_add(const ir.I64Shl());
}
/// Emit an `i64.shr_s` instruction.
void i64_shr_s() {
assert(
_verifyTypes(
const [ir.NumType.i64, ir.NumType.i64],
const [ir.NumType.i64],
trace: const ['i64.shr_s'],
),
);
_add(const ir.I64ShrS());
}
/// Emit an `i64.shr_u` instruction.
void i64_shr_u() {
assert(
_verifyTypes(
const [ir.NumType.i64, ir.NumType.i64],
const [ir.NumType.i64],
trace: const ['i64.shr_u'],
),
);
_add(const ir.I64ShrU());
}
/// Emit an `i64.rotl` instruction.
void i64_rotl() {
assert(
_verifyTypes(
const [ir.NumType.i64, ir.NumType.i64],
const [ir.NumType.i64],
trace: const ['i64.rotl'],
),
);
_add(const ir.I64Rotl());
}
/// Emit an `i64.rotr` instruction.
void i64_rotr() {
assert(
_verifyTypes(
const [ir.NumType.i64, ir.NumType.i64],
const [ir.NumType.i64],
trace: const ['i64.rotr'],
),
);
_add(const ir.I64Rotr());
}
/// Emit an `f32.abs` instruction.
void f32_abs() {
assert(
_verifyTypes(
const [ir.NumType.f32],
const [ir.NumType.f32],
trace: const ['f32.abs'],
),
);
_add(const ir.F32Abs());
}
/// Emit an `f32.neg` instruction.
void f32_neg() {
assert(
_verifyTypes(
const [ir.NumType.f32],
const [ir.NumType.f32],
trace: const ['f32.neg'],
),
);
_add(const ir.F32Neg());
}
/// Emit an `f32.ceil` instruction.
void f32_ceil() {
assert(
_verifyTypes(
const [ir.NumType.f32],
const [ir.NumType.f32],
trace: const ['f32.ceil'],
),
);
_add(const ir.F32Ceil());
}
/// Emit an `f32.floor` instruction.
void f32_floor() {
assert(
_verifyTypes(
const [ir.NumType.f32],
const [ir.NumType.f32],
trace: const ['f32.floor'],
),
);
_add(const ir.F32Floor());
}
/// Emit an `f32.trunc` instruction.
void f32_trunc() {
assert(
_verifyTypes(
const [ir.NumType.f32],
const [ir.NumType.f32],
trace: const ['f32.trunc'],
),
);
_add(const ir.F32Trunc());
}
/// Emit an `f32.nearest` instruction.
void f32_nearest() {
assert(
_verifyTypes(
const [ir.NumType.f32],
const [ir.NumType.f32],
trace: const ['f32.nearest'],
),
);
_add(const ir.F32Nearest());
}
/// Emit an `f32.sqrt` instruction.
void f32_sqrt() {
assert(
_verifyTypes(
const [ir.NumType.f32],
const [ir.NumType.f32],
trace: const ['f32.sqrt'],
),
);
_add(const ir.F32Sqrt());
}
/// Emit an `f32.add` instruction.
void f32_add() {
assert(
_verifyTypes(
const [ir.NumType.f32, ir.NumType.f32],
const [ir.NumType.f32],
trace: const ['f32.add'],
),
);
_add(const ir.F32Add());
}
/// Emit an `f32.sub` instruction.
void f32_sub() {
assert(
_verifyTypes(
const [ir.NumType.f32, ir.NumType.f32],
const [ir.NumType.f32],
trace: const ['f32.sub'],
),
);
_add(const ir.F32Sub());
}
/// Emit an `f32.mul` instruction.
void f32_mul() {
assert(
_verifyTypes(
const [ir.NumType.f32, ir.NumType.f32],
const [ir.NumType.f32],
trace: const ['f32.mul'],
),
);
_add(const ir.F32Mul());
}
/// Emit an `f32.div` instruction.
void f32_div() {
assert(
_verifyTypes(
const [ir.NumType.f32, ir.NumType.f32],
const [ir.NumType.f32],
trace: const ['f32.div'],
),
);
_add(const ir.F32Div());
}
/// Emit an `f32.min` instruction.
void f32_min() {
assert(
_verifyTypes(
const [ir.NumType.f32, ir.NumType.f32],
const [ir.NumType.f32],
trace: const ['f32.min'],
),
);
_add(const ir.F32Min());
}
/// Emit an `f32.max` instruction.
void f32_max() {
assert(
_verifyTypes(
const [ir.NumType.f32, ir.NumType.f32],
const [ir.NumType.f32],
trace: const ['f32.max'],
),
);
_add(const ir.F32Max());
}
/// Emit an `f32.copysign` instruction.
void f32_copysign() {
assert(
_verifyTypes(
const [ir.NumType.f32, ir.NumType.f32],
const [ir.NumType.f32],
trace: const ['f32.copysign'],
),
);
_add(const ir.F32Copysign());
}
/// Emit an `f64.abs` instruction.
void f64_abs() {
assert(
_verifyTypes(
const [ir.NumType.f64],
const [ir.NumType.f64],
trace: const ['f64.abs'],
),
);
_add(const ir.F64Abs());
}
/// Emit an `f64.neg` instruction.
void f64_neg() {
assert(
_verifyTypes(
const [ir.NumType.f64],
const [ir.NumType.f64],
trace: const ['f64.neg'],
),
);
_add(const ir.F64Neg());
}
/// Emit an `f64.ceil` instruction.
void f64_ceil() {
assert(
_verifyTypes(
const [ir.NumType.f64],
const [ir.NumType.f64],
trace: const ['f64.ceil'],
),
);
_add(const ir.F64Ceil());
}
/// Emit an `f64.floor` instruction.
void f64_floor() {
assert(
_verifyTypes(
const [ir.NumType.f64],
const [ir.NumType.f64],
trace: const ['f64.floor'],
),
);
_add(const ir.F64Floor());
}
/// Emit an `f64.trunc` instruction.
void f64_trunc() {
assert(
_verifyTypes(
const [ir.NumType.f64],
const [ir.NumType.f64],
trace: const ['f64.trunc'],
),
);
_add(const ir.F64Trunc());
}
/// Emit an `f64.nearest` instruction.
void f64_nearest() {
assert(
_verifyTypes(
const [ir.NumType.f64],
const [ir.NumType.f64],
trace: const ['f64.nearest'],
),
);
_add(const ir.F64Nearest());
}
/// Emit an `f64.sqrt` instruction.
void f64_sqrt() {
assert(
_verifyTypes(
const [ir.NumType.f64],
const [ir.NumType.f64],
trace: const ['f64.sqrt'],
),
);
_add(const ir.F64Sqrt());
}
/// Emit an `f64.add` instruction.
void f64_add() {
assert(
_verifyTypes(
const [ir.NumType.f64, ir.NumType.f64],
const [ir.NumType.f64],
trace: const ['f64.add'],
),
);
_add(const ir.F64Add());
}
/// Emit an `f64.sub` instruction.
void f64_sub() {
assert(
_verifyTypes(
const [ir.NumType.f64, ir.NumType.f64],
const [ir.NumType.f64],
trace: const ['f64.sub'],
),
);
_add(const ir.F64Sub());
}
/// Emit an `f64.mul` instruction.
void f64_mul() {
assert(
_verifyTypes(
const [ir.NumType.f64, ir.NumType.f64],
const [ir.NumType.f64],
trace: const ['f64.mul'],
),
);
_add(const ir.F64Mul());
}
/// Emit an `f64.div` instruction.
void f64_div() {
assert(
_verifyTypes(
const [ir.NumType.f64, ir.NumType.f64],
const [ir.NumType.f64],
trace: const ['f64.div'],
),
);
_add(const ir.F64Div());
}
/// Emit an `f64.min` instruction.
void f64_min() {
assert(
_verifyTypes(
const [ir.NumType.f64, ir.NumType.f64],
const [ir.NumType.f64],
trace: const ['f64.min'],
),
);
_add(const ir.F64Min());
}
/// Emit an `f64.max` instruction.
void f64_max() {
assert(
_verifyTypes(
const [ir.NumType.f64, ir.NumType.f64],
const [ir.NumType.f64],
trace: const ['f64.max'],
),
);
_add(const ir.F64Max());
}
/// Emit an `f64.copysign` instruction.
void f64_copysign() {
assert(
_verifyTypes(
const [ir.NumType.f64, ir.NumType.f64],
const [ir.NumType.f64],
trace: const ['f64.copysign'],
),
);
_add(const ir.F64Copysign());
}
/// Emit an `i32.wrap_i64` instruction.
void i32_wrap_i64() {
assert(
_verifyTypes(
const [ir.NumType.i64],
const [ir.NumType.i32],
trace: const ['i32.wrap_i64'],
),
);
_add(const ir.I32WrapI64());
}
/// Emit an `i32.trunc_f32_s` instruction.
void i32_trunc_f32_s() {
assert(
_verifyTypes(
const [ir.NumType.f32],
const [ir.NumType.i32],
trace: const ['i32.trunc_f32_s'],
),
);
_add(const ir.I32TruncF32S());
}
/// Emit an `i32.trunc_f32_u` instruction.
void i32_trunc_f32_u() {
assert(
_verifyTypes(
const [ir.NumType.f32],
const [ir.NumType.i32],
trace: const ['i32.trunc_f32_u'],
),
);
_add(const ir.I32TruncF32U());
}
/// Emit an `i32.trunc_f64_s` instruction.
void i32_trunc_f64_s() {
assert(
_verifyTypes(
const [ir.NumType.f64],
const [ir.NumType.i32],
trace: const ['i32.trunc_f64_s'],
),
);
_add(const ir.I32TruncF64S());
}
/// Emit an `i32.trunc_f64_u` instruction.
void i32_trunc_f64_u() {
assert(
_verifyTypes(
const [ir.NumType.f64],
const [ir.NumType.i32],
trace: const ['i32.trunc_f64_u'],
),
);
_add(const ir.I32TruncF64U());
}
/// Emit an `i64.extend_i32_s` instruction.
void i64_extend_i32_s() {
assert(
_verifyTypes(
const [ir.NumType.i32],
const [ir.NumType.i64],
trace: const ['i64.extend_i32_s'],
),
);
_add(const ir.I64ExtendI32S());
}
/// Emit an `i64.extend_i32_u` instruction.
void i64_extend_i32_u() {
assert(
_verifyTypes(
const [ir.NumType.i32],
const [ir.NumType.i64],
trace: const ['i64.extend_i32_u'],
),
);
_add(const ir.I64ExtendI32U());
}
/// Emit an `i64.trunc_f32_s` instruction.
void i64_trunc_f32_s() {
assert(
_verifyTypes(
const [ir.NumType.f32],
const [ir.NumType.i64],
trace: const ['i64.trunc_f32_s'],
),
);
_add(const ir.I64TruncF32S());
}
/// Emit an `i64.trunc_f32_u` instruction.
void i64_trunc_f32_u() {
assert(
_verifyTypes(
const [ir.NumType.f32],
const [ir.NumType.i64],
trace: const ['i64.trunc_f32_u'],
),
);
_add(const ir.I64TruncF32U());
}
/// Emit an `i64.trunc_f64_s` instruction.
void i64_trunc_f64_s() {
assert(
_verifyTypes(
const [ir.NumType.f64],
const [ir.NumType.i64],
trace: const ['i64.trunc_f64_s'],
),
);
_add(const ir.I64TruncF64S());
}
/// Emit an `i64.trunc_f64_u` instruction.
void i64_trunc_f64_u() {
assert(
_verifyTypes(
const [ir.NumType.f64],
const [ir.NumType.i64],
trace: const ['i64.trunc_f64_u'],
),
);
_add(const ir.I64TruncF64U());
}
/// Emit an `f32.convert_i32_s` instruction.
void f32_convert_i32_s() {
assert(
_verifyTypes(
const [ir.NumType.i32],
const [ir.NumType.f32],
trace: const ['f32.convert_i32_s'],
),
);
_add(const ir.F32ConvertI32S());
}
/// Emit an `f32.convert_i32_u` instruction.
void f32_convert_i32_u() {
assert(
_verifyTypes(
const [ir.NumType.i32],
const [ir.NumType.f32],
trace: const ['f32.convert_i32_u'],
),
);
_add(const ir.F32ConvertI32U());
}
/// Emit an `f32.convert_i64_s` instruction.
void f32_convert_i64_s() {
assert(
_verifyTypes(
const [ir.NumType.i64],
const [ir.NumType.f32],
trace: const ['f32.convert_i64_s'],
),
);
_add(const ir.F32ConvertI64S());
}
/// Emit an `f32.convert_i64_u` instruction.
void f32_convert_i64_u() {
assert(
_verifyTypes(
const [ir.NumType.i64],
const [ir.NumType.f32],
trace: const ['f32.convert_i64_u'],
),
);
_add(const ir.F32ConvertI64U());
}
/// Emit an `f32.demote_f64` instruction.
void f32_demote_f64() {
assert(
_verifyTypes(
const [ir.NumType.f64],
const [ir.NumType.f32],
trace: const ['f32.demote_f64'],
),
);
_add(const ir.F32DemoteF64());
}
/// Emit an `f64.convert_i32_s` instruction.
void f64_convert_i32_s() {
assert(
_verifyTypes(
const [ir.NumType.i32],
const [ir.NumType.f64],
trace: const ['f64.convert_i32_s'],
),
);
_add(const ir.F64ConvertI32S());
}
/// Emit an `f64.convert_i32_u` instruction.
void f64_convert_i32_u() {
assert(
_verifyTypes(
const [ir.NumType.i32],
const [ir.NumType.f64],
trace: const ['f64.convert_i32_u'],
),
);
_add(const ir.F64ConvertI32U());
}
/// Emit an `f64.convert_i64_s` instruction.
void f64_convert_i64_s() {
assert(
_verifyTypes(
const [ir.NumType.i64],
const [ir.NumType.f64],
trace: const ['f64.convert_i64_s'],
),
);
_add(const ir.F64ConvertI64S());
}
/// Emit an `f64.convert_i64_u` instruction.
void f64_convert_i64_u() {
assert(
_verifyTypes(
const [ir.NumType.i64],
const [ir.NumType.f64],
trace: const ['f64.convert_i64_u'],
),
);
_add(const ir.F64ConvertI64U());
}
/// Emit an `f64.promote_f32` instruction.
void f64_promote_f32() {
assert(
_verifyTypes(
const [ir.NumType.f32],
const [ir.NumType.f64],
trace: const ['f64.promote_f32'],
),
);
_add(const ir.F64PromoteF32());
}
/// Emit an `i32.reinterpret_f32` instruction.
void i32_reinterpret_f32() {
assert(
_verifyTypes(
const [ir.NumType.f32],
const [ir.NumType.i32],
trace: const ['i32.reinterpret_f32'],
),
);
_add(const ir.I32ReinterpretF32());
}
/// Emit an `i64.reinterpret_f64` instruction.
void i64_reinterpret_f64() {
assert(
_verifyTypes(
const [ir.NumType.f64],
const [ir.NumType.i64],
trace: const ['i64.reinterpret_f64'],
),
);
_add(const ir.I64ReinterpretF64());
}
/// Emit an `f32.reinterpret_i32` instruction.
void f32_reinterpret_i32() {
assert(
_verifyTypes(
const [ir.NumType.i32],
const [ir.NumType.f32],
trace: const ['f32.reinterpret_i32'],
),
);
_add(const ir.F32ReinterpretI32());
}
/// Emit an `f64.reinterpret_i64` instruction.
void f64_reinterpret_i64() {
assert(
_verifyTypes(
const [ir.NumType.i64],
const [ir.NumType.f64],
trace: const ['f64.reinterpret_i64'],
),
);
_add(const ir.F64ReinterpretI64());
}
/// Emit an `i32.extend8_s` instruction.
void i32_extend8_s() {
assert(
_verifyTypes(
const [ir.NumType.i32],
const [ir.NumType.i32],
trace: const ['i32.extend8_s'],
),
);
_add(const ir.I32Extend8S());
}
/// Emit an `i32.extend16_s` instruction.
void i32_extend16_s() {
assert(
_verifyTypes(
const [ir.NumType.i32],
const [ir.NumType.i32],
trace: const ['i32.extend16_s'],
),
);
_add(const ir.I32Extend16S());
}
/// Emit an `i64.extend8_s` instruction.
void i64_extend8_s() {
assert(
_verifyTypes(
const [ir.NumType.i64],
const [ir.NumType.i64],
trace: const ['i64.extend8_s'],
),
);
_add(const ir.I64Extend8S());
}
/// Emit an `i64.extend16_s` instruction.
void i64_extend16_s() {
assert(
_verifyTypes(
const [ir.NumType.i64],
const [ir.NumType.i64],
trace: const ['i64.extend16_s'],
),
);
_add(const ir.I64Extend16S());
}
/// Emit an `i64.extend32_s` instruction.
void i64_extend32_s() {
assert(
_verifyTypes(
const [ir.NumType.i64],
const [ir.NumType.i64],
trace: const ['i64.extend32_s'],
),
);
_add(const ir.I64Extend32S());
}
/// Emit an `i32.trunc_sat_f32_s` instruction.
void i32_trunc_sat_f32_s() {
assert(
_verifyTypes(
const [ir.NumType.f32],
const [ir.NumType.i32],
trace: const ['i32.trunc_sat_f32_s'],
),
);
_add(const ir.I32TruncSatF32S());
}
/// Emit an `i32.trunc_sat_f32_u` instruction.
void i32_trunc_sat_f32_u() {
assert(
_verifyTypes(
const [ir.NumType.f32],
const [ir.NumType.i32],
trace: const ['i32.trunc_sat_f32_u'],
),
);
_add(const ir.I32TruncSatF32U());
}
/// Emit an `i32.trunc_sat_f64_s` instruction.
void i32_trunc_sat_f64_s() {
assert(
_verifyTypes(
const [ir.NumType.f64],
const [ir.NumType.i32],
trace: const ['i32.trunc_sat_f64_s'],
),
);
_add(const ir.I32TruncSatF64S());
}
/// Emit an `i32.trunc_sat_f64_u` instruction.
void i32_trunc_sat_f64_u() {
assert(
_verifyTypes(
const [ir.NumType.f64],
const [ir.NumType.i32],
trace: const ['i32.trunc_sat_f64_u'],
),
);
_add(const ir.I32TruncSatF64U());
}
/// Emit an `i64.trunc_sat_f32_s` instruction.
void i64_trunc_sat_f32_s() {
assert(
_verifyTypes(
const [ir.NumType.f32],
const [ir.NumType.i64],
trace: const ['i64.trunc_sat_f32_s'],
),
);
_add(const ir.I64TruncSatF32S());
}
/// Emit an `i64.trunc_sat_f32_u` instruction.
void i64_trunc_sat_f32_u() {
assert(
_verifyTypes(
const [ir.NumType.f32],
const [ir.NumType.i64],
trace: const ['i64.trunc_sat_f32_u'],
),
);
_add(const ir.I64TruncSatF32U());
}
/// Emit an `i64.trunc_sat_f64_s` instruction.
void i64_trunc_sat_f64_s() {
assert(
_verifyTypes(
const [ir.NumType.f64],
const [ir.NumType.i64],
trace: const ['i64.trunc_sat_f64_s'],
),
);
_add(const ir.I64TruncSatF64S());
}
/// Emit an `i64.trunc_sat_f64_u` instruction.
void i64_trunc_sat_f64_u() {
assert(
_verifyTypes(
const [ir.NumType.f64],
const [ir.NumType.i64],
trace: const ['i64.trunc_sat_f64_u'],
),
);
_add(const ir.I64TruncSatF64U());
}
void i8x16_splat() {
assert(
_verifyTypes(
const [ir.NumType.i32],
const [ir.NumType.v128],
trace: const ['i8x16.splat'],
),
);
_add(ir.V128Instruction.i8x16Splat);
}
void i16x8_splat() {
assert(
_verifyTypes(
const [ir.NumType.i32],
const [ir.NumType.v128],
trace: const ['i16x8.splat'],
),
);
_add(ir.V128Instruction.i16x8Splat);
}
void i32x4_splat() {
assert(
_verifyTypes(
const [ir.NumType.i32],
const [ir.NumType.v128],
trace: const ['i32x4.splat'],
),
);
_add(ir.V128Instruction.i32x4Splat);
}
void i64x2_splat() {
assert(
_verifyTypes(
const [ir.NumType.i64],
const [ir.NumType.v128],
trace: const ['i64x2.splat'],
),
);
_add(ir.V128Instruction.i64x2Splat);
}
void f32x4_splat() {
assert(
_verifyTypes(
const [ir.NumType.f32],
const [ir.NumType.v128],
trace: const ['f32x4.splat'],
),
);
_add(ir.V128Instruction.f32x4Splat);
}
void f64x2_splat() {
assert(
_verifyTypes(
const [ir.NumType.f64],
const [ir.NumType.v128],
trace: const ['f64x2.splat'],
),
);
_add(ir.V128Instruction.f64x2Splat);
}
void f32x4_eq() {
assert(
_verifyTypes(
const [ir.NumType.v128, ir.NumType.v128],
const [ir.NumType.v128],
trace: const ['f32x4.eq'],
),
);
_add(ir.V128Instruction.f32x4Eq);
}
void f32x4_ne() {
assert(
_verifyTypes(
const [ir.NumType.v128, ir.NumType.v128],
const [ir.NumType.v128],
trace: const ['f32x4.ne'],
),
);
_add(ir.V128Instruction.f32x4Ne);
}
void f32x4_lt() {
assert(
_verifyTypes(
const [ir.NumType.v128, ir.NumType.v128],
const [ir.NumType.v128],
trace: const ['f32x4.lt'],
),
);
_add(ir.V128Instruction.f32x4Lt);
}
void f32x4_gt() {
assert(
_verifyTypes(
const [ir.NumType.v128, ir.NumType.v128],
const [ir.NumType.v128],
trace: const ['f32x4.gt'],
),
);
_add(ir.V128Instruction.f32x4Gt);
}
void f32x4_le() {
assert(
_verifyTypes(
const [ir.NumType.v128, ir.NumType.v128],
const [ir.NumType.v128],
trace: const ['f32x4.le'],
),
);
_add(ir.V128Instruction.f32x4Le);
}
void f32x4_ge() {
assert(
_verifyTypes(
const [ir.NumType.v128, ir.NumType.v128],
const [ir.NumType.v128],
trace: const ['f32x4.ge'],
),
);
_add(ir.V128Instruction.f32x4Ge);
}
void f32x4_abs() {
assert(
_verifyTypes(
const [ir.NumType.v128],
const [ir.NumType.v128],
trace: const ['f32x4.abs'],
),
);
_add(ir.V128Instruction.f32x4Abs);
}
void f32x4_neg() {
assert(
_verifyTypes(
const [ir.NumType.v128],
const [ir.NumType.v128],
trace: const ['f32x4.neg'],
),
);
_add(ir.V128Instruction.f32x4Neg);
}
void f32x4_sqrt() {
assert(
_verifyTypes(
const [ir.NumType.v128],
const [ir.NumType.v128],
trace: const ['f32x4.sqrt'],
),
);
_add(ir.V128Instruction.f32x4Sqrt);
}
void f32x4_add() {
assert(
_verifyTypes(
const [ir.NumType.v128, ir.NumType.v128],
const [ir.NumType.v128],
trace: const ['f32x4.add'],
),
);
_add(ir.V128Instruction.f32x4Add);
}
void f32x4_sub() {
assert(
_verifyTypes(
const [ir.NumType.v128, ir.NumType.v128],
const [ir.NumType.v128],
trace: const ['f32x4.sub'],
),
);
_add(ir.V128Instruction.f32x4Sub);
}
void f32x4_mul() {
assert(
_verifyTypes(
const [ir.NumType.v128, ir.NumType.v128],
const [ir.NumType.v128],
trace: const ['f32x4.mul'],
),
);
_add(ir.V128Instruction.f32x4Mul);
}
void f32x4_div() {
assert(
_verifyTypes(
const [ir.NumType.v128, ir.NumType.v128],
const [ir.NumType.v128],
trace: const ['f32x4.div'],
),
);
_add(ir.V128Instruction.f32x4Div);
}
void f32x4_min() {
assert(
_verifyTypes(
const [ir.NumType.v128, ir.NumType.v128],
const [ir.NumType.v128],
trace: const ['f32x4.min'],
),
);
_add(ir.V128Instruction.f32x4Min);
}
void f32x4_max() {
assert(
_verifyTypes(
const [ir.NumType.v128, ir.NumType.v128],
const [ir.NumType.v128],
trace: const ['f32x4.max'],
),
);
_add(ir.V128Instruction.f32x4Max);
}
void f32x4_pmin() {
assert(
_verifyTypes(
const [ir.NumType.v128, ir.NumType.v128],
const [ir.NumType.v128],
trace: const ['f32x4.pmin'],
),
);
_add(ir.V128Instruction.f32x4PMin);
}
void f32x4_pmax() {
assert(
_verifyTypes(
const [ir.NumType.v128, ir.NumType.v128],
const [ir.NumType.v128],
trace: const ['f32x4.pmax'],
),
);
_add(ir.V128Instruction.f32x4PMax);
}
void f64x2_eq() {
assert(
_verifyTypes(
const [ir.NumType.v128, ir.NumType.v128],
const [ir.NumType.v128],
trace: const ['f64x2.eq'],
),
);
_add(ir.V128Instruction.f64x2Eq);
}
void f64x2_ne() {
assert(
_verifyTypes(
const [ir.NumType.v128, ir.NumType.v128],
const [ir.NumType.v128],
trace: const ['f64x2.ne'],
),
);
_add(ir.V128Instruction.f64x2Ne);
}
void f64x2_lt() {
assert(
_verifyTypes(
const [ir.NumType.v128, ir.NumType.v128],
const [ir.NumType.v128],
trace: const ['f64x2.lt'],
),
);
_add(ir.V128Instruction.f64x2Lt);
}
void f64x2_gt() {
assert(
_verifyTypes(
const [ir.NumType.v128, ir.NumType.v128],
const [ir.NumType.v128],
trace: const ['f64x2.gt'],
),
);
_add(ir.V128Instruction.f64x2Gt);
}
void f64x2_le() {
assert(
_verifyTypes(
const [ir.NumType.v128, ir.NumType.v128],
const [ir.NumType.v128],
trace: const ['f64x2.le'],
),
);
_add(ir.V128Instruction.f64x2Le);
}
void f64x2_ge() {
assert(
_verifyTypes(
const [ir.NumType.v128, ir.NumType.v128],
const [ir.NumType.v128],
trace: const ['f64x2.ge'],
),
);
_add(ir.V128Instruction.f64x2Ge);
}
void f64x2_abs() {
assert(
_verifyTypes(
const [ir.NumType.v128],
const [ir.NumType.v128],
trace: const ['f64x2.abs'],
),
);
_add(ir.V128Instruction.f64x2Abs);
}
/// Emit an `f32x4.ceil` instruction.
void f32x4_ceil() {
assert(
_verifyTypes(
const [ir.NumType.v128],
const [ir.NumType.v128],
trace: const ['f32x4.ceil'],
),
);
_add(ir.V128Instruction.f32x4Ceil);
}
/// Emit an `f32x4.floor` instruction.
void f32x4_floor() {
assert(
_verifyTypes(
const [ir.NumType.v128],
const [ir.NumType.v128],
trace: const ['f32x4.floor'],
),
);
_add(ir.V128Instruction.f32x4Floor);
}
/// Emit an `f32x4.trunc` instruction.
void f32x4_trunc() {
assert(
_verifyTypes(
const [ir.NumType.v128],
const [ir.NumType.v128],
trace: const ['f32x4.trunc'],
),
);
_add(ir.V128Instruction.f32x4Trunc);
}
/// Emit an `f32x4.nearest` instruction.
void f32x4_nearest() {
assert(
_verifyTypes(
const [ir.NumType.v128],
const [ir.NumType.v128],
trace: const ['f32x4.nearest'],
),
);
_add(ir.V128Instruction.f32x4Nearest);
}
void f64x2_neg() {
assert(
_verifyTypes(
const [ir.NumType.v128],
const [ir.NumType.v128],
trace: const ['f64x2.neg'],
),
);
_add(ir.V128Instruction.f64x2Neg);
}
void f64x2_sqrt() {
assert(
_verifyTypes(
const [ir.NumType.v128],
const [ir.NumType.v128],
trace: const ['f64x2.sqrt'],
),
);
_add(ir.V128Instruction.f64x2Sqrt);
}
void f64x2_add() {
assert(
_verifyTypes(
const [ir.NumType.v128, ir.NumType.v128],
const [ir.NumType.v128],
trace: const ['f64x2.add'],
),
);
_add(ir.V128Instruction.f64x2Add);
}
void f64x2_sub() {
assert(
_verifyTypes(
const [ir.NumType.v128, ir.NumType.v128],
const [ir.NumType.v128],
trace: const ['f64x2.sub'],
),
);
_add(ir.V128Instruction.f64x2Sub);
}
void f64x2_mul() {
assert(
_verifyTypes(
const [ir.NumType.v128, ir.NumType.v128],
const [ir.NumType.v128],
trace: const ['f64x2.mul'],
),
);
_add(ir.V128Instruction.f64x2Mul);
}
void f64x2_div() {
assert(
_verifyTypes(
const [ir.NumType.v128, ir.NumType.v128],
const [ir.NumType.v128],
trace: const ['f64x2.div'],
),
);
_add(ir.V128Instruction.f64x2Div);
}
void f64x2_min() {
assert(
_verifyTypes(
const [ir.NumType.v128, ir.NumType.v128],
const [ir.NumType.v128],
trace: const ['f64x2.min'],
),
);
_add(ir.V128Instruction.f64x2Min);
}
void f64x2_max() {
assert(
_verifyTypes(
const [ir.NumType.v128, ir.NumType.v128],
const [ir.NumType.v128],
trace: const ['f64x2.max'],
),
);
_add(ir.V128Instruction.f64x2Max);
}
void f64x2_pmin() {
assert(
_verifyTypes(
const [ir.NumType.v128, ir.NumType.v128],
const [ir.NumType.v128],
trace: const ['f64x2.pmin'],
),
);
_add(ir.V128Instruction.f64x2PMin);
}
void f64x2_pmax() {
assert(
_verifyTypes(
const [ir.NumType.v128, ir.NumType.v128],
const [ir.NumType.v128],
trace: const ['f64x2.pmax'],
),
);
_add(ir.V128Instruction.f64x2PMax);
}
void f64x2_ceil() {
assert(
_verifyTypes(
const [ir.NumType.v128],
const [ir.NumType.v128],
trace: const ['f64x2.ceil'],
),
);
_add(ir.V128Instruction.f64x2Ceil);
}
void f64x2_floor() {
assert(
_verifyTypes(
const [ir.NumType.v128],
const [ir.NumType.v128],
trace: const ['f64x2.floor'],
),
);
_add(ir.V128Instruction.f64x2Floor);
}
void f64x2_trunc() {
assert(
_verifyTypes(
const [ir.NumType.v128],
const [ir.NumType.v128],
trace: const ['f64x2.trunc'],
),
);
_add(ir.V128Instruction.f64x2Trunc);
}
void f64x2_nearest() {
assert(
_verifyTypes(
const [ir.NumType.v128],
const [ir.NumType.v128],
trace: const ['f64x2.nearest'],
),
);
_add(ir.V128Instruction.f64x2Nearest);
}
void i8x16_extract_lane_s(int lane) {
assert(
_verifyTypes(
const [ir.NumType.v128],
const [ir.NumType.i32],
trace: const ['i8x16.extract_lane_s'],
),
);
_add(ir.I8x16ExtractLaneS(lane));
}
void i8x16_extract_lane_u(int lane) {
assert(
_verifyTypes(
const [ir.NumType.v128],
const [ir.NumType.i32],
trace: const ['i8x16.extract_lane_u'],
),
);
_add(ir.I8x16ExtractLaneU(lane));
}
void i16x8_extract_lane_s(int lane) {
assert(
_verifyTypes(
const [ir.NumType.v128],
const [ir.NumType.i32],
trace: const ['i16x8.extract_lane_s'],
),
);
_add(ir.I16x8ExtractLaneS(lane));
}
void i16x8_extract_lane_u(int lane) {
assert(
_verifyTypes(
const [ir.NumType.v128],
const [ir.NumType.i32],
trace: const ['i16x8.extract_lane_u'],
),
);
_add(ir.I16x8ExtractLaneU(lane));
}
void i32x4_extract_lane(int lane) {
assert(
_verifyTypes(
const [ir.NumType.v128],
const [ir.NumType.i32],
trace: const ['i32x4.extract_lane'],
),
);
_add(ir.I32x4ExtractLane(lane));
}
void i64x2_extract_lane(int lane) {
assert(
_verifyTypes(
const [ir.NumType.v128],
const [ir.NumType.i64],
trace: const ['i64x2.extract_lane'],
),
);
_add(ir.I64x2ExtractLane(lane));
}
void f32x4_extract_lane(int lane) {
assert(
_verifyTypes(
const [ir.NumType.v128],
const [ir.NumType.f32],
trace: const ['f32x4.extract_lane'],
),
);
_add(ir.F32x4ExtractLane(lane));
}
void f64x2_extract_lane(int lane) {
assert(
_verifyTypes(
const [ir.NumType.v128],
const [ir.NumType.f64],
trace: const ['f64x2.extract_lane'],
),
);
_add(ir.F64x2ExtractLane(lane));
}
void i8x16_replace_lane(int lane) {
assert(
_verifyTypes(
const [ir.NumType.v128, ir.NumType.i32],
const [ir.NumType.v128],
trace: const ['i8x16.replace_lane'],
),
);
_add(ir.I8x16ReplaceLane(lane));
}
void i16x8_replace_lane(int lane) {
assert(
_verifyTypes(
const [ir.NumType.v128, ir.NumType.i32],
const [ir.NumType.v128],
trace: const ['i16x8.replace_lane'],
),
);
_add(ir.I16x8ReplaceLane(lane));
}
void i32x4_replace_lane(int lane) {
assert(
_verifyTypes(
const [ir.NumType.v128, ir.NumType.i32],
const [ir.NumType.v128],
trace: const ['i32x4.replace_lane'],
),
);
_add(ir.I32x4ReplaceLane(lane));
}
void i64x2_replace_lane(int lane) {
assert(
_verifyTypes(
const [ir.NumType.v128, ir.NumType.i64],
const [ir.NumType.v128],
trace: const ['i64x2.replace_lane'],
),
);
_add(ir.I64x2ReplaceLane(lane));
}
void f32x4_replace_lane(int lane) {
assert(
_verifyTypes(
const [ir.NumType.v128, ir.NumType.f32],
const [ir.NumType.v128],
trace: const ['f32x4.replace_lane'],
),
);
_add(ir.F32x4ReplaceLane(lane));
}
void f64x2_replace_lane(int lane) {
assert(
_verifyTypes(
const [ir.NumType.v128, ir.NumType.f64],
const [ir.NumType.v128],
trace: const ['f64x2.replace_lane'],
),
);
_add(ir.F64x2ReplaceLane(lane));
}
void i8x16_add() {
assert(
_verifyTypes(
const [ir.NumType.v128, ir.NumType.v128],
const [ir.NumType.v128],
trace: const ['i8x16.add'],
),
);
_add(ir.V128Instruction.i8x16Add);
}
void i8x16_sub() {
assert(
_verifyTypes(
const [ir.NumType.v128, ir.NumType.v128],
const [ir.NumType.v128],
trace: const ['i8x16.sub'],
),
);
_add(ir.V128Instruction.i8x16Sub);
}
void i8x16_neg() {
assert(
_verifyTypes(
const [ir.NumType.v128],
const [ir.NumType.v128],
trace: const ['i8x16.neg'],
),
);
_add(ir.V128Instruction.i8x16Neg);
}
void i8x16_eq() {
assert(
_verifyTypes(
const [ir.NumType.v128, ir.NumType.v128],
const [ir.NumType.v128],
trace: const ['i8x16.eq'],
),
);
_add(ir.V128Instruction.i8x16Eq);
}
void i16x8_eq() {
assert(
_verifyTypes(
const [ir.NumType.v128, ir.NumType.v128],
const [ir.NumType.v128],
trace: const ['i16x8.eq'],
),
);
_add(ir.V128Instruction.i16x8Eq);
}
void i32x4_eq() {
assert(
_verifyTypes(
const [ir.NumType.v128, ir.NumType.v128],
const [ir.NumType.v128],
trace: const ['i32x4.eq'],
),
);
_add(ir.V128Instruction.i32x4Eq);
}
void i64x2_eq() {
assert(
_verifyTypes(
const [ir.NumType.v128, ir.NumType.v128],
const [ir.NumType.v128],
trace: const ['i64x2.eq'],
),
);
_add(ir.V128Instruction.i64x2Eq);
}
void i16x8_add() {
assert(
_verifyTypes(
const [ir.NumType.v128, ir.NumType.v128],
const [ir.NumType.v128],
trace: const ['i16x8.add'],
),
);
_add(ir.V128Instruction.i16x8Add);
}
void i16x8_sub() {
assert(
_verifyTypes(
const [ir.NumType.v128, ir.NumType.v128],
const [ir.NumType.v128],
trace: const ['i16x8.sub'],
),
);
_add(ir.V128Instruction.i16x8Sub);
}
void i16x8_mul() {
assert(
_verifyTypes(
const [ir.NumType.v128, ir.NumType.v128],
const [ir.NumType.v128],
trace: const ['i16x8.mul'],
),
);
_add(ir.V128Instruction.i16x8Mul);
}
void i16x8_neg() {
assert(
_verifyTypes(
const [ir.NumType.v128],
const [ir.NumType.v128],
trace: const ['i16x8.neg'],
),
);
_add(ir.V128Instruction.i16x8Neg);
}
void i32x4_add() {
assert(
_verifyTypes(
const [ir.NumType.v128, ir.NumType.v128],
const [ir.NumType.v128],
trace: const ['i32x4.add'],
),
);
_add(ir.V128Instruction.i32x4Add);
}
void i32x4_sub() {
assert(
_verifyTypes(
const [ir.NumType.v128, ir.NumType.v128],
const [ir.NumType.v128],
trace: const ['i32x4.sub'],
),
);
_add(ir.V128Instruction.i32x4Sub);
}
void i32x4_mul() {
assert(
_verifyTypes(
const [ir.NumType.v128, ir.NumType.v128],
const [ir.NumType.v128],
trace: const ['i32x4.mul'],
),
);
_add(ir.V128Instruction.i32x4Mul);
}
void i32x4_dot_i16x8() {
assert(
_verifyTypes(
const [ir.NumType.v128, ir.NumType.v128],
const [ir.NumType.v128],
trace: const ['i32x4.dot_i16x8_s'],
),
);
_add(ir.V128Instruction.i32x4DotI16x8);
}
void i32x4_neg() {
assert(
_verifyTypes(
const [ir.NumType.v128],
const [ir.NumType.v128],
trace: const ['i32x4.neg'],
),
);
_add(ir.V128Instruction.i32x4Neg);
}
void i64x2_add() {
assert(
_verifyTypes(
const [ir.NumType.v128, ir.NumType.v128],
const [ir.NumType.v128],
trace: const ['i64x2.add'],
),
);
_add(ir.V128Instruction.i64x2Add);
}
void i64x2_sub() {
assert(
_verifyTypes(
const [ir.NumType.v128, ir.NumType.v128],
const [ir.NumType.v128],
trace: const ['i64x2.sub'],
),
);
_add(ir.V128Instruction.i64x2Sub);
}
void i64x2_mul() {
assert(
_verifyTypes(
const [ir.NumType.v128, ir.NumType.v128],
const [ir.NumType.v128],
trace: const ['i64x2.mul'],
),
);
_add(ir.V128Instruction.i64x2Mul);
}
void i64x2_neg() {
assert(
_verifyTypes(
const [ir.NumType.v128],
const [ir.NumType.v128],
trace: const ['i64x2.neg'],
),
);
_add(ir.V128Instruction.i64x2Neg);
}
void v128_not() {
assert(
_verifyTypes(
const [ir.NumType.v128],
const [ir.NumType.v128],
trace: const ['v128.not'],
),
);
_add(ir.V128Instruction.v128Not);
}
void v128_and() {
assert(
_verifyTypes(
const [ir.NumType.v128, ir.NumType.v128],
const [ir.NumType.v128],
trace: const ['v128.and'],
),
);
_add(ir.V128Instruction.v128And);
}
void v128_andnot() {
assert(
_verifyTypes(
const [ir.NumType.v128, ir.NumType.v128],
const [ir.NumType.v128],
trace: const ['v128.andnot'],
),
);
_add(ir.V128Instruction.v128AndNot);
}
void v128_or() {
assert(
_verifyTypes(
const [ir.NumType.v128, ir.NumType.v128],
const [ir.NumType.v128],
trace: const ['v128.or'],
),
);
_add(ir.V128Instruction.v128Or);
}
void v128_xor() {
assert(
_verifyTypes(
const [ir.NumType.v128, ir.NumType.v128],
const [ir.NumType.v128],
trace: const ['v128.xor'],
),
);
_add(ir.V128Instruction.v128Xor);
}
void v128_bitselect() {
assert(
_verifyTypes(
const [ir.NumType.v128, ir.NumType.v128, ir.NumType.v128],
const [ir.NumType.v128],
trace: const ['v128.bitselect'],
),
);
_add(ir.V128Instruction.v128BitSelect);
}
void v128_any_true() {
assert(
_verifyTypes(
const [ir.NumType.v128],
const [ir.NumType.i32],
trace: const ['v128.any_true'],
),
);
_add(ir.V128Instruction.v128AnyTrue);
}
void i8x16_all_true() {
assert(
_verifyTypes(
const [ir.NumType.v128],
const [ir.NumType.i32],
trace: const ['i8x16.all_true'],
),
);
_add(ir.V128Instruction.i8x16AllTrue);
}
void i8x16_bitmask() {
assert(
_verifyTypes(
const [ir.NumType.v128],
const [ir.NumType.i32],
trace: const ['i8x16.bitmask'],
),
);
_add(ir.V128Instruction.i8x16Bitmask);
}
void i16x8_all_true() {
assert(
_verifyTypes(
const [ir.NumType.v128],
const [ir.NumType.i32],
trace: const ['i16x8.all_true'],
),
);
_add(ir.V128Instruction.i16x8AllTrue);
}
void i16x8_bitmask() {
assert(
_verifyTypes(
const [ir.NumType.v128],
const [ir.NumType.i32],
trace: const ['i16x8.bitmask'],
),
);
_add(ir.V128Instruction.i16x8Bitmask);
}
void i32x4_all_true() {
assert(
_verifyTypes(
const [ir.NumType.v128],
const [ir.NumType.i32],
trace: const ['i32x4.all_true'],
),
);
_add(ir.V128Instruction.i32x4AllTrue);
}
void i32x4_bitmask() {
assert(
_verifyTypes(
const [ir.NumType.v128],
const [ir.NumType.i32],
trace: const ['i32x4.bitmask'],
),
);
_add(ir.V128Instruction.i32x4Bitmask);
}
void i64x2_all_true() {
assert(
_verifyTypes(
const [ir.NumType.v128],
const [ir.NumType.i32],
trace: const ['i64x2.all_true'],
),
);
_add(ir.V128Instruction.i64x2AllTrue);
}
void i64x2_bitmask() {
assert(
_verifyTypes(
const [ir.NumType.v128],
const [ir.NumType.i32],
trace: const ['i64x2.bitmask'],
),
);
_add(ir.V128Instruction.i64x2Bitmask);
}
void i8x16_shuffle(List<int> lanes) {
assert(
_verifyTypes(
const [ir.NumType.v128, ir.NumType.v128],
const [ir.NumType.v128],
trace: const ['i8x16.shuffle'],
),
);
assert(lanes.length == 16);
_add(ir.I8x16Shuffle(lanes));
}
String _localTraceString(ir.Local local) {
final localName = localNames[local.index];
if (localName == null) {
return local.toString();
} else {
return '$local ($localName)';
}
}
}
class _PatchableRegion {
final int start;
final InstructionsBuilder patchBuilder;
_PatchableRegion._PatchPoint(this.start, this.patchBuilder);
}