ast-decoder.h 13.2 KB
Newer Older
1 2 3 4 5 6 7
// Copyright 2015 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef V8_WASM_AST_DECODER_H_
#define V8_WASM_AST_DECODER_H_

8 9
#include "src/base/compiler-specific.h"
#include "src/globals.h"
10
#include "src/signature.h"
11
#include "src/wasm/decoder.h"
12 13 14 15 16 17
#include "src/wasm/wasm-opcodes.h"
#include "src/wasm/wasm-result.h"

namespace v8 {
namespace internal {

18 19
class BitVector;  // forward declaration

20 21 22 23 24 25
namespace compiler {  // external declarations from compiler.
class WasmGraphBuilder;
}

namespace wasm {

26
const uint32_t kMaxNumWasmLocals = 8000000;
27
struct WasmGlobal;
28

29 30 31 32
// Helpers for decoding different kinds of operands which follow bytecodes.
struct LocalIndexOperand {
  uint32_t index;
  LocalType type;
33
  unsigned length;
34 35 36 37 38 39 40 41 42

  inline LocalIndexOperand(Decoder* decoder, const byte* pc) {
    index = decoder->checked_read_u32v(pc, 1, &length, "local index");
    type = kAstStmt;
  }
};

struct ImmI8Operand {
  int8_t value;
43
  unsigned length;
44 45 46 47 48 49 50 51
  inline ImmI8Operand(Decoder* decoder, const byte* pc) {
    value = bit_cast<int8_t>(decoder->checked_read_u8(pc, 1, "immi8"));
    length = 1;
  }
};

struct ImmI32Operand {
  int32_t value;
52
  unsigned length;
53
  inline ImmI32Operand(Decoder* decoder, const byte* pc) {
54
    value = decoder->checked_read_i32v(pc, 1, &length, "immi32");
55 56 57 58 59
  }
};

struct ImmI64Operand {
  int64_t value;
60
  unsigned length;
61
  inline ImmI64Operand(Decoder* decoder, const byte* pc) {
62
    value = decoder->checked_read_i64v(pc, 1, &length, "immi64");
63 64 65 66 67
  }
};

struct ImmF32Operand {
  float value;
68
  unsigned length;
69 70 71 72 73 74 75 76
  inline ImmF32Operand(Decoder* decoder, const byte* pc) {
    value = bit_cast<float>(decoder->checked_read_u32(pc, 1, "immf32"));
    length = 4;
  }
};

struct ImmF64Operand {
  double value;
77
  unsigned length;
78 79 80 81 82 83 84 85 86
  inline ImmF64Operand(Decoder* decoder, const byte* pc) {
    value = bit_cast<double>(decoder->checked_read_u64(pc, 1, "immf64"));
    length = 8;
  }
};

struct GlobalIndexOperand {
  uint32_t index;
  LocalType type;
87
  const WasmGlobal* global;
88
  unsigned length;
89 90 91

  inline GlobalIndexOperand(Decoder* decoder, const byte* pc) {
    index = decoder->checked_read_u32v(pc, 1, &length, "global index");
92
    global = nullptr;
93 94 95 96
    type = kAstStmt;
  }
};

97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160
struct BlockTypeOperand {
  uint32_t arity;
  const byte* types;  // pointer to encoded types for the block.
  unsigned length;

