/* * 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. */ #ifndef WABT_INTERP_ISTREAM_H_ #define WABT_INTERP_ISTREAM_H_ #include #include #include #include "src/common.h" #include "src/opcode.h" #include "src/stream.h" namespace wabt { namespace interp { using u8 = uint8_t; using u16 = uint16_t; using u32 = uint32_t; using u64 = uint64_t; using f32 = float; using f64 = double; using Buffer = std::vector; using ValueType = wabt::Type; // Group instructions based on their immediates their operands. This way we can // simplify instruction decoding, disassembling, and tracing. There is an // example of an instruction that uses this encoding on the right. enum class InstrKind { Imm_0_Op_0, // Nop Imm_0_Op_1, // i32.eqz Imm_0_Op_2, // i32.add Imm_0_Op_3, // select Imm_Jump_Op_0, // br Imm_Jump_Op_1, // br_if Imm_Index_Op_0, // global.get Imm_Index_Op_1, // global.set Imm_Index_Op_2, // table.set Imm_Index_Op_3, // memory.fill Imm_Index_Op_N, // call Imm_Index_Index_Op_3, // memory.init Imm_Index_Index_Op_N, // call_indirect Imm_Index_Offset_Op_1, // i32.load Imm_Index_Offset_Op_2, // i32.store Imm_Index_Offset_Op_3, // i32.atomic.rmw.cmpxchg Imm_Index_Offset_Lane_Op_2, // v128.load8_lane Imm_I32_Op_0, // i32.const Imm_I64_Op_0, // i64.const Imm_F32_Op_0, // f32.const Imm_F64_Op_0, // f64.const Imm_I32_I32_Op_0, // drop_keep Imm_I8_Op_1, // i32x4.extract_lane Imm_I8_Op_2, // i32x4.replace_lane Imm_V128_Op_0, // v128.const Imm_V128_Op_2, // i8x16.shuffle }; struct Instr { Opcode op; InstrKind kind; union { u8 imm_u8; u32 imm_u32; f32 imm_f32; u64 imm_u64; f64 imm_f64; v128 imm_v128; struct { u32 fst, snd; } imm_u32x2; struct { u32 fst, snd; u8 idx; } imm_u32x2_u8; }; }; class Istream { public: using SerializedOpcode = u32; // TODO: change to u16 using Offset = u32; static const Offset kInvalidOffset = ~0; // Each br_table entry is made up of three instructions: // // interp_drop_keep $drop $keep // interp_catch_drop $catches // br $label // // Each opcode is a SerializedOpcode, and each immediate is a u32. static const Offset kBrTableEntrySize = sizeof(SerializedOpcode) * 3 + 4 * sizeof(u32); // Emit API. void Emit(u32); void Emit(Opcode::Enum); void Emit(Opcode::Enum, u8); void Emit(Opcode::Enum, u32); void Emit(Opcode::Enum, u64); void Emit(Opcode::Enum, v128); void Emit(Opcode::Enum, u32, u32); void Emit(Opcode::Enum, u32, u32, u8); void EmitDropKeep(u32 drop, u32 keep); void EmitCatchDrop(u32 drop); Offset EmitFixupU32(); void ResolveFixupU32(Offset); Offset end() const; // Read API. Instr Read(Offset*) const; // Disassemble/Trace API. // TODO separate out disassembly/tracing? struct TraceSource { virtual ~TraceSource() {} // Whatever content should go before the instruction on each line, e.g. the // call stack size, value stack size, and istream offset. virtual std::string Header(Offset) = 0; virtual std::string Pick(Index, Instr) = 0; }; struct DisassemblySource : TraceSource { std::string Header(Offset) override; std::string Pick(Index, Instr) override; }; void Disassemble(Stream*) const; Offset Disassemble(Stream*, Offset) const; void Disassemble(Stream*, Offset from, Offset to) const; Offset Trace(Stream*, Offset, TraceSource*) const; private: template void WABT_VECTORCALL EmitAt(Offset, T val); template void WABT_VECTORCALL EmitInternal(T val); template T WABT_VECTORCALL ReadAt(Offset*) const; Buffer data_; }; } // namespace interp } // namespace wabt #endif // WABT_INTERP_ISTREAM_H_