wasm-text.cc 6.55 KB
Newer Older
1 2 3 4 5 6
// 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/wasm-text.h"

7
#include "src/debug/interface-types.h"
8
#include "src/objects-inl.h"
9 10
#include "src/ostreams.h"
#include "src/vector.h"
11
#include "src/wasm/function-body-decoder-impl.h"
12
#include "src/wasm/function-body-decoder.h"
13 14 15 16
#include "src/wasm/wasm-module.h"
#include "src/wasm/wasm-opcodes.h"
#include "src/zone/zone.h"

17
using namespace v8;
18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
using namespace v8::internal;
using namespace v8::internal::wasm;

namespace {
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

35 36 37 38
void wasm::PrintWasmText(const WasmModule *module,
                         const ModuleWireBytes &wire_bytes, uint32_t func_index,
                         std::ostream &os,
                         debug::WasmDisassembly::OffsetTable *offset_table) {
39 40 41 42 43 44 45
  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;
46
  int control_depth = 1;
47 48 49

  // Print the function signature.
  os << "func";
50
  WasmName fun_name = wire_bytes.GetNameOrNull(fun);
51 52 53 54
  if (IsValidFunctionName(fun_name)) {
    os << " $";
    os.write(fun_name.start(), fun_name.length());
  }
55
  if (fun->sig->parameter_count()) {
56
    os << " (param";
57 58
    for (auto param : fun->sig->parameters())
      os << ' ' << WasmOpcodes::TypeName(param);
59 60
    os << ')';
  }
61
  if (fun->sig->return_count()) {
62
    os << " (result";
63 64
    for (auto ret : fun->sig->returns())
      os << ' ' << WasmOpcodes::TypeName(ret);
65 66 67 68 69 70
    os << ')';
  }
  os << "\n";
  ++line_nr;

  // Print the local declarations.
71
  BodyLocalDecls decls(&zone);
72
  Vector<const byte> func_bytes = wire_bytes.GetFunctionBytes(fun);
73 74
  BytecodeIterator i(func_bytes.begin(), func_bytes.end(), &decls);
  DCHECK_LT(func_bytes.begin(), i.pc());
75
  if (!decls.type_list.empty()) {
76
    os << "(local";
77 78
    for (const ValueType &v : decls.type_list) {
      os << ' ' << WasmOpcodes::TypeName(v);
79 80 81 82 83 84 85 86 87 88 89 90 91
    }
    os << ")\n";
    ++line_nr;
  }

  for (; i.has_next(); i.next()) {
    WasmOpcode opcode = i.current();
    if (opcode == kExprElse || opcode == kExprEnd) --control_depth;

    DCHECK_LE(0, control_depth);
    const int kMaxIndentation = 64;
    int indentation = std::min(kMaxIndentation, 2 * control_depth);
    if (offset_table) {
92
      offset_table->emplace_back(i.pc_offset(), line_nr, indentation);
93 94 95 96 97 98 99 100 101 102 103 104
    }

    // 64 whitespaces
    const char padding[kMaxIndentation + 1] =
        "                                                                ";
    os.write(padding, indentation);

    switch (opcode) {
      case kExprLoop:
      case kExprIf:
      case kExprBlock:
      case kExprTry: {
105
        BlockTypeOperand<false> operand(&i, i.pc());
106
        os << WasmOpcodes::OpcodeName(opcode);
107 108 109 110 111 112 113 114
        for (unsigned i = 0; i < operand.arity; i++) {
          os << " " << WasmOpcodes::TypeName(operand.read_entry(i));
        }
        control_depth++;
        break;
      }
      case kExprBr:
      case kExprBrIf: {
115
        BreakDepthOperand<false> operand(&i, i.pc());
116
        os << WasmOpcodes::OpcodeName(opcode) << ' ' << operand.depth;
117 118 119 120 121 122 123 124 125 126
        break;
      }
      case kExprElse:
        os << "else";
        control_depth++;
        break;
      case kExprEnd:
        os << "end";
        break;
      case kExprBrTable: {
127 128
        BranchTableOperand<false> operand(&i, i.pc());
        BranchTableIterator<false> iterator(&i, operand);
129 130 131 132 133
        os << "br_table";
        while (iterator.has_next()) os << ' ' << iterator.next();
        break;
      }
      case kExprCallIndirect: {
134
        CallIndirectOperand<false> operand(&i, i.pc());
135
        DCHECK_EQ(0, operand.table_index);
136 137 138 139
        os << "call_indirect " << operand.index;
        break;
      }
      case kExprCallFunction: {
140
        CallFunctionOperand<false> operand(&i, i.pc());
141 142 143 144 145 146 147
        os << "call " << operand.index;
        break;
      }
      case kExprGetLocal:
      case kExprSetLocal:
      case kExprTeeLocal:
      case kExprCatch: {
148
        LocalIndexOperand<false> operand(&i, i.pc());
149
        os << WasmOpcodes::OpcodeName(opcode) << ' ' << operand.index;
150 151 152 153
        break;
      }
      case kExprGetGlobal:
      case kExprSetGlobal: {
154
        GlobalIndexOperand<false> operand(&i, i.pc());
155
        os << WasmOpcodes::OpcodeName(opcode) << ' ' << operand.index;
156 157 158 159
        break;
      }
#define CASE_CONST(type, str, cast_type)                           \
  case kExpr##type##Const: {                                       \
160
    Imm##type##Operand<false> operand(&i, i.pc());                 \
161 162 163 164 165 166 167 168 169 170 171
    os << #str ".const " << static_cast<cast_type>(operand.value); \
    break;                                                         \
  }
        CASE_CONST(I32, i32, int32_t)
        CASE_CONST(I64, i64, int64_t)
        CASE_CONST(F32, f32, float)
        CASE_CONST(F64, f64, double)

#define CASE_OPCODE(opcode, _, __) case kExpr##opcode:
        FOREACH_LOAD_MEM_OPCODE(CASE_OPCODE)
        FOREACH_STORE_MEM_OPCODE(CASE_OPCODE) {
172
          MemoryAccessOperand<false> operand(&i, i.pc(), kMaxUInt32);
173
          os << WasmOpcodes::OpcodeName(opcode) << " offset=" << operand.offset
174 175 176 177 178 179 180 181 182 183 184
             << " align=" << (1ULL << operand.alignment);
          break;
        }

        FOREACH_SIMPLE_OPCODE(CASE_OPCODE)
      case kExprUnreachable:
      case kExprNop:
      case kExprReturn:
      case kExprMemorySize:
      case kExprGrowMemory:
      case kExprDrop:
185
      case kExprSelect:
186
      case kExprThrow:
187
        os << WasmOpcodes::OpcodeName(opcode);
188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210
        break;

        // This group is just printed by their internal opcode name, as they
        // should never be shown to end-users.
        FOREACH_ASMJS_COMPAT_OPCODE(CASE_OPCODE)
        // TODO(wasm): Add correct printing for SIMD and atomic opcodes once
        // they are publicly available.
        FOREACH_SIMD_0_OPERAND_OPCODE(CASE_OPCODE)
        FOREACH_SIMD_1_OPERAND_OPCODE(CASE_OPCODE)
        FOREACH_ATOMIC_OPCODE(CASE_OPCODE)
        os << WasmOpcodes::OpcodeName(opcode);
        break;

      default:
        UNREACHABLE();
        break;
    }
    os << '\n';
    ++line_nr;
  }
  DCHECK_EQ(0, control_depth);
  DCHECK(i.ok());
}