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(
int index = static_cast<int>(function_->func_index);
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) {
*decode_ms = decode_timer.Elapsed().InMillisecondsF();
......
......@@ -97,5 +97,11 @@ std::ostream& operator<<(std::ostream& os, const AsUC32& c) {
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 v8
......@@ -66,6 +66,12 @@ struct AsEscapedUC16ForJSON {
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
// printable/space ASCII range. Additionally escapes '\' making escaping
......@@ -83,6 +89,9 @@ std::ostream& operator<<(std::ostream& os, const AsUC16& c);
// of printable ASCII range.
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 v8
......
......@@ -157,10 +157,17 @@ class WasmDecoder : public Decoder {
return false;
}
inline bool Validate(const byte* pc, CallFunctionOperand& operand) {
inline bool Complete(const byte* pc, CallFunctionOperand& operand) {
ModuleEnv* m = module_;
if (m && m->module && operand.index < m->module->functions.size()) {
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());
if (operand.arity != expected) {
error(pc, pc + 1,
......@@ -174,10 +181,17 @@ class WasmDecoder : public Decoder {
return false;
}
inline bool Validate(const byte* pc, CallIndirectOperand& operand) {
inline bool Complete(const byte* pc, CallIndirectOperand& operand) {
ModuleEnv* m = module_;
if (m && m->module && operand.index < m->module->signatures.size()) {
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());
if (operand.arity != expected) {
error(pc, pc + 1,
......@@ -191,10 +205,17 @@ class WasmDecoder : public Decoder {
return false;
}
inline bool Validate(const byte* pc, CallImportOperand& operand) {
inline bool Complete(const byte* pc, CallImportOperand& operand) {
ModuleEnv* m = module_;
if (m && m->module && operand.index < m->module->import_table.size()) {
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());
if (operand.arity != expected) {
error(pc, pc + 1, "arity mismatch in import call (expected %u, got %u)",
......@@ -391,7 +412,7 @@ class WasmDecoder : public Decoder {
// shift-reduce strategy with multiple internal stacks.
class SR_WasmDecoder : public WasmDecoder {
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),
zone_(zone),
builder_(builder),
......@@ -1526,20 +1547,22 @@ int OpcodeArity(const byte* pc, const byte* end) {
}
void PrintAstForDebugging(const byte* start, const byte* end) {
FunctionBody body = {nullptr, nullptr, start, start, end};
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);
SR_WasmDecoder decoder(&zone, nullptr, body);
OFStream os(stdout);
int line_nr = 0;
// Print the function signature.
if (body.sig) {
os << "// signature: " << *body.sig << std::endl;
++line_nr;
}
// Print the local declarations.
......@@ -1556,24 +1579,35 @@ void PrintAst(base::AccountingAllocator* allocator, FunctionBody& body) {
os << std::endl;
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;
++line_nr;
}
os << "// body: \n";
int control_depth = 0;
os << "// body: " << std::endl;
++line_nr;
unsigned control_depth = 0;
while (pc < body.end) {
size_t length = decoder.OpcodeLength(pc);
WasmOpcode opcode = static_cast<WasmOpcode>(*pc);
if (opcode == kExprElse) control_depth--;
for (int i = 0; i < control_depth && i < 32; i++) printf(" ");
printf("k%s,", WasmOpcodes::OpcodeName(opcode));
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));
}
// 64 whitespaces
const char* padding =
" ";
os.write(padding, num_whitespaces);
os << "k" << WasmOpcodes::OpcodeName(opcode) << ",";
for (size_t i = 1; i < length; i++) {
printf(" 0x%02x,", pc[i]);
os << " " << AsHex(pc[i], 2) << ",";
}
switch (opcode) {
......@@ -1606,7 +1640,7 @@ void PrintAst(base::AccountingAllocator* allocator, FunctionBody& body) {
}
case kExprCallIndirect: {
CallIndirectOperand operand(&decoder, pc);
if (decoder.Validate(pc, operand)) {
if (decoder.Complete(pc, operand)) {
os << " // sig #" << operand.index << ": " << *operand.sig;
} else {
os << " // arity=" << operand.arity << " sig #" << operand.index;
......@@ -1615,7 +1649,7 @@ void PrintAst(base::AccountingAllocator* allocator, FunctionBody& body) {
}
case kExprCallImport: {
CallImportOperand operand(&decoder, pc);
if (decoder.Validate(pc, operand)) {
if (decoder.Complete(pc, operand)) {
os << " // import #" << operand.index << ": " << *operand.sig;
} else {
os << " // arity=" << operand.arity << " import #" << operand.index;
......@@ -1624,7 +1658,7 @@ void PrintAst(base::AccountingAllocator* allocator, FunctionBody& body) {
}
case kExprCallFunction: {
CallFunctionOperand operand(&decoder, pc);
if (decoder.Validate(pc, operand)) {
if (decoder.Complete(pc, operand)) {
os << " // function #" << operand.index << ": " << *operand.sig;
} else {
os << " // arity=" << operand.arity << " function #" << operand.index;
......@@ -1642,7 +1676,10 @@ void PrintAst(base::AccountingAllocator* allocator, FunctionBody& body) {
pc += length;
os << std::endl;
++line_nr;
}
return decoder.ok();
}
BitVector* AnalyzeLoopAssignmentForTesting(Zone* zone, size_t num_locals,
......
......@@ -213,6 +213,11 @@ struct FunctionBody {
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;
typedef Result<Tree*> TreeResult;
......@@ -222,7 +227,9 @@ TreeResult VerifyWasmCode(base::AccountingAllocator* allocator,
FunctionBody& body);
TreeResult BuildTFGraph(base::AccountingAllocator* allocator,
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.
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