// 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_ #include "src/signature.h" #include "src/wasm/decoder.h" #include "src/wasm/wasm-opcodes.h" #include "src/wasm/wasm-result.h" namespace v8 { namespace internal { class BitVector; // forward declaration namespace compiler { // external declarations from compiler. class WasmGraphBuilder; } namespace wasm { // Helpers for decoding different kinds of operands which follow bytecodes. struct LocalIndexOperand { uint32_t index; LocalType type; unsigned length; inline LocalIndexOperand(Decoder* decoder, const byte* pc) { index = decoder->checked_read_u32v(pc, 1, &length, "local index"); type = kAstStmt; } }; struct ImmI8Operand { int8_t value; unsigned length; 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; unsigned length; inline ImmI32Operand(Decoder* decoder, const byte* pc) { value = decoder->checked_read_i32v(pc, 1, &length, "immi32"); } }; struct ImmI64Operand { int64_t value; unsigned length; inline ImmI64Operand(Decoder* decoder, const byte* pc) { value = decoder->checked_read_i64v(pc, 1, &length, "immi64"); } }; struct ImmF32Operand { float value; unsigned length; inline ImmF32Operand(Decoder* decoder, const byte* pc) { value = bit_cast<float>(decoder->checked_read_u32(pc, 1, "immf32")); length = 4; } }; struct ImmF64Operand { double value; unsigned length; 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; MachineType machine_type; unsigned length; inline GlobalIndexOperand(Decoder* decoder, const byte* pc) { index = decoder->checked_read_u32v(pc, 1, &length, "global index"); type = kAstStmt; machine_type = MachineType::None(); } }; struct Control; struct BreakDepthOperand { uint32_t arity; uint32_t depth; Control* target; unsigned length; inline BreakDepthOperand(Decoder* decoder, const byte* pc) { unsigned len1 = 0; unsigned len2 = 0; arity = decoder->checked_read_u32v(pc, 1, &len1, "argument count"); depth = decoder->checked_read_u32v(pc, 1 + len1, &len2, "break depth"); length = len1 + len2; target = nullptr; } }; struct CallIndirectOperand { uint32_t arity; uint32_t index; FunctionSig* sig; unsigned length; inline CallIndirectOperand(Decoder* decoder, const byte* pc) { unsigned len1 = 0; unsigned len2 = 0; arity = decoder->checked_read_u32v(pc, 1, &len1, "argument count"); index = decoder->checked_read_u32v(pc, 1 + len1, &len2, "signature index"); length = len1 + len2; sig = nullptr; } }; struct CallFunctionOperand { uint32_t arity; uint32_t index; FunctionSig* sig; unsigned length; inline CallFunctionOperand(Decoder* decoder, const byte* pc) { unsigned len1 = 0; unsigned len2 = 0; arity = decoder->checked_read_u32v(pc, 1, &len1, "argument count"); index = decoder->checked_read_u32v(pc, 1 + len1, &len2, "function index"); length = len1 + len2; sig = nullptr; } }; struct CallImportOperand { uint32_t arity; uint32_t index; FunctionSig* sig; unsigned length; inline CallImportOperand(Decoder* decoder, const byte* pc) { unsigned len1 = 0; unsigned len2 = 0; arity = decoder->checked_read_u32v(pc, 1, &len1, "argument count"); index = decoder->checked_read_u32v(pc, 1 + len1, &len2, "import index"); length = len1 + len2; sig = nullptr; } }; struct BranchTableOperand { uint32_t arity; uint32_t table_count; const byte* table; unsigned length; inline BranchTableOperand(Decoder* decoder, const byte* pc) { unsigned len1 = 0; unsigned len2 = 0; arity = decoder->checked_read_u32v(pc, 1, &len1, "argument count"); table_count = decoder->checked_read_u32v(pc, 1 + len1, &len2, "table count"); if (table_count > (UINT_MAX / sizeof(uint32_t)) - 1 || len1 + len2 > UINT_MAX - (table_count + 1) * sizeof(uint32_t)) { decoder->error(pc, "branch table size overflow"); } length = len1 + len2 + (table_count + 1) * sizeof(uint32_t); uint32_t table_start = 1 + len1 + len2; if (decoder->check(pc, table_start, (table_count + 1) * sizeof(uint32_t), "expected <table entries>")) { table = pc + table_start; } else { table = nullptr; } } inline uint32_t read_entry(Decoder* decoder, unsigned i) { DCHECK(i <= table_count); return table ? decoder->read_u32(table + i * sizeof(uint32_t)) : 0; } }; struct MemoryAccessOperand { uint32_t alignment; uint32_t offset; unsigned length; inline MemoryAccessOperand(Decoder* decoder, const byte* pc) { unsigned alignment_length; alignment = decoder->checked_read_u32v(pc, 1, &alignment_length, "alignment"); unsigned offset_length; offset = decoder->checked_read_u32v(pc, 1 + alignment_length, &offset_length, "offset"); length = alignment_length + offset_length; } }; struct ReturnArityOperand { uint32_t arity; unsigned length; inline ReturnArityOperand(Decoder* decoder, const byte* pc) { arity = decoder->checked_read_u32v(pc, 1, &length, "return count"); } }; typedef compiler::WasmGraphBuilder TFBuilder; struct ModuleEnv; // forward declaration of module interface. // 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 }; static inline FunctionBody FunctionBodyForTesting(const byte* start, const byte* end) { return {nullptr, nullptr, start, start, end}; } struct Tree; typedef Result<Tree*> TreeResult; std::ostream& operator<<(std::ostream& os, const Tree& tree); TreeResult VerifyWasmCode(base::AccountingAllocator* allocator, FunctionBody& body); TreeResult BuildTFGraph(base::AccountingAllocator* allocator, TFBuilder* builder, FunctionBody& body); bool PrintAst(base::AccountingAllocator* allocator, const FunctionBody& body, std::ostream& os, std::vector<std::tuple<uint32_t, int, int>>* offset_table); // A simplified form of AST printing, e.g. from a debugger. void PrintAstForDebugging(const byte* start, const byte* end); inline TreeResult VerifyWasmCode(base::AccountingAllocator* allocator, ModuleEnv* module, FunctionSig* sig, const byte* start, const byte* end) { FunctionBody body = {module, sig, nullptr, start, end}; return VerifyWasmCode(allocator, body); } inline TreeResult BuildTFGraph(base::AccountingAllocator* allocator, TFBuilder* builder, ModuleEnv* module, FunctionSig* sig, const byte* start, const byte* end) { FunctionBody body = {module, sig, nullptr, start, end}; return BuildTFGraph(allocator, builder, body); } 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) {} }; bool DecodeLocalDecls(AstLocalDecls& decls, const byte* start, const byte* end); BitVector* AnalyzeLoopAssignmentForTesting(Zone* zone, size_t num_locals, const byte* start, const byte* end); // Computes the length of the opcode at the given address. unsigned OpcodeLength(const byte* pc, const byte* end); // Computes the arity (number of sub-nodes) of the opcode at the given address. unsigned OpcodeArity(const byte* pc, const byte* end); } // namespace wasm } // namespace internal } // namespace v8 #endif // V8_WASM_AST_DECODER_H_