Chromium Code Reviews| Index: src/wasm/s-expr.cc |
| diff --git a/src/wasm/s-expr.cc b/src/wasm/s-expr.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..211afe74e9091254178cd573dd1026e608349696 |
| --- /dev/null |
| +++ b/src/wasm/s-expr.cc |
| @@ -0,0 +1,296 @@ |
| +// Copyright 2016 the V8 project authors. All rights reserved. |
| +// Use of this source code is governed by a BSD-style license that can be |
| +// found in the LICENSE file. |
| + |
| +#include "src/wasm/s-expr.h" |
| + |
| +#include "src/ostreams.h" |
| +#include "src/wasm/ast-decoder.h" |
| +#include "src/wasm/wasm-module.h" |
| +#include "src/wasm/wasm-opcodes.h" |
| +#include "src/zone/zone.h" |
| + |
| +using namespace v8::internal; |
| +using namespace v8::internal::wasm; |
| + |
| +namespace { |
| +const char *GetOpName(WasmOpcode opcode) { |
| +#define CASE_OP(name, str) \ |
| + case kExpr##name: \ |
| + return str; |
| +#define CASE_I32_OP(name, str) CASE_OP(I32##name, "i32." str) |
| +#define CASE_I64_OP(name, str) CASE_OP(I64##name, "i64." str) |
| +#define CASE_F32_OP(name, str) CASE_OP(F32##name, "f32." str) |
| +#define CASE_F64_OP(name, str) CASE_OP(F64##name, "f64." str) |
| +#define CASE_INT_OP(name, str) CASE_I32_OP(name, str) CASE_I64_OP(name, str) |
| +#define CASE_FLOAT_OP(name, str) CASE_F32_OP(name, str) CASE_F64_OP(name, str) |
| +#define CASE_ALL_OP(name, str) CASE_FLOAT_OP(name, str) CASE_INT_OP(name, str) |
| +#define CASE_SIGN_OP(TYPE, name, str) \ |
| + CASE_##TYPE##_OP(name##S, str "_s") CASE_##TYPE##_OP(name##U, str "_u") |
| +#define CASE_ALL_SIGN_OP(name, str) \ |
| + CASE_FLOAT_OP(name, str) CASE_SIGN_OP(INT, name, str) |
| +#define CASE_CONVERT_OP(name, RES, SRC, src_suffix, str) \ |
| + CASE_##RES##_OP(U##name##SRC, str "_u/" src_suffix) \ |
| + CASE_##RES##_OP(S##name##SRC, str "_s/" src_suffix) |
| + |
| + switch (opcode) { |
| + CASE_INT_OP(Eqz, "eqz") |
|
titzer
2016/11/21 14:57:26
Nice! This is well macro'd code :-)
|
| + CASE_ALL_OP(Eq, "eq") |
| + CASE_ALL_OP(Ne, "ne") |
| + CASE_ALL_OP(Add, "add") |
| + CASE_ALL_OP(Sub, "sub") |
| + CASE_ALL_OP(Mul, "mul") |
| + CASE_ALL_SIGN_OP(Lt, "lt") |
| + CASE_ALL_SIGN_OP(Gt, "gt") |
| + CASE_ALL_SIGN_OP(Le, "le") |
| + CASE_ALL_SIGN_OP(Ge, "ge") |
| + CASE_INT_OP(Clz, "clz") |
| + CASE_INT_OP(Ctz, "ctz") |
| + CASE_INT_OP(Popcnt, "popcnt") |
| + CASE_ALL_SIGN_OP(Div, "div") |
| + CASE_SIGN_OP(INT, Rem, "rem") |
| + CASE_INT_OP(And, "and") |
| + CASE_INT_OP(Ior, "or") |
| + CASE_INT_OP(Xor, "xor") |
| + CASE_INT_OP(Shl, "shl") |
| + CASE_SIGN_OP(INT, Shr, "shr") |
|
rossberg
2016/11/21 15:03:51
Nit: there should be CASE_INT_SIGN_OP for symmetry
Clemens Hammacher
2016/11/21 17:48:40
I use CASE_SIGN_OP for both I64 and INT. So I woul
|
| + CASE_INT_OP(Rol, "rol") |
| + CASE_INT_OP(Ror, "ror") |
| + CASE_FLOAT_OP(Abs, "abs") |
| + CASE_FLOAT_OP(Neg, "neg") |
| + CASE_FLOAT_OP(Ceil, "ceil") |
| + CASE_FLOAT_OP(Floor, "floor") |
| + CASE_FLOAT_OP(Trunc, "trunc") |
| + CASE_FLOAT_OP(NearestInt, "nearest") |
| + CASE_FLOAT_OP(Sqrt, "sqrt") |
| + CASE_FLOAT_OP(Min, "min") |
| + CASE_FLOAT_OP(Max, "max") |
| + CASE_FLOAT_OP(CopySign, "copysign") |
| + CASE_I32_OP(ConvertI64, "wrap/i64") |
| + CASE_CONVERT_OP(Convert, INT, F32, "f32", "trunc") |
| + CASE_CONVERT_OP(Convert, INT, F64, "f64", "trunc") |
| + CASE_CONVERT_OP(Convert, I64, I32, "i32", "extend") |
| + CASE_CONVERT_OP(Convert, F32, I32, "i32", "convert") |
| + CASE_CONVERT_OP(Convert, F32, I64, "i64", "convert") |
| + CASE_F32_OP(ConvertF64, "demote/f64") |
| + CASE_CONVERT_OP(Convert, F64, I32, "i32", "convert") |
| + CASE_CONVERT_OP(Convert, F64, I64, "i64", "convert") |
| + CASE_F64_OP(ConvertF32, "promote/f32") |
| + CASE_I32_OP(ReinterpretF32, "reinterpret/f32") |
| + CASE_I64_OP(ReinterpretF64, "reinterpret/f64") |
| + CASE_F32_OP(ReinterpretI32, "reinterpret/i32") |
| + CASE_F64_OP(ReinterpretI64, "reinterpret/i64") |
| + CASE_OP(Unreachable, "unreachable") |
| + CASE_OP(Nop, "nop") |
| + CASE_OP(Return, "return") |
| + CASE_OP(MemorySize, "current_memory") |
| + CASE_OP(GrowMemory, "grow_memory") |
| + CASE_OP(Loop, "loop") |
| + CASE_OP(If, "if") |
| + CASE_OP(Block, "block") |
| + CASE_OP(Try, "try") |
| + CASE_OP(Throw, "throw") |
| + CASE_OP(Catch, "catch") |
| + CASE_OP(Drop, "drop") |
| + CASE_ALL_OP(LoadMem, "load") |
| + CASE_SIGN_OP(INT, LoadMem8, "load8") |
| + CASE_SIGN_OP(INT, LoadMem16, "load16") |
| + CASE_SIGN_OP(I64, LoadMem32, "load32") |
| + CASE_ALL_OP(StoreMem, "store") |
| + CASE_INT_OP(StoreMem8, "store8") |
| + CASE_INT_OP(StoreMem16, "store16") |
| + CASE_I64_OP(StoreMem32, "store32") |
| + CASE_OP(SetLocal, "set_local") |
| + CASE_OP(GetLocal, "get_local") |
| + CASE_OP(TeeLocal, "tee_local") |
| + default: |
| + UNREACHABLE(); |
| + return WasmOpcodes::OpcodeName(opcode); |
| + } |
| +} |
| + |
| +bool IsValidFunctionName(const Vector<const char> &name) { |
| + if (name.is_empty()) return false; |
| + const char *special_chars = "_.+-*/\\^~=<>!?@#$%&|:'`"; |
| + for (char c : name) { |
| + bool valid_char = (c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') || |
| + (c >= 'A' && c <= 'Z') || strchr(special_chars, c); |
| + if (!valid_char) return false; |
| + } |
| + return true; |
| +} |
| + |
| +} // namespace |
| + |
| +void wasm::PrintSExpr( |
| + const WasmModule *module, uint32_t func_index, std::ostream &os, |
| + std::vector<std::tuple<uint32_t, int, int>> *offset_table) { |
| + DCHECK_NOT_NULL(module); |
| + DCHECK_GT(module->functions.size(), func_index); |
| + const WasmFunction *fun = &module->functions[func_index]; |
| + |
| + AccountingAllocator allocator; |
| + Zone zone(&allocator, ZONE_NAME); |
| + int line_nr = 0; |
| + int control_depth = 0; |
| + |
| + // Print the function signature. |
| + os << "func"; |
| + Vector<const char> fun_name( |
| + reinterpret_cast<const char *>(module->module_start + fun->name_offset), |
| + fun->name_length); |
| + if (IsValidFunctionName(fun_name)) { |
| + os << " $"; |
| + os.write(fun_name.start(), fun_name.length()); |
| + } |
| + size_t param_count = fun->sig->parameter_count(); |
| + if (param_count) { |
| + os << " (param"; |
| + for (size_t i = 0; i < param_count; ++i) |
| + os << ' ' << WasmOpcodes::TypeName(fun->sig->GetParam(i)); |
| + os << ')'; |
| + } |
| + size_t return_count = fun->sig->return_count(); |
| + if (return_count) { |
| + os << " (result"; |
| + for (size_t i = 0; i < return_count; ++i) |
| + os << ' ' << WasmOpcodes::TypeName(fun->sig->GetReturn(i)); |
| + os << ')'; |
| + } |
| + os << "\n"; |
| + ++line_nr; |
| + |
| + // Print the local declarations. |
| + AstLocalDecls decls(&zone); |
| + const byte *code_start = module->module_start + fun->code_start_offset; |
| + const byte *code_end = module->module_start + fun->code_end_offset; |
| + BytecodeIterator i(code_start, code_end, &decls); |
| + DCHECK_LT(code_start, i.pc()); |
| + if (!decls.local_types.empty()) { |
| + os << "(local"; |
| + for (auto p : decls.local_types) { |
| + for (unsigned i = 0; i < p.second; ++i) |
| + os << ' ' << WasmOpcodes::TypeName(p.first); |
| + } |
| + os << ")\n"; |
| + os << std::endl; |
| + ++line_nr; |
| + } |
| + |
| + for (; i.has_next(); i.next()) { |
| + WasmOpcode opcode = i.current(); |
| + if (opcode == kExprElse || opcode == kExprEnd) --control_depth; |
| + |
| + DCHECK_LE(0, control_depth); |
| + int num_whitespaces = control_depth < 32 ? 2 * control_depth : 64; |
|
rossberg
2016/11/21 15:03:50
Maybe turn the 32 into a constant.
Clemens Hammacher
2016/11/21 17:48:40
Done.
|
| + if (offset_table) { |
| + offset_table->push_back( |
| + std::make_tuple(i.pc_offset(), line_nr, num_whitespaces)); |
| + } |
| + |
| + // 64 whitespaces |
| + const char *padding = |
| + " "; |
| + os.write(padding, num_whitespaces); |
| + |
| + switch (opcode) { |
| + case kExprLoop: |
| + case kExprIf: |
| + case kExprBlock: |
| + case kExprTry: { |
| + BlockTypeOperand operand(&i, i.pc()); |
| + os << GetOpName(opcode); |
| + for (unsigned i = 0; i < operand.arity; i++) { |
| + os << " " << WasmOpcodes::TypeName(operand.read_entry(i)); |
| + } |
| + control_depth++; |
| + break; |
| + } |
| + case kExprBr: |
| + case kExprBrIf: { |
| + BreakDepthOperand operand(&i, i.pc()); |
| + os << "br" << (opcode == kExprBr ? "" : "_if") << " " << operand.depth; |
|
rossberg
2016/11/21 15:03:51
Why not use GetOpName here?
Clemens Hammacher
2016/11/21 17:48:40
Yeah, it's just two alternatives, so I thought inl
|
| + break; |
| + } |
| + case kExprElse: |
| + os << "else"; |
| + control_depth++; |
| + break; |
| + case kExprEnd: |
| + os << "end"; |
| + break; |
| + case kExprBrTable: { |
| + BranchTableOperand operand(&i, i.pc()); |
| + os << "br_table"; |
| + for (unsigned op = 0, num_ops = operand.table_count; op < num_ops; ++op) |
| + os << ' ' << operand.read_entry(&i, op); |
| + break; |
| + } |
| + case kExprCallIndirect: { |
| + CallIndirectOperand operand(&i, i.pc()); |
| + DCHECK_EQ(0U, operand.table_index); |
| + os << "call_indirect " << operand.index; |
| + break; |
| + } |
| + case kExprCallFunction: { |
| + CallFunctionOperand operand(&i, i.pc()); |
| + os << "call " << operand.index; |
| + break; |
| + } |
| + case kExprGetLocal: |
| + case kExprSetLocal: |
| + case kExprTeeLocal: |
| + case kExprCatch: { |
| + LocalIndexOperand operand(&i, i.pc()); |
| + os << GetOpName(opcode) << ' ' << operand.index; |
| + break; |
| + } |
| + case kExprGetGlobal: |
| + case kExprSetGlobal: { |
| + GlobalIndexOperand operand(&i, i.pc()); |
| + os << (opcode == kExprGetLocal ? 'g' : 's') << "et_global " |
|
rossberg
2016/11/21 15:03:51
GetOpName?
Clemens Hammacher
2016/11/21 17:48:40
Same.
|
| + << operand.index; |
| + break; |
| + } |
| +#define CASE_CONST(type, str) \ |
| + case kExpr##type##Const: { \ |
| + Imm##type##Operand operand(&i, i.pc()); \ |
| + os << #str ".const " << operand.value; \ |
| + break; \ |
| + } |
| + CASE_CONST(I8, i8) |
| + CASE_CONST(I32, i32) |
| + CASE_CONST(I64, i64) |
| + CASE_CONST(F32, f32) |
| + CASE_CONST(F64, f64) |
| + |
| +#define CASE_OPCODE(opcode, _, __) case kExpr##opcode: |
| + FOREACH_SIMPLE_OPCODE(CASE_OPCODE) |
|
rossberg
2016/11/21 15:03:51
Nit: indent less?
Clemens Hammacher
2016/11/21 17:48:40
This is clang-format messing it up. I tried to pla
rossberg
2016/11/22 11:51:58
clang-format, making your code horrible since 2015
|
| + case kExprUnreachable: |
| + case kExprNop: |
| + case kExprReturn: |
| + case kExprMemorySize: |
| + case kExprGrowMemory: |
| + case kExprDrop: |
| + case kExprThrow: |
| + os << GetOpName(opcode); |
| + break; |
| + |
| + FOREACH_LOAD_MEM_OPCODE(CASE_OPCODE) |
|
rossberg
2016/11/21 15:03:51
Nit: indent less?
Clemens Hammacher
2016/11/21 17:48:40
Same.
|
| + FOREACH_STORE_MEM_OPCODE(CASE_OPCODE) { |
| + MemoryAccessOperand operand(&i, i.pc(), kMaxUInt32); |
| + os << GetOpName(opcode) << " offset=" << operand.offset |
| + << " align=" << (1ULL << operand.alignment); |
| + break; |
| + } |
| + default: |
| + // Better print anything than failing. |
|
rossberg
2016/11/21 15:03:51
Better fail than introducing silent bugs
Clemens Hammacher
2016/11/21 17:48:40
Hm, this requires us to also handle the opcodes wh
|
| + os << WasmOpcodes::OpcodeName(opcode); |
| + break; |
| + } |
| + os << std::endl; |
| + ++line_nr; |
| + } |
| + DCHECK_EQ(0, control_depth); |
| + DCHECK(i.ok()); |
| +} |