1// Copyright 2015 the V8 project authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#if !V8_ENABLE_WEBASSEMBLY
6#error This header should only be included if WebAssembly is enabled.
7#endif  // !V8_ENABLE_WEBASSEMBLY
8
9#ifndef V8_WASM_WASM_MODULE_BUILDER_H_
10#define V8_WASM_WASM_MODULE_BUILDER_H_
11
12#include "src/base/memory.h"
13#include "src/base/platform/wrappers.h"
14#include "src/base/vector.h"
15#include "src/codegen/signature.h"
16#include "src/wasm/leb-helper.h"
17#include "src/wasm/local-decl-encoder.h"
18#include "src/wasm/value-type.h"
19#include "src/wasm/wasm-module.h"
20#include "src/wasm/wasm-opcodes.h"
21#include "src/wasm/wasm-result.h"
22#include "src/zone/zone-containers.h"
23
24namespace v8 {
25namespace internal {
26namespace wasm {
27
28class ZoneBuffer : public ZoneObject {
29 public:
30  // This struct is just a type tag for Zone::NewArray<T>(size_t) call.
31  struct Buffer {};
32
33  static constexpr size_t kInitialSize = 1024;
34  explicit ZoneBuffer(Zone* zone, size_t initial = kInitialSize)
35      : zone_(zone), buffer_(zone->NewArray<byte, Buffer>(initial)) {
36    pos_ = buffer_;
37    end_ = buffer_ + initial;
38  }
39
40  void write_u8(uint8_t x) {
41    EnsureSpace(1);
42    *(pos_++) = x;
43  }
44
45  void write_u16(uint16_t x) {
46    EnsureSpace(2);
47    base::WriteLittleEndianValue<uint16_t>(reinterpret_cast<Address>(pos_), x);
48    pos_ += 2;
49  }
50
51  void write_u32(uint32_t x) {
52    EnsureSpace(4);
53    base::WriteLittleEndianValue<uint32_t>(reinterpret_cast<Address>(pos_), x);
54    pos_ += 4;
55  }
56
57  void write_u64(uint64_t x) {
58    EnsureSpace(8);
59    base::WriteLittleEndianValue<uint64_t>(reinterpret_cast<Address>(pos_), x);
60    pos_ += 8;
61  }
62
63  void write_u32v(uint32_t val) {
64    EnsureSpace(kMaxVarInt32Size);
65    LEBHelper::write_u32v(&pos_, val);
66  }
67
68  void write_i32v(int32_t val) {
69    EnsureSpace(kMaxVarInt32Size);
70    LEBHelper::write_i32v(&pos_, val);
71  }
72
73  void write_u64v(uint64_t val) {
74    EnsureSpace(kMaxVarInt64Size);
75    LEBHelper::write_u64v(&pos_, val);
76  }
77
78  void write_i64v(int64_t val) {
79    EnsureSpace(kMaxVarInt64Size);
80    LEBHelper::write_i64v(&pos_, val);
81  }
82
83  void write_size(size_t val) {
84    EnsureSpace(kMaxVarInt32Size);
85    DCHECK_EQ(val, static_cast<uint32_t>(val));
86    LEBHelper::write_u32v(&pos_, static_cast<uint32_t>(val));
87  }
88
89  void write_f32(float val) { write_u32(bit_cast<uint32_t>(val)); }
90
91  void write_f64(double val) { write_u64(bit_cast<uint64_t>(val)); }
92
93  void write(const byte* data, size_t size) {
94    if (size == 0) return;
95    EnsureSpace(size);
96    memcpy(pos_, data, size);
97    pos_ += size;
98  }
99
100  void write_string(base::Vector<const char> name) {
101    write_size(name.length());
102    write(reinterpret_cast<const byte*>(name.begin()), name.length());
103  }
104
105  size_t reserve_u32v() {
106    size_t off = offset();
107    EnsureSpace(kMaxVarInt32Size);
108    pos_ += kMaxVarInt32Size;
109    return off;
110  }
111
112  // Patch a (padded) u32v at the given offset to be the given value.
113  void patch_u32v(size_t offset, uint32_t val) {
114    byte* ptr = buffer_ + offset;
115    for (size_t pos = 0; pos != kPaddedVarInt32Size; ++pos) {
116      uint32_t next = val >> 7;
117      byte out = static_cast<byte>(val & 0x7f);
118      if (pos != kPaddedVarInt32Size - 1) {
119        *(ptr++) = 0x80 | out;
120        val = next;
121      } else {
122        *(ptr++) = out;
123      }
124    }
125  }
126
127  void patch_u8(size_t offset, byte val) {
128    DCHECK_GE(size(), offset);
129    buffer_[offset] = val;
130  }
131
132  size_t offset() const { return static_cast<size_t>(pos_ - buffer_); }
133  size_t size() const { return static_cast<size_t>(pos_ - buffer_); }
134  const byte* data() const { return buffer_; }
135  const byte* begin() const { return buffer_; }
136  const byte* end() const { return pos_; }
137
138  void EnsureSpace(size_t size) {
139    if ((pos_ + size) > end_) {
140      size_t new_size = size + (end_ - buffer_) * 2;
141      byte* new_buffer = zone_->NewArray<byte, Buffer>(new_size);
142      memcpy(new_buffer, buffer_, (pos_ - buffer_));
143      pos_ = new_buffer + (pos_ - buffer_);
144      buffer_ = new_buffer;
145      end_ = new_buffer + new_size;
146    }
147    DCHECK(pos_ + size <= end_);
148  }
149
150  void Truncate(size_t size) {
151    DCHECK_GE(offset(), size);
152    pos_ = buffer_ + size;
153  }
154
155  byte** pos_ptr() { return &pos_; }
156
157 private:
158  Zone* zone_;
159  byte* buffer_;
160  byte* pos_;
161  byte* end_;
162};
163
164class WasmModuleBuilder;
165
166class V8_EXPORT_PRIVATE WasmFunctionBuilder : public ZoneObject {
167 public:
168  // Building methods.
169  void SetSignature(const FunctionSig* sig);
170  void SetSignature(uint32_t sig_index);
171  uint32_t AddLocal(ValueType type);
172  void EmitByte(byte b);
173  void EmitI32V(int32_t val);
174  void EmitU32V(uint32_t val);
175  void EmitCode(const byte* code, uint32_t code_size);
176  void Emit(WasmOpcode opcode);
177  void EmitWithPrefix(WasmOpcode opcode);
178  void EmitGetLocal(uint32_t index);
179  void EmitSetLocal(uint32_t index);
180  void EmitTeeLocal(uint32_t index);
181  void EmitI32Const(int32_t val);
182  void EmitI64Const(int64_t val);
183  void EmitF32Const(float val);
184  void EmitF64Const(double val);
185  void EmitS128Const(Simd128 val);
186  void EmitWithU8(WasmOpcode opcode, const byte immediate);
187  void EmitWithU8U8(WasmOpcode opcode, const byte imm1, const byte imm2);
188  void EmitWithI32V(WasmOpcode opcode, int32_t immediate);
189  void EmitWithU32V(WasmOpcode opcode, uint32_t immediate);
190  void EmitValueType(ValueType type);
191  void EmitDirectCallIndex(uint32_t index);
192  void SetName(base::Vector<const char> name);
193  void AddAsmWasmOffset(size_t call_position, size_t to_number_position);
194  void SetAsmFunctionStartPosition(size_t function_position);
195  void SetCompilationHint(WasmCompilationHintStrategy strategy,
196                          WasmCompilationHintTier baseline,
197                          WasmCompilationHintTier top_tier);
198
199  size_t GetPosition() const { return body_.size(); }
200  void FixupByte(size_t position, byte value) {
201    body_.patch_u8(position, value);
202  }
203  void DeleteCodeAfter(size_t position);
204
205  void WriteSignature(ZoneBuffer* buffer) const;
206  void WriteBody(ZoneBuffer* buffer) const;
207  void WriteAsmWasmOffsetTable(ZoneBuffer* buffer) const;
208
209  WasmModuleBuilder* builder() const { return builder_; }
210  uint32_t func_index() { return func_index_; }
211  uint32_t sig_index() { return signature_index_; }
212  inline const FunctionSig* signature();
213
214 private:
215  explicit WasmFunctionBuilder(WasmModuleBuilder* builder);
216  friend class WasmModuleBuilder;
217  friend Zone;
218
219  struct DirectCallIndex {
220    size_t offset;
221    uint32_t direct_index;
222  };
223
224  WasmModuleBuilder* builder_;
225  LocalDeclEncoder locals_;
226  uint32_t signature_index_;
227  uint32_t func_index_;
228  ZoneBuffer body_;
229  base::Vector<const char> name_;
230  ZoneVector<uint32_t> i32_temps_;
231  ZoneVector<uint32_t> i64_temps_;
232  ZoneVector<uint32_t> f32_temps_;
233  ZoneVector<uint32_t> f64_temps_;
234  ZoneVector<DirectCallIndex> direct_calls_;
235
236  // Delta-encoded mapping from wasm bytes to asm.js source positions.
237  ZoneBuffer asm_offsets_;
238  uint32_t last_asm_byte_offset_ = 0;
239  uint32_t last_asm_source_position_ = 0;
240  uint32_t asm_func_start_source_position_ = 0;
241  uint8_t hint_ = kNoCompilationHint;
242};
243
244class V8_EXPORT_PRIVATE WasmModuleBuilder : public ZoneObject {
245 public:
246  explicit WasmModuleBuilder(Zone* zone);
247  WasmModuleBuilder(const WasmModuleBuilder&) = delete;
248  WasmModuleBuilder& operator=(const WasmModuleBuilder&) = delete;
249
250  // Static representation of wasm element segment (table initializer). This is
251  // different than the version in wasm-module.h.
252  class WasmElemSegment {
253   public:
254    // asm.js gives function indices starting with the first non-imported
255    // function.
256    enum FunctionIndexingMode {
257      kRelativeToImports,
258      kRelativeToDeclaredFunctions
259    };
260    enum Status {
261      kStatusActive,      // copied automatically during instantiation.
262      kStatusPassive,     // copied explicitly after instantiation.
263      kStatusDeclarative  // purely declarative and never copied.
264    };
265    struct Entry {
266      enum Kind { kGlobalGetEntry, kRefFuncEntry, kRefNullEntry } kind;
267      uint32_t index;
268      Entry(Kind kind, uint32_t index) : kind(kind), index(index) {}
269      Entry() : kind(kRefNullEntry), index(0) {}
270    };
271
272    // Construct an active segment.
273    WasmElemSegment(Zone* zone, ValueType type, uint32_t table_index,
274                    WasmInitExpr offset)
275        : type(type),
276          table_index(table_index),
277          offset(offset),
278          entries(zone),
279          status(kStatusActive) {
280      DCHECK(IsValidOffsetKind(offset.kind()));
281    }
282
283    // Construct a passive or declarative segment, which has no table
284    // index or offset.
285    WasmElemSegment(Zone* zone, ValueType type, bool declarative)
286        : type(type),
287          table_index(0),
288          entries(zone),
289          status(declarative ? kStatusDeclarative : kStatusPassive) {
290      DCHECK(IsValidOffsetKind(offset.kind()));
291    }
292
293    MOVE_ONLY_NO_DEFAULT_CONSTRUCTOR(WasmElemSegment);
294
295    ValueType type;
296    uint32_t table_index;
297    WasmInitExpr offset;
298    FunctionIndexingMode indexing_mode = kRelativeToImports;
299    ZoneVector<Entry> entries;
300    Status status;
301
302   private:
303    // This ensures no {WasmInitExpr} with subexpressions is used, which would
304    // cause a memory leak because those are stored in an std::vector. Such
305    // offset would also be mistyped.
306    bool IsValidOffsetKind(WasmInitExpr::Operator kind) {
307      return kind == WasmInitExpr::kI32Const ||
308             kind == WasmInitExpr::kGlobalGet;
309    }
310  };
311
312  // Building methods.
313  uint32_t AddImport(base::Vector<const char> name, FunctionSig* sig,
314                     base::Vector<const char> module = {});
315  WasmFunctionBuilder* AddFunction(const FunctionSig* sig = nullptr);
316  WasmFunctionBuilder* AddFunction(uint32_t sig_index);
317  uint32_t AddGlobal(ValueType type, bool mutability = true,
318                     WasmInitExpr init = WasmInitExpr());
319  uint32_t AddGlobalImport(base::Vector<const char> name, ValueType type,
320                           bool mutability,
321                           base::Vector<const char> module = {});
322  void AddDataSegment(const byte* data, uint32_t size, uint32_t dest);
323  // Add an element segment to this {WasmModuleBuilder}. {segment}'s enties
324  // have to be initialized.
325  void AddElementSegment(WasmElemSegment segment);
326  // Helper method to create an active segment with one function. Assumes that
327  // table segment at {table_index} is typed as funcref.
328  void SetIndirectFunction(uint32_t table_index, uint32_t index_in_table,
329                           uint32_t direct_function_index,
330                           WasmElemSegment::FunctionIndexingMode indexing_mode);
331  // Increase the starting size of the table at {table_index} by {count}. Also
332  // increases the maximum table size if needed. Returns the former starting
333  // size, or the maximum uint32_t value if the maximum table size has been
334  // exceeded.
335  uint32_t IncreaseTableMinSize(uint32_t table_index, uint32_t count);
336  // Adds the signature to the module if it does not already exist.
337  uint32_t AddSignature(const FunctionSig* sig,
338                        uint32_t supertype = kNoSuperType);
339  // Does not deduplicate function signatures.
340  uint32_t ForceAddSignature(const FunctionSig* sig,
341                             uint32_t supertype = kNoSuperType);
342  uint32_t AddException(const FunctionSig* type);
343  uint32_t AddStructType(StructType* type, uint32_t supertype = kNoSuperType);
344  uint32_t AddArrayType(ArrayType* type, uint32_t supertype = kNoSuperType);
345  uint32_t AddTable(ValueType type, uint32_t min_size);
346  uint32_t AddTable(ValueType type, uint32_t min_size, uint32_t max_size);
347  uint32_t AddTable(ValueType type, uint32_t min_size, uint32_t max_size,
348                    WasmInitExpr init);
349  void MarkStartFunction(WasmFunctionBuilder* builder);
350  void AddExport(base::Vector<const char> name, ImportExportKindCode kind,
351                 uint32_t index);
352  void AddExport(base::Vector<const char> name, WasmFunctionBuilder* builder) {
353    AddExport(name, kExternalFunction, builder->func_index());
354  }
355  uint32_t AddExportedGlobal(ValueType type, bool mutability, WasmInitExpr init,
356                             base::Vector<const char> name);
357  void ExportImportedFunction(base::Vector<const char> name, int import_index);
358  void SetMinMemorySize(uint32_t value);
359  void SetMaxMemorySize(uint32_t value);
360  void SetHasSharedMemory();
361
362  void StartRecursiveTypeGroup() {
363    DCHECK_EQ(current_recursive_group_start_, -1);
364    current_recursive_group_start_ = static_cast<int>(types_.size());
365  }
366
367  void EndRecursiveTypeGroup() {
368    // Make sure we are in a recursive group.
369    DCHECK_NE(current_recursive_group_start_, -1);
370    // Make sure the current recursive group has at least one element.
371    DCHECK_GT(static_cast<int>(types_.size()), current_recursive_group_start_);
372    recursive_groups_.emplace(
373        current_recursive_group_start_,
374        static_cast<uint32_t>(types_.size()) - current_recursive_group_start_);
375    current_recursive_group_start_ = -1;
376  }
377
378  // Writing methods.
379  void WriteTo(ZoneBuffer* buffer) const;
380  void WriteAsmJsOffsetTable(ZoneBuffer* buffer) const;
381
382  Zone* zone() { return zone_; }
383
384  ValueType GetTableType(uint32_t index) { return tables_[index].type; }
385
386  bool IsSignature(uint32_t index) {
387    return types_[index].kind == TypeDefinition::kFunction;
388  }
389
390  const FunctionSig* GetSignature(uint32_t index) {
391    DCHECK(types_[index].kind == TypeDefinition::kFunction);
392    return types_[index].function_sig;
393  }
394
395  bool IsStructType(uint32_t index) {
396    return types_[index].kind == TypeDefinition::kStruct;
397  }
398  const StructType* GetStructType(uint32_t index) {
399    return types_[index].struct_type;
400  }
401
402  bool IsArrayType(uint32_t index) {
403    return types_[index].kind == TypeDefinition::kArray;
404  }
405  const ArrayType* GetArrayType(uint32_t index) {
406    return types_[index].array_type;
407  }
408
409  WasmFunctionBuilder* GetFunction(uint32_t index) { return functions_[index]; }
410  int NumExceptions() { return static_cast<int>(exceptions_.size()); }
411
412  int NumTypes() { return static_cast<int>(types_.size()); }
413
414  int NumTables() { return static_cast<int>(tables_.size()); }
415
416  int NumFunctions() { return static_cast<int>(functions_.size()); }
417
418  const FunctionSig* GetExceptionType(int index) {
419    return types_[exceptions_[index]].function_sig;
420  }
421
422 private:
423  struct WasmFunctionImport {
424    base::Vector<const char> module;
425    base::Vector<const char> name;
426    uint32_t sig_index;
427  };
428
429  struct WasmGlobalImport {
430    base::Vector<const char> module;
431    base::Vector<const char> name;
432    ValueTypeCode type_code;
433    bool mutability;
434  };
435
436  struct WasmExport {
437    base::Vector<const char> name;
438    ImportExportKindCode kind;
439    int index;  // Can be negative for re-exported imports.
440  };
441
442  struct WasmGlobal {
443    ValueType type;
444    bool mutability;
445    WasmInitExpr init;
446  };
447
448  struct WasmTable {
449    ValueType type;
450    uint32_t min_size;
451    uint32_t max_size;
452    bool has_maximum;
453    WasmInitExpr init;
454  };
455
456  struct WasmDataSegment {
457    ZoneVector<byte> data;
458    uint32_t dest;
459  };
460
461  friend class WasmFunctionBuilder;
462  Zone* zone_;
463  ZoneVector<TypeDefinition> types_;
464  ZoneVector<WasmFunctionImport> function_imports_;
465  ZoneVector<WasmGlobalImport> global_imports_;
466  ZoneVector<WasmExport> exports_;
467  ZoneVector<WasmFunctionBuilder*> functions_;
468  ZoneVector<WasmTable> tables_;
469  ZoneVector<WasmDataSegment> data_segments_;
470  ZoneVector<WasmElemSegment> element_segments_;
471  ZoneVector<WasmGlobal> globals_;
472  ZoneVector<int> exceptions_;
473  ZoneUnorderedMap<FunctionSig, uint32_t> signature_map_;
474  int current_recursive_group_start_;
475  // first index -> size
476  ZoneUnorderedMap<uint32_t, uint32_t> recursive_groups_;
477  int start_function_index_;
478  uint32_t min_memory_size_;
479  uint32_t max_memory_size_;
480  bool has_max_memory_size_;
481  bool has_shared_memory_;
482#if DEBUG
483  // Once AddExportedImport is called, no more imports can be added.
484  bool adding_imports_allowed_ = true;
485#endif
486};
487
488const FunctionSig* WasmFunctionBuilder::signature() {
489  return builder_->types_[signature_index_].function_sig;
490}
491
492}  // namespace wasm
493}  // namespace internal
494}  // namespace v8
495
496#endif  // V8_WASM_WASM_MODULE_BUILDER_H_
497