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,
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,
FunctionBody& body) {
Zone zone(allocator);
......@@ -1511,9 +1522,8 @@ bool PrintAst(base::AccountingAllocator* allocator, const FunctionBody& body,
// Print the local declarations.
AstLocalDecls decls(&zone);
decoder.DecodeLocalDecls(decls);
const byte* pc = decoder.pc();
if (body.start != decoder.pc()) {
BytecodeIterator i(body.start, body.end, &decls);
if (body.start != i.pc()) {
os << "// locals: ";
for (auto p : decls.local_types) {
LocalType type = p.first;
......@@ -1523,7 +1533,7 @@ bool PrintAst(base::AccountingAllocator* allocator, const FunctionBody& body,
os << std::endl;
++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 << std::endl;
......@@ -1533,16 +1543,16 @@ bool PrintAst(base::AccountingAllocator* allocator, const FunctionBody& body,
os << "// body: " << std::endl;
++line_nr;
unsigned control_depth = 0;
while (pc < body.end) {
unsigned length = decoder.OpcodeLength(pc);
for (; i.has_next(); i.next()) {
unsigned length = decoder.OpcodeLength(i.pc());
WasmOpcode opcode = static_cast<WasmOpcode>(*pc);
WasmOpcode opcode = i.current();
if (opcode == kExprElse) control_depth--;
int num_whitespaces = control_depth < 32 ? 2 * control_depth : 64;
if (offset_table) {
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
......@@ -1551,8 +1561,8 @@ bool PrintAst(base::AccountingAllocator* allocator, const FunctionBody& body,
os.write(padding, num_whitespaces);
os << "k" << WasmOpcodes::OpcodeName(opcode) << ",";
for (size_t i = 1; i < length; ++i) {
os << " " << AsHex(pc[i], 2) << ",";
for (size_t j = 1; j < length; ++j) {
os << " " << AsHex(i.pc()[j], 2) << ",";
}
switch (opcode) {
......@@ -1560,32 +1570,32 @@ bool PrintAst(base::AccountingAllocator* allocator, const FunctionBody& body,
case kExprElse:
case kExprLoop:
case kExprBlock:
os << " // @" << static_cast<int>(pc - body.start);
os << " // @" << i.pc_offset();
control_depth++;
break;
case kExprEnd:
os << " // @" << static_cast<int>(pc - body.start);
os << " // @" << i.pc_offset();
control_depth--;
break;
case kExprBr: {
BreakDepthOperand operand(&decoder, pc);
BreakDepthOperand operand(&i, i.pc());
os << " // arity=" << operand.arity << " depth=" << operand.depth;
break;
}
case kExprBrIf: {
BreakDepthOperand operand(&decoder, pc);
BreakDepthOperand operand(&i, i.pc());
os << " // arity=" << operand.arity << " depth" << operand.depth;
break;
}
case kExprBrTable: {
BranchTableOperand operand(&decoder, pc);
BranchTableOperand operand(&i, i.pc());
os << " // arity=" << operand.arity
<< " entries=" << operand.table_count;
break;
}
case kExprCallIndirect: {
CallIndirectOperand operand(&decoder, pc);
if (decoder.Complete(pc, operand)) {
CallIndirectOperand operand(&i, i.pc());
if (decoder.Complete(i.pc(), operand)) {
os << " // sig #" << operand.index << ": " << *operand.sig;
} else {
os << " // arity=" << operand.arity << " sig #" << operand.index;
......@@ -1593,8 +1603,8 @@ bool PrintAst(base::AccountingAllocator* allocator, const FunctionBody& body,
break;
}
case kExprCallImport: {
CallImportOperand operand(&decoder, pc);
if (decoder.Complete(pc, operand)) {
CallImportOperand operand(&i, i.pc());
if (decoder.Complete(i.pc(), operand)) {
os << " // import #" << operand.index << ": " << *operand.sig;
} else {
os << " // arity=" << operand.arity << " import #" << operand.index;
......@@ -1602,8 +1612,8 @@ bool PrintAst(base::AccountingAllocator* allocator, const FunctionBody& body,
break;
}
case kExprCallFunction: {
CallFunctionOperand operand(&decoder, pc);
if (decoder.Complete(pc, operand)) {
CallFunctionOperand operand(&i, i.pc());
if (decoder.Complete(i.pc(), operand)) {
os << " // function #" << operand.index << ": " << *operand.sig;
} else {
os << " // arity=" << operand.arity << " function #" << operand.index;
......@@ -1611,15 +1621,13 @@ bool PrintAst(base::AccountingAllocator* allocator, const FunctionBody& body,
break;
}
case kExprReturn: {
ReturnArityOperand operand(&decoder, pc);
ReturnArityOperand operand(&i, i.pc());
os << " // arity=" << operand.arity;
break;
}
default:
break;
}
pc += length;
os << std::endl;
++line_nr;
}
......
......@@ -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.
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 internal
} // namespace v8
......
......@@ -761,104 +761,101 @@ class ControlTransfers : public ZoneObject {
std::vector<Control> control_stack;
size_t value_depth = 0;
Decoder decoder(start, end); // for reading operands.
const byte* pc = start + locals_encoded_size;
while (pc < end) {
WasmOpcode opcode = static_cast<WasmOpcode>(*pc);
TRACE("@%td: control %s (depth = %zu)\n", (pc - start),
for (BytecodeIterator i(start + locals_encoded_size, end); i.has_next();
i.next()) {
WasmOpcode opcode = i.current();
TRACE("@%u: control %s (depth = %zu)\n", i.pc_offset(),
WasmOpcodes::OpcodeName(opcode), value_depth);
switch (opcode) {
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);
control_stack.push_back({pc, label, nullptr});
control_stack.push_back({i.pc(), label, nullptr});
break;
}
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* label2 = new (zone) CLabel(zone, value_depth);
control_stack.push_back({pc, label1, nullptr});
control_stack.push_back({pc, label2, nullptr});
label2->Bind(&map_, start, pc, false);
control_stack.push_back({i.pc(), label1, nullptr});
control_stack.push_back({i.pc(), label2, nullptr});
label2->Bind(&map_, start, i.pc(), false);
break;
}
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--;
CLabel* end_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});
else_label->Ref(&map_, start, {pc, value_depth, false});
control_stack.push_back({i.pc(), end_label, else_label});
else_label->Ref(&map_, start, {i.pc(), value_depth, false});
break;
}
case kExprElse: {
Control* c = &control_stack.back();
TRACE("control @%td $%zu: Else\n", (pc - start), value_depth);
c->end_label->Ref(&map_, start, {pc, value_depth, false});
TRACE("control @%u $%zu: Else\n", i.pc_offset(), value_depth);
c->end_label->Ref(&map_, start, {i.pc(), value_depth, false});
value_depth = c->end_label->value_depth;
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;
break;
}
case kExprEnd: {
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) {
// only loops have bound labels.
DCHECK_EQ(kExprLoop, *c->pc);
control_stack.pop_back();
c = &control_stack.back();
}
if (c->else_label) c->else_label->Bind(&map_, start, pc + 1, true);
c->end_label->Ref(&map_, start, {pc, value_depth, false});
c->end_label->Bind(&map_, start, pc + 1, true);
if (c->else_label)
c->else_label->Bind(&map_, start, i.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;
control_stack.pop_back();
break;
}
case kExprBr: {
BreakDepthOperand operand(&decoder, pc);
TRACE("control @%td $%zu: Br[arity=%u, depth=%u]\n", (pc - start),
BreakDepthOperand operand(&i, i.pc());
TRACE("control @%u $%zu: Br[arity=%u, depth=%u]\n", i.pc_offset(),
value_depth, operand.arity, operand.depth);
value_depth -= operand.arity;
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++;
break;
}
case kExprBrIf: {
BreakDepthOperand operand(&decoder, pc);
TRACE("control @%td $%zu: BrIf[arity=%u, depth=%u]\n", (pc - start),
BreakDepthOperand operand(&i, i.pc());
TRACE("control @%u $%zu: BrIf[arity=%u, depth=%u]\n", i.pc_offset(),
value_depth, operand.arity, operand.depth);
value_depth -= (operand.arity + 1);
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++;
break;
}
case kExprBrTable: {
BranchTableOperand operand(&decoder, pc);
TRACE("control @%td $%zu: BrTable[arity=%u count=%u]\n", (pc - start),
BranchTableOperand operand(&i, i.pc());
TRACE("control @%u $%zu: BrTable[arity=%u count=%u]\n", i.pc_offset(),
value_depth, operand.arity, operand.table_count);
value_depth -= (operand.arity + 1);
for (uint32_t i = 0; i < operand.table_count + 1; ++i) {
uint32_t target = operand.read_entry(&decoder, i);
for (uint32_t j = 0; j < operand.table_count + 1; ++j) {
uint32_t target = operand.read_entry(&i, j);
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++;
break;
}
default: {
value_depth = value_depth - OpcodeArity(pc, end) + 1;
value_depth = value_depth - OpcodeArity(i.pc(), end) + 1;
break;
}
}
pc += OpcodeLength(pc, end);
}
}
......
......@@ -2437,6 +2437,60 @@ TEST_F(LocalDeclDecoderTest, UseEncoder) {
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 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