Commit 9c006b72 authored by titzer's avatar titzer Committed by Commit bot

[wasm] Refactor handling of operands to bytecodes.

This cleans up and simplifyies handling the bytes followin an opcode
with little helper structs that will be useful in the interpreter and
already have been in keeping OpcodeArity and OpcodeLength up to date
with the decoder.

R=bradnelson@chromium.org, ahaas@chromium.org
BUG=

Review URL: https://codereview.chromium.org/1664883002

Cr-Commit-Position: refs/heads/master@{#33723}
parent 9aa612cb
This diff is collapsed.
......@@ -6,6 +6,7 @@
#define V8_WASM_AST_DECODER_H_
#include "src/signature.h"
#include "src/wasm/decoder.h"
#include "src/wasm/wasm-opcodes.h"
#include "src/wasm/wasm-result.h"
......@@ -20,6 +21,156 @@ class WasmGraphBuilder;
namespace wasm {
// Helpers for decoding different kinds of operands which follow bytecodes.
struct LocalIndexOperand {
uint32_t index;
LocalType type;
int length;
inline LocalIndexOperand(Decoder* decoder, const byte* pc) {
index = decoder->checked_read_u32v(pc, 1, &length, "local index");
type = kAstStmt;
}
};
struct ImmI8Operand {
int8_t value;
int length;
inline ImmI8Operand(Decoder* decoder, const byte* pc) {
value = bit_cast<int8_t>(decoder->checked_read_u8(pc, 1, "immi8"));
length = 1;
}
};
struct ImmI32Operand {
int32_t value;
int length;
inline ImmI32Operand(Decoder* decoder, const byte* pc) {
value = bit_cast<int32_t>(decoder->checked_read_u32(pc, 1, "immi32"));
length = 4;
}
};
struct ImmI64Operand {
int64_t value;
int length;
inline ImmI64Operand(Decoder* decoder, const byte* pc) {
value = bit_cast<int64_t>(decoder->checked_read_u64(pc, 1, "immi64"));
length = 8;
}
};
struct ImmF32Operand {
float value;
int length;
inline ImmF32Operand(Decoder* decoder, const byte* pc) {
value = bit_cast<float>(decoder->checked_read_u32(pc, 1, "immf32"));
length = 4;
}
};
struct ImmF64Operand {
double value;
int length;
inline ImmF64Operand(Decoder* decoder, const byte* pc) {
value = bit_cast<double>(decoder->checked_read_u64(pc, 1, "immf64"));
length = 8;
}
};
struct GlobalIndexOperand {
uint32_t index;
LocalType type;
MachineType machine_type;
int length;
inline GlobalIndexOperand(Decoder* decoder, const byte* pc) {
index = decoder->checked_read_u32v(pc, 1, &length, "global index");
type = kAstStmt;
machine_type = MachineType::None();
}
};
struct Block;
struct BreakDepthOperand {
uint32_t depth;
Block* target;
int length;
inline BreakDepthOperand(Decoder* decoder, const byte* pc) {
depth = decoder->checked_read_u8(pc, 1, "break depth");
length = 1;
target = nullptr;
}
};
struct BlockCountOperand {
uint32_t count;
int length;
inline BlockCountOperand(Decoder* decoder, const byte* pc) {
count = decoder->checked_read_u8(pc, 1, "block count");
length = 1;
}
};
struct SignatureIndexOperand {
uint32_t index;
FunctionSig* sig;
int length;
inline SignatureIndexOperand(Decoder* decoder, const byte* pc) {
index = decoder->checked_read_u32v(pc, 1, &length, "signature index");
sig = nullptr;
}
};
struct FunctionIndexOperand {
uint32_t index;
FunctionSig* sig;
int length;
inline FunctionIndexOperand(Decoder* decoder, const byte* pc) {
index = decoder->checked_read_u32v(pc, 1, &length, "function index");
sig = nullptr;
}
};
struct TableSwitchOperand {
uint32_t case_count;
uint32_t table_count;
const byte* table;
int length;
inline TableSwitchOperand(Decoder* decoder, const byte* pc) {
case_count = decoder->checked_read_u16(pc, 1, "expected #cases");
table_count = decoder->checked_read_u16(pc, 3, "expected #entries");
length = 4 + table_count * 2;
if (decoder->check(pc, 5, table_count * 2, "expected <table entries>")) {
table = pc + 5;
} else {
table = nullptr;
}
}
inline uint16_t read_entry(Decoder* decoder, int i) {
DCHECK(i >= 0 && static_cast<uint32_t>(i) < table_count);
return table ? decoder->read_u16(table + i * sizeof(uint16_t)) : 0;
}
};
struct MemoryAccessOperand {
bool aligned;
uint32_t offset;
int length;
inline MemoryAccessOperand(Decoder* decoder, const byte* pc) {
byte bitfield = decoder->checked_read_u8(pc, 1, "memory access byte");
aligned = MemoryAccess::AlignmentField::decode(bitfield);
if (MemoryAccess::OffsetField::decode(bitfield)) {
offset = decoder->checked_read_u32v(pc, 2, &length, "memory offset");
length++;
} else {
offset = 0;
length = 1;
}
}
};
typedef compiler::WasmGraphBuilder TFBuilder;
struct ModuleEnv; // forward declaration of module interface.
......@@ -34,7 +185,6 @@ struct FunctionEnv {
uint32_t local_f64_count; // number of float64 locals
uint32_t total_locals; // sum of parameters and all locals
bool IsValidLocal(uint32_t index) { return index < total_locals; }
uint32_t GetLocalCount() { return total_locals; }
LocalType GetLocalType(uint32_t index) {
if (index < static_cast<uint32_t>(sig->parameter_count())) {
......@@ -112,10 +262,10 @@ BitVector* AnalyzeLoopAssignmentForTesting(Zone* zone, FunctionEnv* env,
const byte* start, const byte* end);
// Computes the length of the opcode at the given address.
int OpcodeLength(const byte* pc);
int OpcodeLength(const byte* pc, const byte* end);
// Computes the arity (number of sub-nodes) of the opcode at the given address.
int OpcodeArity(FunctionEnv* env, const byte* pc);
int OpcodeArity(FunctionEnv* env, const byte* pc, const byte* end);
} // namespace wasm
} // namespace internal
} // namespace v8
......
......@@ -44,6 +44,68 @@ class Decoder {
virtual ~Decoder() {}
inline bool check(const byte* base, int offset, int length, const char* msg) {
DCHECK_GE(base, start_);
if ((base + offset + length) > limit_) {
error(base, base + offset, msg);
return false;
}
return true;
}
// Reads a single 8-bit byte, reporting an error if out of bounds.
inline uint8_t checked_read_u8(const byte* base, int offset,
const char* msg = "expected 1 byte") {
return check(base, offset, 1, msg) ? base[offset] : 0;
}
// Reads 16-bit word, reporting an error if out of bounds.
inline uint16_t checked_read_u16(const byte* base, int offset,
const char* msg = "expected 2 bytes") {
return check(base, offset, 2, msg) ? read_u16(base + offset) : 0;
}
// Reads 32-bit word, reporting an error if out of bounds.
inline uint32_t checked_read_u32(const byte* base, int offset,
const char* msg = "expected 4 bytes") {
return check(base, offset, 4, msg) ? read_u32(base + offset) : 0;
}
// Reads 64-bit word, reporting an error if out of bounds.
inline uint64_t checked_read_u64(const byte* base, int offset,
const char* msg = "expected 8 bytes") {
return check(base, offset, 8, msg) ? read_u64(base + offset) : 0;
}
uint32_t checked_read_u32v(const byte* base, int offset, int* length,
const char* msg = "expected LEB128") {
if (!check(base, offset, 1, msg)) {
*length = 0;
return 0;
}
const ptrdiff_t kMaxDiff = 5; // maximum 5 bytes.
const byte* ptr = base + offset;
const byte* end = ptr + kMaxDiff;
if (end > limit_) end = limit_;
int shift = 0;
byte b = 0;
uint32_t result = 0;
while (ptr < end) {
b = *ptr++;
result = result | ((b & 0x7F) << shift);
if ((b & 0x80) == 0) break;
shift += 7;
}
DCHECK_LE(ptr - (base + offset), kMaxDiff);
*length = static_cast<int>(ptr - (base + offset));
if (ptr == end && (b & 0x80)) {
error(base, ptr, msg);
return 0;
}
return result;
}
// Reads a single 16-bit unsigned integer (little endian).
inline uint16_t read_u16(const byte* ptr) {
DCHECK(ptr >= start_ && (ptr + 2) <= end_);
......@@ -170,6 +232,12 @@ class Decoder {
}
}
bool RangeOk(const byte* pc, int length) {
if (pc < start_ || pc_ >= limit_) return false;
if ((pc + length) >= limit_) return false;
return true;
}
void error(const char* msg) { error(pc_, nullptr, msg); }
void error(const byte* pc, const char* msg) { error(pc, nullptr, msg); }
......
......@@ -1913,14 +1913,12 @@ class WasmOpcodeLengthTest : public TestWithZone {
WasmOpcodeLengthTest() : TestWithZone() {}
};
#define EXPECT_LENGTH(expected, opcode) \
{ \
static const byte code[] = {opcode, 0, 0, 0, 0, 0, 0, 0, 0}; \
EXPECT_EQ(expected, OpcodeLength(code)); \
#define EXPECT_LENGTH(expected, opcode) \
{ \
static const byte code[] = {opcode, 0, 0, 0, 0, 0, 0, 0, 0}; \
EXPECT_EQ(expected, OpcodeLength(code, code + sizeof(code))); \
}
TEST_F(WasmOpcodeLengthTest, Statements) {
EXPECT_LENGTH(1, kExprNop);
EXPECT_LENGTH(2, kExprBlock);
......@@ -1961,11 +1959,11 @@ TEST_F(WasmOpcodeLengthTest, VariableLength) {
byte size5[] = {kExprLoadGlobal, 1 | 0x80, 2 | 0x80, 3 | 0x80, 4};
byte size6[] = {kExprLoadGlobal, 1 | 0x80, 2 | 0x80, 3 | 0x80, 4 | 0x80, 5};
EXPECT_EQ(2, OpcodeLength(size2));
EXPECT_EQ(3, OpcodeLength(size3));
EXPECT_EQ(4, OpcodeLength(size4));
EXPECT_EQ(5, OpcodeLength(size5));
EXPECT_EQ(6, OpcodeLength(size6));
EXPECT_EQ(2, OpcodeLength(size2, size2 + sizeof(size2)));
EXPECT_EQ(3, OpcodeLength(size3, size3 + sizeof(size3)));
EXPECT_EQ(4, OpcodeLength(size4, size4 + sizeof(size4)));
EXPECT_EQ(5, OpcodeLength(size5, size5 + sizeof(size5)));
EXPECT_EQ(6, OpcodeLength(size6, size6 + sizeof(size6)));
}
......@@ -2130,14 +2128,12 @@ class WasmOpcodeArityTest : public TestWithZone {
WasmOpcodeArityTest() : TestWithZone() {}
};
#define EXPECT_ARITY(expected, ...) \
{ \
static const byte code[] = {__VA_ARGS__}; \
EXPECT_EQ(expected, OpcodeArity(&env, code)); \
#define EXPECT_ARITY(expected, ...) \
{ \
static const byte code[] = {__VA_ARGS__}; \
EXPECT_EQ(expected, OpcodeArity(&env, code, code + sizeof(code))); \
}
TEST_F(WasmOpcodeArityTest, Control) {
FunctionEnv env;
EXPECT_ARITY(0, kExprNop);
......
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