• 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 #ifndef V8_WASM_WASM_MODULE_BUILDER_H_
6 #define V8_WASM_WASM_MODULE_BUILDER_H_
7 
8 #include "src/base/memory.h"
9 #include "src/codegen/signature.h"
10 #include "src/utils/vector.h"
11 #include "src/wasm/leb-helper.h"
12 #include "src/wasm/local-decl-encoder.h"
13 #include "src/wasm/value-type.h"
14 #include "src/wasm/wasm-module.h"
15 #include "src/wasm/wasm-opcodes.h"
16 #include "src/wasm/wasm-result.h"
17 #include "src/zone/zone-containers.h"
18 
19 namespace v8 {
20 namespace internal {
21 namespace wasm {
22 
23 class ZoneBuffer : public ZoneObject {
24  public:
25   // This struct is just a type tag for Zone::NewArray<T>(size_t) call.
26   struct Buffer {};
27 
28   static constexpr size_t kInitialSize = 1024;
29   explicit ZoneBuffer(Zone* zone, size_t initial = kInitialSize)
zone_(zone)30       : zone_(zone), buffer_(zone->NewArray<byte, Buffer>(initial)) {
31     pos_ = buffer_;
32     end_ = buffer_ + initial;
33   }
34 
write_u8(uint8_t x)35   void write_u8(uint8_t x) {
36     EnsureSpace(1);
37     *(pos_++) = x;
38   }
39 
write_u16(uint16_t x)40   void write_u16(uint16_t x) {
41     EnsureSpace(2);
42     base::WriteLittleEndianValue<uint16_t>(reinterpret_cast<Address>(pos_), x);
43     pos_ += 2;
44   }
45 
write_u32(uint32_t x)46   void write_u32(uint32_t x) {
47     EnsureSpace(4);
48     base::WriteLittleEndianValue<uint32_t>(reinterpret_cast<Address>(pos_), x);
49     pos_ += 4;
50   }
51 
write_u64(uint64_t x)52   void write_u64(uint64_t x) {
53     EnsureSpace(8);
54     base::WriteLittleEndianValue<uint64_t>(reinterpret_cast<Address>(pos_), x);
55     pos_ += 8;
56   }
57 
write_u32v(uint32_t val)58   void write_u32v(uint32_t val) {
59     EnsureSpace(kMaxVarInt32Size);
60     LEBHelper::write_u32v(&pos_, val);
61   }
62 
write_i32v(int32_t val)63   void write_i32v(int32_t val) {
64     EnsureSpace(kMaxVarInt32Size);
65     LEBHelper::write_i32v(&pos_, val);
66   }
67 
write_u64v(uint64_t val)68   void write_u64v(uint64_t val) {
69     EnsureSpace(kMaxVarInt64Size);
70     LEBHelper::write_u64v(&pos_, val);
71   }
72 
write_i64v(int64_t val)73   void write_i64v(int64_t val) {
74     EnsureSpace(kMaxVarInt64Size);
75     LEBHelper::write_i64v(&pos_, val);
76   }
77 
write_size(size_t val)78   void write_size(size_t val) {
79     EnsureSpace(kMaxVarInt32Size);
80     DCHECK_EQ(val, static_cast<uint32_t>(val));
81     LEBHelper::write_u32v(&pos_, static_cast<uint32_t>(val));
82   }
83 
write_f32(float val)84   void write_f32(float val) { write_u32(bit_cast<uint32_t>(val)); }
85 
write_f64(double val)86   void write_f64(double val) { write_u64(bit_cast<uint64_t>(val)); }
87 
write(const byte * data,size_t size)88   void write(const byte* data, size_t size) {
89     if (size == 0) return;
90     EnsureSpace(size);
91     memcpy(pos_, data, size);
92     pos_ += size;
93   }
94 
write_string(Vector<const char> name)95   void write_string(Vector<const char> name) {
96     write_size(name.length());
97     write(reinterpret_cast<const byte*>(name.begin()), name.length());
98   }
99 
reserve_u32v()100   size_t reserve_u32v() {
101     size_t off = offset();
102     EnsureSpace(kMaxVarInt32Size);
103     pos_ += kMaxVarInt32Size;
104     return off;
105   }
106 
107   // Patch a (padded) u32v at the given offset to be the given value.
patch_u32v(size_t offset,uint32_t val)108   void patch_u32v(size_t offset, uint32_t val) {
109     byte* ptr = buffer_ + offset;
110     for (size_t pos = 0; pos != kPaddedVarInt32Size; ++pos) {
111       uint32_t next = val >> 7;
112       byte out = static_cast<byte>(val & 0x7f);
113       if (pos != kPaddedVarInt32Size - 1) {
114         *(ptr++) = 0x80 | out;
115         val = next;
116       } else {
117         *(ptr++) = out;
118       }
119     }
120   }
121 
patch_u8(size_t offset,byte val)122   void patch_u8(size_t offset, byte val) {
123     DCHECK_GE(size(), offset);
124     buffer_[offset] = val;
125   }
126 
offset()127   size_t offset() const { return static_cast<size_t>(pos_ - buffer_); }
size()128   size_t size() const { return static_cast<size_t>(pos_ - buffer_); }
data()129   const byte* data() const { return buffer_; }
begin()130   const byte* begin() const { return buffer_; }
end()131   const byte* end() const { return pos_; }
132 
EnsureSpace(size_t size)133   void EnsureSpace(size_t size) {
134     if ((pos_ + size) > end_) {
135       size_t new_size = size + (end_ - buffer_) * 2;
136       byte* new_buffer = zone_->NewArray<byte, Buffer>(new_size);
137       memcpy(new_buffer, buffer_, (pos_ - buffer_));
138       pos_ = new_buffer + (pos_ - buffer_);
139       buffer_ = new_buffer;
140       end_ = new_buffer + new_size;
141     }
142     DCHECK(pos_ + size <= end_);
143   }
144 
Truncate(size_t size)145   void Truncate(size_t size) {
146     DCHECK_GE(offset(), size);
147     pos_ = buffer_ + size;
148   }
149 
pos_ptr()150   byte** pos_ptr() { return &pos_; }
151 
152  private:
153   Zone* zone_;
154   byte* buffer_;
155   byte* pos_;
156   byte* end_;
157 };
158 
159 class WasmModuleBuilder;
160 
161 class V8_EXPORT_PRIVATE WasmFunctionBuilder : public ZoneObject {
162  public:
163   // Building methods.
164   void SetSignature(FunctionSig* sig);
165   uint32_t AddLocal(ValueType type);
166   void EmitByte(byte b);
167   void EmitI32V(int32_t val);
168   void EmitU32V(uint32_t val);
169   void EmitCode(const byte* code, uint32_t code_size);
170   void Emit(WasmOpcode opcode);
171   void EmitWithPrefix(WasmOpcode opcode);
172   void EmitGetLocal(uint32_t index);
173   void EmitSetLocal(uint32_t index);
174   void EmitTeeLocal(uint32_t index);
175   void EmitI32Const(int32_t val);
176   void EmitI64Const(int64_t val);
177   void EmitF32Const(float val);
178   void EmitF64Const(double val);
179   void EmitS128Const(Simd128 val);
180   void EmitWithU8(WasmOpcode opcode, const byte immediate);
181   void EmitWithU8U8(WasmOpcode opcode, const byte imm1, const byte imm2);
182   void EmitWithI32V(WasmOpcode opcode, int32_t immediate);
183   void EmitWithU32V(WasmOpcode opcode, uint32_t immediate);
184   void EmitDirectCallIndex(uint32_t index);
185   void SetName(Vector<const char> name);
186   void AddAsmWasmOffset(size_t call_position, size_t to_number_position);
187   void SetAsmFunctionStartPosition(size_t function_position);
188   void SetCompilationHint(WasmCompilationHintStrategy strategy,
189                           WasmCompilationHintTier baseline,
190                           WasmCompilationHintTier top_tier);
191 
GetPosition()192   size_t GetPosition() const { return body_.size(); }
FixupByte(size_t position,byte value)193   void FixupByte(size_t position, byte value) {
194     body_.patch_u8(position, value);
195   }
196   void DeleteCodeAfter(size_t position);
197 
198   void WriteSignature(ZoneBuffer* buffer) const;
199   void WriteBody(ZoneBuffer* buffer) const;
200   void WriteAsmWasmOffsetTable(ZoneBuffer* buffer) const;
201 
builder()202   WasmModuleBuilder* builder() const { return builder_; }
func_index()203   uint32_t func_index() { return func_index_; }
204   FunctionSig* signature();
205 
206  private:
207   explicit WasmFunctionBuilder(WasmModuleBuilder* builder);
208   friend class WasmModuleBuilder;
209   friend Zone;
210 
211   struct DirectCallIndex {
212     size_t offset;
213     uint32_t direct_index;
214   };
215 
216   WasmModuleBuilder* builder_;
217   LocalDeclEncoder locals_;
218   uint32_t signature_index_;
219   uint32_t func_index_;
220   ZoneBuffer body_;
221   Vector<const char> name_;
222   ZoneVector<uint32_t> i32_temps_;
223   ZoneVector<uint32_t> i64_temps_;
224   ZoneVector<uint32_t> f32_temps_;
225   ZoneVector<uint32_t> f64_temps_;
226   ZoneVector<DirectCallIndex> direct_calls_;
227 
228   // Delta-encoded mapping from wasm bytes to asm.js source positions.
229   ZoneBuffer asm_offsets_;
230   uint32_t last_asm_byte_offset_ = 0;
231   uint32_t last_asm_source_position_ = 0;
232   uint32_t asm_func_start_source_position_ = 0;
233   uint8_t hint_ = kNoCompilationHint;
234 };
235 
236 class V8_EXPORT_PRIVATE WasmModuleBuilder : public ZoneObject {
237  public:
238   explicit WasmModuleBuilder(Zone* zone);
239   WasmModuleBuilder(const WasmModuleBuilder&) = delete;
240   WasmModuleBuilder& operator=(const WasmModuleBuilder&) = delete;
241 
242   // Building methods.
243   uint32_t AddImport(Vector<const char> name, FunctionSig* sig,
244                      Vector<const char> module = {});
245   WasmFunctionBuilder* AddFunction(FunctionSig* sig = nullptr);
246   uint32_t AddGlobal(ValueType type, bool mutability = true,
247                      WasmInitExpr init = WasmInitExpr());
248   uint32_t AddGlobalImport(Vector<const char> name, ValueType type,
249                            bool mutability, Vector<const char> module = {});
250   void AddDataSegment(const byte* data, uint32_t size, uint32_t dest);
251   uint32_t AddSignature(FunctionSig* sig);
252   uint32_t AddStructType(StructType* type);
253   uint32_t AddArrayType(ArrayType* type);
254   // In the current implementation, it's supported to have uninitialized slots
255   // at the beginning and/or end of the indirect function table, as long as
256   // the filled slots form a contiguous block in the middle.
257   uint32_t AllocateIndirectFunctions(uint32_t count);
258   void SetIndirectFunction(uint32_t indirect, uint32_t direct);
259   void SetMaxTableSize(uint32_t max);
260   uint32_t AddTable(ValueType type, uint32_t min_size);
261   uint32_t AddTable(ValueType type, uint32_t min_size, uint32_t max_size);
262   void MarkStartFunction(WasmFunctionBuilder* builder);
263   void AddExport(Vector<const char> name, ImportExportKindCode kind,
264                  uint32_t index);
AddExport(Vector<const char> name,WasmFunctionBuilder * builder)265   void AddExport(Vector<const char> name, WasmFunctionBuilder* builder) {
266     AddExport(name, kExternalFunction, builder->func_index());
267   }
268   uint32_t AddExportedGlobal(ValueType type, bool mutability, WasmInitExpr init,
269                              Vector<const char> name);
270   void ExportImportedFunction(Vector<const char> name, int import_index);
271   void SetMinMemorySize(uint32_t value);
272   void SetMaxMemorySize(uint32_t value);
273   void SetHasSharedMemory();
274 
275   // Writing methods.
276   void WriteTo(ZoneBuffer* buffer) const;
277   void WriteAsmJsOffsetTable(ZoneBuffer* buffer) const;
278 
zone()279   Zone* zone() { return zone_; }
280 
GetSignature(uint32_t index)281   FunctionSig* GetSignature(uint32_t index) {
282     DCHECK(types_[index].kind == Type::kFunctionSig);
283     return types_[index].sig;
284   }
285 
286  private:
287   struct Type {
288     enum Kind { kFunctionSig, kStructType, kArrayType };
TypeType289     explicit Type(FunctionSig* signature)
290         : kind(kFunctionSig), sig(signature) {}
TypeType291     explicit Type(StructType* struct_type)
292         : kind(kStructType), struct_type(struct_type) {}
TypeType293     explicit Type(ArrayType* array_type)
294         : kind(kArrayType), array_type(array_type) {}
295     Kind kind;
296     union {
297       FunctionSig* sig;
298       StructType* struct_type;
299       ArrayType* array_type;
300     };
301   };
302 
303   struct WasmFunctionImport {
304     Vector<const char> module;
305     Vector<const char> name;
306     uint32_t sig_index;
307   };
308 
309   struct WasmGlobalImport {
310     Vector<const char> module;
311     Vector<const char> name;
312     ValueTypeCode type_code;
313     bool mutability;
314   };
315 
316   struct WasmExport {
317     Vector<const char> name;
318     ImportExportKindCode kind;
319     int index;  // Can be negative for re-exported imports.
320   };
321 
322   struct WasmGlobal {
323     MOVE_ONLY_NO_DEFAULT_CONSTRUCTOR(WasmGlobal);
324 
325     ValueType type;
326     bool mutability;
327     WasmInitExpr init;
328   };
329 
330   struct WasmTable {
331     ValueType type;
332     uint32_t min_size;
333     uint32_t max_size;
334     bool has_maximum;
335   };
336 
337   struct WasmDataSegment {
338     ZoneVector<byte> data;
339     uint32_t dest;
340   };
341 
342   friend class WasmFunctionBuilder;
343   Zone* zone_;
344   ZoneVector<Type> types_;
345   ZoneVector<WasmFunctionImport> function_imports_;
346   ZoneVector<WasmGlobalImport> global_imports_;
347   ZoneVector<WasmExport> exports_;
348   ZoneVector<WasmFunctionBuilder*> functions_;
349   ZoneVector<WasmTable> tables_;
350   ZoneVector<WasmDataSegment> data_segments_;
351   ZoneVector<uint32_t> indirect_functions_;
352   ZoneVector<WasmGlobal> globals_;
353   ZoneUnorderedMap<FunctionSig, uint32_t> signature_map_;
354   int start_function_index_;
355   uint32_t max_table_size_ = 0;
356   uint32_t min_memory_size_;
357   uint32_t max_memory_size_;
358   bool has_max_memory_size_;
359   bool has_shared_memory_;
360 #if DEBUG
361   // Once AddExportedImport is called, no more imports can be added.
362   bool adding_imports_allowed_ = true;
363   // Indirect functions must be allocated before adding extra tables.
364   bool allocating_indirect_functions_allowed_ = true;
365 #endif
366 };
367 
signature()368 inline FunctionSig* WasmFunctionBuilder::signature() {
369   return builder_->types_[signature_index_].sig;
370 }
371 
372 }  // namespace wasm
373 }  // namespace internal
374 }  // namespace v8
375 
376 #endif  // V8_WASM_WASM_MODULE_BUILDER_H_
377