• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
24 namespace v8 {
25 namespace internal {
26 namespace wasm {
27 
28 class 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)
zone_(zone)35       : zone_(zone), buffer_(zone->NewArray<byte, Buffer>(initial)) {
36     pos_ = buffer_;
37     end_ = buffer_ + initial;
38   }
39 
write_u8(uint8_t x)40   void write_u8(uint8_t x) {
41     EnsureSpace(1);
42     *(pos_++) = x;
43   }
44 
write_u16(uint16_t x)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 
write_u32(uint32_t x)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 
write_u64(uint64_t x)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 
write_u32v(uint32_t val)63   void write_u32v(uint32_t val) {
64     EnsureSpace(kMaxVarInt32Size);
65     LEBHelper::write_u32v(&pos_, val);
66   }
67 
write_i32v(int32_t val)68   void write_i32v(int32_t val) {
69     EnsureSpace(kMaxVarInt32Size);
70     LEBHelper::write_i32v(&pos_, val);
71   }
72 
write_u64v(uint64_t val)73   void write_u64v(uint64_t val) {
74     EnsureSpace(kMaxVarInt64Size);
75     LEBHelper::write_u64v(&pos_, val);
76   }
77 
write_i64v(int64_t val)78   void write_i64v(int64_t val) {
79     EnsureSpace(kMaxVarInt64Size);
80     LEBHelper::write_i64v(&pos_, val);
81   }
82 
write_size(size_t val)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 
write_f32(float val)89   void write_f32(float val) { write_u32(bit_cast<uint32_t>(val)); }
90 
write_f64(double val)91   void write_f64(double val) { write_u64(bit_cast<uint64_t>(val)); }
92 
write(const byte * data,size_t size)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 
write_string(base::Vector<const char> name)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 
reserve_u32v()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.
patch_u32v(size_t offset,uint32_t val)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 
patch_u8(size_t offset,byte val)127   void patch_u8(size_t offset, byte val) {
128     DCHECK_GE(size(), offset);
129     buffer_[offset] = val;
130   }
131 
offset()132   size_t offset() const { return static_cast<size_t>(pos_ - buffer_); }
size()133   size_t size() const { return static_cast<size_t>(pos_ - buffer_); }
data()134   const byte* data() const { return buffer_; }
begin()135   const byte* begin() const { return buffer_; }
end()136   const byte* end() const { return pos_; }
137 
EnsureSpace(size_t size)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 
Truncate(size_t size)150   void Truncate(size_t size) {
151     DCHECK_GE(offset(), size);
152     pos_ = buffer_ + size;
153   }
154 
pos_ptr()155   byte** pos_ptr() { return &pos_; }
156 
157  private:
158   Zone* zone_;
159   byte* buffer_;
160   byte* pos_;
161   byte* end_;
162 };
163 
164 class WasmModuleBuilder;
165 
166 class 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 
GetPosition()199   size_t GetPosition() const { return body_.size(); }
FixupByte(size_t position,byte value)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 
builder()209   WasmModuleBuilder* builder() const { return builder_; }
func_index()210   uint32_t func_index() { return func_index_; }
sig_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 
244 class 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;
EntryEntry268       Entry(Kind kind, uint32_t index) : kind(kind), index(index) {}
EntryEntry269       Entry() : kind(kRefNullEntry), index(0) {}
270     };
271 
272     // Construct an active segment.
WasmElemSegment(Zone * zone,ValueType type,uint32_t table_index,WasmInitExpr offset)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.
WasmElemSegment(Zone * zone,ValueType type,bool declarative)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.
IsValidOffsetKind(WasmInitExpr::Operator kind)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);
AddExport(base::Vector<const char> name,WasmFunctionBuilder * builder)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 
StartRecursiveTypeGroup()362   void StartRecursiveTypeGroup() {
363     DCHECK_EQ(current_recursive_group_start_, -1);
364     current_recursive_group_start_ = static_cast<int>(types_.size());
365   }
366 
EndRecursiveTypeGroup()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 
zone()382   Zone* zone() { return zone_; }
383 
GetTableType(uint32_t index)384   ValueType GetTableType(uint32_t index) { return tables_[index].type; }
385 
IsSignature(uint32_t index)386   bool IsSignature(uint32_t index) {
387     return types_[index].kind == TypeDefinition::kFunction;
388   }
389 
GetSignature(uint32_t index)390   const FunctionSig* GetSignature(uint32_t index) {
391     DCHECK(types_[index].kind == TypeDefinition::kFunction);
392     return types_[index].function_sig;
393   }
394 
IsStructType(uint32_t index)395   bool IsStructType(uint32_t index) {
396     return types_[index].kind == TypeDefinition::kStruct;
397   }
GetStructType(uint32_t index)398   const StructType* GetStructType(uint32_t index) {
399     return types_[index].struct_type;
400   }
401 
IsArrayType(uint32_t index)402   bool IsArrayType(uint32_t index) {
403     return types_[index].kind == TypeDefinition::kArray;
404   }
GetArrayType(uint32_t index)405   const ArrayType* GetArrayType(uint32_t index) {
406     return types_[index].array_type;
407   }
408 
GetFunction(uint32_t index)409   WasmFunctionBuilder* GetFunction(uint32_t index) { return functions_[index]; }
NumExceptions()410   int NumExceptions() { return static_cast<int>(exceptions_.size()); }
411 
NumTypes()412   int NumTypes() { return static_cast<int>(types_.size()); }
413 
NumTables()414   int NumTables() { return static_cast<int>(tables_.size()); }
415 
NumFunctions()416   int NumFunctions() { return static_cast<int>(functions_.size()); }
417 
GetExceptionType(int index)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 
signature()488 const 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