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