Home | History | Annotate | Download | only in wasm
      1 // Copyright 2016 the V8 project authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "src/wasm/wasm-text.h"
      6 
      7 #include "src/debug/interface-types.h"
      8 #include "src/objects-inl.h"
      9 #include "src/ostreams.h"
     10 #include "src/vector.h"
     11 #include "src/wasm/function-body-decoder-impl.h"
     12 #include "src/wasm/function-body-decoder.h"
     13 #include "src/wasm/wasm-module.h"
     14 #include "src/wasm/wasm-opcodes.h"
     15 #include "src/zone/zone.h"
     16 
     17 using namespace v8;
     18 using namespace v8::internal;
     19 using namespace v8::internal::wasm;
     20 
     21 namespace {
     22 bool IsValidFunctionName(const Vector<const char> &name) {
     23   if (name.is_empty()) return false;
     24   const char *special_chars = "_.+-*/\\^~=<>!?@#$%&|:'`";
     25   for (char c : name) {
     26     bool valid_char = (c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') ||
     27                       (c >= 'A' && c <= 'Z') || strchr(special_chars, c);
     28     if (!valid_char) return false;
     29   }
     30   return true;
     31 }
     32 
     33 }  // namespace
     34 
     35 void wasm::PrintWasmText(const WasmModule *module,
     36                          const ModuleWireBytes &wire_bytes, uint32_t func_index,
     37                          std::ostream &os,
     38                          debug::WasmDisassembly::OffsetTable *offset_table) {
     39   DCHECK_NOT_NULL(module);
     40   DCHECK_GT(module->functions.size(), func_index);
     41   const WasmFunction *fun = &module->functions[func_index];
     42 
     43   AccountingAllocator allocator;
     44   Zone zone(&allocator, ZONE_NAME);
     45   int line_nr = 0;
     46   int control_depth = 1;
     47 
     48   // Print the function signature.
     49   os << "func";
     50   WasmName fun_name = wire_bytes.GetNameOrNull(fun);
     51   if (IsValidFunctionName(fun_name)) {
     52     os << " $";
     53     os.write(fun_name.start(), fun_name.length());
     54   }
     55   size_t param_count = fun->sig->parameter_count();
     56   if (param_count) {
     57     os << " (param";
     58     for (size_t i = 0; i < param_count; ++i)
     59       os << ' ' << WasmOpcodes::TypeName(fun->sig->GetParam(i));
     60     os << ')';
     61   }
     62   size_t return_count = fun->sig->return_count();
     63   if (return_count) {
     64     os << " (result";
     65     for (size_t i = 0; i < return_count; ++i)
     66       os << ' ' << WasmOpcodes::TypeName(fun->sig->GetReturn(i));
     67     os << ')';
     68   }
     69   os << "\n";
     70   ++line_nr;
     71 
     72   // Print the local declarations.
     73   BodyLocalDecls decls(&zone);
     74   Vector<const byte> func_bytes = wire_bytes.GetFunctionBytes(fun);
     75   BytecodeIterator i(func_bytes.begin(), func_bytes.end(), &decls);
     76   DCHECK_LT(func_bytes.begin(), i.pc());
     77   if (!decls.type_list.empty()) {
     78     os << "(local";
     79     for (const ValueType &v : decls.type_list) {
     80       os << ' ' << WasmOpcodes::TypeName(v);
     81     }
     82     os << ")\n";
     83     ++line_nr;
     84   }
     85 
     86   for (; i.has_next(); i.next()) {
     87     WasmOpcode opcode = i.current();
     88     if (opcode == kExprElse || opcode == kExprEnd) --control_depth;
     89 
     90     DCHECK_LE(0, control_depth);
     91     const int kMaxIndentation = 64;
     92     int indentation = std::min(kMaxIndentation, 2 * control_depth);
     93     if (offset_table) {
     94       offset_table->emplace_back(i.pc_offset(), line_nr, indentation);
     95     }
     96 
     97     // 64 whitespaces
     98     const char padding[kMaxIndentation + 1] =
     99         "                                                                ";
    100     os.write(padding, indentation);
    101 
    102     switch (opcode) {
    103       case kExprLoop:
    104       case kExprIf:
    105       case kExprBlock:
    106       case kExprTry: {
    107         BlockTypeOperand operand(&i, i.pc());
    108         os << WasmOpcodes::OpcodeName(opcode);
    109         for (unsigned i = 0; i < operand.arity; i++) {
    110           os << " " << WasmOpcodes::TypeName(operand.read_entry(i));
    111         }
    112         control_depth++;
    113         break;
    114       }
    115       case kExprBr:
    116       case kExprBrIf: {
    117         BreakDepthOperand operand(&i, i.pc());
    118         os << WasmOpcodes::OpcodeName(opcode) << ' ' << operand.depth;
    119         break;
    120       }
    121       case kExprElse:
    122         os << "else";
    123         control_depth++;
    124         break;
    125       case kExprEnd:
    126         os << "end";
    127         break;
    128       case kExprBrTable: {
    129         BranchTableOperand operand(&i, i.pc());
    130         BranchTableIterator iterator(&i, operand);
    131         os << "br_table";
    132         while (iterator.has_next()) os << ' ' << iterator.next();
    133         break;
    134       }
    135       case kExprCallIndirect: {
    136         CallIndirectOperand operand(&i, i.pc());
    137         DCHECK_EQ(0, operand.table_index);
    138         os << "call_indirect " << operand.index;
    139         break;
    140       }
    141       case kExprCallFunction: {
    142         CallFunctionOperand operand(&i, i.pc());
    143         os << "call " << operand.index;
    144         break;
    145       }
    146       case kExprGetLocal:
    147       case kExprSetLocal:
    148       case kExprTeeLocal:
    149       case kExprCatch: {
    150         LocalIndexOperand operand(&i, i.pc());
    151         os << WasmOpcodes::OpcodeName(opcode) << ' ' << operand.index;
    152         break;
    153       }
    154       case kExprGetGlobal:
    155       case kExprSetGlobal: {
    156         GlobalIndexOperand operand(&i, i.pc());
    157         os << WasmOpcodes::OpcodeName(opcode) << ' ' << operand.index;
    158         break;
    159       }
    160 #define CASE_CONST(type, str, cast_type)                           \
    161   case kExpr##type##Const: {                                       \
    162     Imm##type##Operand operand(&i, i.pc());                        \
    163     os << #str ".const " << static_cast<cast_type>(operand.value); \
    164     break;                                                         \
    165   }
    166         CASE_CONST(I32, i32, int32_t)
    167         CASE_CONST(I64, i64, int64_t)
    168         CASE_CONST(F32, f32, float)
    169         CASE_CONST(F64, f64, double)
    170 
    171 #define CASE_OPCODE(opcode, _, __) case kExpr##opcode:
    172         FOREACH_LOAD_MEM_OPCODE(CASE_OPCODE)
    173         FOREACH_STORE_MEM_OPCODE(CASE_OPCODE) {
    174           MemoryAccessOperand operand(&i, i.pc(), kMaxUInt32);
    175           os << WasmOpcodes::OpcodeName(opcode) << " offset=" << operand.offset
    176              << " align=" << (1ULL << operand.alignment);
    177           break;
    178         }
    179 
    180         FOREACH_SIMPLE_OPCODE(CASE_OPCODE)
    181       case kExprUnreachable:
    182       case kExprNop:
    183       case kExprReturn:
    184       case kExprMemorySize:
    185       case kExprGrowMemory:
    186       case kExprDrop:
    187       case kExprSelect:
    188       case kExprThrow:
    189         os << WasmOpcodes::OpcodeName(opcode);
    190         break;
    191 
    192         // This group is just printed by their internal opcode name, as they
    193         // should never be shown to end-users.
    194         FOREACH_ASMJS_COMPAT_OPCODE(CASE_OPCODE)
    195         // TODO(wasm): Add correct printing for SIMD and atomic opcodes once
    196         // they are publicly available.
    197         FOREACH_SIMD_0_OPERAND_OPCODE(CASE_OPCODE)
    198         FOREACH_SIMD_1_OPERAND_OPCODE(CASE_OPCODE)
    199         FOREACH_ATOMIC_OPCODE(CASE_OPCODE)
    200         os << WasmOpcodes::OpcodeName(opcode);
    201         break;
    202 
    203       default:
    204         UNREACHABLE();
    205         break;
    206     }
    207     os << '\n';
    208     ++line_nr;
    209   }
    210   DCHECK_EQ(0, control_depth);
    211   DCHECK(i.ok());
    212 }
    213