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