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 #include "src/wasm/wasm-module-builder.h"
6
7 #include "src/base/memory.h"
8 #include "src/codegen/signature.h"
9 #include "src/handles/handles.h"
10 #include "src/init/v8.h"
11 #include "src/objects/objects-inl.h"
12 #include "src/wasm/function-body-decoder.h"
13 #include "src/wasm/leb-helper.h"
14 #include "src/wasm/wasm-constants.h"
15 #include "src/wasm/wasm-module.h"
16 #include "src/wasm/wasm-opcodes.h"
17 #include "src/zone/zone-containers.h"
18
19 namespace v8 {
20 namespace internal {
21 namespace wasm {
22
23 namespace {
24
25 // Emit a section code and the size as a padded varint that can be patched
26 // later.
EmitSection(SectionCode code,ZoneBuffer * buffer)27 size_t EmitSection(SectionCode code, ZoneBuffer* buffer) {
28 // Emit the section code.
29 buffer->write_u8(code);
30
31 // Emit a placeholder for the length.
32 return buffer->reserve_u32v();
33 }
34
35 // Patch the size of a section after it's finished.
FixupSection(ZoneBuffer * buffer,size_t start)36 void FixupSection(ZoneBuffer* buffer, size_t start) {
37 buffer->patch_u32v(start, static_cast<uint32_t>(buffer->offset() - start -
38 kPaddedVarInt32Size));
39 }
40
41 } // namespace
42
WasmFunctionBuilder(WasmModuleBuilder * builder)43 WasmFunctionBuilder::WasmFunctionBuilder(WasmModuleBuilder* builder)
44 : builder_(builder),
45 locals_(builder->zone()),
46 signature_index_(0),
47 func_index_(static_cast<uint32_t>(builder->functions_.size())),
48 body_(builder->zone(), 256),
49 i32_temps_(builder->zone()),
50 i64_temps_(builder->zone()),
51 f32_temps_(builder->zone()),
52 f64_temps_(builder->zone()),
53 direct_calls_(builder->zone()),
54 asm_offsets_(builder->zone(), 8) {}
55
EmitByte(byte val)56 void WasmFunctionBuilder::EmitByte(byte val) { body_.write_u8(val); }
57
EmitI32V(int32_t val)58 void WasmFunctionBuilder::EmitI32V(int32_t val) { body_.write_i32v(val); }
59
EmitU32V(uint32_t val)60 void WasmFunctionBuilder::EmitU32V(uint32_t val) { body_.write_u32v(val); }
61
SetSignature(FunctionSig * sig)62 void WasmFunctionBuilder::SetSignature(FunctionSig* sig) {
63 DCHECK(!locals_.has_sig());
64 locals_.set_sig(sig);
65 signature_index_ = builder_->AddSignature(sig);
66 }
67
AddLocal(ValueType type)68 uint32_t WasmFunctionBuilder::AddLocal(ValueType type) {
69 DCHECK(locals_.has_sig());
70 return locals_.AddLocals(1, type);
71 }
72
EmitGetLocal(uint32_t local_index)73 void WasmFunctionBuilder::EmitGetLocal(uint32_t local_index) {
74 EmitWithU32V(kExprLocalGet, local_index);
75 }
76
EmitSetLocal(uint32_t local_index)77 void WasmFunctionBuilder::EmitSetLocal(uint32_t local_index) {
78 EmitWithU32V(kExprLocalSet, local_index);
79 }
80
EmitTeeLocal(uint32_t local_index)81 void WasmFunctionBuilder::EmitTeeLocal(uint32_t local_index) {
82 EmitWithU32V(kExprLocalTee, local_index);
83 }
84
EmitCode(const byte * code,uint32_t code_size)85 void WasmFunctionBuilder::EmitCode(const byte* code, uint32_t code_size) {
86 body_.write(code, code_size);
87 }
88
Emit(WasmOpcode opcode)89 void WasmFunctionBuilder::Emit(WasmOpcode opcode) { body_.write_u8(opcode); }
90
EmitWithPrefix(WasmOpcode opcode)91 void WasmFunctionBuilder::EmitWithPrefix(WasmOpcode opcode) {
92 DCHECK_NE(0, opcode & 0xff00);
93 body_.write_u8(opcode >> 8);
94 if ((opcode >> 8) == WasmOpcode::kSimdPrefix) {
95 // SIMD opcodes are LEB encoded
96 body_.write_u32v(opcode & 0xff);
97 } else {
98 body_.write_u8(opcode);
99 }
100 }
101
EmitWithU8(WasmOpcode opcode,const byte immediate)102 void WasmFunctionBuilder::EmitWithU8(WasmOpcode opcode, const byte immediate) {
103 body_.write_u8(opcode);
104 body_.write_u8(immediate);
105 }
106
EmitWithU8U8(WasmOpcode opcode,const byte imm1,const byte imm2)107 void WasmFunctionBuilder::EmitWithU8U8(WasmOpcode opcode, const byte imm1,
108 const byte imm2) {
109 body_.write_u8(opcode);
110 body_.write_u8(imm1);
111 body_.write_u8(imm2);
112 }
113
EmitWithI32V(WasmOpcode opcode,int32_t immediate)114 void WasmFunctionBuilder::EmitWithI32V(WasmOpcode opcode, int32_t immediate) {
115 body_.write_u8(opcode);
116 body_.write_i32v(immediate);
117 }
118
EmitWithU32V(WasmOpcode opcode,uint32_t immediate)119 void WasmFunctionBuilder::EmitWithU32V(WasmOpcode opcode, uint32_t immediate) {
120 body_.write_u8(opcode);
121 body_.write_u32v(immediate);
122 }
123
EmitI32Const(int32_t value)124 void WasmFunctionBuilder::EmitI32Const(int32_t value) {
125 EmitWithI32V(kExprI32Const, value);
126 }
127
EmitI64Const(int64_t value)128 void WasmFunctionBuilder::EmitI64Const(int64_t value) {
129 body_.write_u8(kExprI64Const);
130 body_.write_i64v(value);
131 }
132
EmitF32Const(float value)133 void WasmFunctionBuilder::EmitF32Const(float value) {
134 body_.write_u8(kExprF32Const);
135 body_.write_f32(value);
136 }
137
EmitF64Const(double value)138 void WasmFunctionBuilder::EmitF64Const(double value) {
139 body_.write_u8(kExprF64Const);
140 body_.write_f64(value);
141 }
142
EmitDirectCallIndex(uint32_t index)143 void WasmFunctionBuilder::EmitDirectCallIndex(uint32_t index) {
144 DirectCallIndex call;
145 call.offset = body_.size();
146 call.direct_index = index;
147 direct_calls_.push_back(call);
148 byte placeholder_bytes[kMaxVarInt32Size] = {0};
149 EmitCode(placeholder_bytes, arraysize(placeholder_bytes));
150 }
151
SetName(Vector<const char> name)152 void WasmFunctionBuilder::SetName(Vector<const char> name) { name_ = name; }
153
AddAsmWasmOffset(size_t call_position,size_t to_number_position)154 void WasmFunctionBuilder::AddAsmWasmOffset(size_t call_position,
155 size_t to_number_position) {
156 // We only want to emit one mapping per byte offset.
157 DCHECK(asm_offsets_.size() == 0 || body_.size() > last_asm_byte_offset_);
158
159 DCHECK_LE(body_.size(), kMaxUInt32);
160 uint32_t byte_offset = static_cast<uint32_t>(body_.size());
161 asm_offsets_.write_u32v(byte_offset - last_asm_byte_offset_);
162 last_asm_byte_offset_ = byte_offset;
163
164 DCHECK_GE(std::numeric_limits<uint32_t>::max(), call_position);
165 uint32_t call_position_u32 = static_cast<uint32_t>(call_position);
166 asm_offsets_.write_i32v(call_position_u32 - last_asm_source_position_);
167
168 DCHECK_GE(std::numeric_limits<uint32_t>::max(), to_number_position);
169 uint32_t to_number_position_u32 = static_cast<uint32_t>(to_number_position);
170 asm_offsets_.write_i32v(to_number_position_u32 - call_position_u32);
171 last_asm_source_position_ = to_number_position_u32;
172 }
173
SetAsmFunctionStartPosition(size_t function_position)174 void WasmFunctionBuilder::SetAsmFunctionStartPosition(
175 size_t function_position) {
176 DCHECK_EQ(0, asm_func_start_source_position_);
177 DCHECK_GE(std::numeric_limits<uint32_t>::max(), function_position);
178 uint32_t function_position_u32 = static_cast<uint32_t>(function_position);
179 // Must be called before emitting any asm.js source position.
180 DCHECK_EQ(0, asm_offsets_.size());
181 asm_func_start_source_position_ = function_position_u32;
182 last_asm_source_position_ = function_position_u32;
183 }
184
SetCompilationHint(WasmCompilationHintStrategy strategy,WasmCompilationHintTier baseline,WasmCompilationHintTier top_tier)185 void WasmFunctionBuilder::SetCompilationHint(
186 WasmCompilationHintStrategy strategy, WasmCompilationHintTier baseline,
187 WasmCompilationHintTier top_tier) {
188 uint8_t hint_byte = static_cast<uint8_t>(strategy) |
189 static_cast<uint8_t>(baseline) << 2 |
190 static_cast<uint8_t>(top_tier) << 4;
191 DCHECK_NE(hint_byte, kNoCompilationHint);
192 hint_ = hint_byte;
193 }
194
DeleteCodeAfter(size_t position)195 void WasmFunctionBuilder::DeleteCodeAfter(size_t position) {
196 DCHECK_LE(position, body_.size());
197 body_.Truncate(position);
198 }
199
WriteSignature(ZoneBuffer * buffer) const200 void WasmFunctionBuilder::WriteSignature(ZoneBuffer* buffer) const {
201 buffer->write_u32v(signature_index_);
202 }
203
WriteBody(ZoneBuffer * buffer) const204 void WasmFunctionBuilder::WriteBody(ZoneBuffer* buffer) const {
205 size_t locals_size = locals_.Size();
206 buffer->write_size(locals_size + body_.size());
207 buffer->EnsureSpace(locals_size);
208 byte** ptr = buffer->pos_ptr();
209 locals_.Emit(*ptr);
210 (*ptr) += locals_size; // UGLY: manual bump of position pointer
211 if (body_.size() > 0) {
212 size_t base = buffer->offset();
213 buffer->write(body_.begin(), body_.size());
214 for (DirectCallIndex call : direct_calls_) {
215 buffer->patch_u32v(
216 base + call.offset,
217 call.direct_index +
218 static_cast<uint32_t>(builder_->function_imports_.size()));
219 }
220 }
221 }
222
WriteAsmWasmOffsetTable(ZoneBuffer * buffer) const223 void WasmFunctionBuilder::WriteAsmWasmOffsetTable(ZoneBuffer* buffer) const {
224 if (asm_func_start_source_position_ == 0 && asm_offsets_.size() == 0) {
225 buffer->write_size(0);
226 return;
227 }
228 size_t locals_enc_size = LEBHelper::sizeof_u32v(locals_.Size());
229 size_t func_start_size =
230 LEBHelper::sizeof_u32v(asm_func_start_source_position_);
231 buffer->write_size(asm_offsets_.size() + locals_enc_size + func_start_size);
232 // Offset of the recorded byte offsets.
233 DCHECK_GE(kMaxUInt32, locals_.Size());
234 buffer->write_u32v(static_cast<uint32_t>(locals_.Size()));
235 // Start position of the function.
236 buffer->write_u32v(asm_func_start_source_position_);
237 buffer->write(asm_offsets_.begin(), asm_offsets_.size());
238 }
239
WasmModuleBuilder(Zone * zone)240 WasmModuleBuilder::WasmModuleBuilder(Zone* zone)
241 : zone_(zone),
242 types_(zone),
243 function_imports_(zone),
244 global_imports_(zone),
245 exports_(zone),
246 functions_(zone),
247 tables_(zone),
248 data_segments_(zone),
249 indirect_functions_(zone),
250 globals_(zone),
251 signature_map_(zone),
252 start_function_index_(-1),
253 min_memory_size_(16),
254 max_memory_size_(0),
255 has_max_memory_size_(false),
256 has_shared_memory_(false) {}
257
AddFunction(FunctionSig * sig)258 WasmFunctionBuilder* WasmModuleBuilder::AddFunction(FunctionSig* sig) {
259 functions_.push_back(zone_->New<WasmFunctionBuilder>(this));
260 // Add the signature if one was provided here.
261 if (sig) functions_.back()->SetSignature(sig);
262 return functions_.back();
263 }
264
AddDataSegment(const byte * data,uint32_t size,uint32_t dest)265 void WasmModuleBuilder::AddDataSegment(const byte* data, uint32_t size,
266 uint32_t dest) {
267 data_segments_.push_back({ZoneVector<byte>(zone()), dest});
268 ZoneVector<byte>& vec = data_segments_.back().data;
269 for (uint32_t i = 0; i < size; i++) {
270 vec.push_back(data[i]);
271 }
272 }
273
AddSignature(FunctionSig * sig)274 uint32_t WasmModuleBuilder::AddSignature(FunctionSig* sig) {
275 auto sig_entry = signature_map_.find(*sig);
276 if (sig_entry != signature_map_.end()) return sig_entry->second;
277 uint32_t index = static_cast<uint32_t>(types_.size());
278 signature_map_.emplace(*sig, index);
279 types_.push_back(Type(sig));
280 return index;
281 }
282
AddStructType(StructType * type)283 uint32_t WasmModuleBuilder::AddStructType(StructType* type) {
284 uint32_t index = static_cast<uint32_t>(types_.size());
285 types_.push_back(Type(type));
286 return index;
287 }
288
AddArrayType(ArrayType * type)289 uint32_t WasmModuleBuilder::AddArrayType(ArrayType* type) {
290 uint32_t index = static_cast<uint32_t>(types_.size());
291 types_.push_back(Type(type));
292 return index;
293 }
294
AllocateIndirectFunctions(uint32_t count)295 uint32_t WasmModuleBuilder::AllocateIndirectFunctions(uint32_t count) {
296 DCHECK(allocating_indirect_functions_allowed_);
297 uint32_t index = static_cast<uint32_t>(indirect_functions_.size());
298 DCHECK_GE(FLAG_wasm_max_table_size, index);
299 if (count > FLAG_wasm_max_table_size - index) {
300 return std::numeric_limits<uint32_t>::max();
301 }
302 uint32_t new_size = static_cast<uint32_t>(indirect_functions_.size()) + count;
303 DCHECK(max_table_size_ == 0 || new_size <= max_table_size_);
304 indirect_functions_.resize(new_size, WasmElemSegment::kNullIndex);
305 uint32_t max = max_table_size_ > 0 ? max_table_size_ : new_size;
306 if (tables_.empty()) {
307 // This cannot use {AddTable} because that would flip the
308 // {allocating_indirect_functions_allowed_} flag.
309 tables_.push_back({kWasmFuncRef, new_size, max, true});
310 } else {
311 // There can only be the indirect function table so far, otherwise the
312 // {allocating_indirect_functions_allowed_} flag would have been false.
313 DCHECK_EQ(1u, tables_.size());
314 DCHECK_EQ(kWasmFuncRef, tables_[0].type);
315 DCHECK(tables_[0].has_maximum);
316 tables_[0].min_size = new_size;
317 tables_[0].max_size = max;
318 }
319 return index;
320 }
321
SetIndirectFunction(uint32_t indirect,uint32_t direct)322 void WasmModuleBuilder::SetIndirectFunction(uint32_t indirect,
323 uint32_t direct) {
324 indirect_functions_[indirect] = direct;
325 }
326
SetMaxTableSize(uint32_t max)327 void WasmModuleBuilder::SetMaxTableSize(uint32_t max) {
328 DCHECK_GE(FLAG_wasm_max_table_size, max);
329 DCHECK_GE(max, indirect_functions_.size());
330 max_table_size_ = max;
331 DCHECK(allocating_indirect_functions_allowed_);
332 if (!tables_.empty()) {
333 tables_[0].max_size = max;
334 }
335 }
336
AddTable(ValueType type,uint32_t min_size)337 uint32_t WasmModuleBuilder::AddTable(ValueType type, uint32_t min_size) {
338 #if DEBUG
339 allocating_indirect_functions_allowed_ = false;
340 #endif
341 tables_.push_back({type, min_size, 0, false});
342 return static_cast<uint32_t>(tables_.size() - 1);
343 }
344
AddTable(ValueType type,uint32_t min_size,uint32_t max_size)345 uint32_t WasmModuleBuilder::AddTable(ValueType type, uint32_t min_size,
346 uint32_t max_size) {
347 #if DEBUG
348 allocating_indirect_functions_allowed_ = false;
349 #endif
350 tables_.push_back({type, min_size, max_size, true});
351 return static_cast<uint32_t>(tables_.size() - 1);
352 }
353
AddImport(Vector<const char> name,FunctionSig * sig,Vector<const char> module)354 uint32_t WasmModuleBuilder::AddImport(Vector<const char> name, FunctionSig* sig,
355 Vector<const char> module) {
356 DCHECK(adding_imports_allowed_);
357 function_imports_.push_back({module, name, AddSignature(sig)});
358 return static_cast<uint32_t>(function_imports_.size() - 1);
359 }
360
AddGlobalImport(Vector<const char> name,ValueType type,bool mutability,Vector<const char> module)361 uint32_t WasmModuleBuilder::AddGlobalImport(Vector<const char> name,
362 ValueType type, bool mutability,
363 Vector<const char> module) {
364 global_imports_.push_back({module, name, type.value_type_code(), mutability});
365 return static_cast<uint32_t>(global_imports_.size() - 1);
366 }
367
MarkStartFunction(WasmFunctionBuilder * function)368 void WasmModuleBuilder::MarkStartFunction(WasmFunctionBuilder* function) {
369 start_function_index_ = function->func_index();
370 }
371
AddExport(Vector<const char> name,ImportExportKindCode kind,uint32_t index)372 void WasmModuleBuilder::AddExport(Vector<const char> name,
373 ImportExportKindCode kind, uint32_t index) {
374 DCHECK_LE(index, std::numeric_limits<int>::max());
375 exports_.push_back({name, kind, static_cast<int>(index)});
376 }
377
AddExportedGlobal(ValueType type,bool mutability,WasmInitExpr init,Vector<const char> name)378 uint32_t WasmModuleBuilder::AddExportedGlobal(ValueType type, bool mutability,
379 WasmInitExpr init,
380 Vector<const char> name) {
381 uint32_t index = AddGlobal(type, mutability, std::move(init));
382 AddExport(name, kExternalGlobal, index);
383 return index;
384 }
385
ExportImportedFunction(Vector<const char> name,int import_index)386 void WasmModuleBuilder::ExportImportedFunction(Vector<const char> name,
387 int import_index) {
388 #if DEBUG
389 // The size of function_imports_ must not change any more.
390 adding_imports_allowed_ = false;
391 #endif
392 exports_.push_back(
393 {name, kExternalFunction,
394 import_index - static_cast<int>(function_imports_.size())});
395 }
396
AddGlobal(ValueType type,bool mutability,WasmInitExpr init)397 uint32_t WasmModuleBuilder::AddGlobal(ValueType type, bool mutability,
398 WasmInitExpr init) {
399 globals_.push_back({type, mutability, std::move(init)});
400 return static_cast<uint32_t>(globals_.size() - 1);
401 }
402
SetMinMemorySize(uint32_t value)403 void WasmModuleBuilder::SetMinMemorySize(uint32_t value) {
404 min_memory_size_ = value;
405 }
406
SetMaxMemorySize(uint32_t value)407 void WasmModuleBuilder::SetMaxMemorySize(uint32_t value) {
408 has_max_memory_size_ = true;
409 max_memory_size_ = value;
410 }
411
SetHasSharedMemory()412 void WasmModuleBuilder::SetHasSharedMemory() { has_shared_memory_ = true; }
413
414 namespace {
WriteValueType(ZoneBuffer * buffer,const ValueType & type)415 void WriteValueType(ZoneBuffer* buffer, const ValueType& type) {
416 buffer->write_u8(type.value_type_code());
417 if (type.has_depth()) {
418 buffer->write_u32v(type.depth());
419 }
420 if (type.encoding_needs_heap_type()) {
421 buffer->write_i32v(type.heap_type().code());
422 }
423 }
424
WriteGlobalInitializer(ZoneBuffer * buffer,const WasmInitExpr & init,ValueType type)425 void WriteGlobalInitializer(ZoneBuffer* buffer, const WasmInitExpr& init,
426 ValueType type) {
427 switch (init.kind()) {
428 case WasmInitExpr::kI32Const:
429 buffer->write_u8(kExprI32Const);
430 buffer->write_i32v(init.immediate().i32_const);
431 break;
432 case WasmInitExpr::kI64Const:
433 buffer->write_u8(kExprI64Const);
434 buffer->write_i64v(init.immediate().i64_const);
435 break;
436 case WasmInitExpr::kF32Const:
437 buffer->write_u8(kExprF32Const);
438 buffer->write_f32(init.immediate().f32_const);
439 break;
440 case WasmInitExpr::kF64Const:
441 buffer->write_u8(kExprF64Const);
442 buffer->write_f64(init.immediate().f64_const);
443 break;
444 case WasmInitExpr::kS128Const:
445 buffer->write_u8(kSimdPrefix);
446 buffer->write_u8(kExprS128Const & 0xFF);
447 buffer->write(init.immediate().s128_const.data(), kSimd128Size);
448 break;
449 case WasmInitExpr::kGlobalGet:
450 buffer->write_u8(kExprGlobalGet);
451 buffer->write_u32v(init.immediate().index);
452 break;
453 case WasmInitExpr::kRefNullConst:
454 buffer->write_u8(kExprRefNull);
455 buffer->write_i32v(HeapType(init.immediate().heap_type).code());
456 break;
457 case WasmInitExpr::kRefFuncConst:
458 buffer->write_u8(kExprRefFunc);
459 buffer->write_u32v(init.immediate().index);
460 break;
461 case WasmInitExpr::kNone: {
462 // No initializer, emit a default value.
463 switch (type.kind()) {
464 case ValueType::kI32:
465 buffer->write_u8(kExprI32Const);
466 // LEB encoding of 0.
467 buffer->write_u8(0);
468 break;
469 case ValueType::kI64:
470 buffer->write_u8(kExprI64Const);
471 // LEB encoding of 0.
472 buffer->write_u8(0);
473 break;
474 case ValueType::kF32:
475 buffer->write_u8(kExprF32Const);
476 buffer->write_f32(0.f);
477 break;
478 case ValueType::kF64:
479 buffer->write_u8(kExprF64Const);
480 buffer->write_f64(0.);
481 break;
482 case ValueType::kOptRef:
483 buffer->write_u8(kExprRefNull);
484 break;
485 case ValueType::kI8:
486 case ValueType::kI16:
487 case ValueType::kStmt:
488 case ValueType::kS128:
489 case ValueType::kBottom:
490 case ValueType::kRef:
491 case ValueType::kRtt:
492 UNREACHABLE();
493 }
494 break;
495 }
496 case WasmInitExpr::kRttCanon:
497 STATIC_ASSERT((kExprRttCanon >> 8) == kGCPrefix);
498 buffer->write_u8(kGCPrefix);
499 buffer->write_u8(static_cast<uint8_t>(kExprRttCanon));
500 buffer->write_i32v(HeapType(init.immediate().heap_type).code());
501 break;
502 case WasmInitExpr::kRttSub:
503 // TODO(7748): If immediates for rtts remain in the standard, adapt this
504 // to emit them.
505 STATIC_ASSERT((kExprRttSub >> 8) == kGCPrefix);
506 buffer->write_u8(kGCPrefix);
507 buffer->write_u8(static_cast<uint8_t>(kExprRttSub));
508 buffer->write_i32v(HeapType(init.immediate().heap_type).code());
509 WriteGlobalInitializer(buffer, *init.operand(), kWasmBottom);
510 break;
511 }
512 }
513
514 } // namespace
515
WriteTo(ZoneBuffer * buffer) const516 void WasmModuleBuilder::WriteTo(ZoneBuffer* buffer) const {
517 // == Emit magic =============================================================
518 buffer->write_u32(kWasmMagic);
519 buffer->write_u32(kWasmVersion);
520
521 // == Emit types =============================================================
522 if (types_.size() > 0) {
523 size_t start = EmitSection(kTypeSectionCode, buffer);
524 buffer->write_size(types_.size());
525
526 for (const Type& type : types_) {
527 switch (type.kind) {
528 case Type::kFunctionSig: {
529 FunctionSig* sig = type.sig;
530 buffer->write_u8(kWasmFunctionTypeCode);
531 buffer->write_size(sig->parameter_count());
532 for (auto param : sig->parameters()) {
533 WriteValueType(buffer, param);
534 }
535 buffer->write_size(sig->return_count());
536 for (auto ret : sig->returns()) {
537 WriteValueType(buffer, ret);
538 }
539 break;
540 }
541 case Type::kStructType: {
542 StructType* struct_type = type.struct_type;
543 buffer->write_u8(kWasmStructTypeCode);
544 buffer->write_size(struct_type->field_count());
545 for (uint32_t i = 0; i < struct_type->field_count(); i++) {
546 WriteValueType(buffer, struct_type->field(i));
547 buffer->write_u8(struct_type->mutability(i) ? 1 : 0);
548 }
549 break;
550 }
551 case Type::kArrayType: {
552 ArrayType* array_type = type.array_type;
553 buffer->write_u8(kWasmArrayTypeCode);
554 WriteValueType(buffer, array_type->element_type());
555 buffer->write_u8(array_type->mutability() ? 1 : 0);
556 break;
557 }
558 }
559 }
560 FixupSection(buffer, start);
561 }
562
563 // == Emit imports ===========================================================
564 if (global_imports_.size() + function_imports_.size() > 0) {
565 size_t start = EmitSection(kImportSectionCode, buffer);
566 buffer->write_size(global_imports_.size() + function_imports_.size());
567 for (auto import : global_imports_) {
568 buffer->write_string(import.module); // module name
569 buffer->write_string(import.name); // field name
570 buffer->write_u8(kExternalGlobal);
571 buffer->write_u8(import.type_code);
572 buffer->write_u8(import.mutability ? 1 : 0);
573 }
574 for (auto import : function_imports_) {
575 buffer->write_string(import.module); // module name
576 buffer->write_string(import.name); // field name
577 buffer->write_u8(kExternalFunction);
578 buffer->write_u32v(import.sig_index);
579 }
580 FixupSection(buffer, start);
581 }
582
583 // == Emit function signatures ===============================================
584 uint32_t num_function_names = 0;
585 if (functions_.size() > 0) {
586 size_t start = EmitSection(kFunctionSectionCode, buffer);
587 buffer->write_size(functions_.size());
588 for (auto* function : functions_) {
589 function->WriteSignature(buffer);
590 if (!function->name_.empty()) ++num_function_names;
591 }
592 FixupSection(buffer, start);
593 }
594
595 // == Emit tables ============================================================
596 if (tables_.size() > 0) {
597 size_t start = EmitSection(kTableSectionCode, buffer);
598 buffer->write_size(tables_.size());
599 for (const WasmTable& table : tables_) {
600 WriteValueType(buffer, table.type);
601 buffer->write_u8(table.has_maximum ? kWithMaximum : kNoMaximum);
602 buffer->write_size(table.min_size);
603 if (table.has_maximum) buffer->write_size(table.max_size);
604 }
605 FixupSection(buffer, start);
606 }
607
608 // == Emit memory declaration ================================================
609 {
610 size_t start = EmitSection(kMemorySectionCode, buffer);
611 buffer->write_u8(1); // memory count
612 if (has_shared_memory_) {
613 buffer->write_u8(has_max_memory_size_ ? kSharedWithMaximum
614 : kSharedNoMaximum);
615 } else {
616 buffer->write_u8(has_max_memory_size_ ? kWithMaximum : kNoMaximum);
617 }
618 buffer->write_u32v(min_memory_size_);
619 if (has_max_memory_size_) {
620 buffer->write_u32v(max_memory_size_);
621 }
622 FixupSection(buffer, start);
623 }
624
625 // == Emit globals ===========================================================
626 if (globals_.size() > 0) {
627 size_t start = EmitSection(kGlobalSectionCode, buffer);
628 buffer->write_size(globals_.size());
629
630 for (const WasmGlobal& global : globals_) {
631 WriteValueType(buffer, global.type);
632 buffer->write_u8(global.mutability ? 1 : 0);
633 WriteGlobalInitializer(buffer, global.init, global.type);
634 buffer->write_u8(kExprEnd);
635 }
636 FixupSection(buffer, start);
637 }
638
639 // == emit exports ===========================================================
640 if (exports_.size() > 0) {
641 size_t start = EmitSection(kExportSectionCode, buffer);
642 buffer->write_size(exports_.size());
643 for (auto ex : exports_) {
644 buffer->write_string(ex.name);
645 buffer->write_u8(ex.kind);
646 switch (ex.kind) {
647 case kExternalFunction:
648 buffer->write_size(ex.index + function_imports_.size());
649 break;
650 case kExternalGlobal:
651 buffer->write_size(ex.index + global_imports_.size());
652 break;
653 case kExternalMemory:
654 case kExternalTable:
655 // The WasmModuleBuilder doesn't support importing tables or memories
656 // yet, so there is no index offset to add.
657 buffer->write_size(ex.index);
658 break;
659 case kExternalException:
660 UNREACHABLE();
661 }
662 }
663 FixupSection(buffer, start);
664 }
665
666 // == emit start function index ==============================================
667 if (start_function_index_ >= 0) {
668 size_t start = EmitSection(kStartSectionCode, buffer);
669 buffer->write_size(start_function_index_ + function_imports_.size());
670 FixupSection(buffer, start);
671 }
672
673 // == emit function table elements ===========================================
674 if (indirect_functions_.size() > 0) {
675 size_t start = EmitSection(kElementSectionCode, buffer);
676 buffer->write_u8(1); // count of entries
677 buffer->write_u8(0); // table index
678 uint32_t first_element = 0;
679 while (first_element < indirect_functions_.size() &&
680 indirect_functions_[first_element] == WasmElemSegment::kNullIndex) {
681 first_element++;
682 }
683 uint32_t last_element =
684 static_cast<uint32_t>(indirect_functions_.size() - 1);
685 while (last_element >= first_element &&
686 indirect_functions_[last_element] == WasmElemSegment::kNullIndex) {
687 last_element--;
688 }
689 buffer->write_u8(kExprI32Const); // offset
690 buffer->write_u32v(first_element);
691 buffer->write_u8(kExprEnd);
692 uint32_t element_count = last_element - first_element + 1;
693 buffer->write_size(element_count);
694 for (uint32_t i = first_element; i <= last_element; i++) {
695 buffer->write_size(indirect_functions_[i] + function_imports_.size());
696 }
697
698 FixupSection(buffer, start);
699 }
700
701 // == emit compilation hints section =========================================
702 bool emit_compilation_hints = false;
703 for (auto* fn : functions_) {
704 if (fn->hint_ != kNoCompilationHint) {
705 emit_compilation_hints = true;
706 break;
707 }
708 }
709 if (emit_compilation_hints) {
710 // Emit the section code.
711 buffer->write_u8(kUnknownSectionCode);
712 // Emit a placeholder for section length.
713 size_t start = buffer->reserve_u32v();
714 // Emit custom section name.
715 buffer->write_string(CStrVector("compilationHints"));
716 // Emit hint count.
717 buffer->write_size(functions_.size());
718 // Emit hint bytes.
719 for (auto* fn : functions_) {
720 uint8_t hint_byte =
721 fn->hint_ != kNoCompilationHint ? fn->hint_ : kDefaultCompilationHint;
722 buffer->write_u8(hint_byte);
723 }
724 FixupSection(buffer, start);
725 }
726
727 // == emit code ==============================================================
728 if (functions_.size() > 0) {
729 size_t start = EmitSection(kCodeSectionCode, buffer);
730 buffer->write_size(functions_.size());
731 for (auto* function : functions_) {
732 function->WriteBody(buffer);
733 }
734 FixupSection(buffer, start);
735 }
736
737 // == emit data segments =====================================================
738 if (data_segments_.size() > 0) {
739 size_t start = EmitSection(kDataSectionCode, buffer);
740 buffer->write_size(data_segments_.size());
741
742 for (auto segment : data_segments_) {
743 buffer->write_u8(0); // linear memory segment
744 buffer->write_u8(kExprI32Const); // initializer expression for dest
745 buffer->write_u32v(segment.dest);
746 buffer->write_u8(kExprEnd);
747 buffer->write_u32v(static_cast<uint32_t>(segment.data.size()));
748 buffer->write(&segment.data[0], segment.data.size());
749 }
750 FixupSection(buffer, start);
751 }
752
753 // == Emit names =============================================================
754 if (num_function_names > 0 || !function_imports_.empty()) {
755 // Emit the section code.
756 buffer->write_u8(kUnknownSectionCode);
757 // Emit a placeholder for the length.
758 size_t start = buffer->reserve_u32v();
759 // Emit the section string.
760 buffer->write_string(CStrVector("name"));
761 // Emit a subsection for the function names.
762 buffer->write_u8(NameSectionKindCode::kFunction);
763 // Emit a placeholder for the subsection length.
764 size_t functions_start = buffer->reserve_u32v();
765 // Emit the function names.
766 // Imports are always named.
767 uint32_t num_imports = static_cast<uint32_t>(function_imports_.size());
768 buffer->write_size(num_imports + num_function_names);
769 uint32_t function_index = 0;
770 for (; function_index < num_imports; ++function_index) {
771 const WasmFunctionImport* import = &function_imports_[function_index];
772 DCHECK(!import->name.empty());
773 buffer->write_u32v(function_index);
774 buffer->write_string(import->name);
775 }
776 if (num_function_names > 0) {
777 for (auto* function : functions_) {
778 DCHECK_EQ(function_index,
779 function->func_index() + function_imports_.size());
780 if (!function->name_.empty()) {
781 buffer->write_u32v(function_index);
782 buffer->write_string(function->name_);
783 }
784 ++function_index;
785 }
786 }
787 FixupSection(buffer, functions_start);
788 FixupSection(buffer, start);
789 }
790 }
791
WriteAsmJsOffsetTable(ZoneBuffer * buffer) const792 void WasmModuleBuilder::WriteAsmJsOffsetTable(ZoneBuffer* buffer) const {
793 // == Emit asm.js offset table ===============================================
794 buffer->write_size(functions_.size());
795 // Emit the offset table per function.
796 for (auto* function : functions_) {
797 function->WriteAsmWasmOffsetTable(buffer);
798 }
799 }
800 } // namespace wasm
801 } // namespace internal
802 } // namespace v8
803