Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1206)

Unified Diff: src/wasm/module-decoder.cc

Issue 1504713014: Initial import of v8-native WASM. (Closed) Base URL: https://chromium.googlesource.com/v8/v8.git@master
Patch Set: Created 5 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « src/wasm/module-decoder.h ('k') | src/wasm/wasm-js.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: src/wasm/module-decoder.cc
diff --git a/src/wasm/module-decoder.cc b/src/wasm/module-decoder.cc
new file mode 100644
index 0000000000000000000000000000000000000000..82588bda07e9afacbe33ac8610a230a5b372ab13
--- /dev/null
+++ b/src/wasm/module-decoder.cc
@@ -0,0 +1,532 @@
+// 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.
+
+#include "src/macro-assembler.h"
+#include "src/objects.h"
+#include "src/v8.h"
+
+#include "src/wasm/decoder.h"
+#include "src/wasm/module-decoder.h"
+
+namespace v8 {
+namespace internal {
+namespace wasm {
+
+#if DEBUG
+#define TRACE(...) \
+ do { \
+ if (FLAG_trace_wasm_decoder) PrintF(__VA_ARGS__); \
+ } while (false)
+#else
+#define TRACE(...)
+#endif
+
+
+// The main logic for decoding the bytes of a module.
+class ModuleDecoder : public Decoder {
+ public:
+ ModuleDecoder(Zone* zone, const byte* module_start, const byte* module_end,
+ bool asm_js)
+ : Decoder(module_start, module_end), module_zone(zone), asm_js_(asm_js) {
+ result_.start = start_;
+ if (limit_ < start_) {
+ error(start_, "end is less than start");
+ limit_ = start_;
+ }
+ }
+
+ virtual void onFirstError() {
+ pc_ = limit_; // On error, terminate section decoding loop.
+ }
+
+ // Decodes an entire module.
+ ModuleResult DecodeModule(WasmModule* module, bool verify_functions = true) {
+ pc_ = start_;
+ module->module_start = start_;
+ module->module_end = limit_;
+ module->min_mem_size_log2 = 0;
+ module->max_mem_size_log2 = 0;
+ module->mem_export = false;
+ module->mem_external = false;
+ module->globals = new std::vector<WasmGlobal>();
+ module->signatures = new std::vector<FunctionSig*>();
+ module->functions = new std::vector<WasmFunction>();
+ module->data_segments = new std::vector<WasmDataSegment>();
+ module->function_table = new std::vector<uint16_t>();
+
+ bool sections[kMaxModuleSectionCode];
+ memset(sections, 0, sizeof(sections));
+
+ // Decode the module sections.
+ while (pc_ < limit_) {
+ TRACE("DecodeSection\n");
+ WasmSectionDeclCode section =
+ static_cast<WasmSectionDeclCode>(u8("section"));
+ // Each section should appear at most once.
+ if (section < kMaxModuleSectionCode) {
+ CheckForPreviousSection(sections, section, false);
+ sections[section] = true;
+ }
+
+ switch (section) {
+ case kDeclEnd:
+ // Terminate section decoding.
+ limit_ = pc_;
+ break;
+ case kDeclMemory:
+ module->min_mem_size_log2 = u8("min memory");
+ module->max_mem_size_log2 = u8("max memory");
+ module->mem_export = u8("export memory") != 0;
+ break;
+ case kDeclSignatures: {
+ int length;
+ uint32_t signatures_count = u32v(&length, "signatures count");
+ module->signatures->reserve(SafeReserve(signatures_count));
+ // Decode signatures.
+ for (uint32_t i = 0; i < signatures_count; i++) {
+ if (failed()) break;
+ TRACE("DecodeSignature[%d] module+%d\n", i,
+ static_cast<int>(pc_ - start_));
+ FunctionSig* s = sig(); // read function sig.
+ module->signatures->push_back(s);
+ }
+ break;
+ }
+ case kDeclFunctions: {
+ // Functions require a signature table first.
+ CheckForPreviousSection(sections, kDeclSignatures, true);
+ int length;
+ uint32_t functions_count = u32v(&length, "functions count");
+ module->functions->reserve(SafeReserve(functions_count));
+ // Set up module environment for verification.
+ ModuleEnv menv;
+ menv.module = module;
+ menv.globals_area = 0;
+ menv.mem_start = 0;
+ menv.mem_end = 0;
+ menv.function_code = nullptr;
+ menv.asm_js = asm_js_;
+ // Decode functions.
+ for (uint32_t i = 0; i < functions_count; i++) {
+ if (failed()) break;
+ TRACE("DecodeFunction[%d] module+%d\n", i,
+ static_cast<int>(pc_ - start_));
+
+ module->functions->push_back(
+ {nullptr, 0, 0, 0, 0, 0, 0, false, false});
+ WasmFunction* function = &module->functions->back();
+ DecodeFunctionInModule(module, function, false);
+ }
+ if (ok() && verify_functions) {
+ for (uint32_t i = 0; i < functions_count; i++) {
+ if (failed()) break;
+ WasmFunction* function = &module->functions->at(i);
+ if (!function->external) {
+ VerifyFunctionBody(i, &menv, function);
+ if (result_.failed())
+ error(result_.error_pc, result_.error_msg.get());
+ }
+ }
+ }
+ break;
+ }
+ case kDeclGlobals: {
+ int length;
+ uint32_t globals_count = u32v(&length, "globals count");
+ module->globals->reserve(SafeReserve(globals_count));
+ // Decode globals.
+ for (uint32_t i = 0; i < globals_count; i++) {
+ if (failed()) break;
+ TRACE("DecodeGlobal[%d] module+%d\n", i,
+ static_cast<int>(pc_ - start_));
+ module->globals->push_back({0, MachineType::Int32(), 0, false});
+ WasmGlobal* global = &module->globals->back();
+ DecodeGlobalInModule(global);
+ }
+ break;
+ }
+ case kDeclDataSegments: {
+ int length;
+ uint32_t data_segments_count = u32v(&length, "data segments count");
+ module->data_segments->reserve(SafeReserve(data_segments_count));
+ // Decode data segments.
+ for (uint32_t i = 0; i < data_segments_count; i++) {
+ if (failed()) break;
+ TRACE("DecodeDataSegment[%d] module+%d\n", i,
+ static_cast<int>(pc_ - start_));
+ module->data_segments->push_back({0, 0, 0});
+ WasmDataSegment* segment = &module->data_segments->back();
+ DecodeDataSegmentInModule(segment);
+ }
+ break;
+ }
+ case kDeclFunctionTable: {
+ // An indirect function table requires functions first.
+ CheckForPreviousSection(sections, kDeclFunctions, true);
+ int length;
+ uint32_t function_table_count = u32v(&length, "function table count");
+ module->function_table->reserve(SafeReserve(function_table_count));
+ // Decode function table.
+ for (uint32_t i = 0; i < function_table_count; i++) {
+ if (failed()) break;
+ TRACE("DecodeFunctionTable[%d] module+%d\n", i,
+ static_cast<int>(pc_ - start_));
+ uint16_t index = u16();
+ if (index >= module->functions->size()) {
+ error(pc_ - 2, "invalid function index");
+ break;
+ }
+ module->function_table->push_back(index);
+ }
+ break;
+ }
+ default:
+ error(pc_ - 1, nullptr, "unrecognized section 0x%02x", section);
+ break;
+ }
+ }
+
+ return toResult(module);
+ }
+
+ uint32_t SafeReserve(uint32_t count) {
+ // Avoid OOM by only reserving up to a certain size.
+ const uint32_t kMaxReserve = 20000;
+ return count < kMaxReserve ? count : kMaxReserve;
+ }
+
+ void CheckForPreviousSection(bool* sections, WasmSectionDeclCode section,
+ bool present) {
+ if (section >= kMaxModuleSectionCode) return;
+ if (sections[section] == present) return;
+ const char* name = "";
+ switch (section) {
+ case kDeclMemory:
+ name = "memory";
+ break;
+ case kDeclSignatures:
+ name = "signatures";
+ break;
+ case kDeclFunctions:
+ name = "function declaration";
+ break;
+ case kDeclGlobals:
+ name = "global variable";
+ break;
+ case kDeclDataSegments:
+ name = "data segment";
+ break;
+ case kDeclFunctionTable:
+ name = "function table";
+ break;
+ default:
+ name = "";
+ break;
+ }
+ if (present) {
+ error(pc_ - 1, nullptr, "required %s section missing", name);
+ } else {
+ error(pc_ - 1, nullptr, "%s section already present", name);
+ }
+ }
+
+ // Decodes a single anonymous function starting at {start_}.
+ FunctionResult DecodeSingleFunction(ModuleEnv* module_env,
+ WasmFunction* function) {
+ pc_ = start_;
+ function->sig = sig(); // read signature
+ function->name_offset = 0; // ---- name
+ function->code_start_offset = off(pc_ + 8); // ---- code start
+ function->code_end_offset = off(limit_); // ---- code end
+ function->local_int32_count = u16(); // read u16
+ function->local_int64_count = u16(); // read u16
+ function->local_float32_count = u16(); // read u16
+ function->local_float64_count = u16(); // read u16
+ function->exported = false; // ---- exported
+ function->external = false; // ---- external
+
+ if (ok()) VerifyFunctionBody(0, module_env, function);
+
+ FunctionResult result;
+ result.CopyFrom(result_); // Copy error code and location.
+ result.val = function;
+ return result;
+ }
+
+ // Decodes a single function signature at {start}.
+ FunctionSig* DecodeFunctionSignature(const byte* start) {
+ pc_ = start;
+ FunctionSig* result = sig();
+ return ok() ? result : nullptr;
+ }
+
+ private:
+ Zone* module_zone;
+ ModuleResult result_;
+ bool asm_js_;
+
+ uint32_t off(const byte* ptr) { return static_cast<uint32_t>(ptr - start_); }
+
+ // Decodes a single global entry inside a module starting at {pc_}.
+ void DecodeGlobalInModule(WasmGlobal* global) {
+ global->name_offset = string("global name");
+ global->type = mem_type();
+ global->offset = 0;
+ global->exported = u8("exported") != 0;
+ }
+
+ // Decodes a single function entry inside a module starting at {pc_}.
+ void DecodeFunctionInModule(WasmModule* module, WasmFunction* function,
+ bool verify_body = true) {
+ byte decl_bits = u8("function decl");
+
+ const byte* sigpos = pc_;
+ function->sig_index = u16("signature index");
+
+ if (function->sig_index >= module->signatures->size()) {
+ return error(sigpos, "invalid signature index");
+ } else {
+ function->sig = module->signatures->at(function->sig_index);
+ }
+
+ TRACE(" +%d <function attributes:%s%s%s%s%s>\n",
+ static_cast<int>(pc_ - start_),
+ decl_bits & kDeclFunctionName ? " name" : "",
+ decl_bits & kDeclFunctionImport ? " imported" : "",
+ decl_bits & kDeclFunctionLocals ? " locals" : "",
+ decl_bits & kDeclFunctionExport ? " exported" : "",
+ (decl_bits & kDeclFunctionImport) == 0 ? " body" : "");
+
+ if (decl_bits & kDeclFunctionName) {
+ function->name_offset = string("function name");
+ }
+
+ function->exported = decl_bits & kDeclFunctionExport;
+
+ // Imported functions have no locals or body.
+ if (decl_bits & kDeclFunctionImport) {
+ function->external = true;
+ return;
+ }
+
+ if (decl_bits & kDeclFunctionLocals) {
+ function->local_int32_count = u16("int32 count");
+ function->local_int64_count = u16("int64 count");
+ function->local_float32_count = u16("float32 count");
+ function->local_float64_count = u16("float64 count");
+ }
+
+ uint16_t size = u16("body size");
+ if (ok()) {
+ if ((pc_ + size) > limit_) {
+ return error(pc_, limit_,
+ "expected %d bytes for function body, fell off end", size);
+ }
+ function->code_start_offset = static_cast<uint32_t>(pc_ - start_);
+ function->code_end_offset = function->code_start_offset + size;
+ TRACE(" +%d %-20s: (%d bytes)\n", static_cast<int>(pc_ - start_),
+ "function body", size);
+ pc_ += size;
+ }
+ }
+
+ // Decodes a single data segment entry inside a module starting at {pc_}.
+ void DecodeDataSegmentInModule(WasmDataSegment* segment) {
+ segment->dest_addr =
+ u32("destination"); // TODO(titzer): check it's within the memory size.
+ segment->source_offset = offset("source offset");
+ segment->source_size =
+ u32("source size"); // TODO(titzer): check the size is reasonable.
+ segment->init = u8("init");
+ }
+
+ // Verifies the body (code) of a given function.
+ void VerifyFunctionBody(uint32_t func_num, ModuleEnv* menv,
+ WasmFunction* function) {
+ if (FLAG_trace_wasm_decode_time) {
+ // TODO(titzer): clean me up a bit.
+ OFStream os(stdout);
+ os << "Verifying WASM function:";
+ if (function->name_offset > 0) {
+ os << menv->module->GetName(function->name_offset);
+ }
+ os << std::endl;
+ }
+ FunctionEnv fenv;
+ fenv.module = menv;
+ fenv.sig = function->sig;
+ fenv.local_int32_count = function->local_int32_count;
+ fenv.local_int64_count = function->local_int64_count;
+ fenv.local_float32_count = function->local_float32_count;
+ fenv.local_float64_count = function->local_float64_count;
+ fenv.SumLocals();
+
+ TreeResult result =
+ VerifyWasmCode(&fenv, start_, start_ + function->code_start_offset,
+ start_ + function->code_end_offset);
+ if (result.failed()) {
+ // Wrap the error message from the function decoder.
+ std::ostringstream str;
+ str << "in function #" << func_num << ": ";
+ // TODO(titzer): add function name for the user?
+ str << result;
+ const char* raw = str.str().c_str();
+ size_t len = strlen(raw);
+ char* buffer = new char[len];
+ strncpy(buffer, raw, len);
+ buffer[len - 1] = 0;
+
+ // Copy error code and location.
+ result_.CopyFrom(result);
+ result_.error_msg.Reset(buffer);
+ }
+ }
+
+ // Reads a single 32-bit unsigned integer interpreted as an offset, checking
+ // the offset is within bounds and advances.
+ uint32_t offset(const char* name = nullptr) {
+ uint32_t offset = u32(name ? name : "offset");
+ if (offset > (limit_ - start_)) {
+ error(pc_ - sizeof(uint32_t), "offset out of bounds of module");
+ }
+ return offset;
+ }
+
+ // Reads a single 32-bit unsigned integer interpreted as an offset into the
+ // data and validating the string there and advances.
+ uint32_t string(const char* name = nullptr) {
+ return offset(name ? name : "string"); // TODO(titzer): validate string
+ }
+
+ // Reads a single 8-bit integer, interpreting it as a local type.
+ LocalType local_type() {
+ byte val = u8("local type");
+ LocalTypeCode t = static_cast<LocalTypeCode>(val);
+ switch (t) {
+ case kLocalVoid:
+ return kAstStmt;
+ case kLocalI32:
+ return kAstI32;
+ case kLocalI64:
+ return kAstI64;
+ case kLocalF32:
+ return kAstF32;
+ case kLocalF64:
+ return kAstF64;
+ default:
+ error(pc_ - 1, "invalid local type");
+ return kAstStmt;
+ }
+ }
+
+ // Reads a single 8-bit integer, interpreting it as a memory type.
+ MachineType mem_type() {
+ byte val = u8("memory type");
+ MemTypeCode t = static_cast<MemTypeCode>(val);
+ switch (t) {
+ case kMemI8:
+ return MachineType::Int8();
+ case kMemU8:
+ return MachineType::Uint8();
+ case kMemI16:
+ return MachineType::Int16();
+ case kMemU16:
+ return MachineType::Uint16();
+ case kMemI32:
+ return MachineType::Int32();
+ case kMemU32:
+ return MachineType::Uint32();
+ case kMemI64:
+ return MachineType::Int64();
+ case kMemU64:
+ return MachineType::Uint64();
+ case kMemF32:
+ return MachineType::Float32();
+ case kMemF64:
+ return MachineType::Float64();
+ default:
+ error(pc_ - 1, "invalid memory type");
+ return MachineType::None();
+ }
+ }
+
+ // Parses an inline function signature.
+ FunctionSig* sig() {
+ byte count = u8("param count");
+ LocalType ret = local_type();
+ FunctionSig::Builder builder(module_zone, ret == kAstStmt ? 0 : 1, count);
+ if (ret != kAstStmt) builder.AddReturn(ret);
+
+ for (int i = 0; i < count; i++) {
+ LocalType param = local_type();
+ if (param == kAstStmt) error(pc_ - 1, "invalid void parameter type");
+ builder.AddParam(param);
+ }
+ return builder.Build();
+ }
+};
+
+
+// Helpers for nice error messages.
+class ModuleError : public ModuleResult {
+ public:
+ explicit ModuleError(const char* msg) {
+ error_code = kError;
+ size_t len = strlen(msg) + 1;
+ char* result = new char[len];
+ strncpy(result, msg, len);
+ result[len - 1] = 0;
+ error_msg.Reset(result);
+ }
+};
+
+
+// Helpers for nice error messages.
+class FunctionError : public FunctionResult {
+ public:
+ explicit FunctionError(const char* msg) {
+ error_code = kError;
+ size_t len = strlen(msg) + 1;
+ char* result = new char[len];
+ strncpy(result, msg, len);
+ result[len - 1] = 0;
+ error_msg.Reset(result);
+ }
+};
+
+
+ModuleResult DecodeWasmModule(Isolate* isolate, Zone* zone,
+ const byte* module_start, const byte* module_end,
+ bool verify_functions, bool asm_js) {
+ size_t size = module_end - module_start;
+ if (module_start > module_end) return ModuleError("start > end");
+ if (size >= kMaxModuleSize) return ModuleError("size > maximum module size");
+ WasmModule* module = new WasmModule();
+ ModuleDecoder decoder(zone, module_start, module_end, asm_js);
+ return decoder.DecodeModule(module, verify_functions);
+}
+
+
+FunctionSig* DecodeWasmSignatureForTesting(Zone* zone, const byte* start,
+ const byte* end) {
+ ModuleDecoder decoder(zone, start, end, false);
+ return decoder.DecodeFunctionSignature(start);
+}
+
+
+FunctionResult DecodeWasmFunction(Isolate* isolate, Zone* zone,
+ ModuleEnv* module_env,
+ const byte* function_start,
+ const byte* function_end) {
+ size_t size = function_end - function_start;
+ if (function_start > function_end) return FunctionError("start > end");
+ if (size > kMaxFunctionSize)
+ return FunctionError("size > maximum function size");
+ WasmFunction* function = new WasmFunction();
+ ModuleDecoder decoder(zone, function_start, function_end, false);
+ return decoder.DecodeSingleFunction(module_env, function);
+}
+} // namespace wasm
+} // namespace internal
+} // namespace v8
« no previous file with comments | « src/wasm/module-decoder.h ('k') | src/wasm/wasm-js.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698