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