wasm-text.cc 7.63 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 18 19
namespace v8 {
namespace internal {
namespace wasm {
20

21 22 23 24 25 26 27 28 29 30 31 32 33 34
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
void PrintWasmText(const WasmModule* module, const ModuleWireBytes& wire_bytes,
                   uint32_t func_index, std::ostream& os,
                   debug::WasmDisassembly::OffsetTable* offset_table) {
38 39 40 41 42 43 44
  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;
45
  int control_depth = 1;
46 47 48

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

  // Print the local declarations.
69
  BodyLocalDecls decls(&zone);
70
  Vector<const byte> func_bytes = wire_bytes.GetFunctionBytes(fun);
71 72
  BytecodeIterator i(func_bytes.begin(), func_bytes.end(), &decls);
  DCHECK_LT(func_bytes.begin(), i.pc());
73
  if (!decls.type_list.empty()) {
74
    os << "(local";
75
    for (const ValueType &v : decls.type_list) {
76
      os << ' ' << ValueTypes::TypeName(v);
77 78 79 80 81 82 83 84 85 86 87 88 89
    }
    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) {
90
      offset_table->emplace_back(i.pc_offset(), line_nr, indentation);
91 92 93 94 95 96 97 98 99 100 101 102
    }

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

    switch (opcode) {
      case kExprLoop:
      case kExprIf:
      case kExprBlock:
      case kExprTry: {
103 104
        BlockTypeImmediate<Decoder::kNoValidate> imm(kAllWasmFeatures, &i,
                                                     i.pc());
105
        os << WasmOpcodes::OpcodeName(opcode);
106 107 108 109
        if (imm.type == kWasmVar) {
          os << " (type " << imm.sig_index << ")";
        } else if (imm.out_arity() > 0) {
          os << " " << ValueTypes::TypeName(imm.out_type(0));
110 111 112 113 114 115
        }
        control_depth++;
        break;
      }
      case kExprBr:
      case kExprBrIf: {
116 117
        BreakDepthImmediate<Decoder::kNoValidate> imm(&i, i.pc());
        os << WasmOpcodes::OpcodeName(opcode) << ' ' << imm.depth;
118 119 120 121 122 123 124 125 126 127
        break;
      }
      case kExprElse:
        os << "else";
        control_depth++;
        break;
      case kExprEnd:
        os << "end";
        break;
      case kExprBrTable: {
128 129
        BranchTableImmediate<Decoder::kNoValidate> imm(&i, i.pc());
        BranchTableIterator<Decoder::kNoValidate> iterator(&i, imm);
130 131 132 133 134
        os << "br_table";
        while (iterator.has_next()) os << ' ' << iterator.next();
        break;
      }
      case kExprCallIndirect: {
135 136 137
        CallIndirectImmediate<Decoder::kNoValidate> imm(&i, i.pc());
        DCHECK_EQ(0, imm.table_index);
        os << "call_indirect " << imm.sig_index;
138 139 140
        break;
      }
      case kExprCallFunction: {
141 142
        CallFunctionImmediate<Decoder::kNoValidate> imm(&i, i.pc());
        os << "call " << imm.index;
143 144 145 146
        break;
      }
      case kExprGetLocal:
      case kExprSetLocal:
147
      case kExprTeeLocal: {
148 149
        LocalIndexImmediate<Decoder::kNoValidate> imm(&i, i.pc());
        os << WasmOpcodes::OpcodeName(opcode) << ' ' << imm.index;
150 151
        break;
      }
152 153
      case kExprThrow:
      case kExprCatch: {
154 155
        ExceptionIndexImmediate<Decoder::kNoValidate> imm(&i, i.pc());
        os << WasmOpcodes::OpcodeName(opcode) << ' ' << imm.index;
156 157
        break;
      }
158 159
      case kExprGetGlobal:
      case kExprSetGlobal: {
160 161
        GlobalIndexImmediate<Decoder::kNoValidate> imm(&i, i.pc());
        os << WasmOpcodes::OpcodeName(opcode) << ' ' << imm.index;
162 163
        break;
      }
164 165 166 167 168
#define CASE_CONST(type, str, cast_type)                        \
  case kExpr##type##Const: {                                    \
    Imm##type##Immediate<Decoder::kNoValidate> imm(&i, i.pc()); \
    os << #str ".const " << static_cast<cast_type>(imm.value);  \
    break;                                                      \
169 170 171 172 173
  }
        CASE_CONST(I32, i32, int32_t)
        CASE_CONST(I64, i64, int64_t)
        CASE_CONST(F32, f32, float)
        CASE_CONST(F64, f64, double)
174
#undef CASE_CONST
175 176 177 178

#define CASE_OPCODE(opcode, _, __) case kExpr##opcode:
        FOREACH_LOAD_MEM_OPCODE(CASE_OPCODE)
        FOREACH_STORE_MEM_OPCODE(CASE_OPCODE) {
179 180 181 182
          MemoryAccessImmediate<Decoder::kNoValidate> imm(&i, i.pc(),
                                                          kMaxUInt32);
          os << WasmOpcodes::OpcodeName(opcode) << " offset=" << imm.offset
             << " align=" << (1ULL << imm.alignment);
183 184 185 186 187 188 189 190
          break;
        }

        FOREACH_SIMPLE_OPCODE(CASE_OPCODE)
      case kExprUnreachable:
      case kExprNop:
      case kExprReturn:
      case kExprMemorySize:
191
      case kExprMemoryGrow:
192
      case kExprDrop:
193
      case kExprSelect:
194
        os << WasmOpcodes::OpcodeName(opcode);
195
        break;
196 197 198 199
      case kAtomicPrefix: {
        WasmOpcode atomic_opcode = i.prefixed_opcode();
        switch (atomic_opcode) {
          FOREACH_ATOMIC_OPCODE(CASE_OPCODE) {
200 201
            MemoryAccessImmediate<Decoder::kNoValidate> imm(&i, i.pc(),
                                                            kMaxUInt32);
202
            os << WasmOpcodes::OpcodeName(atomic_opcode)
203 204
               << " offset=" << imm.offset
               << " align=" << (1ULL << imm.alignment);
205 206 207 208 209 210
            break;
          }
          default:
            UNREACHABLE();
            break;
        }
211
        break;
212
      }
213 214 215 216 217 218 219 220

        // 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)
221
        FOREACH_SIMD_MASK_OPERAND_OPCODE(CASE_OPCODE)
222
        FOREACH_SIMD_MEM_OPCODE(CASE_OPCODE)
223 224
        os << WasmOpcodes::OpcodeName(opcode);
        break;
225
#undef CASE_OPCODE
226 227 228 229 230 231 232 233 234 235 236

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

}  // namespace wasm
}  // namespace internal
}  // namespace v8