| 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 |