summaryrefslogtreecommitdiff
path: root/src/interp/istream.cc
diff options
context:
space:
mode:
authorBen Smith <binji@chromium.org>2020-02-21 14:11:44 -0800
committerGitHub <noreply@github.com>2020-02-21 14:11:44 -0800
commit24a12ecfdde0246527506c87bd82460beb761c57 (patch)
tree242550e84d516926a71bef00342f4f57d011d4f6 /src/interp/istream.cc
parentbb4b65186668ff3ebd2f088778337608969a9567 (diff)
downloadwabt-24a12ecfdde0246527506c87bd82460beb761c57.tar.gz
wabt-24a12ecfdde0246527506c87bd82460beb761c57.tar.bz2
wabt-24a12ecfdde0246527506c87bd82460beb761c57.zip
New interpreter (#1330)
It's modeled closely on the wasm-c-api. All runtime objects are garbage-collected. The GC'd objects are subclasses of `Object` and all stored in the `Store`. GC roots can be created using a RefPtr<T> struct. The `Module` stores no runtime objects, only description structs (named `*Desc`, e.g. `FuncDesc`). Instantiation takes these descriptors and turns them into runtime objects (e.g. `FuncDesc` is used to create a `Func`). Only import-matching occurs during instantiation; all other validation is assumed to have occurred during Module compilation. As with the previous interpreter, the wasm instructions are not executed directly, and instead compiled to an instruction stream that is easier to interpret (e.g. all branches become gotos). It's still a stack machine, though. The previous interpreter would always compile and instantiate in one step, which means that it was always known whether an imported function is a host function or wasm function. As a result, calls between modules that stayed in wasm could be very cheap (just update PC). Now that the module is compiled before instantiation, an imported function call always has to check first, which may be a slight performance hit. One major difference to the structure of the interpreter is that floating-point values are passed directly, instead of using their equivalent bit pattern. This is more convenient in general, but requires annotating all functions with WABT_VECTORCALL so Visual Studio x86 works properly (otherwise floats are passed in the x87 FP stack). Instruction stream decoding/tracing/disassembling is now all handled in the `Istream` class. This reduces a lot of duplication between the three, which makes the logging-all-opcodes and tracing-all-opcodes less valuable, so I've removed them. Here are the changes to files: binary-reader-metadata.{h,cc} -> [removed] interp-{disassemble.trace}.cc -> istream.{h,cc} There are new helper files: interp-util.{h,cc}: Primarily print debugging functions interp-math.h: Templates used for handling wasm math
Diffstat (limited to 'src/interp/istream.cc')
-rw-r--r--src/interp/istream.cc864
1 files changed, 864 insertions, 0 deletions
diff --git a/src/interp/istream.cc b/src/interp/istream.cc
new file mode 100644
index 00000000..51ce23d0
--- /dev/null
+++ b/src/interp/istream.cc
@@ -0,0 +1,864 @@
+/*
+ * Copyright 2020 WebAssembly Community Group participants
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "src/interp/istream.h"
+
+#include <cinttypes>
+
+namespace wabt {
+namespace interp {
+
+template <typename T>
+void WABT_VECTORCALL Istream::EmitAt(Offset offset, T val) {
+ u32 new_size = offset + sizeof(T);
+ if (new_size > data_.size()) {
+ data_.resize(new_size);
+ }
+ memcpy(data_.data() + offset, &val, sizeof(val));
+}
+
+template <typename T>
+void WABT_VECTORCALL Istream::EmitInternal(T val) {
+ EmitAt(end(), val);
+}
+
+void Istream::Emit(u32 val) {
+ EmitInternal(val);
+}
+
+void Istream::Emit(Opcode::Enum op) {
+ EmitInternal(static_cast<SerializedOpcode>(op));
+}
+
+void Istream::Emit(Opcode::Enum op, u8 val) {
+ Emit(op);
+ EmitInternal(val);
+}
+
+void Istream::Emit(Opcode::Enum op, u32 val) {
+ Emit(op);
+ EmitInternal(val);
+}
+
+void Istream::Emit(Opcode::Enum op, u64 val) {
+ Emit(op);
+ EmitInternal(val);
+}
+
+void Istream::Emit(Opcode::Enum op, v128 val) {
+ Emit(op);
+ EmitInternal(val);
+}
+
+void Istream::Emit(Opcode::Enum op, u32 val1, u32 val2) {
+ Emit(op);
+ EmitInternal(val1);
+ EmitInternal(val2);
+}
+
+void Istream::EmitDropKeep(u32 drop, u32 keep) {
+ if (drop > 0) {
+ if (drop == 1 && keep == 0) {
+ Emit(Opcode::Drop);
+ } else {
+ Emit(Opcode::InterpDropKeep, drop, keep);
+ }
+ }
+}
+
+Istream::Offset Istream::EmitFixupU32() {
+ auto result = end();
+ EmitInternal(kInvalidOffset);
+ return result;
+}
+
+void Istream::ResolveFixupU32(Offset fixup_offset) {
+ EmitAt(fixup_offset, end());
+}
+
+Istream::Offset Istream::end() const {
+ return static_cast<u32>(data_.size());
+}
+
+template <typename T>
+T WABT_VECTORCALL Istream::ReadAt(Offset* offset) const {
+ assert(*offset + sizeof(T) <= data_.size());
+ T result;
+ memcpy(&result, data_.data() + *offset, sizeof(T));
+ *offset += sizeof(T);
+ return result;
+}
+
+Instr Istream::Read(Offset* offset) const {
+ Instr instr;
+ instr.op = static_cast<Opcode::Enum>(ReadAt<SerializedOpcode>(offset));
+
+ switch (instr.op) {
+ case Opcode::Drop:
+ case Opcode::Nop:
+ case Opcode::Return:
+ case Opcode::Unreachable:
+ case Opcode::RefNull:
+ // 0 immediates, 0 operands.
+ instr.kind = InstrKind::Imm_0_Op_0;
+ break;
+
+ case Opcode::F32Abs:
+ case Opcode::F32Ceil:
+ case Opcode::F32ConvertI32S:
+ case Opcode::F32ConvertI32U:
+ case Opcode::F32ConvertI64S:
+ case Opcode::F32ConvertI64U:
+ case Opcode::F32DemoteF64:
+ case Opcode::F32Floor:
+ case Opcode::F32Nearest:
+ case Opcode::F32Neg:
+ case Opcode::F32ReinterpretI32:
+ case Opcode::F32Sqrt:
+ case Opcode::F32Trunc:
+ case Opcode::F32X4Abs:
+ case Opcode::F32X4ConvertI32X4S:
+ case Opcode::F32X4ConvertI32X4U:
+ case Opcode::F32X4Neg:
+ case Opcode::F32X4Splat:
+ case Opcode::F32X4Sqrt:
+ case Opcode::F64Abs:
+ case Opcode::F64Ceil:
+ case Opcode::F64ConvertI32S:
+ case Opcode::F64ConvertI32U:
+ case Opcode::F64ConvertI64S:
+ case Opcode::F64ConvertI64U:
+ case Opcode::F64Floor:
+ case Opcode::F64Nearest:
+ case Opcode::F64Neg:
+ case Opcode::F64PromoteF32:
+ case Opcode::F64ReinterpretI64:
+ case Opcode::F64Sqrt:
+ case Opcode::F64Trunc:
+ case Opcode::F64X2Abs:
+ case Opcode::F64X2Neg:
+ case Opcode::F64X2Splat:
+ case Opcode::F64X2Sqrt:
+ case Opcode::I16X8AllTrue:
+ case Opcode::I16X8AnyTrue:
+ case Opcode::I16X8Neg:
+ case Opcode::I16X8Splat:
+ case Opcode::I16X8WidenHighI8X16S:
+ case Opcode::I16X8WidenHighI8X16U:
+ case Opcode::I16X8WidenLowI8X16S:
+ case Opcode::I16X8WidenLowI8X16U:
+ case Opcode::I32Clz:
+ case Opcode::I32Ctz:
+ case Opcode::I32Eqz:
+ case Opcode::I32Extend16S:
+ case Opcode::I32Extend8S:
+ case Opcode::I32Popcnt:
+ case Opcode::I32ReinterpretF32:
+ case Opcode::I32TruncF32S:
+ case Opcode::I32TruncF32U:
+ case Opcode::I32TruncF64S:
+ case Opcode::I32TruncF64U:
+ case Opcode::I32TruncSatF32S:
+ case Opcode::I32TruncSatF32U:
+ case Opcode::I32TruncSatF64S:
+ case Opcode::I32TruncSatF64U:
+ case Opcode::I32WrapI64:
+ case Opcode::I32X4AllTrue:
+ case Opcode::I32X4AnyTrue:
+ case Opcode::I32X4Neg:
+ case Opcode::I32X4Splat:
+ case Opcode::I32X4TruncSatF32X4S:
+ case Opcode::I32X4TruncSatF32X4U:
+ case Opcode::I32X4WidenHighI16X8S:
+ case Opcode::I32X4WidenHighI16X8U:
+ case Opcode::I32X4WidenLowI16X8S:
+ case Opcode::I32X4WidenLowI16X8U:
+ case Opcode::I64Clz:
+ case Opcode::I64Ctz:
+ case Opcode::I64Eqz:
+ case Opcode::I64Extend16S:
+ case Opcode::I64Extend32S:
+ case Opcode::I64Extend8S:
+ case Opcode::I64ExtendI32S:
+ case Opcode::I64ExtendI32U:
+ case Opcode::I64Popcnt:
+ case Opcode::I64ReinterpretF64:
+ case Opcode::I64TruncF32S:
+ case Opcode::I64TruncF32U:
+ case Opcode::I64TruncF64S:
+ case Opcode::I64TruncF64U:
+ case Opcode::I64TruncSatF32S:
+ case Opcode::I64TruncSatF32U:
+ case Opcode::I64TruncSatF64S:
+ case Opcode::I64TruncSatF64U:
+ case Opcode::I64X2Neg:
+ case Opcode::I64X2Splat:
+ case Opcode::I8X16AllTrue:
+ case Opcode::I8X16AnyTrue:
+ case Opcode::I8X16Neg:
+ case Opcode::I8X16Splat:
+ case Opcode::RefIsNull:
+ case Opcode::V128Not:
+ // 0 immediates, 1 operand.
+ instr.kind = InstrKind::Imm_0_Op_1;
+ break;
+
+ case Opcode::F32Add:
+ case Opcode::F32Copysign:
+ case Opcode::F32Div:
+ case Opcode::F32Eq:
+ case Opcode::F32Ge:
+ case Opcode::F32Gt:
+ case Opcode::F32Le:
+ case Opcode::F32Lt:
+ case Opcode::F32Max:
+ case Opcode::F32Min:
+ case Opcode::F32Mul:
+ case Opcode::F32Ne:
+ case Opcode::F32Sub:
+ case Opcode::F32X4Add:
+ case Opcode::F32X4Div:
+ case Opcode::F32X4Eq:
+ case Opcode::F32X4Ge:
+ case Opcode::F32X4Gt:
+ case Opcode::F32X4Le:
+ case Opcode::F32X4Lt:
+ case Opcode::F32X4Max:
+ case Opcode::F32X4Min:
+ case Opcode::F32X4Mul:
+ case Opcode::F32X4Ne:
+ case Opcode::F32X4Sub:
+ case Opcode::F64Add:
+ case Opcode::F64Copysign:
+ case Opcode::F64Div:
+ case Opcode::F64Eq:
+ case Opcode::F64Ge:
+ case Opcode::F64Gt:
+ case Opcode::F64Le:
+ case Opcode::F64Lt:
+ case Opcode::F64Max:
+ case Opcode::F64Min:
+ case Opcode::F64Mul:
+ case Opcode::F64Ne:
+ case Opcode::F64Sub:
+ case Opcode::F64X2Add:
+ case Opcode::F64X2Div:
+ case Opcode::F64X2Eq:
+ case Opcode::F64X2Ge:
+ case Opcode::F64X2Gt:
+ case Opcode::F64X2Le:
+ case Opcode::F64X2Lt:
+ case Opcode::F64X2Max:
+ case Opcode::F64X2Min:
+ case Opcode::F64X2Mul:
+ case Opcode::F64X2Ne:
+ case Opcode::F64X2Sub:
+ case Opcode::I16X8Add:
+ case Opcode::I16X8AddSaturateS:
+ case Opcode::I16X8AddSaturateU:
+ case Opcode::I16X8AvgrU:
+ case Opcode::I16X8Eq:
+ case Opcode::I16X8GeS:
+ case Opcode::I16X8GeU:
+ case Opcode::I16X8GtS:
+ case Opcode::I16X8GtU:
+ case Opcode::I16X8LeS:
+ case Opcode::I16X8LeU:
+ case Opcode::I16X8LtS:
+ case Opcode::I16X8LtU:
+ case Opcode::I16X8MaxS:
+ case Opcode::I16X8MaxU:
+ case Opcode::I16X8MinS:
+ case Opcode::I16X8MinU:
+ case Opcode::I16X8Mul:
+ case Opcode::I16X8NarrowI32X4S:
+ case Opcode::I16X8NarrowI32X4U:
+ case Opcode::I16X8Ne:
+ case Opcode::I16X8Shl:
+ case Opcode::I16X8ShrS:
+ case Opcode::I16X8ShrU:
+ case Opcode::I16X8Sub:
+ case Opcode::I16X8SubSaturateS:
+ case Opcode::I16X8SubSaturateU:
+ case Opcode::I32Add:
+ case Opcode::I32And:
+ case Opcode::I32DivS:
+ case Opcode::I32DivU:
+ case Opcode::I32Eq:
+ case Opcode::I32GeS:
+ case Opcode::I32GeU:
+ case Opcode::I32GtS:
+ case Opcode::I32GtU:
+ case Opcode::I32LeS:
+ case Opcode::I32LeU:
+ case Opcode::I32LtS:
+ case Opcode::I32LtU:
+ case Opcode::I32Mul:
+ case Opcode::I32Ne:
+ case Opcode::I32Or:
+ case Opcode::I32RemS:
+ case Opcode::I32RemU:
+ case Opcode::I32Rotl:
+ case Opcode::I32Rotr:
+ case Opcode::I32Shl:
+ case Opcode::I32ShrS:
+ case Opcode::I32ShrU:
+ case Opcode::I32Sub:
+ case Opcode::I32X4Add:
+ case Opcode::I32X4Eq:
+ case Opcode::I32X4GeS:
+ case Opcode::I32X4GeU:
+ case Opcode::I32X4GtS:
+ case Opcode::I32X4GtU:
+ case Opcode::I32X4LeS:
+ case Opcode::I32X4LeU:
+ case Opcode::I32X4LtS:
+ case Opcode::I32X4LtU:
+ case Opcode::I32X4MaxS:
+ case Opcode::I32X4MaxU:
+ case Opcode::I32X4MinS:
+ case Opcode::I32X4MinU:
+ case Opcode::I32X4Mul:
+ case Opcode::I32X4Ne:
+ case Opcode::I32X4Shl:
+ case Opcode::I32X4ShrS:
+ case Opcode::I32X4ShrU:
+ case Opcode::I32X4Sub:
+ case Opcode::I32Xor:
+ case Opcode::I64Add:
+ case Opcode::I64And:
+ case Opcode::I64DivS:
+ case Opcode::I64DivU:
+ case Opcode::I64Eq:
+ case Opcode::I64GeS:
+ case Opcode::I64GeU:
+ case Opcode::I64GtS:
+ case Opcode::I64GtU:
+ case Opcode::I64LeS:
+ case Opcode::I64LeU:
+ case Opcode::I64LtS:
+ case Opcode::I64LtU:
+ case Opcode::I64Mul:
+ case Opcode::I64Ne:
+ case Opcode::I64Or:
+ case Opcode::I64RemS:
+ case Opcode::I64RemU:
+ case Opcode::I64Rotl:
+ case Opcode::I64Rotr:
+ case Opcode::I64Shl:
+ case Opcode::I64ShrS:
+ case Opcode::I64ShrU:
+ case Opcode::I64Sub:
+ case Opcode::I64X2Add:
+ case Opcode::I64X2Shl:
+ case Opcode::I64X2ShrS:
+ case Opcode::I64X2ShrU:
+ case Opcode::I64X2Sub:
+ case Opcode::I64X2Mul:
+ case Opcode::I64Xor:
+ case Opcode::I8X16Add:
+ case Opcode::I8X16AddSaturateS:
+ case Opcode::I8X16AddSaturateU:
+ case Opcode::I8X16AvgrU:
+ case Opcode::I8X16Eq:
+ case Opcode::I8X16GeS:
+ case Opcode::I8X16GeU:
+ case Opcode::I8X16GtS:
+ case Opcode::I8X16GtU:
+ case Opcode::I8X16LeS:
+ case Opcode::I8X16LeU:
+ case Opcode::I8X16LtS:
+ case Opcode::I8X16LtU:
+ case Opcode::I8X16MaxS:
+ case Opcode::I8X16MaxU:
+ case Opcode::I8X16MinS:
+ case Opcode::I8X16MinU:
+ case Opcode::I8X16NarrowI16X8S:
+ case Opcode::I8X16NarrowI16X8U:
+ case Opcode::I8X16Ne:
+ case Opcode::I8X16Shl:
+ case Opcode::I8X16ShrS:
+ case Opcode::I8X16ShrU:
+ case Opcode::I8X16Sub:
+ case Opcode::I8X16SubSaturateS:
+ case Opcode::I8X16SubSaturateU:
+ case Opcode::V128And:
+ case Opcode::V128Andnot:
+ case Opcode::V128BitSelect:
+ case Opcode::V128Or:
+ case Opcode::V128Xor:
+ case Opcode::V8X16Swizzle:
+ // 0 immediates, 2 operands
+ instr.kind = InstrKind::Imm_0_Op_2;
+ break;
+
+ case Opcode::Select:
+ case Opcode::SelectT:
+ // 0 immediates, 3 operands
+ instr.kind = InstrKind::Imm_0_Op_3;
+ break;
+
+ case Opcode::Br:
+ // Jump target immediate, 0 operands.
+ instr.kind = InstrKind::Imm_Jump_Op_0;
+ instr.imm_u32 = ReadAt<u32>(offset);
+ break;
+
+ case Opcode::BrIf:
+ case Opcode::BrTable:
+ case Opcode::InterpBrUnless:
+ // Jump target immediate, 1 operand.
+ instr.kind = InstrKind::Imm_Jump_Op_1;
+ instr.imm_u32 = ReadAt<u32>(offset);
+ break;
+
+ case Opcode::GlobalGet:
+ case Opcode::LocalGet:
+ case Opcode::MemorySize:
+ case Opcode::TableSize:
+ case Opcode::DataDrop:
+ case Opcode::ElemDrop:
+ case Opcode::RefFunc:
+ // Index immediate, 0 operands.
+ instr.kind = InstrKind::Imm_Index_Op_0;
+ instr.imm_u32 = ReadAt<u32>(offset);
+ break;
+
+ case Opcode::GlobalSet:
+ case Opcode::LocalSet:
+ case Opcode::LocalTee:
+ case Opcode::MemoryGrow:
+ case Opcode::TableGet:
+ // Index immediate, 1 operand.
+ instr.kind = InstrKind::Imm_Index_Op_1;
+ instr.imm_u32 = ReadAt<u32>(offset);
+ break;
+
+ case Opcode::TableSet:
+ case Opcode::TableGrow:
+ // Index immediate, 2 operands.
+ instr.kind = InstrKind::Imm_Index_Op_2;
+ instr.imm_u32 = ReadAt<u32>(offset);
+ break;
+
+ case Opcode::MemoryFill:
+ case Opcode::TableFill:
+ // Index immediate, 3 operands.
+ instr.kind = InstrKind::Imm_Index_Op_3;
+ instr.imm_u32 = ReadAt<u32>(offset);
+ break;
+
+ case Opcode::Call:
+ case Opcode::InterpCallHost:
+ instr.kind = InstrKind::Imm_Index_Op_N;
+ instr.imm_u32 = ReadAt<u32>(offset);
+ break;
+
+ case Opcode::CallIndirect:
+ case Opcode::ReturnCallIndirect:
+ // Index immediate, N operands.
+ instr.kind = InstrKind::Imm_Index_Index_Op_N;
+ instr.imm_u32x2.fst = ReadAt<u32>(offset);
+ instr.imm_u32x2.snd = ReadAt<u32>(offset);
+ break;
+
+ case Opcode::MemoryInit:
+ case Opcode::TableInit:
+ case Opcode::MemoryCopy:
+ case Opcode::TableCopy:
+ // Index + index immediates, 3 operands.
+ instr.kind = InstrKind::Imm_Index_Index_Op_3;
+ instr.imm_u32x2.fst = ReadAt<u32>(offset);
+ instr.imm_u32x2.snd = ReadAt<u32>(offset);
+ break;
+
+ case Opcode::F32Load:
+ case Opcode::F64Load:
+ case Opcode::I16X8Load8X8S:
+ case Opcode::I16X8Load8X8U:
+ case Opcode::V16X8LoadSplat:
+ case Opcode::I32AtomicLoad:
+ case Opcode::I32AtomicLoad16U:
+ case Opcode::I32AtomicLoad8U:
+ case Opcode::I32Load:
+ case Opcode::I32Load16S:
+ case Opcode::I32Load16U:
+ case Opcode::I32Load8S:
+ case Opcode::I32Load8U:
+ case Opcode::I32X4Load16X4S:
+ case Opcode::I32X4Load16X4U:
+ case Opcode::V32X4LoadSplat:
+ case Opcode::I64AtomicLoad:
+ case Opcode::I64AtomicLoad16U:
+ case Opcode::I64AtomicLoad32U:
+ case Opcode::I64AtomicLoad8U:
+ case Opcode::I64Load:
+ case Opcode::I64Load16S:
+ case Opcode::I64Load16U:
+ case Opcode::I64Load32S:
+ case Opcode::I64Load32U:
+ case Opcode::I64Load8S:
+ case Opcode::I64Load8U:
+ case Opcode::I64X2Load32X2S:
+ case Opcode::I64X2Load32X2U:
+ case Opcode::V64X2LoadSplat:
+ case Opcode::V8X16LoadSplat:
+ case Opcode::V128Load:
+ // Index + memory offset immediates, 1 operand.
+ instr.kind = InstrKind::Imm_Index_Offset_Op_1;
+ instr.imm_u32x2.fst = ReadAt<u32>(offset);
+ instr.imm_u32x2.snd = ReadAt<u32>(offset);
+ break;
+
+ case Opcode::AtomicNotify:
+ case Opcode::F32Store:
+ case Opcode::F64Store:
+ case Opcode::I32AtomicRmw16AddU:
+ case Opcode::I32AtomicRmw16AndU:
+ case Opcode::I32AtomicRmw16OrU:
+ case Opcode::I32AtomicRmw16SubU:
+ case Opcode::I32AtomicRmw16XchgU:
+ case Opcode::I32AtomicRmw16XorU:
+ case Opcode::I32AtomicRmw8AddU:
+ case Opcode::I32AtomicRmw8AndU:
+ case Opcode::I32AtomicRmw8OrU:
+ case Opcode::I32AtomicRmw8SubU:
+ case Opcode::I32AtomicRmw8XchgU:
+ case Opcode::I32AtomicRmw8XorU:
+ case Opcode::I32AtomicRmwAdd:
+ case Opcode::I32AtomicRmwAnd:
+ case Opcode::I32AtomicRmwOr:
+ case Opcode::I32AtomicRmwSub:
+ case Opcode::I32AtomicRmwXchg:
+ case Opcode::I32AtomicRmwXor:
+ case Opcode::I32AtomicStore:
+ case Opcode::I32AtomicStore16:
+ case Opcode::I32AtomicStore8:
+ case Opcode::I32Store:
+ case Opcode::I32Store16:
+ case Opcode::I32Store8:
+ case Opcode::I64AtomicRmw16AddU:
+ case Opcode::I64AtomicRmw16AndU:
+ case Opcode::I64AtomicRmw16OrU:
+ case Opcode::I64AtomicRmw16SubU:
+ case Opcode::I64AtomicRmw16XchgU:
+ case Opcode::I64AtomicRmw16XorU:
+ case Opcode::I64AtomicRmw32AddU:
+ case Opcode::I64AtomicRmw32AndU:
+ case Opcode::I64AtomicRmw32OrU:
+ case Opcode::I64AtomicRmw32SubU:
+ case Opcode::I64AtomicRmw32XchgU:
+ case Opcode::I64AtomicRmw32XorU:
+ case Opcode::I64AtomicRmw8AddU:
+ case Opcode::I64AtomicRmw8AndU:
+ case Opcode::I64AtomicRmw8OrU:
+ case Opcode::I64AtomicRmw8SubU:
+ case Opcode::I64AtomicRmw8XchgU:
+ case Opcode::I64AtomicRmw8XorU:
+ case Opcode::I64AtomicRmwAdd:
+ case Opcode::I64AtomicRmwAnd:
+ case Opcode::I64AtomicRmwOr:
+ case Opcode::I64AtomicRmwSub:
+ case Opcode::I64AtomicRmwXchg:
+ case Opcode::I64AtomicRmwXor:
+ case Opcode::I64AtomicStore:
+ case Opcode::I64AtomicStore16:
+ case Opcode::I64AtomicStore32:
+ case Opcode::I64AtomicStore8:
+ case Opcode::I64Store:
+ case Opcode::I64Store16:
+ case Opcode::I64Store32:
+ case Opcode::I64Store8:
+ case Opcode::V128Store:
+ // Index and memory offset immediates, 2 operands.
+ instr.kind = InstrKind::Imm_Index_Offset_Op_2;
+ instr.imm_u32x2.fst = ReadAt<u32>(offset);
+ instr.imm_u32x2.snd = ReadAt<u32>(offset);
+ break;
+
+ case Opcode::I32AtomicRmw16CmpxchgU:
+ case Opcode::I32AtomicRmw8CmpxchgU:
+ case Opcode::I32AtomicRmwCmpxchg:
+ case Opcode::I32AtomicWait:
+ case Opcode::I64AtomicRmw16CmpxchgU:
+ case Opcode::I64AtomicRmw32CmpxchgU:
+ case Opcode::I64AtomicRmw8CmpxchgU:
+ case Opcode::I64AtomicRmwCmpxchg:
+ case Opcode::I64AtomicWait:
+ // Index and memory offset immediates, 3 operands.
+ instr.kind = InstrKind::Imm_Index_Offset_Op_3;
+ instr.imm_u32x2.fst = ReadAt<u32>(offset);
+ instr.imm_u32x2.snd = ReadAt<u32>(offset);
+ break;
+
+ case Opcode::I32Const:
+ case Opcode::InterpAlloca:
+ // i32/f32 immediate, 0 operands.
+ instr.kind = InstrKind::Imm_I32_Op_0;
+ instr.imm_u32 = ReadAt<u32>(offset);
+ break;
+
+ case Opcode::I64Const:
+ // i64 immediate, 0 operands.
+ instr.kind = InstrKind::Imm_I64_Op_0;
+ instr.imm_u64 = ReadAt<u64>(offset);
+ break;
+
+ case Opcode::F32Const:
+ // f32 immediate, 0 operands.
+ instr.kind = InstrKind::Imm_F32_Op_0;
+ instr.imm_f32 = ReadAt<f32>(offset);
+ break;
+
+ case Opcode::F64Const:
+ // f64 immediate, 0 operands.
+ instr.kind = InstrKind::Imm_F64_Op_0;
+ instr.imm_f64 = ReadAt<f64>(offset);
+ break;
+
+ case Opcode::InterpDropKeep:
+ // i32 and i32 immediates, 0 operands.
+ instr.kind = InstrKind::Imm_I32_I32_Op_0;
+ instr.imm_u32x2.fst = ReadAt<u32>(offset);
+ instr.imm_u32x2.snd = ReadAt<u32>(offset);
+ break;
+
+ case Opcode::I8X16ExtractLaneS:
+ case Opcode::I8X16ExtractLaneU:
+ case Opcode::I16X8ExtractLaneS:
+ case Opcode::I16X8ExtractLaneU:
+ case Opcode::I32X4ExtractLane:
+ case Opcode::I64X2ExtractLane:
+ case Opcode::F32X4ExtractLane:
+ case Opcode::F64X2ExtractLane:
+ // u8 immediate, 1 operand.
+ instr.kind = InstrKind::Imm_I8_Op_1;
+ instr.imm_u8 = ReadAt<u8>(offset);
+ break;
+
+ case Opcode::I8X16ReplaceLane:
+ case Opcode::I16X8ReplaceLane:
+ case Opcode::I32X4ReplaceLane:
+ case Opcode::I64X2ReplaceLane:
+ case Opcode::F32X4ReplaceLane:
+ case Opcode::F64X2ReplaceLane:
+ // u8 immediate, 2 operands.
+ instr.kind = InstrKind::Imm_I8_Op_2;
+ instr.imm_u8 = ReadAt<u8>(offset);
+ break;
+
+ case Opcode::V128Const:
+ // v128 immediate, 0 operands.
+ instr.kind = InstrKind::Imm_V128_Op_0;
+ instr.imm_v128 = ReadAt<v128>(offset);
+ break;
+
+ case Opcode::V8X16Shuffle:
+ // v128 immediate, 2 operands.
+ instr.kind = InstrKind::Imm_V128_Op_2;
+ instr.imm_v128 = ReadAt<v128>(offset);
+ break;
+
+ case Opcode::Block:
+ case Opcode::BrOnExn:
+ case Opcode::Catch:
+ case Opcode::Else:
+ case Opcode::End:
+ case Opcode::If:
+ case Opcode::InterpData:
+ case Opcode::Invalid:
+ case Opcode::Loop:
+ case Opcode::Rethrow:
+ case Opcode::Throw:
+ case Opcode::Try:
+ case Opcode::ReturnCall:
+ // Not used.
+ break;
+ }
+ return instr;
+}
+
+void Istream::Disassemble(Stream* stream) const {
+ Disassemble(stream, 0, data_.size());
+}
+
+std::string Istream::DisassemblySource::Header(Offset offset) {
+ return StringPrintf("%4u", offset);
+}
+
+std::string Istream::DisassemblySource::Pick(Index index, Instr instr) {
+ return StringPrintf("%%[-%d]", index);
+}
+
+Istream::Offset Istream::Disassemble(Stream* stream, Offset offset) const {
+ DisassemblySource source;
+ return Trace(stream, offset, &source);
+}
+
+void Istream::Disassemble(Stream* stream, Offset from, Offset to) const {
+ DisassemblySource source;
+ assert(from <= data_.size() && to <= data_.size() && from <= to);
+
+ Offset pc = from;
+ while (pc < to) {
+ pc = Trace(stream, pc, &source);
+ }
+}
+
+Istream::Offset Istream::Trace(Stream* stream,
+ Offset offset,
+ TraceSource* source) const {
+ Offset start = offset;
+ Instr instr = Read(&offset);
+ stream->Writef("%s| %s", source->Header(start).c_str(), instr.op.GetName());
+
+ switch (instr.kind) {
+ case InstrKind::Imm_0_Op_0:
+ stream->Writef("\n");
+ break;
+
+ case InstrKind::Imm_0_Op_1:
+ stream->Writef(" %s\n", source->Pick(1, instr).c_str());
+ break;
+
+ case InstrKind::Imm_0_Op_2:
+ stream->Writef(" %s, %s\n", source->Pick(2, instr).c_str(),
+ source->Pick(1, instr).c_str());
+ break;
+
+ case InstrKind::Imm_0_Op_3:
+ stream->Writef(" %s, %s, %s\n", source->Pick(3, instr).c_str(),
+ source->Pick(2, instr).c_str(),
+ source->Pick(1, instr).c_str());
+ break;
+
+ case InstrKind::Imm_Jump_Op_0:
+ stream->Writef(" @%u\n", instr.imm_u32);
+ break;
+
+ case InstrKind::Imm_Jump_Op_1:
+ stream->Writef(" @%u, %s\n", instr.imm_u32,
+ source->Pick(1, instr).c_str());
+ break;
+
+ case InstrKind::Imm_Index_Op_0:
+ stream->Writef(" $%u\n", instr.imm_u32);
+ break;
+
+ case InstrKind::Imm_Index_Op_1:
+ stream->Writef(" $%u, %s\n", instr.imm_u32,
+ source->Pick(1, instr).c_str());
+ break;
+
+ case InstrKind::Imm_Index_Op_2:
+ stream->Writef(" $%u, %s, %s\n", instr.imm_u32,
+ source->Pick(2, instr).c_str(),
+ source->Pick(1, instr).c_str());
+ break;
+
+ case InstrKind::Imm_Index_Op_3:
+ stream->Writef(
+ " $%u, %s, %s, %s\n", instr.imm_u32, source->Pick(3, instr).c_str(),
+ source->Pick(2, instr).c_str(), source->Pick(1, instr).c_str());
+ break;
+
+ case InstrKind::Imm_Index_Op_N:
+ stream->Writef(" $%u\n", instr.imm_u32); // TODO param/result count?
+ break;
+
+ case InstrKind::Imm_Index_Index_Op_3:
+ stream->Writef(" $%u, $%u, %s, %s, %s\n", instr.imm_u32x2.fst,
+ instr.imm_u32x2.snd, source->Pick(3, instr).c_str(),
+ source->Pick(2, instr).c_str(),
+ source->Pick(1, instr).c_str());
+ break;
+
+ case InstrKind::Imm_Index_Index_Op_N:
+ stream->Writef(" $%u, $%u\n", instr.imm_u32x2.fst,
+ instr.imm_u32x2.snd); // TODO param/result count?
+ break;
+
+ case InstrKind::Imm_Index_Offset_Op_1:
+ stream->Writef(" $%u:%s+$%u\n", instr.imm_u32x2.fst,
+ source->Pick(1, instr).c_str(), instr.imm_u32x2.snd);
+ break;
+
+ case InstrKind::Imm_Index_Offset_Op_2:
+ stream->Writef(" $%u:%s+$%u, %s\n", instr.imm_u32x2.fst,
+ source->Pick(2, instr).c_str(), instr.imm_u32x2.snd,
+ source->Pick(1, instr).c_str());
+ break;
+
+ case InstrKind::Imm_Index_Offset_Op_3:
+ stream->Writef(" $%u:%s+$%u, %s, %s\n", instr.imm_u32x2.fst,
+ source->Pick(3, instr).c_str(), instr.imm_u32x2.snd,
+ source->Pick(2, instr).c_str(),
+ source->Pick(1, instr).c_str());
+ break;
+
+ case InstrKind::Imm_I32_Op_0:
+ stream->Writef(" %u\n", instr.imm_u32);
+ break;
+
+ case InstrKind::Imm_I64_Op_0:
+ stream->Writef(" %" PRIu64 "\n", instr.imm_u64);
+ break;
+
+ case InstrKind::Imm_F32_Op_0:
+ stream->Writef(" %g\n", instr.imm_f32);
+ break;
+
+ case InstrKind::Imm_F64_Op_0:
+ stream->Writef(" %g\n", instr.imm_f64);
+ break;
+
+ case InstrKind::Imm_I32_I32_Op_0:
+ stream->Writef(" $%u $%u\n", instr.imm_u32x2.fst, instr.imm_u32x2.snd);
+ break;
+
+ case InstrKind::Imm_I8_Op_1:
+ // TODO: cleanup
+ stream->Writef(" %s : (Lane imm: %u)\n", source->Pick(1, instr).c_str(),
+ instr.imm_u8);
+ break;
+
+ case InstrKind::Imm_I8_Op_2:
+ // TODO: cleanup
+ stream->Writef(" %s, %s : (Lane imm: $%u)\n",
+ source->Pick(2, instr).c_str(),
+ source->Pick(1, instr).c_str(), instr.imm_u8);
+ break;
+
+ case InstrKind::Imm_V128_Op_0:
+ stream->Writef(" i32x4 0x%08x 0x%08x 0x%08x 0x%08x\n",
+ instr.imm_v128.v[0], instr.imm_v128.v[1],
+ instr.imm_v128.v[2], instr.imm_v128.v[3]);
+ break;
+
+ case InstrKind::Imm_V128_Op_2:
+ // TODO: cleanup
+ stream->Writef(
+ " %s, %s : (Lane imm: i32x4 0x%08x 0x%08x 0x%08x 0x%08x )\n",
+ source->Pick(2, instr).c_str(), source->Pick(1, instr).c_str(),
+ instr.imm_v128.v[0], instr.imm_v128.v[1], instr.imm_v128.v[2],
+ instr.imm_v128.v[3]);
+ break;
+ }
+ return offset;
+}
+
+} // namespace interp
+} // namespace wabt