• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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(kAllWasmFeatures, &i,
104                                                      i.pc());
105         os << WasmOpcodes::OpcodeName(opcode);
106         if (imm.type == kWasmVar) {
107           os << " (type " << imm.sig_index << ")";
108         } else if (imm.out_arity() > 0) {
109           os << " " << ValueTypes::TypeName(imm.out_type(0));
110         }
111         control_depth++;
112         break;
113       }
114       case kExprBr:
115       case kExprBrIf: {
116         BreakDepthImmediate<Decoder::kNoValidate> imm(&i, i.pc());
117         os << WasmOpcodes::OpcodeName(opcode) << ' ' << imm.depth;
118         break;
119       }
120       case kExprElse:
121         os << "else";
122         control_depth++;
123         break;
124       case kExprEnd:
125         os << "end";
126         break;
127       case kExprBrTable: {
128         BranchTableImmediate<Decoder::kNoValidate> imm(&i, i.pc());
129         BranchTableIterator<Decoder::kNoValidate> iterator(&i, imm);
130         os << "br_table";
131         while (iterator.has_next()) os << ' ' << iterator.next();
132         break;
133       }
134       case kExprCallIndirect: {
135         CallIndirectImmediate<Decoder::kNoValidate> imm(&i, i.pc());
136         DCHECK_EQ(0, imm.table_index);
137         os << "call_indirect " << imm.sig_index;
138         break;
139       }
140       case kExprCallFunction: {
141         CallFunctionImmediate<Decoder::kNoValidate> imm(&i, i.pc());
142         os << "call " << imm.index;
143         break;
144       }
145       case kExprGetLocal:
146       case kExprSetLocal:
147       case kExprTeeLocal: {
148         LocalIndexImmediate<Decoder::kNoValidate> imm(&i, i.pc());
149         os << WasmOpcodes::OpcodeName(opcode) << ' ' << imm.index;
150         break;
151       }
152       case kExprThrow:
153       case kExprCatch: {
154         ExceptionIndexImmediate<Decoder::kNoValidate> imm(&i, i.pc());
155         os << WasmOpcodes::OpcodeName(opcode) << ' ' << imm.index;
156         break;
157       }
158       case kExprGetGlobal:
159       case kExprSetGlobal: {
160         GlobalIndexImmediate<Decoder::kNoValidate> imm(&i, i.pc());
161         os << WasmOpcodes::OpcodeName(opcode) << ' ' << imm.index;
162         break;
163       }
164 #define CASE_CONST(type, str, cast_type)                        \
165   case kExpr##type##Const: {                                    \
166     Imm##type##Immediate<Decoder::kNoValidate> imm(&i, i.pc()); \
167     os << #str ".const " << static_cast<cast_type>(imm.value);  \
168     break;                                                      \
169   }
170         CASE_CONST(I32, i32, int32_t)
171         CASE_CONST(I64, i64, int64_t)
172         CASE_CONST(F32, f32, float)
173         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           MemoryAccessImmediate<Decoder::kNoValidate> imm(&i, i.pc(),
180                                                           kMaxUInt32);
181           os << WasmOpcodes::OpcodeName(opcode) << " offset=" << imm.offset
182              << " align=" << (1ULL << imm.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         os << WasmOpcodes::OpcodeName(opcode);
195         break;
196       case kAtomicPrefix: {
197         WasmOpcode atomic_opcode = i.prefixed_opcode();
198         switch (atomic_opcode) {
199           FOREACH_ATOMIC_OPCODE(CASE_OPCODE) {
200             MemoryAccessImmediate<Decoder::kNoValidate> imm(&i, i.pc(),
201                                                             kMaxUInt32);
202             os << WasmOpcodes::OpcodeName(atomic_opcode)
203                << " offset=" << imm.offset
204                << " align=" << (1ULL << imm.alignment);
205             break;
206           }
207           default:
208             UNREACHABLE();
209             break;
210         }
211         break;
212       }
213 
214         // This group is just printed by their internal opcode name, as they
215         // should never be shown to end-users.
216         FOREACH_ASMJS_COMPAT_OPCODE(CASE_OPCODE)
217         // TODO(wasm): Add correct printing for SIMD and atomic opcodes once
218         // they are publicly available.
219         FOREACH_SIMD_0_OPERAND_OPCODE(CASE_OPCODE)
220         FOREACH_SIMD_1_OPERAND_OPCODE(CASE_OPCODE)
221         FOREACH_SIMD_MASK_OPERAND_OPCODE(CASE_OPCODE)
222         FOREACH_SIMD_MEM_OPCODE(CASE_OPCODE)
223         os << WasmOpcodes::OpcodeName(opcode);
224         break;
225 #undef CASE_OPCODE
226 
227       default:
228         UNREACHABLE();
229         break;
230     }
231     os << '\n';
232     ++line_nr;
233   }
234   DCHECK_EQ(0, control_depth);
235   DCHECK(i.ok());
236 }
237 
238 }  // namespace wasm
239 }  // namespace internal
240 }  // namespace v8
241