| // 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_WASM_MODULE_BUILDER_H_ |
| #define V8_WASM_WASM_MODULE_BUILDER_H_ |
| |
| #include "src/signature.h" |
| #include "src/zone/zone-containers.h" |
| |
| #include "src/wasm/leb-helper.h" |
| #include "src/wasm/local-decl-encoder.h" |
| #include "src/wasm/wasm-opcodes.h" |
| #include "src/wasm/wasm-result.h" |
| |
| namespace v8 { |
| namespace internal { |
| namespace wasm { |
| |
| class ZoneBuffer : public ZoneObject { |
| public: |
| static constexpr size_t kInitialSize = 1024; |
| explicit ZoneBuffer(Zone* zone, size_t initial = kInitialSize) |
| : zone_(zone), buffer_(reinterpret_cast<byte*>(zone->New(initial))) { |
| pos_ = buffer_; |
| end_ = buffer_ + initial; |
| } |
| |
| void write_u8(uint8_t x) { |
| EnsureSpace(1); |
| *(pos_++) = x; |
| } |
| |
| void write_u16(uint16_t x) { |
| EnsureSpace(2); |
| WriteLittleEndianValue<uint16_t>(pos_, x); |
| pos_ += 2; |
| } |
| |
| void write_u32(uint32_t x) { |
| EnsureSpace(4); |
| WriteLittleEndianValue<uint32_t>(pos_, x); |
| pos_ += 4; |
| } |
| |
| void write_u64(uint64_t x) { |
| EnsureSpace(8); |
| WriteLittleEndianValue<uint64_t>(pos_, x); |
| pos_ += 8; |
| } |
| |
| void write_u32v(uint32_t val) { |
| EnsureSpace(kMaxVarInt32Size); |
| LEBHelper::write_u32v(&pos_, val); |
| } |
| |
| void write_i32v(int32_t val) { |
| EnsureSpace(kMaxVarInt32Size); |
| LEBHelper::write_i32v(&pos_, val); |
| } |
| |
| void write_u64v(uint64_t val) { |
| EnsureSpace(kMaxVarInt64Size); |
| LEBHelper::write_u64v(&pos_, val); |
| } |
| |
| void write_i64v(int64_t val) { |
| EnsureSpace(kMaxVarInt64Size); |
| LEBHelper::write_i64v(&pos_, val); |
| } |
| |
| void write_size(size_t val) { |
| EnsureSpace(kMaxVarInt32Size); |
| DCHECK_EQ(val, static_cast<uint32_t>(val)); |
| LEBHelper::write_u32v(&pos_, static_cast<uint32_t>(val)); |
| } |
| |
| void write_f32(float val) { write_u32(bit_cast<uint32_t>(val)); } |
| |
| void write_f64(double val) { write_u64(bit_cast<uint64_t>(val)); } |
| |
| void write(const byte* data, size_t size) { |
| EnsureSpace(size); |
| memcpy(pos_, data, size); |
| pos_ += size; |
| } |
| |
| void write_string(Vector<const char> name) { |
| write_size(name.length()); |
| write(reinterpret_cast<const byte*>(name.start()), name.length()); |
| } |
| |
| size_t reserve_u32v() { |
| size_t off = offset(); |
| EnsureSpace(kMaxVarInt32Size); |
| pos_ += kMaxVarInt32Size; |
| return off; |
| } |
| |
| // Patch a (padded) u32v at the given offset to be the given value. |
| void patch_u32v(size_t offset, uint32_t val) { |
| byte* ptr = buffer_ + offset; |
| for (size_t pos = 0; pos != kPaddedVarInt32Size; ++pos) { |
| uint32_t next = val >> 7; |
| byte out = static_cast<byte>(val & 0x7f); |
| if (pos != kPaddedVarInt32Size - 1) { |
| *(ptr++) = 0x80 | out; |
| val = next; |
| } else { |
| *(ptr++) = out; |
| } |
| } |
| } |
| |
| void patch_u8(size_t offset, byte val) { |
| DCHECK_GE(size(), offset); |
| buffer_[offset] = val; |
| } |
| |
| size_t offset() const { return static_cast<size_t>(pos_ - buffer_); } |
| size_t size() const { return static_cast<size_t>(pos_ - buffer_); } |
| const byte* begin() const { return buffer_; } |
| const byte* end() const { return pos_; } |
| |
| void EnsureSpace(size_t size) { |
| if ((pos_ + size) > end_) { |
| size_t new_size = size + (end_ - buffer_) * 2; |
| byte* new_buffer = reinterpret_cast<byte*>(zone_->New(new_size)); |
| memcpy(new_buffer, buffer_, (pos_ - buffer_)); |
| pos_ = new_buffer + (pos_ - buffer_); |
| buffer_ = new_buffer; |
| end_ = new_buffer + new_size; |
| } |
| DCHECK(pos_ + size <= end_); |
| } |
| |
| void Truncate(size_t size) { |
| DCHECK_GE(offset(), size); |
| pos_ = buffer_ + size; |
| } |
| |
| byte** pos_ptr() { return &pos_; } |
| |
| private: |
| Zone* zone_; |
| byte* buffer_; |
| byte* pos_; |
| byte* end_; |
| }; |
| |
| class WasmModuleBuilder; |
| |
| class V8_EXPORT_PRIVATE WasmFunctionBuilder : public ZoneObject { |
| public: |
| // Building methods. |
| void SetSignature(FunctionSig* sig); |
| uint32_t AddLocal(ValueType type); |
| void EmitI32V(int32_t val); |
| void EmitU32V(uint32_t val); |
| void EmitCode(const byte* code, uint32_t code_size); |
| void Emit(WasmOpcode opcode); |
| void EmitGetLocal(uint32_t index); |
| void EmitSetLocal(uint32_t index); |
| void EmitTeeLocal(uint32_t index); |
| void EmitI32Const(int32_t val); |
| void EmitI64Const(int64_t val); |
| void EmitF32Const(float val); |
| void EmitF64Const(double val); |
| void EmitWithU8(WasmOpcode opcode, const byte immediate); |
| void EmitWithU8U8(WasmOpcode opcode, const byte imm1, const byte imm2); |
| void EmitWithI32V(WasmOpcode opcode, int32_t immediate); |
| void EmitWithU32V(WasmOpcode opcode, uint32_t immediate); |
| void EmitDirectCallIndex(uint32_t index); |
| void SetName(Vector<const char> name); |
| void AddAsmWasmOffset(size_t call_position, size_t to_number_position); |
| void SetAsmFunctionStartPosition(size_t function_position); |
| |
| size_t GetPosition() const { return body_.size(); } |
| void FixupByte(size_t position, byte value) { |
| body_.patch_u8(position, value); |
| } |
| void DeleteCodeAfter(size_t position); |
| |
| void WriteSignature(ZoneBuffer& buffer) const; |
| void WriteBody(ZoneBuffer& buffer) const; |
| void WriteAsmWasmOffsetTable(ZoneBuffer& buffer) const; |
| |
| WasmModuleBuilder* builder() const { return builder_; } |
| uint32_t func_index() { return func_index_; } |
| FunctionSig* signature(); |
| |
| private: |
| explicit WasmFunctionBuilder(WasmModuleBuilder* builder); |
| friend class WasmModuleBuilder; |
| |
| struct DirectCallIndex { |
| size_t offset; |
| uint32_t direct_index; |
| }; |
| |
| WasmModuleBuilder* builder_; |
| LocalDeclEncoder locals_; |
| uint32_t signature_index_; |
| uint32_t func_index_; |
| ZoneBuffer body_; |
| Vector<const char> name_; |
| ZoneVector<uint32_t> i32_temps_; |
| ZoneVector<uint32_t> i64_temps_; |
| ZoneVector<uint32_t> f32_temps_; |
| ZoneVector<uint32_t> f64_temps_; |
| ZoneVector<DirectCallIndex> direct_calls_; |
| |
| // Delta-encoded mapping from wasm bytes to asm.js source positions. |
| ZoneBuffer asm_offsets_; |
| uint32_t last_asm_byte_offset_ = 0; |
| uint32_t last_asm_source_position_ = 0; |
| uint32_t asm_func_start_source_position_ = 0; |
| }; |
| |
| class V8_EXPORT_PRIVATE WasmModuleBuilder : public ZoneObject { |
| public: |
| explicit WasmModuleBuilder(Zone* zone); |
| |
| // Building methods. |
| uint32_t AddImport(Vector<const char> name, FunctionSig* sig); |
| WasmFunctionBuilder* AddFunction(FunctionSig* sig = nullptr); |
| uint32_t AddGlobal(ValueType type, bool exported, bool mutability = true, |
| const WasmInitExpr& init = WasmInitExpr()); |
| uint32_t AddGlobalImport(Vector<const char> name, ValueType type); |
| void AddDataSegment(const byte* data, uint32_t size, uint32_t dest); |
| uint32_t AddSignature(FunctionSig* sig); |
| uint32_t AllocateIndirectFunctions(uint32_t count); |
| void SetIndirectFunction(uint32_t indirect, uint32_t direct); |
| void MarkStartFunction(WasmFunctionBuilder* builder); |
| void AddExport(Vector<const char> name, WasmFunctionBuilder* builder); |
| void SetMinMemorySize(uint32_t value); |
| void SetMaxMemorySize(uint32_t value); |
| void SetHasSharedMemory(); |
| |
| // Writing methods. |
| void WriteTo(ZoneBuffer& buffer) const; |
| void WriteAsmJsOffsetTable(ZoneBuffer& buffer) const; |
| |
| // TODO(titzer): use SignatureMap from signature-map.h here. |
| // This signature map is zone-allocated, but the other is heap allocated. |
| struct CompareFunctionSigs { |
| bool operator()(FunctionSig* a, FunctionSig* b) const; |
| }; |
| typedef ZoneMap<FunctionSig*, uint32_t, CompareFunctionSigs> SignatureMap; |
| |
| Zone* zone() { return zone_; } |
| |
| FunctionSig* GetSignature(uint32_t index) { return signatures_[index]; } |
| |
| private: |
| struct WasmFunctionImport { |
| Vector<const char> name; |
| uint32_t sig_index; |
| }; |
| |
| struct WasmFunctionExport { |
| Vector<const char> name; |
| uint32_t function_index; |
| }; |
| |
| struct WasmGlobalImport { |
| Vector<const char> name; |
| ValueTypeCode type_code; |
| }; |
| |
| struct WasmGlobal { |
| ValueType type; |
| bool exported; |
| bool mutability; |
| WasmInitExpr init; |
| }; |
| |
| struct WasmDataSegment { |
| ZoneVector<byte> data; |
| uint32_t dest; |
| }; |
| |
| friend class WasmFunctionBuilder; |
| Zone* zone_; |
| ZoneVector<FunctionSig*> signatures_; |
| ZoneVector<WasmFunctionImport> function_imports_; |
| ZoneVector<WasmFunctionExport> function_exports_; |
| ZoneVector<WasmGlobalImport> global_imports_; |
| ZoneVector<WasmFunctionBuilder*> functions_; |
| ZoneVector<WasmDataSegment> data_segments_; |
| ZoneVector<uint32_t> indirect_functions_; |
| ZoneVector<WasmGlobal> globals_; |
| SignatureMap signature_map_; |
| int start_function_index_; |
| uint32_t min_memory_size_; |
| uint32_t max_memory_size_; |
| bool has_max_memory_size_; |
| bool has_shared_memory_; |
| }; |
| |
| inline FunctionSig* WasmFunctionBuilder::signature() { |
| return builder_->signatures_[signature_index_]; |
| } |
| |
| } // namespace wasm |
| } // namespace internal |
| } // namespace v8 |
| |
| #endif // V8_WASM_WASM_MODULE_BUILDER_H_ |