  inline BlockTypeOperand(Decoder* decoder, const byte* pc) {
    uint8_t val = decoder->checked_read_u8(pc, 1, "block type");
    LocalType type = kAstStmt;
    length = 1;
    arity = 0;
    types = nullptr;
    if (decode_local_type(val, &type)) {
      arity = type == kAstStmt ? 0 : 1;
      types = pc + 1;
    } else {
      // Handle multi-value blocks.
      if (!FLAG_wasm_mv_prototype) {
        decoder->error(pc, pc + 1, "invalid block arity > 1");
        return;
      }
      if (val != kMultivalBlock) {
        decoder->error(pc, pc + 1, "invalid block type");
        return;
      }
      // Decode and check the types vector of the block.
      unsigned len = 0;
      uint32_t count = decoder->checked_read_u32v(pc, 2, &len, "block arity");
      // {count} is encoded as {arity-2}, so that a {0} count here corresponds
      // to a block with 2 values. This makes invalid/redundant encodings
      // impossible.
      arity = count + 2;
      length = 1 + len + arity;
      types = pc + 1 + 1 + len;

      for (uint32_t i = 0; i < arity; i++) {
        uint32_t offset = 1 + 1 + len + i;
        val = decoder->checked_read_u8(pc, offset, "block type");
        decode_local_type(val, &type);
        if (type == kAstStmt) {
          decoder->error(pc, pc + offset, "invalid block type");
          return;
        }
      }
    }
  }
  // Decode a byte representing a local type. Return {false} if the encoded
  // byte was invalid or {kMultivalBlock}.
  bool decode_local_type(uint8_t val, LocalType* result) {
    switch (static_cast<LocalTypeCode>(val)) {
      case kLocalVoid:
        *result = kAstStmt;
        return true;
      case kLocalI32:
        *result = kAstI32;
        return true;
      case kLocalI64:
        *result = kAstI64;
        return true;
      case kLocalF32:
        *result = kAstF32;
        return true;
      case kLocalF64:
        *result = kAstF64;
        return true;
161 162 163
      case kLocalS128:
        *result = kAstS128;
        return true;
164 165 166 167 168 169 170 171 172 173 174 175 176
      default:
        *result = kAstStmt;
        return false;
    }
  }
  LocalType read_entry(unsigned index) {
    DCHECK_LT(index, arity);
    LocalType result;
    CHECK(decode_local_type(types[index], &result));
    return result;
  }
};

177
struct Control;
178 179
struct BreakDepthOperand {
  uint32_t depth;
180
  Control* target;
181
  unsigned length;
182
  inline BreakDepthOperand(Decoder* decoder, const byte* pc) {
183
    depth = decoder->checked_read_u32v(pc, 1, &length, "break depth");
184 185 186 187
    target = nullptr;
  }
};

188
struct CallIndirectOperand {
189
  uint32_t table_index;
190 191
  uint32_t index;
  FunctionSig* sig;
192
  unsigned length;
193
  inline CallIndirectOperand(Decoder* decoder, const byte* pc) {
194 195 196 197 198 199 200 201
    unsigned len = 0;
    index = decoder->checked_read_u32v(pc, 1, &len, "signature index");
    table_index = decoder->checked_read_u8(pc, 1 + len, "table index");
    if (table_index != 0) {
      decoder->error(pc, pc + 1 + len, "expected table index 0, found %u",
                     table_index);
    }
    length = 1 + len;
202 203 204 205
    sig = nullptr;
  }
};

206
struct CallFunctionOperand {
207 208
  uint32_t index;
  FunctionSig* sig;
209
  unsigned length;
210
  inline CallFunctionOperand(Decoder* decoder, const byte* pc) {
211 212
    unsigned len1 = 0;
    unsigned len2 = 0;
213 214
    index = decoder->checked_read_u32v(pc, 1 + len1, &len2, "function index");
    length = len1 + len2;
215 216 217 218
    sig = nullptr;
  }
};

219 220 221 222 223 224 225 226 227 228 229 230
struct MemoryIndexOperand {
  uint32_t index;
  unsigned length;
  inline MemoryIndexOperand(Decoder* decoder, const byte* pc) {
    index = decoder->checked_read_u8(pc, 1, "memory index");
    if (index != 0) {
      decoder->error(pc, pc + 1, "expected memory index 0, found %u", index);
    }
    length = 1;
  }
};

231
struct BranchTableOperand {
232
  uint32_t table_count;
233
  const byte* start;
234
  const byte* table;
235
  inline BranchTableOperand(Decoder* decoder, const byte* pc) {
236 237
    DCHECK_EQ(kExprBrTable, decoder->checked_read_u8(pc, 0, "opcode"));
    start = pc + 1;
238
    unsigned len1 = 0;
239
    table_count = decoder->checked_read_u32v(pc, 1, &len1, "table count");
240
    if (table_count > (UINT_MAX / sizeof(uint32_t)) - 1 ||
241
        len1 > UINT_MAX - (table_count + 1) * sizeof(uint32_t)) {
242 243
      decoder->error(pc, "branch table size overflow");
    }
244
    table = pc + 1 + len1;
245
  }
246 247
  inline uint32_t read_entry(Decoder* decoder, unsigned i) {
    DCHECK(i <= table_count);
248
    return table ? decoder->read_u32(table + i * sizeof(uint32_t)) : 0;
249 250 251
  }
};

252 253 254 255
// A helper to iterate over a branch table.
class BranchTableIterator {
 public:
  unsigned cur_index() { return index_; }
256
  bool has_next() { return decoder_->ok() && index_ <= table_count_; }
257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288
  uint32_t next() {
    DCHECK(has_next());
    index_++;
    unsigned length = 0;
    uint32_t result =
        decoder_->checked_read_u32v(pc_, 0, &length, "branch table entry");
    pc_ += length;
    return result;
  }
  // length, including the length of the {BranchTableOperand}, but not the
  // opcode.
  unsigned length() {
    while (has_next()) next();
    return static_cast<unsigned>(pc_ - start_);
  }
  const byte* pc() { return pc_; }

