Commit 8bf9ba4e authored by Ben Smith's avatar Ben Smith Committed by Commit Bot

[wasm] Add unittest for PrintWasmText

PrintWasmText is used for disassembling wasm code in DevTools, but many
instructions are not implemented. This test should make it easier to
remember to implement this when adding new instructions.

Change-Id: I6030a70113320f11a1ac0436bf0d220b5c41e6d1
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1647475
Commit-Queue: Ben Smith <binji@chromium.org>
Reviewed-by: 's avatarAndreas Haas <ahaas@chromium.org>
Cr-Commit-Position: refs/heads/master@{#62063}
parent 05e3b641
......@@ -140,16 +140,18 @@ void PrintWasmText(const WasmModule* module, const ModuleWireBytes& wire_bytes,
while (iterator.has_next()) os << ' ' << iterator.next();
break;
}
case kExprCallIndirect: {
case kExprCallIndirect:
case kExprReturnCallIndirect: {
CallIndirectImmediate<Decoder::kNoValidate> imm(kAllWasmFeatures, &i,
i.pc());
DCHECK_EQ(0, imm.table_index);
os << "call_indirect " << imm.sig_index;
os << WasmOpcodes::OpcodeName(opcode) << ' ' << imm.sig_index;
break;
}
case kExprCallFunction: {
case kExprCallFunction:
case kExprReturnCall: {
CallFunctionImmediate<Decoder::kNoValidate> imm(&i, i.pc());
os << "call " << imm.index;
os << WasmOpcodes::OpcodeName(opcode) << ' ' << imm.index;
break;
}
case kExprGetLocal:
......@@ -170,6 +172,18 @@ void PrintWasmText(const WasmModule* module, const ModuleWireBytes& wire_bytes,
os << WasmOpcodes::OpcodeName(opcode) << ' ' << imm.index;
break;
}
case kExprGetTable:
case kExprSetTable: {
TableIndexImmediate<Decoder::kNoValidate> imm(&i, i.pc());
os << WasmOpcodes::OpcodeName(opcode) << ' ' << imm.index;
break;
}
case kExprSelectWithType: {
SelectTypeImmediate<Decoder::kNoValidate> imm(&i, i.pc());
os << WasmOpcodes::OpcodeName(opcode) << ' '
<< ValueTypes::TypeName(imm.type);
break;
}
#define CASE_CONST(type, str, cast_type) \
case kExpr##type##Const: { \
Imm##type##Immediate<Decoder::kNoValidate> imm(&i, i.pc()); \
......@@ -182,6 +196,12 @@ void PrintWasmText(const WasmModule* module, const ModuleWireBytes& wire_bytes,
CASE_CONST(F64, f64, double)
#undef CASE_CONST
case kExprRefFunc: {
FunctionIndexImmediate<Decoder::kNoValidate> imm(&i, i.pc());
os << WasmOpcodes::OpcodeName(opcode) << ' ' << imm.index;
break;
}
#define CASE_OPCODE(opcode, _, __) case kExpr##opcode:
FOREACH_LOAD_MEM_OPCODE(CASE_OPCODE)
FOREACH_STORE_MEM_OPCODE(CASE_OPCODE) {
......@@ -193,6 +213,7 @@ void PrintWasmText(const WasmModule* module, const ModuleWireBytes& wire_bytes,
}
FOREACH_SIMPLE_OPCODE(CASE_OPCODE)
FOREACH_SIMPLE_PROTOTYPE_OPCODE(CASE_OPCODE)
case kExprUnreachable:
case kExprNop:
case kExprReturn:
......@@ -200,13 +221,133 @@ void PrintWasmText(const WasmModule* module, const ModuleWireBytes& wire_bytes,
case kExprMemoryGrow:
case kExprDrop:
case kExprSelect:
case kExprRethrow:
case kExprRefNull:
os << WasmOpcodes::OpcodeName(opcode);
break;
case kNumericPrefix: {
WasmOpcode numeric_opcode = i.prefixed_opcode();
switch (numeric_opcode) {
case kExprI32SConvertSatF32:
case kExprI32UConvertSatF32:
case kExprI32SConvertSatF64:
case kExprI32UConvertSatF64:
case kExprI64SConvertSatF32:
case kExprI64UConvertSatF32:
case kExprI64SConvertSatF64:
case kExprI64UConvertSatF64:
case kExprMemoryCopy:
case kExprMemoryFill:
os << WasmOpcodes::OpcodeName(opcode);
break;
case kExprMemoryInit: {
MemoryInitImmediate<Decoder::kNoValidate> imm(&i, i.pc());
os << WasmOpcodes::OpcodeName(opcode) << ' '
<< imm.data_segment_index;
break;
}
case kExprDataDrop: {
DataDropImmediate<Decoder::kNoValidate> imm(&i, i.pc());
os << WasmOpcodes::OpcodeName(opcode) << ' ' << imm.index;
break;
}
case kExprTableInit: {
TableInitImmediate<Decoder::kNoValidate> imm(&i, i.pc());
os << WasmOpcodes::OpcodeName(opcode) << ' '
<< imm.elem_segment_index << ' ' << imm.table.index;
break;
}
case kExprElemDrop: {
ElemDropImmediate<Decoder::kNoValidate> imm(&i, i.pc());
os << WasmOpcodes::OpcodeName(opcode) << ' ' << imm.index;
break;
}
case kExprTableCopy: {
TableCopyImmediate<Decoder::kNoValidate> imm(&i, i.pc());
os << WasmOpcodes::OpcodeName(opcode) << ' ' << imm.table_src.index
<< ' ' << imm.table_dst.index;
break;
}
case kExprTableGrow:
case kExprTableSize:
case kExprTableFill: {
TableIndexImmediate<Decoder::kNoValidate> imm(&i, i.pc() + 1);
os << WasmOpcodes::OpcodeName(opcode) << ' ' << imm.index;
break;
}
default:
UNREACHABLE();
break;
}
break;
}
case kSimdPrefix: {
WasmOpcode simd_opcode = i.prefixed_opcode();
switch (simd_opcode) {
case kExprS128LoadMem:
case kExprS128StoreMem: {
MemoryAccessImmediate<Decoder::kNoValidate> imm(&i, i.pc(),
kMaxUInt32);
os << WasmOpcodes::OpcodeName(opcode) << " offset=" << imm.offset
<< " align=" << (1ULL << imm.alignment);
break;
}
case kExprS8x16Shuffle: {
Simd8x16ShuffleImmediate<Decoder::kNoValidate> imm(&i, i.pc());
os << WasmOpcodes::OpcodeName(opcode);
for (uint8_t v : imm.shuffle) {
os << ' ' << v;
}
break;
}
case kExprI8x16ExtractLane:
case kExprI16x8ExtractLane:
case kExprI32x4ExtractLane:
case kExprF32x4ExtractLane:
case kExprI8x16ReplaceLane:
case kExprI16x8ReplaceLane:
case kExprI32x4ReplaceLane:
case kExprF32x4ReplaceLane: {
SimdLaneImmediate<Decoder::kNoValidate> imm(&i, i.pc());
os << WasmOpcodes::OpcodeName(opcode) << ' ' << imm.lane;
break;
}
case kExprI8x16Shl:
case kExprI8x16ShrS:
case kExprI8x16ShrU:
case kExprI16x8Shl:
case kExprI16x8ShrS:
case kExprI16x8ShrU:
case kExprI32x4Shl:
case kExprI32x4ShrS:
case kExprI32x4ShrU: {
SimdShiftImmediate<Decoder::kNoValidate> imm(&i, i.pc());
os << WasmOpcodes::OpcodeName(opcode) << ' ' << imm.shift;
break;
}
FOREACH_SIMD_0_OPERAND_OPCODE(CASE_OPCODE) {
os << WasmOpcodes::OpcodeName(opcode);
break;
}
default:
UNREACHABLE();
break;
}
break;
}
case kAtomicPrefix: {
WasmOpcode atomic_opcode = i.prefixed_opcode();
switch (atomic_opcode) {
FOREACH_ATOMIC_OPCODE(CASE_OPCODE) {
MemoryAccessImmediate<Decoder::kNoValidate> imm(&i, i.pc(),
MemoryAccessImmediate<Decoder::kNoValidate> imm(&i, i.pc() + 1,
kMaxUInt32);
os << WasmOpcodes::OpcodeName(atomic_opcode)
<< " offset=" << imm.offset
......@@ -222,14 +363,9 @@ void PrintWasmText(const WasmModule* module, const ModuleWireBytes& wire_bytes,
// This group is just printed by their internal opcode name, as they
// should never be shown to end-users.
FOREACH_ASMJS_COMPAT_OPCODE(CASE_OPCODE)
// TODO(wasm): Add correct printing for SIMD and atomic opcodes once
// they are publicly available.
FOREACH_SIMD_0_OPERAND_OPCODE(CASE_OPCODE)
FOREACH_SIMD_1_OPERAND_OPCODE(CASE_OPCODE)
FOREACH_SIMD_MASK_OPERAND_OPCODE(CASE_OPCODE)
FOREACH_SIMD_MEM_OPCODE(CASE_OPCODE)
FOREACH_ASMJS_COMPAT_OPCODE(CASE_OPCODE) {
os << WasmOpcodes::OpcodeName(opcode);
}
break;
#undef CASE_OPCODE
......
......@@ -7,9 +7,10 @@
#include <cstdint>
#include <ostream>
#include <tuple>
#include <vector>
#include "src/common/globals.h"
namespace v8 {
namespace debug {
......@@ -26,10 +27,10 @@ struct ModuleWireBytes;
// Generate disassembly according to official text format.
// Output disassembly to the given output stream, and optionally return an
// offset table of <byte offset, line, column> via the given pointer.
void PrintWasmText(
const WasmModule *module, const ModuleWireBytes &wire_bytes,
uint32_t func_index, std::ostream &os,
std::vector<debug::WasmDisassemblyOffsetTableEntry> *offset_table);
V8_EXPORT_PRIVATE void PrintWasmText(
const WasmModule* module, const ModuleWireBytes& wire_bytes,
uint32_t func_index, std::ostream& os,
std::vector<debug::WasmDisassemblyOffsetTableEntry>* offset_table);
} // namespace wasm
} // namespace internal
......
......@@ -224,6 +224,7 @@ v8_source_set("unittests_sources") {
"wasm/wasm-macro-gen-unittest.cc",
"wasm/wasm-module-builder-unittest.cc",
"wasm/wasm-opcodes-unittest.cc",
"wasm/wasm-text-unittest.cc",
"zone/zone-allocator-unittest.cc",
"zone/zone-chunk-list-unittest.cc",
"zone/zone-unittest.cc",
......
// Copyright 2019 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <sstream>
#include "test/unittests/test-utils.h"
#include "src/wasm/module-decoder.h"
#include "src/wasm/wasm-module-builder.h"
#include "src/wasm/wasm-module.h"
#include "src/wasm/wasm-opcodes.h"
#include "src/wasm/wasm-text.h"
#include "test/common/wasm/test-signatures.h"
namespace v8 {
namespace internal {
namespace wasm {
class WasmTextTest : public TestWithIsolateAndZone {
public:
TestSignatures sigs;
WasmFeatures enabled_features_;
void TestInstruction(const byte* func_start, size_t func_size) {
WasmModuleBuilder mb(zone());
auto* fb = mb.AddFunction(sigs.v_v());
fb->EmitCode(func_start, static_cast<uint32_t>(func_size));
fb->Emit(kExprEnd);
ZoneBuffer buffer(zone());
mb.WriteTo(buffer);
ModuleWireBytes wire_bytes(
Vector<const byte>(buffer.begin(), buffer.size()));
ModuleResult result = DecodeWasmModule(
enabled_features_, buffer.begin(), buffer.end(), false, kWasmOrigin,
isolate()->counters(), isolate()->wasm_engine()->allocator());
EXPECT_TRUE(result.ok());
std::stringstream ss;
PrintWasmText(result.value().get(), wire_bytes, 0, ss, nullptr);
}
};
TEST_F(WasmTextTest, EveryOpcodeCanBeDecoded) {
static const struct {
WasmOpcode opcode;
const char* debug_name;
} kValues[] = {
#define DECLARE_ELEMENT(name, opcode, sig) {kExpr##name, "kExpr" #name},
FOREACH_OPCODE(DECLARE_ELEMENT)};
#undef DECLARE_ELEMENT
for (const auto& value : kValues) {
// Pad with 0 for any immediate values. If they're not needed, they'll be
// interpreted as unreachable.
byte data[20] = {0};
printf("%s\n", value.debug_name);
switch (value.opcode) {
// Instructions that have a special case because they affect the control
// depth.
case kExprBlock:
case kExprLoop:
case kExprIf:
case kExprTry:
data[0] = value.opcode;
data[1] = kLocalVoid;
data[2] = kExprEnd;
break;
case kExprElse:
data[0] = kExprIf;
data[1] = value.opcode;
data[2] = kExprEnd;
break;
case kExprCatch:
data[0] = kExprTry;
data[1] = value.opcode;
data[2] = kExprEnd;
break;
case kExprEnd:
break;
// Instructions with special requirements for immediates.
case kExprSelectWithType:
data[0] = kExprSelectWithType;
data[1] = 1;
data[2] = kLocalI32;
break;
default: {
if (value.opcode >= 0x100) {
data[0] = value.opcode >> 8; // Prefix byte.
byte opcode = value.opcode & 0xff; // Actual opcode.
if (opcode >= 0x80) {
// Opcode with prefix, and needs to be LEB encoded (3 bytes).
// For now, this can only be in the range [0x80, 0xff], which means
// that the third byte is always 1.
data[1] = (opcode & 0x7f) | 0x80;
data[2] = 1;
} else {
// Opcode with prefix (2 bytes).
data[1] = opcode;
}
} else {
// Single-byte opcode.
data[0] = value.opcode;
}
break;
}
}
TestInstruction(data, arraysize(data));
}
}
} // namespace wasm
} // namespace internal
} // namespace v8
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment