OLD | NEW |
| (Empty) |
1 // Copyright 2015 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 #ifndef V8_WASM_AST_DECODER_H_ | |
6 #define V8_WASM_AST_DECODER_H_ | |
7 | |
8 #include <iterator> | |
9 | |
10 #include "src/base/compiler-specific.h" | |
11 #include "src/base/iterator.h" | |
12 #include "src/globals.h" | |
13 #include "src/signature.h" | |
14 #include "src/wasm/decoder.h" | |
15 #include "src/wasm/wasm-opcodes.h" | |
16 #include "src/wasm/wasm-result.h" | |
17 | |
18 namespace v8 { | |
19 namespace internal { | |
20 | |
21 class BitVector; // forward declaration | |
22 | |
23 namespace compiler { // external declarations from compiler. | |
24 class WasmGraphBuilder; | |
25 } | |
26 | |
27 namespace wasm { | |
28 | |
29 const uint32_t kMaxNumWasmLocals = 8000000; | |
30 struct WasmGlobal; | |
31 | |
32 // Helpers for decoding different kinds of operands which follow bytecodes. | |
33 struct LocalIndexOperand { | |
34 uint32_t index; | |
35 LocalType type; | |
36 unsigned length; | |
37 | |
38 inline LocalIndexOperand(Decoder* decoder, const byte* pc) { | |
39 index = decoder->checked_read_u32v(pc, 1, &length, "local index"); | |
40 type = kAstStmt; | |
41 } | |
42 }; | |
43 | |
44 struct ImmI8Operand { | |
45 int8_t value; | |
46 unsigned length; | |
47 inline ImmI8Operand(Decoder* decoder, const byte* pc) { | |
48 value = bit_cast<int8_t>(decoder->checked_read_u8(pc, 1, "immi8")); | |
49 length = 1; | |
50 } | |
51 }; | |
52 | |
53 struct ImmI32Operand { | |
54 int32_t value; | |
55 unsigned length; | |
56 inline ImmI32Operand(Decoder* decoder, const byte* pc) { | |
57 value = decoder->checked_read_i32v(pc, 1, &length, "immi32"); | |
58 } | |
59 }; | |
60 | |
61 struct ImmI64Operand { | |
62 int64_t value; | |
63 unsigned length; | |
64 inline ImmI64Operand(Decoder* decoder, const byte* pc) { | |
65 value = decoder->checked_read_i64v(pc, 1, &length, "immi64"); | |
66 } | |
67 }; | |
68 | |
69 struct ImmF32Operand { | |
70 float value; | |
71 unsigned length; | |
72 inline ImmF32Operand(Decoder* decoder, const byte* pc) { | |
73 value = bit_cast<float>(decoder->checked_read_u32(pc, 1, "immf32")); | |
74 length = 4; | |
75 } | |
76 }; | |
77 | |
78 struct ImmF64Operand { | |
79 double value; | |
80 unsigned length; | |
81 inline ImmF64Operand(Decoder* decoder, const byte* pc) { | |
82 value = bit_cast<double>(decoder->checked_read_u64(pc, 1, "immf64")); | |
83 length = 8; | |
84 } | |
85 }; | |
86 | |
87 struct GlobalIndexOperand { | |
88 uint32_t index; | |
89 LocalType type; | |
90 const WasmGlobal* global; | |
91 unsigned length; | |
92 | |
93 inline GlobalIndexOperand(Decoder* decoder, const byte* pc) { | |
94 index = decoder->checked_read_u32v(pc, 1, &length, "global index"); | |
95 global = nullptr; | |
96 type = kAstStmt; | |
97 } | |
98 }; | |
99 | |
100 struct BlockTypeOperand { | |
101 uint32_t arity; | |
102 const byte* types; // pointer to encoded types for the block. | |
103 unsigned length; | |
104 | |
105 inline BlockTypeOperand(Decoder* decoder, const byte* pc) { | |
106 uint8_t val = decoder->checked_read_u8(pc, 1, "block type"); | |
107 LocalType type = kAstStmt; | |
108 length = 1; | |
109 arity = 0; | |
110 types = nullptr; | |
111 if (decode_local_type(val, &type)) { | |
112 arity = type == kAstStmt ? 0 : 1; | |
113 types = pc + 1; | |
114 } else { | |
115 // Handle multi-value blocks. | |
116 if (!FLAG_wasm_mv_prototype) { | |
117 decoder->error(pc, pc + 1, "invalid block arity > 1"); | |
118 return; | |
119 } | |
120 if (val != kMultivalBlock) { | |
121 decoder->error(pc, pc + 1, "invalid block type"); | |
122 return; | |
123 } | |
124 // Decode and check the types vector of the block. | |
125 unsigned len = 0; | |
126 uint32_t count = decoder->checked_read_u32v(pc, 2, &len, "block arity"); | |
127 // {count} is encoded as {arity-2}, so that a {0} count here corresponds | |
128 // to a block with 2 values. This makes invalid/redundant encodings | |
129 // impossible. | |
130 arity = count + 2; | |
131 length = 1 + len + arity; | |
132 types = pc + 1 + 1 + len; | |
133 | |
134 for (uint32_t i = 0; i < arity; i++) { | |
135 uint32_t offset = 1 + 1 + len + i; | |
136 val = decoder->checked_read_u8(pc, offset, "block type"); | |
137 decode_local_type(val, &type); | |
138 if (type == kAstStmt) { | |
139 decoder->error(pc, pc + offset, "invalid block type"); | |
140 return; | |
141 } | |
142 } | |
143 } | |
144 } | |
145 // Decode a byte representing a local type. Return {false} if the encoded | |
146 // byte was invalid or {kMultivalBlock}. | |
147 bool decode_local_type(uint8_t val, LocalType* result) { | |
148 switch (static_cast<LocalTypeCode>(val)) { | |
149 case kLocalVoid: | |
150 *result = kAstStmt; | |
151 return true; | |
152 case kLocalI32: | |
153 *result = kAstI32; | |
154 return true; | |
155 case kLocalI64: | |
156 *result = kAstI64; | |
157 return true; | |
158 case kLocalF32: | |
159 *result = kAstF32; | |
160 return true; | |
161 case kLocalF64: | |
162 *result = kAstF64; | |
163 return true; | |
164 case kLocalS128: | |
165 *result = kAstS128; | |
166 return true; | |
167 default: | |
168 *result = kAstStmt; | |
169 return false; | |
170 } | |
171 } | |
172 LocalType read_entry(unsigned index) { | |
173 DCHECK_LT(index, arity); | |
174 LocalType result; | |
175 CHECK(decode_local_type(types[index], &result)); | |
176 return result; | |
177 } | |
178 }; | |
179 | |
180 struct Control; | |
181 struct BreakDepthOperand { | |
182 uint32_t depth; | |
183 Control* target; | |
184 unsigned length; | |
185 inline BreakDepthOperand(Decoder* decoder, const byte* pc) { | |
186 depth = decoder->checked_read_u32v(pc, 1, &length, "break depth"); | |
187 target = nullptr; | |
188 } | |
189 }; | |
190 | |
191 struct CallIndirectOperand { | |
192 uint32_t table_index; | |
193 uint32_t index; | |
194 FunctionSig* sig; | |
195 unsigned length; | |
196 inline CallIndirectOperand(Decoder* decoder, const byte* pc) { | |
197 unsigned len = 0; | |
198 index = decoder->checked_read_u32v(pc, 1, &len, "signature index"); | |
199 table_index = decoder->checked_read_u8(pc, 1 + len, "table index"); | |
200 if (table_index != 0) { | |
201 decoder->error(pc, pc + 1 + len, "expected table index 0, found %u", | |
202 table_index); | |
203 } | |
204 length = 1 + len; | |
205 sig = nullptr; | |
206 } | |
207 }; | |
208 | |
209 struct CallFunctionOperand { | |
210 uint32_t index; | |
211 FunctionSig* sig; | |
212 unsigned length; | |
213 inline CallFunctionOperand(Decoder* decoder, const byte* pc) { | |
214 unsigned len1 = 0; | |
215 unsigned len2 = 0; | |
216 index = decoder->checked_read_u32v(pc, 1 + len1, &len2, "function index"); | |
217 length = len1 + len2; | |
218 sig = nullptr; | |
219 } | |
220 }; | |
221 | |
222 struct MemoryIndexOperand { | |
223 uint32_t index; | |
224 unsigned length; | |
225 inline MemoryIndexOperand(Decoder* decoder, const byte* pc) { | |
226 index = decoder->checked_read_u8(pc, 1, "memory index"); | |
227 if (index != 0) { | |
228 decoder->error(pc, pc + 1, "expected memory index 0, found %u", index); | |
229 } | |
230 length = 1; | |
231 } | |
232 }; | |
233 | |
234 struct BranchTableOperand { | |
235 uint32_t table_count; | |
236 const byte* start; | |
237 const byte* table; | |
238 inline BranchTableOperand(Decoder* decoder, const byte* pc) { | |
239 DCHECK_EQ(kExprBrTable, decoder->checked_read_u8(pc, 0, "opcode")); | |
240 start = pc + 1; | |
241 unsigned len1 = 0; | |
242 table_count = decoder->checked_read_u32v(pc, 1, &len1, "table count"); | |
243 if (table_count > (UINT_MAX / sizeof(uint32_t)) - 1 || | |
244 len1 > UINT_MAX - (table_count + 1) * sizeof(uint32_t)) { | |
245 decoder->error(pc, "branch table size overflow"); | |
246 } | |
247 table = pc + 1 + len1; | |
248 } | |
249 }; | |
250 | |
251 // A helper to iterate over a branch table. | |
252 class BranchTableIterator { | |
253 public: | |
254 unsigned cur_index() { return index_; } | |
255 bool has_next() { return decoder_->ok() && index_ <= table_count_; } | |
256 uint32_t next() { | |
257 DCHECK(has_next()); | |
258 index_++; | |
259 unsigned length = 0; | |
260 uint32_t result = | |
261 decoder_->checked_read_u32v(pc_, 0, &length, "branch table entry"); | |
262 pc_ += length; | |
263 return result; | |
264 } | |
265 // length, including the length of the {BranchTableOperand}, but not the | |
266 // opcode. | |
267 unsigned length() { | |
268 while (has_next()) next(); | |
269 return static_cast<unsigned>(pc_ - start_); | |
270 } | |
271 const byte* pc() { return pc_; } | |
272 | |
273 BranchTableIterator(Decoder* decoder, BranchTableOperand& operand) | |
274 : decoder_(decoder), | |
275 start_(operand.start), | |
276 pc_(operand.table), | |
277 index_(0), | |
278 table_count_(operand.table_count) {} | |
279 | |
280 private: | |
281 Decoder* decoder_; | |
282 const byte* start_; | |
283 const byte* pc_; | |
284 uint32_t index_; // the current index. | |
285 uint32_t table_count_; // the count of entries, not including default. | |
286 }; | |
287 | |
288 struct MemoryAccessOperand { | |
289 uint32_t alignment; | |
290 uint32_t offset; | |
291 unsigned length; | |
292 inline MemoryAccessOperand(Decoder* decoder, const byte* pc, | |
293 uint32_t max_alignment) { | |
294 unsigned alignment_length; | |
295 alignment = | |
296 decoder->checked_read_u32v(pc, 1, &alignment_length, "alignment"); | |
297 if (max_alignment < alignment) { | |
298 decoder->error(pc, pc + 1, | |
299 "invalid alignment; expected maximum alignment is %u, " | |
300 "actual alignment is %u", | |
301 max_alignment, alignment); | |
302 } | |
303 unsigned offset_length; | |
304 offset = decoder->checked_read_u32v(pc, 1 + alignment_length, | |
305 &offset_length, "offset"); | |
306 length = alignment_length + offset_length; | |
307 } | |
308 }; | |
309 | |
310 typedef compiler::WasmGraphBuilder TFBuilder; | |
311 struct ModuleEnv; // forward declaration of module interface. | |
312 | |
313 // All of the various data structures necessary to decode a function body. | |
314 struct FunctionBody { | |
315 ModuleEnv* module; // module environment | |
316 FunctionSig* sig; // function signature | |
317 const byte* base; // base of the module bytes, for error reporting | |
318 const byte* start; // start of the function body | |
319 const byte* end; // end of the function body | |
320 }; | |
321 | |
322 static inline FunctionBody FunctionBodyForTesting(const byte* start, | |
323 const byte* end) { | |
324 return {nullptr, nullptr, start, start, end}; | |
325 } | |
326 | |
327 struct DecodeStruct { | |
328 int unused; | |
329 }; | |
330 typedef Result<DecodeStruct*> DecodeResult; | |
331 inline std::ostream& operator<<(std::ostream& os, const DecodeStruct& tree) { | |
332 return os; | |
333 } | |
334 | |
335 V8_EXPORT_PRIVATE DecodeResult VerifyWasmCode(AccountingAllocator* allocator, | |
336 FunctionBody& body); | |
337 DecodeResult BuildTFGraph(AccountingAllocator* allocator, TFBuilder* builder, | |
338 FunctionBody& body); | |
339 bool PrintAst(AccountingAllocator* allocator, const FunctionBody& body, | |
340 std::ostream& os, | |
341 std::vector<std::tuple<uint32_t, int, int>>* offset_table); | |
342 | |
343 // A simplified form of AST printing, e.g. from a debugger. | |
344 void PrintAstForDebugging(const byte* start, const byte* end); | |
345 | |
346 inline DecodeResult VerifyWasmCode(AccountingAllocator* allocator, | |
347 ModuleEnv* module, FunctionSig* sig, | |
348 const byte* start, const byte* end) { | |
349 FunctionBody body = {module, sig, nullptr, start, end}; | |
350 return VerifyWasmCode(allocator, body); | |
351 } | |
352 | |
353 inline DecodeResult BuildTFGraph(AccountingAllocator* allocator, | |
354 TFBuilder* builder, ModuleEnv* module, | |
355 FunctionSig* sig, const byte* start, | |
356 const byte* end) { | |
357 FunctionBody body = {module, sig, nullptr, start, end}; | |
358 return BuildTFGraph(allocator, builder, body); | |
359 } | |
360 | |
361 struct AstLocalDecls { | |
362 // The size of the encoded declarations. | |
363 uint32_t decls_encoded_size; // size of encoded declarations | |
364 | |
365 // Total number of locals. | |
366 uint32_t total_local_count; | |
367 | |
368 // List of {local type, count} pairs. | |
369 ZoneVector<std::pair<LocalType, uint32_t>> local_types; | |
370 | |
371 // Constructor initializes the vector. | |
372 explicit AstLocalDecls(Zone* zone) | |
373 : decls_encoded_size(0), total_local_count(0), local_types(zone) {} | |
374 }; | |
375 | |
376 V8_EXPORT_PRIVATE bool DecodeLocalDecls(AstLocalDecls& decls, const byte* start, | |
377 const byte* end); | |
378 V8_EXPORT_PRIVATE BitVector* AnalyzeLoopAssignmentForTesting(Zone* zone, | |
379 size_t num_locals, | |
380 const byte* start, | |
381 const byte* end); | |
382 | |
383 // Computes the length of the opcode at the given address. | |
384 V8_EXPORT_PRIVATE unsigned OpcodeLength(const byte* pc, const byte* end); | |
385 | |
386 // A simple forward iterator for bytecodes. | |
387 class V8_EXPORT_PRIVATE BytecodeIterator : public NON_EXPORTED_BASE(Decoder) { | |
388 // Base class for both iterators defined below. | |
389 class iterator_base { | |
390 public: | |
391 inline iterator_base& operator++() { | |
392 DCHECK_LT(ptr_, end_); | |
393 ptr_ += OpcodeLength(ptr_, end_); | |
394 return *this; | |
395 } | |
396 inline bool operator==(const iterator_base& that) { | |
397 return this->ptr_ == that.ptr_; | |
398 } | |
399 inline bool operator!=(const iterator_base& that) { | |
400 return this->ptr_ != that.ptr_; | |
401 } | |
402 | |
403 protected: | |
404 const byte* ptr_; | |
405 const byte* end_; | |
406 iterator_base(const byte* ptr, const byte* end) : ptr_(ptr), end_(end) {} | |
407 }; | |
408 | |
409 public: | |
410 // If one wants to iterate over the bytecode without looking at {pc_offset()}. | |
411 class opcode_iterator | |
412 : public iterator_base, | |
413 public std::iterator<std::input_iterator_tag, WasmOpcode> { | |
414 public: | |
415 inline WasmOpcode operator*() { | |
416 DCHECK_LT(ptr_, end_); | |
417 return static_cast<WasmOpcode>(*ptr_); | |
418 } | |
419 | |
420 private: | |
421 friend class BytecodeIterator; | |
422 opcode_iterator(const byte* ptr, const byte* end) | |
423 : iterator_base(ptr, end) {} | |
424 }; | |
425 // If one wants to iterate over the instruction offsets without looking at | |
426 // opcodes. | |
427 class offset_iterator | |
428 : public iterator_base, | |
429 public std::iterator<std::input_iterator_tag, uint32_t> { | |
430 public: | |
431 inline uint32_t operator*() { | |
432 DCHECK_LT(ptr_, end_); | |
433 return static_cast<uint32_t>(ptr_ - start_); | |
434 } | |
435 | |
436 private: | |
437 const byte* start_; | |
438 friend class BytecodeIterator; | |
439 offset_iterator(const byte* start, const byte* ptr, const byte* end) | |
440 : iterator_base(ptr, end), start_(start) {} | |
441 }; | |
442 | |
443 // Create a new {BytecodeIterator}. If the {decls} pointer is non-null, | |
444 // assume the bytecode starts with local declarations and decode them. | |
445 // Otherwise, do not decode local decls. | |
446 BytecodeIterator(const byte* start, const byte* end, | |
447 AstLocalDecls* decls = nullptr); | |
448 | |
449 base::iterator_range<opcode_iterator> opcodes() { | |
450 return base::iterator_range<opcode_iterator>(opcode_iterator(pc_, end_), | |
451 opcode_iterator(end_, end_)); | |
452 } | |
453 | |
454 base::iterator_range<offset_iterator> offsets() { | |
455 return base::iterator_range<offset_iterator>( | |
456 offset_iterator(start_, pc_, end_), | |
457 offset_iterator(start_, end_, end_)); | |
458 } | |
459 | |
460 WasmOpcode current() { | |
461 return static_cast<WasmOpcode>( | |
462 checked_read_u8(pc_, 0, "expected bytecode")); | |
463 } | |
464 | |
465 void next() { | |
466 if (pc_ < end_) { | |
467 pc_ += OpcodeLength(pc_, end_); | |
468 if (pc_ >= end_) pc_ = end_; | |
469 } | |
470 } | |
471 | |
472 bool has_next() { return pc_ < end_; } | |
473 }; | |
474 | |
475 } // namespace wasm | |
476 } // namespace internal | |
477 } // namespace v8 | |
478 | |
479 #endif // V8_WASM_AST_DECODER_H_ | |
OLD | NEW |