OLD | NEW |
---|---|
(Empty) | |
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/s-expr.h" | |
6 | |
7 #include "src/ostreams.h" | |
8 #include "src/wasm/ast-decoder.h" | |
9 #include "src/wasm/wasm-module.h" | |
10 #include "src/wasm/wasm-opcodes.h" | |
11 #include "src/zone/zone.h" | |
12 | |
13 using namespace v8::internal; | |
14 using namespace v8::internal::wasm; | |
15 | |
16 namespace { | |
17 const char *GetOpName(WasmOpcode opcode) { | |
18 #define CASE_OP(name, str) \ | |
19 case kExpr##name: \ | |
20 return str; | |
21 #define CASE_I32_OP(name, str) CASE_OP(I32##name, "i32." str) | |
22 #define CASE_I64_OP(name, str) CASE_OP(I64##name, "i64." str) | |
23 #define CASE_F32_OP(name, str) CASE_OP(F32##name, "f32." str) | |
24 #define CASE_F64_OP(name, str) CASE_OP(F64##name, "f64." str) | |
25 #define CASE_INT_OP(name, str) CASE_I32_OP(name, str) CASE_I64_OP(name, str) | |
26 #define CASE_FLOAT_OP(name, str) CASE_F32_OP(name, str) CASE_F64_OP(name, str) | |
27 #define CASE_ALL_OP(name, str) CASE_FLOAT_OP(name, str) CASE_INT_OP(name, str) | |
28 #define CASE_SIGN_OP(TYPE, name, str) \ | |
29 CASE_##TYPE##_OP(name##S, str "_s") CASE_##TYPE##_OP(name##U, str "_u") | |
30 #define CASE_ALL_SIGN_OP(name, str) \ | |
31 CASE_FLOAT_OP(name, str) CASE_SIGN_OP(INT, name, str) | |
32 #define CASE_CONVERT_OP(name, RES, SRC, src_suffix, str) \ | |
33 CASE_##RES##_OP(U##name##SRC, str "_u/" src_suffix) \ | |
34 CASE_##RES##_OP(S##name##SRC, str "_s/" src_suffix) | |
35 | |
36 switch (opcode) { | |
37 CASE_INT_OP(Eqz, "eqz") | |
titzer
2016/11/21 14:57:26
Nice! This is well macro'd code :-)
| |
38 CASE_ALL_OP(Eq, "eq") | |
39 CASE_ALL_OP(Ne, "ne") | |
40 CASE_ALL_OP(Add, "add") | |
41 CASE_ALL_OP(Sub, "sub") | |
42 CASE_ALL_OP(Mul, "mul") | |
43 CASE_ALL_SIGN_OP(Lt, "lt") | |
44 CASE_ALL_SIGN_OP(Gt, "gt") | |
45 CASE_ALL_SIGN_OP(Le, "le") | |
46 CASE_ALL_SIGN_OP(Ge, "ge") | |
47 CASE_INT_OP(Clz, "clz") | |
48 CASE_INT_OP(Ctz, "ctz") | |
49 CASE_INT_OP(Popcnt, "popcnt") | |
50 CASE_ALL_SIGN_OP(Div, "div") | |
51 CASE_SIGN_OP(INT, Rem, "rem") | |
52 CASE_INT_OP(And, "and") | |
53 CASE_INT_OP(Ior, "or") | |
54 CASE_INT_OP(Xor, "xor") | |
55 CASE_INT_OP(Shl, "shl") | |
56 CASE_SIGN_OP(INT, Shr, "shr") | |
rossberg
2016/11/21 15:03:51
Nit: there should be CASE_INT_SIGN_OP for symmetry
Clemens Hammacher
2016/11/21 17:48:40
I use CASE_SIGN_OP for both I64 and INT. So I woul
| |
57 CASE_INT_OP(Rol, "rol") | |
58 CASE_INT_OP(Ror, "ror") | |
59 CASE_FLOAT_OP(Abs, "abs") | |
60 CASE_FLOAT_OP(Neg, "neg") | |
61 CASE_FLOAT_OP(Ceil, "ceil") | |
62 CASE_FLOAT_OP(Floor, "floor") | |
63 CASE_FLOAT_OP(Trunc, "trunc") | |
64 CASE_FLOAT_OP(NearestInt, "nearest") | |
65 CASE_FLOAT_OP(Sqrt, "sqrt") | |
66 CASE_FLOAT_OP(Min, "min") | |
67 CASE_FLOAT_OP(Max, "max") | |
68 CASE_FLOAT_OP(CopySign, "copysign") | |
69 CASE_I32_OP(ConvertI64, "wrap/i64") | |
70 CASE_CONVERT_OP(Convert, INT, F32, "f32", "trunc") | |
71 CASE_CONVERT_OP(Convert, INT, F64, "f64", "trunc") | |
72 CASE_CONVERT_OP(Convert, I64, I32, "i32", "extend") | |
73 CASE_CONVERT_OP(Convert, F32, I32, "i32", "convert") | |
74 CASE_CONVERT_OP(Convert, F32, I64, "i64", "convert") | |
75 CASE_F32_OP(ConvertF64, "demote/f64") | |
76 CASE_CONVERT_OP(Convert, F64, I32, "i32", "convert") | |
77 CASE_CONVERT_OP(Convert, F64, I64, "i64", "convert") | |
78 CASE_F64_OP(ConvertF32, "promote/f32") | |
79 CASE_I32_OP(ReinterpretF32, "reinterpret/f32") | |
80 CASE_I64_OP(ReinterpretF64, "reinterpret/f64") | |
81 CASE_F32_OP(ReinterpretI32, "reinterpret/i32") | |
82 CASE_F64_OP(ReinterpretI64, "reinterpret/i64") | |
83 CASE_OP(Unreachable, "unreachable") | |
84 CASE_OP(Nop, "nop") | |
85 CASE_OP(Return, "return") | |
86 CASE_OP(MemorySize, "current_memory") | |
87 CASE_OP(GrowMemory, "grow_memory") | |
88 CASE_OP(Loop, "loop") | |
89 CASE_OP(If, "if") | |
90 CASE_OP(Block, "block") | |
91 CASE_OP(Try, "try") | |
92 CASE_OP(Throw, "throw") | |
93 CASE_OP(Catch, "catch") | |
94 CASE_OP(Drop, "drop") | |
95 CASE_ALL_OP(LoadMem, "load") | |
96 CASE_SIGN_OP(INT, LoadMem8, "load8") | |
97 CASE_SIGN_OP(INT, LoadMem16, "load16") | |
98 CASE_SIGN_OP(I64, LoadMem32, "load32") | |
99 CASE_ALL_OP(StoreMem, "store") | |
100 CASE_INT_OP(StoreMem8, "store8") | |
101 CASE_INT_OP(StoreMem16, "store16") | |
102 CASE_I64_OP(StoreMem32, "store32") | |
103 CASE_OP(SetLocal, "set_local") | |
104 CASE_OP(GetLocal, "get_local") | |
105 CASE_OP(TeeLocal, "tee_local") | |
106 default: | |
107 UNREACHABLE(); | |
108 return WasmOpcodes::OpcodeName(opcode); | |
109 } | |
110 } | |
111 | |
112 bool IsValidFunctionName(const Vector<const char> &name) { | |
113 if (name.is_empty()) return false; | |
114 const char *special_chars = "_.+-*/\\^~=<>!?@#$%&|:'`"; | |
115 for (char c : name) { | |
116 bool valid_char = (c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') || | |
117 (c >= 'A' && c <= 'Z') || strchr(special_chars, c); | |
118 if (!valid_char) return false; | |
119 } | |
120 return true; | |
121 } | |
122 | |
123 } // namespace | |
124 | |
125 void wasm::PrintSExpr( | |
126 const WasmModule *module, uint32_t func_index, std::ostream &os, | |
127 std::vector<std::tuple<uint32_t, int, int>> *offset_table) { | |
128 DCHECK_NOT_NULL(module); | |
129 DCHECK_GT(module->functions.size(), func_index); | |
130 const WasmFunction *fun = &module->functions[func_index]; | |
131 | |
132 AccountingAllocator allocator; | |
133 Zone zone(&allocator, ZONE_NAME); | |
134 int line_nr = 0; | |
135 int control_depth = 0; | |
136 | |
137 // Print the function signature. | |
138 os << "func"; | |
139 Vector<const char> fun_name( | |
140 reinterpret_cast<const char *>(module->module_start + fun->name_offset), | |
141 fun->name_length); | |
142 if (IsValidFunctionName(fun_name)) { | |
143 os << " $"; | |
144 os.write(fun_name.start(), fun_name.length()); | |
145 } | |
146 size_t param_count = fun->sig->parameter_count(); | |
147 if (param_count) { | |
148 os << " (param"; | |
149 for (size_t i = 0; i < param_count; ++i) | |
150 os << ' ' << WasmOpcodes::TypeName(fun->sig->GetParam(i)); | |
151 os << ')'; | |
152 } | |
153 size_t return_count = fun->sig->return_count(); | |
154 if (return_count) { | |
155 os << " (result"; | |
156 for (size_t i = 0; i < return_count; ++i) | |
157 os << ' ' << WasmOpcodes::TypeName(fun->sig->GetReturn(i)); | |
158 os << ')'; | |
159 } | |
160 os << "\n"; | |
161 ++line_nr; | |
162 | |
163 // Print the local declarations. | |
164 AstLocalDecls decls(&zone); | |
165 const byte *code_start = module->module_start + fun->code_start_offset; | |
166 const byte *code_end = module->module_start + fun->code_end_offset; | |
167 BytecodeIterator i(code_start, code_end, &decls); | |
168 DCHECK_LT(code_start, i.pc()); | |
169 if (!decls.local_types.empty()) { | |
170 os << "(local"; | |
171 for (auto p : decls.local_types) { | |
172 for (unsigned i = 0; i < p.second; ++i) | |
173 os << ' ' << WasmOpcodes::TypeName(p.first); | |
174 } | |
175 os << ")\n"; | |
176 os << std::endl; | |
177 ++line_nr; | |
178 } | |
179 | |
180 for (; i.has_next(); i.next()) { | |
181 WasmOpcode opcode = i.current(); | |
182 if (opcode == kExprElse || opcode == kExprEnd) --control_depth; | |
183 | |
184 DCHECK_LE(0, control_depth); | |
185 int num_whitespaces = control_depth < 32 ? 2 * control_depth : 64; | |
rossberg
2016/11/21 15:03:50
Maybe turn the 32 into a constant.
Clemens Hammacher
2016/11/21 17:48:40
Done.
| |
186 if (offset_table) { | |
187 offset_table->push_back( | |
188 std::make_tuple(i.pc_offset(), line_nr, num_whitespaces)); | |
189 } | |
190 | |
191 // 64 whitespaces | |
192 const char *padding = | |
193 " "; | |
194 os.write(padding, num_whitespaces); | |
195 | |
196 switch (opcode) { | |
197 case kExprLoop: | |
198 case kExprIf: | |
199 case kExprBlock: | |
200 case kExprTry: { | |
201 BlockTypeOperand operand(&i, i.pc()); | |
202 os << GetOpName(opcode); | |
203 for (unsigned i = 0; i < operand.arity; i++) { | |
204 os << " " << WasmOpcodes::TypeName(operand.read_entry(i)); | |
205 } | |
206 control_depth++; | |
207 break; | |
208 } | |
209 case kExprBr: | |
210 case kExprBrIf: { | |
211 BreakDepthOperand operand(&i, i.pc()); | |
212 os << "br" << (opcode == kExprBr ? "" : "_if") << " " << operand.depth; | |
rossberg
2016/11/21 15:03:51
Why not use GetOpName here?
Clemens Hammacher
2016/11/21 17:48:40
Yeah, it's just two alternatives, so I thought inl
| |
213 break; | |
214 } | |
215 case kExprElse: | |
216 os << "else"; | |
217 control_depth++; | |
218 break; | |
219 case kExprEnd: | |
220 os << "end"; | |
221 break; | |
222 case kExprBrTable: { | |
223 BranchTableOperand operand(&i, i.pc()); | |
224 os << "br_table"; | |
225 for (unsigned op = 0, num_ops = operand.table_count; op < num_ops; ++op) | |
226 os << ' ' << operand.read_entry(&i, op); | |
227 break; | |
228 } | |
229 case kExprCallIndirect: { | |
230 CallIndirectOperand operand(&i, i.pc()); | |
231 DCHECK_EQ(0U, operand.table_index); | |
232 os << "call_indirect " << operand.index; | |
233 break; | |
234 } | |
235 case kExprCallFunction: { | |
236 CallFunctionOperand operand(&i, i.pc()); | |
237 os << "call " << operand.index; | |
238 break; | |
239 } | |
240 case kExprGetLocal: | |
241 case kExprSetLocal: | |
242 case kExprTeeLocal: | |
243 case kExprCatch: { | |
244 LocalIndexOperand operand(&i, i.pc()); | |
245 os << GetOpName(opcode) << ' ' << operand.index; | |
246 break; | |
247 } | |
248 case kExprGetGlobal: | |
249 case kExprSetGlobal: { | |
250 GlobalIndexOperand operand(&i, i.pc()); | |
251 os << (opcode == kExprGetLocal ? 'g' : 's') << "et_global " | |
rossberg
2016/11/21 15:03:51
GetOpName?
Clemens Hammacher
2016/11/21 17:48:40
Same.
| |
252 << operand.index; | |
253 break; | |
254 } | |
255 #define CASE_CONST(type, str) \ | |
256 case kExpr##type##Const: { \ | |
257 Imm##type##Operand operand(&i, i.pc()); \ | |
258 os << #str ".const " << operand.value; \ | |
259 break; \ | |
260 } | |
261 CASE_CONST(I8, i8) | |
262 CASE_CONST(I32, i32) | |
263 CASE_CONST(I64, i64) | |
264 CASE_CONST(F32, f32) | |
265 CASE_CONST(F64, f64) | |
266 | |
267 #define CASE_OPCODE(opcode, _, __) case kExpr##opcode: | |
268 FOREACH_SIMPLE_OPCODE(CASE_OPCODE) | |
rossberg
2016/11/21 15:03:51
Nit: indent less?
Clemens Hammacher
2016/11/21 17:48:40
This is clang-format messing it up. I tried to pla
rossberg
2016/11/22 11:51:58
clang-format, making your code horrible since 2015
| |
269 case kExprUnreachable: | |
270 case kExprNop: | |
271 case kExprReturn: | |
272 case kExprMemorySize: | |
273 case kExprGrowMemory: | |
274 case kExprDrop: | |
275 case kExprThrow: | |
276 os << GetOpName(opcode); | |
277 break; | |
278 | |
279 FOREACH_LOAD_MEM_OPCODE(CASE_OPCODE) | |
rossberg
2016/11/21 15:03:51
Nit: indent less?
Clemens Hammacher
2016/11/21 17:48:40
Same.
| |
280 FOREACH_STORE_MEM_OPCODE(CASE_OPCODE) { | |
281 MemoryAccessOperand operand(&i, i.pc(), kMaxUInt32); | |
282 os << GetOpName(opcode) << " offset=" << operand.offset | |
283 << " align=" << (1ULL << operand.alignment); | |
284 break; | |
285 } | |
286 default: | |
287 // Better print anything than failing. | |
rossberg
2016/11/21 15:03:51
Better fail than introducing silent bugs
Clemens Hammacher
2016/11/21 17:48:40
Hm, this requires us to also handle the opcodes wh
| |
288 os << WasmOpcodes::OpcodeName(opcode); | |
289 break; | |
290 } | |
291 os << std::endl; | |
292 ++line_nr; | |
293 } | |
294 DCHECK_EQ(0, control_depth); | |
295 DCHECK(i.ok()); | |
296 } | |
OLD | NEW |