Commit 12aa132d authored by clemensh's avatar clemensh Committed by Commit bot

[wasm] Implement AST printing into an ostream

This will be used for disassembling individual wasm function for
showing them in devtools.
The PrintAst function now also optionally provides an offset table
mapping from byte offset to line and column in the generated text.

R=titzer@chromium.org, ahaas@chromium.org
BUG=chromium:613110

Review-Url: https://codereview.chromium.org/2050213002
Cr-Commit-Position: refs/heads/master@{#37026}
parent 13d08bc3
...@@ -3152,7 +3152,8 @@ SourcePositionTable* WasmCompilationUnit::BuildGraphForWasmFunction( ...@@ -3152,7 +3152,8 @@ SourcePositionTable* WasmCompilationUnit::BuildGraphForWasmFunction(
int index = static_cast<int>(function_->func_index); int index = static_cast<int>(function_->func_index);
if (index >= FLAG_trace_wasm_ast_start && index < FLAG_trace_wasm_ast_end) { if (index >= FLAG_trace_wasm_ast_start && index < FLAG_trace_wasm_ast_end) {
PrintAst(isolate_->allocator(), body); OFStream os(stdout);
PrintAst(isolate_->allocator(), body, os, nullptr);
} }
if (FLAG_trace_wasm_decode_time) { if (FLAG_trace_wasm_decode_time) {
*decode_ms = decode_timer.Elapsed().InMillisecondsF(); *decode_ms = decode_timer.Elapsed().InMillisecondsF();
......
...@@ -97,5 +97,11 @@ std::ostream& operator<<(std::ostream& os, const AsUC32& c) { ...@@ -97,5 +97,11 @@ std::ostream& operator<<(std::ostream& os, const AsUC32& c) {
return PrintUC32(os, c.value, IsPrint); return PrintUC32(os, c.value, IsPrint);
} }
std::ostream& operator<<(std::ostream& os, const AsHex& hex) {
char buf[20];
snprintf(buf, sizeof(buf), "%.*" PRIx64, hex.min_width, hex.value);
return os << buf;
}
} // namespace internal } // namespace internal
} // namespace v8 } // namespace v8
...@@ -66,6 +66,12 @@ struct AsEscapedUC16ForJSON { ...@@ -66,6 +66,12 @@ struct AsEscapedUC16ForJSON {
uint16_t value; uint16_t value;
}; };
struct AsHex {
explicit AsHex(uint64_t v, uint8_t min_width = 0)
: value(v), min_width(min_width) {}
uint64_t value;
uint8_t min_width;
};
// Writes the given character to the output escaping everything outside of // Writes the given character to the output escaping everything outside of
// printable/space ASCII range. Additionally escapes '\' making escaping // printable/space ASCII range. Additionally escapes '\' making escaping
...@@ -83,6 +89,9 @@ std::ostream& operator<<(std::ostream& os, const AsUC16& c); ...@@ -83,6 +89,9 @@ std::ostream& operator<<(std::ostream& os, const AsUC16& c);
// of printable ASCII range. // of printable ASCII range.
std::ostream& operator<<(std::ostream& os, const AsUC32& c); std::ostream& operator<<(std::ostream& os, const AsUC32& c);
// Writes the given number to the output in hexadecimal notation.
std::ostream& operator<<(std::ostream& os, const AsHex& v);
} // namespace internal } // namespace internal
} // namespace v8 } // namespace v8
......
...@@ -157,10 +157,17 @@ class WasmDecoder : public Decoder { ...@@ -157,10 +157,17 @@ class WasmDecoder : public Decoder {
return false; return false;
} }
inline bool Validate(const byte* pc, CallFunctionOperand& operand) { inline bool Complete(const byte* pc, CallFunctionOperand& operand) {
ModuleEnv* m = module_; ModuleEnv* m = module_;
if (m && m->module && operand.index < m->module->functions.size()) { if (m && m->module && operand.index < m->module->functions.size()) {
operand.sig = m->module->functions[operand.index].sig; operand.sig = m->module->functions[operand.index].sig;
return true;
}
return false;
}
inline bool Validate(const byte* pc, CallFunctionOperand& operand) {
if (Complete(pc, operand)) {
uint32_t expected = static_cast<uint32_t>(operand.sig->parameter_count()); uint32_t expected = static_cast<uint32_t>(operand.sig->parameter_count());
if (operand.arity != expected) { if (operand.arity != expected) {
error(pc, pc + 1, error(pc, pc + 1,
...@@ -174,10 +181,17 @@ class WasmDecoder : public Decoder { ...@@ -174,10 +181,17 @@ class WasmDecoder : public Decoder {
return false; return false;
} }
inline bool Validate(const byte* pc, CallIndirectOperand& operand) { inline bool Complete(const byte* pc, CallIndirectOperand& operand) {
ModuleEnv* m = module_; ModuleEnv* m = module_;
if (m && m->module && operand.index < m->module->signatures.size()) { if (m && m->module && operand.index < m->module->signatures.size()) {
operand.sig = m->module->signatures[operand.index]; operand.sig = m->module->signatures[operand.index];
return true;
}
return false;
}
inline bool Validate(const byte* pc, CallIndirectOperand& operand) {
if (Complete(pc, operand)) {
uint32_t expected = static_cast<uint32_t>(operand.sig->parameter_count()); uint32_t expected = static_cast<uint32_t>(operand.sig->parameter_count());
if (operand.arity != expected) { if (operand.arity != expected) {
error(pc, pc + 1, error(pc, pc + 1,
...@@ -191,10 +205,17 @@ class WasmDecoder : public Decoder { ...@@ -191,10 +205,17 @@ class WasmDecoder : public Decoder {
return false; return false;
} }
inline bool Validate(const byte* pc, CallImportOperand& operand) { inline bool Complete(const byte* pc, CallImportOperand& operand) {
ModuleEnv* m = module_; ModuleEnv* m = module_;
if (m && m->module && operand.index < m->module->import_table.size()) { if (m && m->module && operand.index < m->module->import_table.size()) {
operand.sig = m->module->import_table[operand.index].sig; operand.sig = m->module->import_table[operand.index].sig;
return true;
}
return false;
}
inline bool Validate(const byte* pc, CallImportOperand& operand) {
if (Complete(pc, operand)) {
uint32_t expected = static_cast<uint32_t>(operand.sig->parameter_count()); uint32_t expected = static_cast<uint32_t>(operand.sig->parameter_count());
if (operand.arity != expected) { if (operand.arity != expected) {
error(pc, pc + 1, "arity mismatch in import call (expected %u, got %u)", error(pc, pc + 1, "arity mismatch in import call (expected %u, got %u)",
...@@ -391,7 +412,7 @@ class WasmDecoder : public Decoder { ...@@ -391,7 +412,7 @@ class WasmDecoder : public Decoder {
// shift-reduce strategy with multiple internal stacks. // shift-reduce strategy with multiple internal stacks.
class SR_WasmDecoder : public WasmDecoder { class SR_WasmDecoder : public WasmDecoder {
public: public:
SR_WasmDecoder(Zone* zone, TFBuilder* builder, FunctionBody& body) SR_WasmDecoder(Zone* zone, TFBuilder* builder, const FunctionBody& body)
: WasmDecoder(body.module, body.sig, body.start, body.end), : WasmDecoder(body.module, body.sig, body.start, body.end),
zone_(zone), zone_(zone),
builder_(builder), builder_(builder),
...@@ -1526,20 +1547,22 @@ int OpcodeArity(const byte* pc, const byte* end) { ...@@ -1526,20 +1547,22 @@ int OpcodeArity(const byte* pc, const byte* end) {
} }
void PrintAstForDebugging(const byte* start, const byte* end) { void PrintAstForDebugging(const byte* start, const byte* end) {
FunctionBody body = {nullptr, nullptr, start, start, end};
base::AccountingAllocator allocator; base::AccountingAllocator allocator;
PrintAst(&allocator, body); OFStream os(stdout);
PrintAst(&allocator, FunctionBodyForTesting(start, end), os, nullptr);
} }
void PrintAst(base::AccountingAllocator* allocator, FunctionBody& body) { bool PrintAst(base::AccountingAllocator* allocator, const FunctionBody& body,
std::ostream& os,
std::vector<std::tuple<uint32_t, int, int>>* offset_table) {
Zone zone(allocator); Zone zone(allocator);
SR_WasmDecoder decoder(&zone, nullptr, body); SR_WasmDecoder decoder(&zone, nullptr, body);
int line_nr = 0;
OFStream os(stdout);
// Print the function signature. // Print the function signature.
if (body.sig) { if (body.sig) {
os << "// signature: " << *body.sig << std::endl; os << "// signature: " << *body.sig << std::endl;
++line_nr;
} }
// Print the local declarations. // Print the local declarations.
...@@ -1556,24 +1579,35 @@ void PrintAst(base::AccountingAllocator* allocator, FunctionBody& body) { ...@@ -1556,24 +1579,35 @@ void PrintAst(base::AccountingAllocator* allocator, FunctionBody& body) {
os << std::endl; os << std::endl;
for (const byte* locals = body.start; locals < pc; locals++) { for (const byte* locals = body.start; locals < pc; locals++) {
printf(" 0x%02x,", *locals); os << (locals == body.start ? "0x" : " 0x") << AsHex(*locals, 2) << ",";
} }
os << std::endl; os << std::endl;
++line_nr;
} }
os << "// body: \n"; os << "// body: " << std::endl;
int control_depth = 0; ++line_nr;
unsigned control_depth = 0;
while (pc < body.end) { while (pc < body.end) {
size_t length = decoder.OpcodeLength(pc); size_t length = decoder.OpcodeLength(pc);
WasmOpcode opcode = static_cast<WasmOpcode>(*pc); WasmOpcode opcode = static_cast<WasmOpcode>(*pc);
if (opcode == kExprElse) control_depth--; if (opcode == kExprElse) control_depth--;
for (int i = 0; i < control_depth && i < 32; i++) printf(" "); int num_whitespaces = control_depth < 32 ? 2 * control_depth : 64;
printf("k%s,", WasmOpcodes::OpcodeName(opcode)); if (offset_table) {
offset_table->push_back(
std::make_tuple(pc - body.start, line_nr, num_whitespaces));
}
// 64 whitespaces
const char* padding =
" ";
os.write(padding, num_whitespaces);
os << "k" << WasmOpcodes::OpcodeName(opcode) << ",";
for (size_t i = 1; i < length; i++) { for (size_t i = 1; i < length; i++) {
printf(" 0x%02x,", pc[i]); os << " " << AsHex(pc[i], 2) << ",";
} }
switch (opcode) { switch (opcode) {
...@@ -1606,7 +1640,7 @@ void PrintAst(base::AccountingAllocator* allocator, FunctionBody& body) { ...@@ -1606,7 +1640,7 @@ void PrintAst(base::AccountingAllocator* allocator, FunctionBody& body) {
} }
case kExprCallIndirect: { case kExprCallIndirect: {
CallIndirectOperand operand(&decoder, pc); CallIndirectOperand operand(&decoder, pc);
if (decoder.Validate(pc, operand)) { if (decoder.Complete(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;
...@@ -1615,7 +1649,7 @@ void PrintAst(base::AccountingAllocator* allocator, FunctionBody& body) { ...@@ -1615,7 +1649,7 @@ void PrintAst(base::AccountingAllocator* allocator, FunctionBody& body) {
} }
case kExprCallImport: { case kExprCallImport: {
CallImportOperand operand(&decoder, pc); CallImportOperand operand(&decoder, pc);
if (decoder.Validate(pc, operand)) { if (decoder.Complete(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;
...@@ -1624,7 +1658,7 @@ void PrintAst(base::AccountingAllocator* allocator, FunctionBody& body) { ...@@ -1624,7 +1658,7 @@ void PrintAst(base::AccountingAllocator* allocator, FunctionBody& body) {
} }
case kExprCallFunction: { case kExprCallFunction: {
CallFunctionOperand operand(&decoder, pc); CallFunctionOperand operand(&decoder, pc);
if (decoder.Validate(pc, operand)) { if (decoder.Complete(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;
...@@ -1642,7 +1676,10 @@ void PrintAst(base::AccountingAllocator* allocator, FunctionBody& body) { ...@@ -1642,7 +1676,10 @@ void PrintAst(base::AccountingAllocator* allocator, FunctionBody& body) {
pc += length; pc += length;
os << std::endl; os << std::endl;
++line_nr;
} }
return decoder.ok();
} }
BitVector* AnalyzeLoopAssignmentForTesting(Zone* zone, size_t num_locals, BitVector* AnalyzeLoopAssignmentForTesting(Zone* zone, size_t num_locals,
......
...@@ -213,6 +213,11 @@ struct FunctionBody { ...@@ -213,6 +213,11 @@ struct FunctionBody {
const byte* end; // end of the function body const byte* end; // end of the function body
}; };
static inline FunctionBody FunctionBodyForTesting(const byte* start,
const byte* end) {
return {nullptr, nullptr, start, start, end};
}
struct Tree; struct Tree;
typedef Result<Tree*> TreeResult; typedef Result<Tree*> TreeResult;
...@@ -222,7 +227,9 @@ TreeResult VerifyWasmCode(base::AccountingAllocator* allocator, ...@@ -222,7 +227,9 @@ TreeResult VerifyWasmCode(base::AccountingAllocator* allocator,
FunctionBody& body); FunctionBody& body);
TreeResult BuildTFGraph(base::AccountingAllocator* allocator, TreeResult BuildTFGraph(base::AccountingAllocator* allocator,
TFBuilder* builder, FunctionBody& body); TFBuilder* builder, FunctionBody& body);
void PrintAst(base::AccountingAllocator* allocator, FunctionBody& body); bool PrintAst(base::AccountingAllocator* allocator, const FunctionBody& body,
std::ostream& os,
std::vector<std::tuple<uint32_t, int, int>>* offset_table);
// A simplified form of AST printing, e.g. from a debugger. // A simplified form of AST printing, e.g. from a debugger.
void PrintAstForDebugging(const byte* start, const byte* end); void PrintAstForDebugging(const byte* start, const byte* end);
......
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