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

[wasm] Add a BytecodeIterator and use in non-performance-critical situations.

R=ahaas@chromium.org
BUG=

Review-Url: https://codereview.chromium.org/2135693002
Cr-Commit-Position: refs/heads/master@{#37642}
parent 85fac499
...@@ -1464,6 +1464,17 @@ bool DecodeLocalDecls(AstLocalDecls& decls, const byte* start, ...@@ -1464,6 +1464,17 @@ bool DecodeLocalDecls(AstLocalDecls& decls, const byte* start,
return decoder.DecodeLocalDecls(decls); return decoder.DecodeLocalDecls(decls);
} }
BytecodeIterator::BytecodeIterator(const byte* start, const byte* end,
AstLocalDecls* decls)
: Decoder(start, end) {
if (decls != nullptr) {
if (DecodeLocalDecls(*decls, start, end)) {
pc_ += decls->decls_encoded_size;
if (pc_ > end_) pc_ = end_;
}
}
}
DecodeResult VerifyWasmCode(base::AccountingAllocator* allocator, DecodeResult VerifyWasmCode(base::AccountingAllocator* allocator,
FunctionBody& body) { FunctionBody& body) {
Zone zone(allocator); Zone zone(allocator);
...@@ -1511,9 +1522,8 @@ bool PrintAst(base::AccountingAllocator* allocator, const FunctionBody& body, ...@@ -1511,9 +1522,8 @@ bool PrintAst(base::AccountingAllocator* allocator, const FunctionBody& body,
// Print the local declarations. // Print the local declarations.
AstLocalDecls decls(&zone); AstLocalDecls decls(&zone);
decoder.DecodeLocalDecls(decls); BytecodeIterator i(body.start, body.end, &decls);
const byte* pc = decoder.pc(); if (body.start != i.pc()) {
if (body.start != decoder.pc()) {
os << "// locals: "; os << "// locals: ";
for (auto p : decls.local_types) { for (auto p : decls.local_types) {
LocalType type = p.first; LocalType type = p.first;
...@@ -1523,7 +1533,7 @@ bool PrintAst(base::AccountingAllocator* allocator, const FunctionBody& body, ...@@ -1523,7 +1533,7 @@ bool PrintAst(base::AccountingAllocator* allocator, const FunctionBody& body,
os << std::endl; os << std::endl;
++line_nr; ++line_nr;
for (const byte* locals = body.start; locals < pc; locals++) { for (const byte* locals = body.start; locals < i.pc(); locals++) {
os << (locals == body.start ? "0x" : " 0x") << AsHex(*locals, 2) << ","; os << (locals == body.start ? "0x" : " 0x") << AsHex(*locals, 2) << ",";
} }
os << std::endl; os << std::endl;
...@@ -1533,16 +1543,16 @@ bool PrintAst(base::AccountingAllocator* allocator, const FunctionBody& body, ...@@ -1533,16 +1543,16 @@ bool PrintAst(base::AccountingAllocator* allocator, const FunctionBody& body,
os << "// body: " << std::endl; os << "// body: " << std::endl;
++line_nr; ++line_nr;
unsigned control_depth = 0; unsigned control_depth = 0;
while (pc < body.end) { for (; i.has_next(); i.next()) {
unsigned length = decoder.OpcodeLength(pc); unsigned length = decoder.OpcodeLength(i.pc());
WasmOpcode opcode = static_cast<WasmOpcode>(*pc); WasmOpcode opcode = i.current();
if (opcode == kExprElse) control_depth--; if (opcode == kExprElse) control_depth--;
int num_whitespaces = control_depth < 32 ? 2 * control_depth : 64; int num_whitespaces = control_depth < 32 ? 2 * control_depth : 64;
if (offset_table) { if (offset_table) {
offset_table->push_back( offset_table->push_back(
std::make_tuple(pc - body.start, line_nr, num_whitespaces)); std::make_tuple(i.pc_offset(), line_nr, num_whitespaces));
} }
// 64 whitespaces // 64 whitespaces
...@@ -1551,8 +1561,8 @@ bool PrintAst(base::AccountingAllocator* allocator, const FunctionBody& body, ...@@ -1551,8 +1561,8 @@ bool PrintAst(base::AccountingAllocator* allocator, const FunctionBody& body,
os.write(padding, num_whitespaces); os.write(padding, num_whitespaces);
os << "k" << WasmOpcodes::OpcodeName(opcode) << ","; os << "k" << WasmOpcodes::OpcodeName(opcode) << ",";
for (size_t i = 1; i < length; ++i) { for (size_t j = 1; j < length; ++j) {
os << " " << AsHex(pc[i], 2) << ","; os << " " << AsHex(i.pc()[j], 2) << ",";
} }
switch (opcode) { switch (opcode) {
...@@ -1560,32 +1570,32 @@ bool PrintAst(base::AccountingAllocator* allocator, const FunctionBody& body, ...@@ -1560,32 +1570,32 @@ bool PrintAst(base::AccountingAllocator* allocator, const FunctionBody& body,
case kExprElse: case kExprElse:
case kExprLoop: case kExprLoop:
case kExprBlock: case kExprBlock:
os << " // @" << static_cast<int>(pc - body.start); os << " // @" << i.pc_offset();
control_depth++; control_depth++;
break; break;
case kExprEnd: case kExprEnd:
os << " // @" << static_cast<int>(pc - body.start); os << " // @" << i.pc_offset();
control_depth--; control_depth--;
break; break;
case kExprBr: { case kExprBr: {
BreakDepthOperand operand(&decoder, pc); BreakDepthOperand operand(&i, i.pc());
os << " // arity=" << operand.arity << " depth=" << operand.depth; os << " // arity=" << operand.arity << " depth=" << operand.depth;
break; break;
} }
case kExprBrIf: { case kExprBrIf: {
BreakDepthOperand operand(&decoder, pc); BreakDepthOperand operand(&i, i.pc());
os << " // arity=" << operand.arity << " depth" << operand.depth; os << " // arity=" << operand.arity << " depth" << operand.depth;
break; break;
} }
case kExprBrTable: { case kExprBrTable: {
BranchTableOperand operand(&decoder, pc); BranchTableOperand operand(&i, i.pc());
os << " // arity=" << operand.arity os << " // arity=" << operand.arity
<< " entries=" << operand.table_count; << " entries=" << operand.table_count;
break; break;
} }
case kExprCallIndirect: { case kExprCallIndirect: {
CallIndirectOperand operand(&decoder, pc); CallIndirectOperand operand(&i, i.pc());
if (decoder.Complete(pc, operand)) { if (decoder.Complete(i.pc(), operand)) {
os << " // sig #" << operand.index << ": " << *operand.sig; os << " // sig #" << operand.index << ": " << *operand.sig;
} else { } else {
os << " // arity=" << operand.arity << " sig #" << operand.index; os << " // arity=" << operand.arity << " sig #" << operand.index;
...@@ -1593,8 +1603,8 @@ bool PrintAst(base::AccountingAllocator* allocator, const FunctionBody& body, ...@@ -1593,8 +1603,8 @@ bool PrintAst(base::AccountingAllocator* allocator, const FunctionBody& body,
break; break;
} }
case kExprCallImport: { case kExprCallImport: {
CallImportOperand operand(&decoder, pc); CallImportOperand operand(&i, i.pc());
if (decoder.Complete(pc, operand)) { if (decoder.Complete(i.pc(), operand)) {
os << " // import #" << operand.index << ": " << *operand.sig; os << " // import #" << operand.index << ": " << *operand.sig;
} else { } else {
os << " // arity=" << operand.arity << " import #" << operand.index; os << " // arity=" << operand.arity << " import #" << operand.index;
...@@ -1602,8 +1612,8 @@ bool PrintAst(base::AccountingAllocator* allocator, const FunctionBody& body, ...@@ -1602,8 +1612,8 @@ bool PrintAst(base::AccountingAllocator* allocator, const FunctionBody& body,
break; break;
} }
case kExprCallFunction: { case kExprCallFunction: {
CallFunctionOperand operand(&decoder, pc); CallFunctionOperand operand(&i, i.pc());
if (decoder.Complete(pc, operand)) { if (decoder.Complete(i.pc(), operand)) {
os << " // function #" << operand.index << ": " << *operand.sig; os << " // function #" << operand.index << ": " << *operand.sig;
} else { } else {
os << " // arity=" << operand.arity << " function #" << operand.index; os << " // arity=" << operand.arity << " function #" << operand.index;
...@@ -1611,15 +1621,13 @@ bool PrintAst(base::AccountingAllocator* allocator, const FunctionBody& body, ...@@ -1611,15 +1621,13 @@ bool PrintAst(base::AccountingAllocator* allocator, const FunctionBody& body,
break; break;
} }
case kExprReturn: { case kExprReturn: {
ReturnArityOperand operand(&decoder, pc); ReturnArityOperand operand(&i, i.pc());
os << " // arity=" << operand.arity; os << " // arity=" << operand.arity;
break; break;
} }
default: default:
break; break;
} }
pc += length;
os << std::endl; os << std::endl;
++line_nr; ++line_nr;
} }
......
...@@ -281,6 +281,59 @@ unsigned OpcodeLength(const byte* pc, const byte* end); ...@@ -281,6 +281,59 @@ unsigned OpcodeLength(const byte* pc, const byte* end);
// Computes the arity (number of sub-nodes) of the opcode at the given address. // Computes the arity (number of sub-nodes) of the opcode at the given address.
unsigned OpcodeArity(const byte* pc, const byte* end); unsigned OpcodeArity(const byte* pc, const byte* end);
// A simple forward iterator for bytecodes.
class BytecodeIterator : public Decoder {
public:
// If one wants to iterate over the bytecode without looking at {pc_offset()}.
class iterator {
public:
inline iterator& operator++() {
DCHECK_LT(ptr_, end_);
ptr_ += OpcodeLength(ptr_, end_);
return *this;
}
inline WasmOpcode operator*() {
DCHECK_LT(ptr_, end_);
return static_cast<WasmOpcode>(*ptr_);
}
inline bool operator==(const iterator& that) {
return this->ptr_ == that.ptr_;
}
inline bool operator!=(const iterator& that) {
return this->ptr_ != that.ptr_;
}
private:
friend class BytecodeIterator;
const byte* ptr_;
const byte* end_;
iterator(const byte* ptr, const byte* end) : ptr_(ptr), end_(end) {}
};
// Create a new {BytecodeIterator}. If the {decls} pointer is non-null,
// assume the bytecode starts with local declarations and decode them.
// Otherwise, do not decode local decls.
BytecodeIterator(const byte* start, const byte* end,
AstLocalDecls* decls = nullptr);
inline iterator begin() const { return iterator(pc_, end_); }
inline iterator end() const { return iterator(end_, end_); }
WasmOpcode current() {
return static_cast<WasmOpcode>(
checked_read_u8(pc_, 0, "expected bytecode"));
}
void next() {
if (pc_ < end_) {
pc_ += OpcodeLength(pc_, end_);
if (pc_ >= end_) pc_ = end_;
}
}
bool has_next() { return pc_ < end_; }
};
} // namespace wasm } // namespace wasm
} // namespace internal } // namespace internal
} // namespace v8 } // namespace v8
......
...@@ -761,104 +761,101 @@ class ControlTransfers : public ZoneObject { ...@@ -761,104 +761,101 @@ class ControlTransfers : public ZoneObject {
std::vector<Control> control_stack; std::vector<Control> control_stack;
size_t value_depth = 0; size_t value_depth = 0;
Decoder decoder(start, end); // for reading operands. for (BytecodeIterator i(start + locals_encoded_size, end); i.has_next();
const byte* pc = start + locals_encoded_size; i.next()) {
WasmOpcode opcode = i.current();
while (pc < end) { TRACE("@%u: control %s (depth = %zu)\n", i.pc_offset(),
WasmOpcode opcode = static_cast<WasmOpcode>(*pc);
TRACE("@%td: control %s (depth = %zu)\n", (pc - start),
WasmOpcodes::OpcodeName(opcode), value_depth); WasmOpcodes::OpcodeName(opcode), value_depth);
switch (opcode) { switch (opcode) {
case kExprBlock: { case kExprBlock: {
TRACE("control @%td $%zu: Block\n", (pc - start), value_depth); TRACE("control @%u $%zu: Block\n", i.pc_offset(), value_depth);
CLabel* label = new (zone) CLabel(zone, value_depth); CLabel* label = new (zone) CLabel(zone, value_depth);
control_stack.push_back({pc, label, nullptr}); control_stack.push_back({i.pc(), label, nullptr});
break; break;
} }
case kExprLoop: { case kExprLoop: {
TRACE("control @%td $%zu: Loop\n", (pc - start), value_depth); TRACE("control @%u $%zu: Loop\n", i.pc_offset(), value_depth);
CLabel* label1 = new (zone) CLabel(zone, value_depth); CLabel* label1 = new (zone) CLabel(zone, value_depth);
CLabel* label2 = new (zone) CLabel(zone, value_depth); CLabel* label2 = new (zone) CLabel(zone, value_depth);
control_stack.push_back({pc, label1, nullptr}); control_stack.push_back({i.pc(), label1, nullptr});
control_stack.push_back({pc, label2, nullptr}); control_stack.push_back({i.pc(), label2, nullptr});
label2->Bind(&map_, start, pc, false); label2->Bind(&map_, start, i.pc(), false);
break; break;
} }
case kExprIf: { case kExprIf: {
TRACE("control @%td $%zu: If\n", (pc - start), value_depth); TRACE("control @%u $%zu: If\n", i.pc_offset(), value_depth);
value_depth--; value_depth--;
CLabel* end_label = new (zone) CLabel(zone, value_depth); CLabel* end_label = new (zone) CLabel(zone, value_depth);
CLabel* else_label = new (zone) CLabel(zone, value_depth); CLabel* else_label = new (zone) CLabel(zone, value_depth);
control_stack.push_back({pc, end_label, else_label}); control_stack.push_back({i.pc(), end_label, else_label});
else_label->Ref(&map_, start, {pc, value_depth, false}); else_label->Ref(&map_, start, {i.pc(), value_depth, false});
break; break;
} }
case kExprElse: { case kExprElse: {
Control* c = &control_stack.back(); Control* c = &control_stack.back();
TRACE("control @%td $%zu: Else\n", (pc - start), value_depth); TRACE("control @%u $%zu: Else\n", i.pc_offset(), value_depth);
c->end_label->Ref(&map_, start, {pc, value_depth, false}); c->end_label->Ref(&map_, start, {i.pc(), value_depth, false});
value_depth = c->end_label->value_depth; value_depth = c->end_label->value_depth;
DCHECK_NOT_NULL(c->else_label); DCHECK_NOT_NULL(c->else_label);
c->else_label->Bind(&map_, start, pc + 1, false); c->else_label->Bind(&map_, start, i.pc() + 1, false);
c->else_label = nullptr; c->else_label = nullptr;
break; break;
} }
case kExprEnd: { case kExprEnd: {
Control* c = &control_stack.back(); Control* c = &control_stack.back();
TRACE("control @%td $%zu: End\n", (pc - start), value_depth); TRACE("control @%u $%zu: End\n", i.pc_offset(), value_depth);
if (c->end_label->target) { if (c->end_label->target) {
// only loops have bound labels. // only loops have bound labels.
DCHECK_EQ(kExprLoop, *c->pc); DCHECK_EQ(kExprLoop, *c->pc);
control_stack.pop_back(); control_stack.pop_back();
c = &control_stack.back(); c = &control_stack.back();
} }
if (c->else_label) c->else_label->Bind(&map_, start, pc + 1, true); if (c->else_label)
c->end_label->Ref(&map_, start, {pc, value_depth, false}); c->else_label->Bind(&map_, start, i.pc() + 1, true);
c->end_label->Bind(&map_, start, pc + 1, true); c->end_label->Ref(&map_, start, {i.pc(), value_depth, false});
c->end_label->Bind(&map_, start, i.pc() + 1, true);
value_depth = c->end_label->value_depth + 1; value_depth = c->end_label->value_depth + 1;
control_stack.pop_back(); control_stack.pop_back();
break; break;
} }
case kExprBr: { case kExprBr: {
BreakDepthOperand operand(&decoder, pc); BreakDepthOperand operand(&i, i.pc());
TRACE("control @%td $%zu: Br[arity=%u, depth=%u]\n", (pc - start), TRACE("control @%u $%zu: Br[arity=%u, depth=%u]\n", i.pc_offset(),
value_depth, operand.arity, operand.depth); value_depth, operand.arity, operand.depth);
value_depth -= operand.arity; value_depth -= operand.arity;
control_stack[control_stack.size() - operand.depth - 1].Ref( control_stack[control_stack.size() - operand.depth - 1].Ref(
&map_, start, pc, value_depth, operand.arity > 0); &map_, start, i.pc(), value_depth, operand.arity > 0);
value_depth++; value_depth++;
break; break;
} }
case kExprBrIf: { case kExprBrIf: {
BreakDepthOperand operand(&decoder, pc); BreakDepthOperand operand(&i, i.pc());
TRACE("control @%td $%zu: BrIf[arity=%u, depth=%u]\n", (pc - start), TRACE("control @%u $%zu: BrIf[arity=%u, depth=%u]\n", i.pc_offset(),
value_depth, operand.arity, operand.depth); value_depth, operand.arity, operand.depth);
value_depth -= (operand.arity + 1); value_depth -= (operand.arity + 1);
control_stack[control_stack.size() - operand.depth - 1].Ref( control_stack[control_stack.size() - operand.depth - 1].Ref(
&map_, start, pc, value_depth, operand.arity > 0); &map_, start, i.pc(), value_depth, operand.arity > 0);
value_depth++; value_depth++;
break; break;
} }
case kExprBrTable: { case kExprBrTable: {
BranchTableOperand operand(&decoder, pc); BranchTableOperand operand(&i, i.pc());
TRACE("control @%td $%zu: BrTable[arity=%u count=%u]\n", (pc - start), TRACE("control @%u $%zu: BrTable[arity=%u count=%u]\n", i.pc_offset(),
value_depth, operand.arity, operand.table_count); value_depth, operand.arity, operand.table_count);
value_depth -= (operand.arity + 1); value_depth -= (operand.arity + 1);
for (uint32_t i = 0; i < operand.table_count + 1; ++i) { for (uint32_t j = 0; j < operand.table_count + 1; ++j) {
uint32_t target = operand.read_entry(&decoder, i); uint32_t target = operand.read_entry(&i, j);
control_stack[control_stack.size() - target - 1].Ref( control_stack[control_stack.size() - target - 1].Ref(
&map_, start, pc + i, value_depth, operand.arity > 0); &map_, start, i.pc() + j, value_depth, operand.arity > 0);
} }
value_depth++; value_depth++;
break; break;
} }
default: { default: {
value_depth = value_depth - OpcodeArity(pc, end) + 1; value_depth = value_depth - OpcodeArity(i.pc(), end) + 1;
break; break;
} }
} }
pc += OpcodeLength(pc, end);
} }
} }
......
...@@ -2437,6 +2437,60 @@ TEST_F(LocalDeclDecoderTest, UseEncoder) { ...@@ -2437,6 +2437,60 @@ TEST_F(LocalDeclDecoderTest, UseEncoder) {
pos = ExpectRun(map, pos, kAstI64, 212); pos = ExpectRun(map, pos, kAstI64, 212);
} }
class BytecodeIteratorTest : public TestWithZone {};
TEST_F(BytecodeIteratorTest, SimpleForeach) {
byte code[] = {WASM_IF_ELSE(WASM_ZERO, WASM_ZERO, WASM_ZERO)};
BytecodeIterator iter(code, code + sizeof(code));
WasmOpcode expected[] = {kExprI8Const, kExprIf, kExprI8Const,
kExprElse, kExprI8Const, kExprEnd};
size_t pos = 0;
for (WasmOpcode opcode : iter) {
if (pos >= arraysize(expected)) {
EXPECT_TRUE(false);
break;
}
EXPECT_EQ(expected[pos++], opcode);
}
EXPECT_EQ(arraysize(expected), pos);
}
TEST_F(BytecodeIteratorTest, ForeachTwice) {
byte code[] = {WASM_IF_ELSE(WASM_ZERO, WASM_ZERO, WASM_ZERO)};
BytecodeIterator iter(code, code + sizeof(code));
int count = 0;
count = 0;
for (WasmOpcode opcode : iter) {
USE(opcode);
count++;
}
EXPECT_EQ(6, count);
count = 0;
for (WasmOpcode opcode : iter) {
USE(opcode);
count++;
}
EXPECT_EQ(6, count);
}
TEST_F(BytecodeIteratorTest, WithAstDecls) {
byte code[] = {1, 1, kLocalI32, WASM_I8(9), WASM_I8(11)};
AstLocalDecls decls(zone());
BytecodeIterator iter(code, code + sizeof(code), &decls);
EXPECT_EQ(3, decls.decls_encoded_size);
EXPECT_EQ(3, iter.pc_offset());
EXPECT_TRUE(iter.has_next());
EXPECT_EQ(kExprI8Const, iter.current());
iter.next();
EXPECT_TRUE(iter.has_next());
EXPECT_EQ(kExprI8Const, iter.current());
iter.next();
EXPECT_FALSE(iter.has_next());
}
} // namespace wasm } // namespace wasm
} // namespace internal } // namespace internal
} // namespace v8 } // 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