// 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/signature.h" #include "src/handles.h" #include "src/v8.h" #include "src/zone-containers.h" #include "src/wasm/ast-decoder.h" #include "src/wasm/encoder.h" #include "src/wasm/wasm-module.h" #include "src/wasm/wasm-opcodes.h" #include "src/v8memory.h" namespace v8 { namespace internal { namespace wasm { /*TODO: add error cases for adding too many locals, too many functions and bad indices in body */ namespace { void EmitUint8(byte** b, uint8_t x) { Memory::uint8_at(*b) = x; *b += 1; } void EmitUint16(byte** b, uint16_t x) { Memory::uint16_at(*b) = x; *b += 2; } void EmitUint32(byte** b, uint32_t x) { Memory::uint32_at(*b) = x; *b += 4; } void EmitVarInt(byte** b, size_t val) { while (true) { size_t next = val >> 7; byte out = static_cast(val & 0x7f); if (next) { *((*b)++) = 0x80 | out; val = next; } else { *((*b)++) = out; break; } } } } // namespace struct WasmFunctionBuilder::Type { bool param_; LocalType type_; }; WasmFunctionBuilder::WasmFunctionBuilder(Zone* zone) : return_type_(kAstI32), locals_(zone), exported_(0), external_(0), body_(zone), local_indices_(zone), name_(zone) {} uint16_t WasmFunctionBuilder::AddParam(LocalType type) { return AddVar(type, true); } uint16_t WasmFunctionBuilder::AddLocal(LocalType type) { return AddVar(type, false); } uint16_t WasmFunctionBuilder::AddVar(LocalType type, bool param) { locals_.push_back({param, type}); return static_cast(locals_.size() - 1); } void WasmFunctionBuilder::ReturnType(LocalType type) { return_type_ = type; } void WasmFunctionBuilder::EmitCode(const byte* code, uint32_t code_size) { EmitCode(code, code_size, nullptr, 0); } void WasmFunctionBuilder::EmitCode(const byte* code, uint32_t code_size, const uint32_t* local_indices, uint32_t indices_size) { size_t size = body_.size(); for (size_t i = 0; i < code_size; i++) { body_.push_back(code[i]); } for (size_t i = 0; i < indices_size; i++) { local_indices_.push_back(local_indices[i] + static_cast(size)); } } void WasmFunctionBuilder::Emit(WasmOpcode opcode) { body_.push_back(static_cast(opcode)); } void WasmFunctionBuilder::EmitWithU8(WasmOpcode opcode, const byte immediate) { body_.push_back(static_cast(opcode)); body_.push_back(immediate); } void WasmFunctionBuilder::EmitWithLocal(WasmOpcode opcode) { body_.push_back(static_cast(opcode)); local_indices_.push_back(static_cast(body_.size()) - 1); } uint32_t WasmFunctionBuilder::EmitEditableImmediate(const byte immediate) { body_.push_back(immediate); return static_cast(body_.size()) - 1; } void WasmFunctionBuilder::EditImmediate(uint32_t offset, const byte immediate) { DCHECK(offset < body_.size()); body_[offset] = immediate; } void WasmFunctionBuilder::Exported(uint8_t flag) { exported_ = flag; } void WasmFunctionBuilder::External(uint8_t flag) { external_ = flag; } void WasmFunctionBuilder::SetName(const unsigned char* name, int name_length) { name_.clear(); if (name_length > 0) { for (int i = 0; i < name_length; i++) { name_.push_back(*(name + i)); } name_.push_back('\0'); } } WasmFunctionEncoder* WasmFunctionBuilder::Build(Zone* zone, WasmModuleBuilder* mb) const { WasmFunctionEncoder* e = new (zone) WasmFunctionEncoder(zone, return_type_, exported_, external_); uint16_t* var_index = zone->NewArray(locals_.size()); IndexVars(e, var_index); if (body_.size() > 0) { // TODO(titzer): iterate over local indexes, not the bytes. const byte* start = &body_[0]; const byte* end = start + body_.size(); size_t local_index = 0; for (size_t i = 0; i < body_.size();) { if (local_index < local_indices_.size() && i == local_indices_[local_index]) { int length = 0; uint32_t index; ReadUnsignedLEB128Operand(start + i, end, &length, &index); uint16_t new_index = var_index[index]; const std::vector& index_vec = UnsignedLEB128From(new_index); for (size_t j = 0; j < index_vec.size(); j++) { e->body_.push_back(index_vec.at(j)); } i += length; local_index++; } else { e->body_.push_back(*(start + i)); i++; } } } FunctionSig::Builder sig(zone, return_type_ == kAstStmt ? 0 : 1, e->params_.size()); if (return_type_ != kAstStmt) { sig.AddReturn(static_cast(return_type_)); } for (size_t i = 0; i < e->params_.size(); i++) { sig.AddParam(static_cast(e->params_[i])); } e->signature_index_ = mb->AddSignature(sig.Build()); e->name_.insert(e->name_.begin(), name_.begin(), name_.end()); return e; } void WasmFunctionBuilder::IndexVars(WasmFunctionEncoder* e, uint16_t* var_index) const { uint16_t param = 0; uint16_t int32 = 0; uint16_t int64 = 0; uint16_t float32 = 0; uint16_t float64 = 0; for (size_t i = 0; i < locals_.size(); i++) { if (locals_.at(i).param_) { param++; } else if (locals_.at(i).type_ == kAstI32) { int32++; } else if (locals_.at(i).type_ == kAstI64) { int64++; } else if (locals_.at(i).type_ == kAstF32) { float32++; } else if (locals_.at(i).type_ == kAstF64) { float64++; } } e->local_int32_count_ = int32; e->local_int64_count_ = int64; e->local_float32_count_ = float32; e->local_float64_count_ = float64; float64 = param + int32 + int64 + float32; float32 = param + int32 + int64; int64 = param + int32; int32 = param; param = 0; for (size_t i = 0; i < locals_.size(); i++) { if (locals_.at(i).param_) { e->params_.push_back(locals_.at(i).type_); var_index[i] = param++; } else if (locals_.at(i).type_ == kAstI32) { var_index[i] = int32++; } else if (locals_.at(i).type_ == kAstI64) { var_index[i] = int64++; } else if (locals_.at(i).type_ == kAstF32) { var_index[i] = float32++; } else if (locals_.at(i).type_ == kAstF64) { var_index[i] = float64++; } } } WasmFunctionEncoder::WasmFunctionEncoder(Zone* zone, LocalType return_type, bool exported, bool external) : params_(zone), exported_(exported), external_(external), body_(zone), name_(zone) {} uint32_t WasmFunctionEncoder::HeaderSize() const { uint32_t size = 3; if (HasLocals()) size += 8; if (!external_) size += 2; if (HasName()) size += 4; return size; } uint32_t WasmFunctionEncoder::BodySize(void) const { return external_ ? 0 : static_cast(body_.size()); } uint32_t WasmFunctionEncoder::NameSize() const { return exported_ ? static_cast(name_.size()) : 0; } void WasmFunctionEncoder::Serialize(byte* buffer, byte** header, byte** body) const { uint8_t decl_bits = (exported_ ? kDeclFunctionExport : 0) | (external_ ? kDeclFunctionImport : 0) | (HasLocals() ? kDeclFunctionLocals : 0) | (HasName() ? kDeclFunctionName : 0); EmitUint8(header, decl_bits); EmitUint16(header, signature_index_); if (HasName()) { uint32_t name_offset = static_cast(*body - buffer); EmitUint32(header, name_offset); std::memcpy(*body, &name_[0], name_.size()); (*body) += name_.size(); } if (HasLocals()) { EmitUint16(header, local_int32_count_); EmitUint16(header, local_int64_count_); EmitUint16(header, local_float32_count_); EmitUint16(header, local_float64_count_); } if (!external_) { EmitUint16(header, static_cast(body_.size())); if (body_.size() > 0) { std::memcpy(*header, &body_[0], body_.size()); (*header) += body_.size(); } } } WasmDataSegmentEncoder::WasmDataSegmentEncoder(Zone* zone, const byte* data, uint32_t size, uint32_t dest) : data_(zone), dest_(dest) { for (size_t i = 0; i < size; i++) { data_.push_back(data[i]); } } uint32_t WasmDataSegmentEncoder::HeaderSize() const { static const int kDataSegmentSize = 13; return kDataSegmentSize; } uint32_t WasmDataSegmentEncoder::BodySize() const { return static_cast(data_.size()); } void WasmDataSegmentEncoder::Serialize(byte* buffer, byte** header, byte** body) const { uint32_t body_offset = static_cast(*body - buffer); EmitUint32(header, dest_); EmitUint32(header, body_offset); EmitUint32(header, static_cast(data_.size())); EmitUint8(header, 1); // init std::memcpy(*body, &data_[0], data_.size()); (*body) += data_.size(); } WasmModuleBuilder::WasmModuleBuilder(Zone* zone) : zone_(zone), signatures_(zone), functions_(zone), data_segments_(zone), indirect_functions_(zone), globals_(zone), signature_map_(zone) {} uint16_t WasmModuleBuilder::AddFunction() { functions_.push_back(new (zone_) WasmFunctionBuilder(zone_)); return static_cast(functions_.size() - 1); } WasmFunctionBuilder* WasmModuleBuilder::FunctionAt(size_t index) { if (functions_.size() > index) { return functions_.at(index); } else { return nullptr; } } void WasmModuleBuilder::AddDataSegment(WasmDataSegmentEncoder* data) { data_segments_.push_back(data); } int WasmModuleBuilder::CompareFunctionSigs::operator()(FunctionSig* a, FunctionSig* b) const { if (a->return_count() < b->return_count()) return -1; if (a->return_count() > b->return_count()) return 1; if (a->parameter_count() < b->parameter_count()) return -1; if (a->parameter_count() > b->parameter_count()) return 1; for (size_t r = 0; r < a->return_count(); r++) { if (a->GetReturn(r) < b->GetReturn(r)) return -1; if (a->GetReturn(r) > b->GetReturn(r)) return 1; } for (size_t p = 0; p < a->parameter_count(); p++) { if (a->GetParam(p) < b->GetParam(p)) return -1; if (a->GetParam(p) > b->GetParam(p)) return 1; } return 0; } uint16_t WasmModuleBuilder::AddSignature(FunctionSig* sig) { SignatureMap::iterator pos = signature_map_.find(sig); if (pos != signature_map_.end()) { return pos->second; } else { uint16_t index = static_cast(signatures_.size()); signature_map_[sig] = index; signatures_.push_back(sig); return index; } } void WasmModuleBuilder::AddIndirectFunction(uint16_t index) { indirect_functions_.push_back(index); } WasmModuleWriter* WasmModuleBuilder::Build(Zone* zone) { WasmModuleWriter* writer = new (zone) WasmModuleWriter(zone); for (auto function : functions_) { writer->functions_.push_back(function->Build(zone, this)); } for (auto segment : data_segments_) { writer->data_segments_.push_back(segment); } for (auto sig : signatures_) { writer->signatures_.push_back(sig); } for (auto index : indirect_functions_) { writer->indirect_functions_.push_back(index); } for (auto global : globals_) { writer->globals_.push_back(global); } return writer; } uint32_t WasmModuleBuilder::AddGlobal(MachineType type, bool exported) { globals_.push_back(std::make_pair(type, exported)); return static_cast(globals_.size() - 1); } WasmModuleWriter::WasmModuleWriter(Zone* zone) : functions_(zone), data_segments_(zone), signatures_(zone), indirect_functions_(zone), globals_(zone) {} struct Sizes { size_t header_size; size_t body_size; size_t total() { return header_size + body_size; } void Add(size_t header, size_t body) { header_size += header; body_size += body; } void AddSection(size_t size) { if (size > 0) { Add(1, 0); while (size > 0) { Add(1, 0); size = size >> 7; } } } }; WasmModuleIndex* WasmModuleWriter::WriteTo(Zone* zone) const { Sizes sizes = {0, 0}; sizes.Add(1, 0); sizes.Add(kDeclMemorySize, 0); sizes.AddSection(signatures_.size()); for (auto sig : signatures_) { sizes.Add(2 + sig->parameter_count(), 0); } sizes.AddSection(globals_.size()); if (globals_.size() > 0) { sizes.Add(kDeclGlobalSize * globals_.size(), 0); } sizes.AddSection(functions_.size()); for (auto function : functions_) { sizes.Add(function->HeaderSize() + function->BodySize(), function->NameSize()); } sizes.AddSection(data_segments_.size()); for (auto segment : data_segments_) { sizes.Add(segment->HeaderSize(), segment->BodySize()); } sizes.AddSection(indirect_functions_.size()); sizes.Add(2 * static_cast(indirect_functions_.size()), 0); if (sizes.body_size > 0) sizes.Add(1, 0); ZoneVector buffer_vector(sizes.total(), zone); byte* buffer = &buffer_vector[0]; byte* header = buffer; byte* body = buffer + sizes.header_size; // -- emit memory declaration ------------------------------------------------ EmitUint8(&header, kDeclMemory); EmitUint8(&header, 16); // min memory size EmitUint8(&header, 16); // max memory size EmitUint8(&header, 0); // memory export // -- emit globals ----------------------------------------------------------- if (globals_.size() > 0) { EmitUint8(&header, kDeclGlobals); EmitVarInt(&header, globals_.size()); for (auto global : globals_) { EmitUint32(&header, 0); EmitUint8(&header, WasmOpcodes::MemTypeCodeFor(global.first)); EmitUint8(&header, global.second); } } // -- emit signatures -------------------------------------------------------- if (signatures_.size() > 0) { EmitUint8(&header, kDeclSignatures); EmitVarInt(&header, signatures_.size()); for (FunctionSig* sig : signatures_) { EmitUint8(&header, static_cast(sig->parameter_count())); if (sig->return_count() > 0) { EmitUint8(&header, WasmOpcodes::LocalTypeCodeFor(sig->GetReturn())); } else { EmitUint8(&header, kLocalVoid); } for (size_t j = 0; j < sig->parameter_count(); j++) { EmitUint8(&header, WasmOpcodes::LocalTypeCodeFor(sig->GetParam(j))); } } } // -- emit functions --------------------------------------------------------- if (functions_.size() > 0) { EmitUint8(&header, kDeclFunctions); EmitVarInt(&header, functions_.size()); for (auto func : functions_) { func->Serialize(buffer, &header, &body); } } // -- emit data segments ----------------------------------------------------- if (data_segments_.size() > 0) { EmitUint8(&header, kDeclDataSegments); EmitVarInt(&header, data_segments_.size()); for (auto segment : data_segments_) { segment->Serialize(buffer, &header, &body); } } // -- emit function table ---------------------------------------------------- if (indirect_functions_.size() > 0) { EmitUint8(&header, kDeclFunctionTable); EmitVarInt(&header, indirect_functions_.size()); for (auto index : indirect_functions_) { EmitUint16(&header, index); } } if (sizes.body_size > 0) EmitUint8(&header, kDeclEnd); return new (zone) WasmModuleIndex(buffer, buffer + sizes.total()); } std::vector UnsignedLEB128From(uint32_t result) { std::vector output; uint8_t next = 0; int shift = 0; do { next = static_cast(result >> shift); if (((result >> shift) & 0xFFFFFF80) != 0) { next = next | 0x80; } output.push_back(next); shift += 7; } while ((next & 0x80) != 0); return output; } } // namespace wasm } // namespace internal } // namespace v8