  BranchTableIterator(Decoder* decoder, BranchTableOperand& operand)
      : decoder_(decoder),
        start_(operand.start),
        pc_(operand.table),
        index_(0),
        table_count_(operand.table_count) {}

 private:
  Decoder* decoder_;
  const byte* start_;
  const byte* pc_;
  uint32_t index_;        // the current index.
  uint32_t table_count_;  // the count of entries, not including default.
};

289
struct MemoryAccessOperand {
290
  uint32_t alignment;
291
  uint32_t offset;
292
  unsigned length;
293 294
  inline MemoryAccessOperand(Decoder* decoder, const byte* pc,
                             uint32_t max_alignment) {
295
    unsigned alignment_length;
296 297
    alignment =
        decoder->checked_read_u32v(pc, 1, &alignment_length, "alignment");
298 299 300 301 302 303
    if (max_alignment < alignment) {
      decoder->error(pc, pc + 1,
                     "invalid alignment; expected maximum alignment is %u, "
                     "actual alignment is %u",
                     max_alignment, alignment);
    }
304
    unsigned offset_length;
305 306 307
    offset = decoder->checked_read_u32v(pc, 1 + alignment_length,
                                        &offset_length, "offset");
    length = alignment_length + offset_length;
308 309 310
  }
};

311 312 313
typedef compiler::WasmGraphBuilder TFBuilder;
struct ModuleEnv;  // forward declaration of module interface.

314 315 316 317 318 319 320
// All of the various data structures necessary to decode a function body.
struct FunctionBody {
  ModuleEnv* module;  // module environment
  FunctionSig* sig;   // function signature
  const byte* base;   // base of the module bytes, for error reporting
  const byte* start;  // start of the function body
  const byte* end;    // end of the function body
321 322
};

323 324 325 326 327
static inline FunctionBody FunctionBodyForTesting(const byte* start,
                                                  const byte* end) {
  return {nullptr, nullptr, start, start, end};
}

328 329 330 331 332 333 334
struct DecodeStruct {
  int unused;
};
typedef Result<DecodeStruct*> DecodeResult;
inline std::ostream& operator<<(std::ostream& os, const DecodeStruct& tree) {
  return os;
}
335

336 337
V8_EXPORT_PRIVATE DecodeResult VerifyWasmCode(AccountingAllocator* allocator,
                                              FunctionBody& body);
338 339 340
DecodeResult BuildTFGraph(AccountingAllocator* allocator, TFBuilder* builder,
                          FunctionBody& body);
bool PrintAst(AccountingAllocator* allocator, const FunctionBody& body,
341 342
              std::ostream& os,
              std::vector<std::tuple<uint32_t, int, int>>* offset_table);
343

344 345 346
// A simplified form of AST printing, e.g. from a debugger.
void PrintAstForDebugging(const byte* start, const byte* end);

347
inline DecodeResult VerifyWasmCode(AccountingAllocator* allocator,
348 349
                                   ModuleEnv* module, FunctionSig* sig,
                                   const byte* start, const byte* end) {
350
  FunctionBody body = {module, sig, nullptr, start, end};
351
  return VerifyWasmCode(allocator, body);
352 353
}

354
inline DecodeResult BuildTFGraph(AccountingAllocator* allocator,
355 356 357
                                 TFBuilder* builder, ModuleEnv* module,
                                 FunctionSig* sig, const byte* start,
                                 const byte* end) {
358
  FunctionBody body = {module, sig, nullptr, start, end};
359
  return BuildTFGraph(allocator, builder, body);
360 361
}

362 363 364 365 366 367 368 369 370 371 372 373 374 375 376
struct AstLocalDecls {
  // The size of the encoded declarations.
  uint32_t decls_encoded_size;  // size of encoded declarations

