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 {
IsValidFunctionName(const Vector<const char> & name)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
PrintWasmText(const WasmModule * module,const ModuleWireBytes & wire_bytes,uint32_t func_index,std::ostream & os,debug::WasmDisassembly::OffsetTable * offset_table)35 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 const WasmFunction *fun = &module->functions[func_index];
41
42 AccountingAllocator allocator;
43 Zone zone(&allocator, ZONE_NAME);
44 int line_nr = 0;
45 int control_depth = 1;
46
47 // Print the function signature.
48 os << "func";
49 WasmName fun_name = wire_bytes.GetNameOrNull(fun, module);
50 if (IsValidFunctionName(fun_name)) {
51 os << " $";
52 os.write(fun_name.start(), fun_name.length());
53 }
54 if (fun->sig->parameter_count()) {
55 os << " (param";
56 for (auto param : fun->sig->parameters())
57 os << ' ' << ValueTypes::TypeName(param);
58 os << ')';
59 }
60 if (fun->sig->return_count()) {
61 os << " (result";
62 for (auto ret : fun->sig->returns()) os << ' ' << ValueTypes::TypeName(ret);
63 os << ')';
64 }
65 os << "\n";
66 ++line_nr;
67
68 // Print the local declarations.
69 BodyLocalDecls decls(&zone);
70 Vector<const byte> func_bytes = wire_bytes.GetFunctionBytes(fun);
71 BytecodeIterator i(func_bytes.begin(), func_bytes.end(), &decls);
72 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 os << ")\n";
79 ++line_nr;
80 }
81
82 for (; i.has_next(); i.next()) {
83 WasmOpcode opcode = i.current();
84 if (opcode == kExprElse || opcode == kExprEnd) --control_depth;
85
86 DCHECK_LE(0, control_depth);
87 const int kMaxIndentation = 64;
88 int indentation = std::min(kMaxIndentation, 2 * control_depth);
89 if (offset_table) {
90 offset_table->emplace_back(i.pc_offset(), line_nr, indentation);
91 }
92
93 // 64 whitespaces
94 const char padding[kMaxIndentation + 1] =
95 " ";
96 os.write(padding, indentation);
97
98 switch (opcode) {
99 case kExprLoop:
100 case kExprIf:
101 case kExprBlock:
102 case kExprTry: {
103 BlockTypeImmediate<Decoder::kNoValidate> imm(&i, i.pc());
104 os << WasmOpcodes::OpcodeName(opcode);
105 if (imm.type == kWasmVar) {
106 os << " (type " << imm.sig_index << ")";
107 } else if (imm.out_arity() > 0) {
108 os << " " << ValueTypes::TypeName(imm.out_type(0));
109 }
110 control_depth++;
111 break;
112 }
113 case kExprBr:
114 case kExprBrIf: {
115 BreakDepthImmediate<Decoder::kNoValidate> imm(&i, i.pc());
116 os << WasmOpcodes::OpcodeName(opcode) << ' ' << imm.depth;
117 break;
118 }
119 case kExprElse:
120 os << "else";
121 control_depth++;
122 break;
123 case kExprEnd:
124 os << "end";
125 break;
126 case kExprBrTable: {
127 BranchTableImmediate<Decoder::kNoValidate> imm(&i, i.pc());
128 BranchTableIterator<Decoder::kNoValidate> iterator(&i, imm);
129 os << "br_table";
130 while (iterator.has_next()) os << ' ' << iterator.next();
131 break;
132 }
133 case kExprCallIndirect: {
134 CallIndirectImmediate<Decoder::kNoValidate> imm(&i, i.pc());
135 DCHECK_EQ(0, imm.table_index);
136 os << "call_indirect " << imm.sig_index;
137 break;
138 }
139 case kExprCallFunction: {
140 CallFunctionImmediate<Decoder::kNoValidate> imm(&i, i.pc());
141 os << "call " << imm.index;
142 break;
143 }
144 case kExprGetLocal:
145 case kExprSetLocal:
146 case kExprTeeLocal: {
147 LocalIndexImmediate<Decoder::kNoValidate> imm(&i, i.pc());
148 os << WasmOpcodes::OpcodeName(opcode) << ' ' << imm.index;
149 break;
150 }
151 case kExprThrow:
152 case kExprCatch: {
153 ExceptionIndexImmediate<Decoder::kNoValidate> imm(&i, i.pc());
154 os << WasmOpcodes::OpcodeName(opcode) << ' ' << imm.index;
155 break;
156 }
157 case kExprGetGlobal:
158 case kExprSetGlobal: {
159 GlobalIndexImmediate<Decoder::kNoValidate> imm(&i, i.pc());
160 os << WasmOpcodes::OpcodeName(opcode) << ' ' << imm.index;
161 break;
162 }
163 #define CASE_CONST(type, str, cast_type) \
164 case kExpr##type##Const: { \
165 Imm##type##Immediate<Decoder::kNoValidate> imm(&i, i.pc()); \
166 os << #str ".const " << static_cast<cast_type>(imm.value); \
167 break; \
168 }
169 CASE_CONST(I32, i32, int32_t)
170 CASE_CONST(I64, i64, int64_t)
171 CASE_CONST(F32, f32, float)
172 CASE_CONST(F64, f64, double)
173 #undef CASE_CONST
174
175 #define CASE_OPCODE(opcode, _, __) case kExpr##opcode:
176 FOREACH_LOAD_MEM_OPCODE(CASE_OPCODE)
177 FOREACH_STORE_MEM_OPCODE(CASE_OPCODE) {
178 MemoryAccessImmediate<Decoder::kNoValidate> imm(&i, i.pc(),
179 kMaxUInt32);
180 os << WasmOpcodes::OpcodeName(opcode) << " offset=" << imm.offset
181 << " align=" << (1ULL << imm.alignment);
182 break;
183 }
184
185 FOREACH_SIMPLE_OPCODE(CASE_OPCODE)
186 case kExprUnreachable:
187 case kExprNop:
188 case kExprReturn:
189 case kExprMemorySize:
190 case kExprGrowMemory:
191 case kExprDrop:
192 case kExprSelect:
193 os << WasmOpcodes::OpcodeName(opcode);
194 break;
195 case kAtomicPrefix: {
196 WasmOpcode atomic_opcode = i.prefixed_opcode();
197 switch (atomic_opcode) {
198 FOREACH_ATOMIC_OPCODE(CASE_OPCODE) {
199 MemoryAccessImmediate<Decoder::kNoValidate> imm(&i, i.pc(),
200 kMaxUInt32);
201 os << WasmOpcodes::OpcodeName(atomic_opcode)
202 << " offset=" << imm.offset
203 << " align=" << (1ULL << imm.alignment);
204 break;
205 }
206 default:
207 UNREACHABLE();
208 break;
209 }
210 break;
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 os << WasmOpcodes::OpcodeName(opcode);
223 break;
224 #undef CASE_OPCODE
225
226 default:
227 UNREACHABLE();
228 break;
229 }
230 os << '\n';
231 ++line_nr;
232 }
233 DCHECK_EQ(0, control_depth);
234 DCHECK(i.ok());
235 }
236
237 } // namespace wasm
238 } // namespace internal
239 } // namespace v8
240