blob: b06bf513746be620003c5538873c14a3d215ab35 [file]
// 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 '../../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));
}
/// 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 i16x8_all_true() {
assert(
_verifyTypes(
const [ir.NumType.v128],
const [ir.NumType.i32],
trace: const ['i16x8.all_true'],
),
);
_add(ir.V128Instruction.i16x8AllTrue);
}
void i32x4_all_true() {
assert(
_verifyTypes(
const [ir.NumType.v128],
const [ir.NumType.i32],
trace: const ['i32x4.all_true'],
),
);
_add(ir.V128Instruction.i32x4AllTrue);
}
void i64x2_all_true() {
assert(
_verifyTypes(
const [ir.NumType.v128],
const [ir.NumType.i32],
trace: const ['i64x2.all_true'],
),
);
_add(ir.V128Instruction.i64x2AllTrue);
}
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);
}