  // Total number of locals.
  uint32_t total_local_count;

  // List of {local type, count} pairs.
  ZoneVector<std::pair<LocalType, uint32_t>> local_types;

  // Constructor initializes the vector.
  explicit AstLocalDecls(Zone* zone)
      : decls_encoded_size(0), total_local_count(0), local_types(zone) {}
};

377 378 379 380 381 382
V8_EXPORT_PRIVATE bool DecodeLocalDecls(AstLocalDecls& decls, const byte* start,
                                        const byte* end);
V8_EXPORT_PRIVATE BitVector* AnalyzeLoopAssignmentForTesting(Zone* zone,
                                                             size_t num_locals,
                                                             const byte* start,
                                                             const byte* end);
383

384
// Computes the length of the opcode at the given address.
385
V8_EXPORT_PRIVATE unsigned OpcodeLength(const byte* pc, const byte* end);
386

387
// A simple forward iterator for bytecodes.
388
class V8_EXPORT_PRIVATE BytecodeIterator : public NON_EXPORTED_BASE(Decoder) {
389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439
 public:
  // If one wants to iterate over the bytecode without looking at {pc_offset()}.
  class iterator {
   public:
    inline iterator& operator++() {
      DCHECK_LT(ptr_, end_);
      ptr_ += OpcodeLength(ptr_, end_);
      return *this;
    }
    inline WasmOpcode operator*() {
      DCHECK_LT(ptr_, end_);
      return static_cast<WasmOpcode>(*ptr_);
    }
    inline bool operator==(const iterator& that) {
      return this->ptr_ == that.ptr_;
    }
    inline bool operator!=(const iterator& that) {
      return this->ptr_ != that.ptr_;
    }

   private:
    friend class BytecodeIterator;
    const byte* ptr_;
    const byte* end_;
    iterator(const byte* ptr, const byte* end) : ptr_(ptr), end_(end) {}
  };

  // Create a new {BytecodeIterator}. If the {decls} pointer is non-null,
  // assume the bytecode starts with local declarations and decode them.
  // Otherwise, do not decode local decls.
  BytecodeIterator(const byte* start, const byte* end,
                   AstLocalDecls* decls = nullptr);

  inline iterator begin() const { return iterator(pc_, end_); }
  inline iterator end() const { return iterator(end_, end_); }

  WasmOpcode current() {
    return static_cast<WasmOpcode>(
        checked_read_u8(pc_, 0, "expected bytecode"));
  }

  void next() {
    if (pc_ < end_) {
      pc_ += OpcodeLength(pc_, end_);
      if (pc_ >= end_) pc_ = end_;
    }
  }

  bool has_next() { return pc_ < end_; }
};

440 441 442 443 444
}  // namespace wasm
}  // namespace internal
}  // namespace v8

#endif  // V8_WASM_AST_DECODER_H_