Index: src/wasm/encoder.cc |
diff --git a/src/wasm/encoder.cc b/src/wasm/encoder.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..cde09720062a0bfbf6880bb9fd0a40adeace5a49 |
--- /dev/null |
+++ b/src/wasm/encoder.cc |
@@ -0,0 +1,588 @@ |
+// 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<byte>(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, const unsigned char* name, |
+ int name_length) |
+ : return_type_(kAstI32), |
+ locals_(zone), |
+ exported_(0), |
+ external_(0), |
+ body_(zone), |
+ local_indices_(zone), |
+ name_(zone) { |
+ if (name_length > 0) { |
+ for (int i = 0; i < name_length; i++) { |
+ name_.push_back(*(name + i)); |
+ } |
+ name_.push_back('\0'); |
+ } |
+} |
+ |
+ |
+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<uint16_t>(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, NULL, 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<uint32_t>(size)); |
+ } |
+} |
+ |
+ |
+void WasmFunctionBuilder::Emit(WasmOpcode opcode) { |
+ body_.push_back(static_cast<byte>(opcode)); |
+} |
+ |
+ |
+void WasmFunctionBuilder::EmitWithU8(WasmOpcode opcode, const byte immediate) { |
+ body_.push_back(static_cast<byte>(opcode)); |
+ body_.push_back(immediate); |
+} |
+ |
+ |
+void WasmFunctionBuilder::EmitWithLocal(WasmOpcode opcode) { |
+ body_.push_back(static_cast<byte>(opcode)); |
+ local_indices_.push_back(static_cast<uint32_t>(body_.size()) - 1); |
+} |
+ |
+ |
+uint32_t WasmFunctionBuilder::EmitEditableImmediate(const byte immediate) { |
+ body_.push_back(immediate); |
+ return static_cast<uint32_t>(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; } |
+ |
+ |
+WasmFunctionEncoder* WasmFunctionBuilder::Build(Zone* zone, |
+ WasmModuleBuilder* mb) const { |
+ WasmFunctionEncoder* e = |
+ new (zone) WasmFunctionEncoder(zone, return_type_, exported_, external_); |
+ auto var_index = new uint16_t[locals_.size()]; |
+ IndexVars(e, var_index); |
+ const byte* start = body_.data(); |
+ 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<uint8_t>& 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<LocalType>(return_type_)); |
+ } |
+ for (size_t i = 0; i < e->params_.size(); i++) { |
+ sig.AddParam(static_cast<LocalType>(e->params_[i])); |
+ } |
+ e->signature_index_ = mb->AddSignature(sig.Build()); |
+ delete[] var_index; |
+ 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<uint32_t>(body_.size()); |
+} |
+ |
+ |
+uint32_t WasmFunctionEncoder::NameSize() const { |
+ return exported_ ? static_cast<uint32_t>(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<uint32_t>(*body - buffer); |
+ EmitUint32(header, name_offset); |
+ std::memcpy(*body, name_.data(), 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<uint16_t>(body_.size())); |
+ std::memcpy(*header, body_.data(), 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<uint32_t>(data_.size()); |
+} |
+ |
+ |
+void WasmDataSegmentEncoder::Serialize(byte* buffer, byte** header, |
+ byte** body) const { |
+ uint32_t body_offset = static_cast<uint32_t>(*body - buffer); |
+ EmitUint32(header, dest_); |
+ EmitUint32(header, body_offset); |
+ EmitUint32(header, static_cast<uint32_t>(data_.size())); |
+ EmitUint8(header, 1); // init |
+ |
+ std::memcpy(*body, data_.data(), 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(const unsigned char* name, |
+ int name_length) { |
+ functions_.push_back(new (zone_) |
+ WasmFunctionBuilder(zone_, name, name_length)); |
+ return static_cast<uint16_t>(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) { |
+ 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<uint16_t>(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<uint32_t>(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<uint32_t>(indirect_functions_.size()), 0); |
+ |
+ if (sizes.body_size > 0) sizes.Add(1, 0); |
+ |
+ ZoneVector<uint8_t> buffer_vector(sizes.total(), zone); |
+ byte* buffer = buffer_vector.data(); |
+ 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<byte>(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<uint8_t> UnsignedLEB128From(uint32_t result) { |
+ std::vector<uint8_t> output; |
+ uint8_t next = 0; |
+ int shift = 0; |
+ do { |
+ next = static_cast<uint8_t>(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 |