V8 API Reference, 7.2.502.16 (for Deno 0.2.4)
wasm-module-builder.cc
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/signature.h"
6 
7 #include "src/handles.h"
8 #include "src/objects-inl.h"
9 #include "src/v8.h"
10 #include "src/zone/zone-containers.h"
11 
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-builder.h"
16 #include "src/wasm/wasm-module.h"
17 #include "src/wasm/wasm-opcodes.h"
18 
19 #include "src/v8memory.h"
20 
21 namespace v8 {
22 namespace internal {
23 namespace wasm {
24 
25 namespace {
26 
27 // Emit a section code and the size as a padded varint that can be patched
28 // later.
29 size_t EmitSection(SectionCode code, ZoneBuffer& buffer) {
30  // Emit the section code.
31  buffer.write_u8(code);
32 
33  // Emit a placeholder for the length.
34  return buffer.reserve_u32v();
35 }
36 
37 // Patch the size of a section after it's finished.
38 void FixupSection(ZoneBuffer& buffer, size_t start) {
39  buffer.patch_u32v(start, static_cast<uint32_t>(buffer.offset() - start -
40  kPaddedVarInt32Size));
41 }
42 
43 } // namespace
44 
45 WasmFunctionBuilder::WasmFunctionBuilder(WasmModuleBuilder* builder)
46  : builder_(builder),
47  locals_(builder->zone()),
48  signature_index_(0),
49  func_index_(static_cast<uint32_t>(builder->functions_.size())),
50  body_(builder->zone(), 256),
51  i32_temps_(builder->zone()),
52  i64_temps_(builder->zone()),
53  f32_temps_(builder->zone()),
54  f64_temps_(builder->zone()),
55  direct_calls_(builder->zone()),
56  asm_offsets_(builder->zone(), 8) {}
57 
58 void WasmFunctionBuilder::EmitI32V(int32_t val) { body_.write_i32v(val); }
59 
60 void WasmFunctionBuilder::EmitU32V(uint32_t val) { body_.write_u32v(val); }
61 
62 void WasmFunctionBuilder::SetSignature(FunctionSig* sig) {
63  DCHECK(!locals_.has_sig());
64  locals_.set_sig(sig);
65  signature_index_ = builder_->AddSignature(sig);
66 }
67 
68 uint32_t WasmFunctionBuilder::AddLocal(ValueType type) {
69  DCHECK(locals_.has_sig());
70  return locals_.AddLocals(1, type);
71 }
72 
73 void WasmFunctionBuilder::EmitGetLocal(uint32_t local_index) {
74  EmitWithU32V(kExprGetLocal, local_index);
75 }
76 
77 void WasmFunctionBuilder::EmitSetLocal(uint32_t local_index) {
78  EmitWithU32V(kExprSetLocal, local_index);
79 }
80 
81 void WasmFunctionBuilder::EmitTeeLocal(uint32_t local_index) {
82  EmitWithU32V(kExprTeeLocal, local_index);
83 }
84 
85 void WasmFunctionBuilder::EmitCode(const byte* code, uint32_t code_size) {
86  body_.write(code, code_size);
87 }
88 
89 void WasmFunctionBuilder::Emit(WasmOpcode opcode) { body_.write_u8(opcode); }
90 
91 void WasmFunctionBuilder::EmitWithU8(WasmOpcode opcode, const byte immediate) {
92  body_.write_u8(opcode);
93  body_.write_u8(immediate);
94 }
95 
96 void WasmFunctionBuilder::EmitWithU8U8(WasmOpcode opcode, const byte imm1,
97  const byte imm2) {
98  body_.write_u8(opcode);
99  body_.write_u8(imm1);
100  body_.write_u8(imm2);
101 }
102 
103 void WasmFunctionBuilder::EmitWithI32V(WasmOpcode opcode, int32_t immediate) {
104  body_.write_u8(opcode);
105  body_.write_i32v(immediate);
106 }
107 
108 void WasmFunctionBuilder::EmitWithU32V(WasmOpcode opcode, uint32_t immediate) {
109  body_.write_u8(opcode);
110  body_.write_u32v(immediate);
111 }
112 
113 void WasmFunctionBuilder::EmitI32Const(int32_t value) {
114  EmitWithI32V(kExprI32Const, value);
115 }
116 
117 void WasmFunctionBuilder::EmitI64Const(int64_t value) {
118  body_.write_u8(kExprI64Const);
119  body_.write_i64v(value);
120 }
121 
122 void WasmFunctionBuilder::EmitF32Const(float value) {
123  body_.write_u8(kExprF32Const);
124  body_.write_f32(value);
125 }
126 
127 void WasmFunctionBuilder::EmitF64Const(double value) {
128  body_.write_u8(kExprF64Const);
129  body_.write_f64(value);
130 }
131 
132 void WasmFunctionBuilder::EmitDirectCallIndex(uint32_t index) {
133  DirectCallIndex call;
134  call.offset = body_.size();
135  call.direct_index = index;
136  direct_calls_.push_back(call);
137  byte placeholder_bytes[kMaxVarInt32Size] = {0};
138  EmitCode(placeholder_bytes, arraysize(placeholder_bytes));
139 }
140 
141 void WasmFunctionBuilder::SetName(Vector<const char> name) { name_ = name; }
142 
143 void WasmFunctionBuilder::AddAsmWasmOffset(size_t call_position,
144  size_t to_number_position) {
145  // We only want to emit one mapping per byte offset.
146  DCHECK(asm_offsets_.size() == 0 || body_.size() > last_asm_byte_offset_);
147 
148  DCHECK_LE(body_.size(), kMaxUInt32);
149  uint32_t byte_offset = static_cast<uint32_t>(body_.size());
150  asm_offsets_.write_u32v(byte_offset - last_asm_byte_offset_);
151  last_asm_byte_offset_ = byte_offset;
152 
153  DCHECK_GE(std::numeric_limits<uint32_t>::max(), call_position);
154  uint32_t call_position_u32 = static_cast<uint32_t>(call_position);
155  asm_offsets_.write_i32v(call_position_u32 - last_asm_source_position_);
156 
157  DCHECK_GE(std::numeric_limits<uint32_t>::max(), to_number_position);
158  uint32_t to_number_position_u32 = static_cast<uint32_t>(to_number_position);
159  asm_offsets_.write_i32v(to_number_position_u32 - call_position_u32);
160  last_asm_source_position_ = to_number_position_u32;
161 }
162 
163 void WasmFunctionBuilder::SetAsmFunctionStartPosition(
164  size_t function_position) {
165  DCHECK_EQ(0, asm_func_start_source_position_);
166  DCHECK_GE(std::numeric_limits<uint32_t>::max(), function_position);
167  uint32_t function_position_u32 = static_cast<uint32_t>(function_position);
168  // Must be called before emitting any asm.js source position.
169  DCHECK_EQ(0, asm_offsets_.size());
170  asm_func_start_source_position_ = function_position_u32;
171  last_asm_source_position_ = function_position_u32;
172 }
173 
174 void WasmFunctionBuilder::DeleteCodeAfter(size_t position) {
175  DCHECK_LE(position, body_.size());
176  body_.Truncate(position);
177 }
178 
179 void WasmFunctionBuilder::WriteSignature(ZoneBuffer& buffer) const {
180  buffer.write_u32v(signature_index_);
181 }
182 
183 void WasmFunctionBuilder::WriteBody(ZoneBuffer& buffer) const {
184  size_t locals_size = locals_.Size();
185  buffer.write_size(locals_size + body_.size());
186  buffer.EnsureSpace(locals_size);
187  byte** ptr = buffer.pos_ptr();
188  locals_.Emit(*ptr);
189  (*ptr) += locals_size; // UGLY: manual bump of position pointer
190  if (body_.size() > 0) {
191  size_t base = buffer.offset();
192  buffer.write(body_.begin(), body_.size());
193  for (DirectCallIndex call : direct_calls_) {
194  buffer.patch_u32v(
195  base + call.offset,
196  call.direct_index +
197  static_cast<uint32_t>(builder_->function_imports_.size()));
198  }
199  }
200 }
201 
202 void WasmFunctionBuilder::WriteAsmWasmOffsetTable(ZoneBuffer& buffer) const {
203  if (asm_func_start_source_position_ == 0 && asm_offsets_.size() == 0) {
204  buffer.write_size(0);
205  return;
206  }
207  size_t locals_enc_size = LEBHelper::sizeof_u32v(locals_.Size());
208  size_t func_start_size =
209  LEBHelper::sizeof_u32v(asm_func_start_source_position_);
210  buffer.write_size(asm_offsets_.size() + locals_enc_size + func_start_size);
211  // Offset of the recorded byte offsets.
212  DCHECK_GE(kMaxUInt32, locals_.Size());
213  buffer.write_u32v(static_cast<uint32_t>(locals_.Size()));
214  // Start position of the function.
215  buffer.write_u32v(asm_func_start_source_position_);
216  buffer.write(asm_offsets_.begin(), asm_offsets_.size());
217 }
218 
219 WasmModuleBuilder::WasmModuleBuilder(Zone* zone)
220  : zone_(zone),
221  signatures_(zone),
222  function_imports_(zone),
223  function_exports_(zone),
224  global_imports_(zone),
225  functions_(zone),
226  data_segments_(zone),
227  indirect_functions_(zone),
228  globals_(zone),
229  signature_map_(zone),
230  start_function_index_(-1),
231  min_memory_size_(16),
232  max_memory_size_(0),
233  has_max_memory_size_(false),
234  has_shared_memory_(false) {}
235 
236 WasmFunctionBuilder* WasmModuleBuilder::AddFunction(FunctionSig* sig) {
237  functions_.push_back(new (zone_) WasmFunctionBuilder(this));
238  // Add the signature if one was provided here.
239  if (sig) functions_.back()->SetSignature(sig);
240  return functions_.back();
241 }
242 
243 void WasmModuleBuilder::AddDataSegment(const byte* data, uint32_t size,
244  uint32_t dest) {
245  data_segments_.push_back({ZoneVector<byte>(zone()), dest});
246  ZoneVector<byte>& vec = data_segments_.back().data;
247  for (uint32_t i = 0; i < size; i++) {
248  vec.push_back(data[i]);
249  }
250 }
251 
252 uint32_t WasmModuleBuilder::AddSignature(FunctionSig* sig) {
253  auto sig_entry = signature_map_.find(*sig);
254  if (sig_entry != signature_map_.end()) return sig_entry->second;
255  uint32_t index = static_cast<uint32_t>(signatures_.size());
256  signature_map_.emplace(*sig, index);
257  signatures_.push_back(sig);
258  return index;
259 }
260 
261 uint32_t WasmModuleBuilder::AllocateIndirectFunctions(uint32_t count) {
262  uint32_t index = static_cast<uint32_t>(indirect_functions_.size());
263  DCHECK_GE(FLAG_wasm_max_table_size, index);
264  if (count > FLAG_wasm_max_table_size - index) {
265  return std::numeric_limits<uint32_t>::max();
266  }
267  indirect_functions_.resize(indirect_functions_.size() + count);
268  return index;
269 }
270 
271 void WasmModuleBuilder::SetIndirectFunction(uint32_t indirect,
272  uint32_t direct) {
273  indirect_functions_[indirect] = direct;
274 }
275 
276 uint32_t WasmModuleBuilder::AddImport(Vector<const char> name,
277  FunctionSig* sig) {
278  function_imports_.push_back({name, AddSignature(sig)});
279  return static_cast<uint32_t>(function_imports_.size() - 1);
280 }
281 
282 uint32_t WasmModuleBuilder::AddGlobalImport(Vector<const char> name,
283  ValueType type) {
284  global_imports_.push_back({name, ValueTypes::ValueTypeCodeFor(type)});
285  return static_cast<uint32_t>(global_imports_.size() - 1);
286 }
287 
288 void WasmModuleBuilder::MarkStartFunction(WasmFunctionBuilder* function) {
289  start_function_index_ = function->func_index();
290 }
291 
292 void WasmModuleBuilder::AddExport(Vector<const char> name,
293  WasmFunctionBuilder* function) {
294  function_exports_.push_back({name, function->func_index()});
295 }
296 
297 uint32_t WasmModuleBuilder::AddGlobal(ValueType type, bool exported,
298  bool mutability,
299  const WasmInitExpr& init) {
300  globals_.push_back({type, exported, mutability, init});
301  return static_cast<uint32_t>(globals_.size() - 1);
302 }
303 
304 void WasmModuleBuilder::SetMinMemorySize(uint32_t value) {
305  min_memory_size_ = value;
306 }
307 
308 void WasmModuleBuilder::SetMaxMemorySize(uint32_t value) {
309  has_max_memory_size_ = true;
310  max_memory_size_ = value;
311 }
312 
313 void WasmModuleBuilder::SetHasSharedMemory() { has_shared_memory_ = true; }
314 
315 void WasmModuleBuilder::WriteTo(ZoneBuffer& buffer) const {
316  // == Emit magic =============================================================
317  buffer.write_u32(kWasmMagic);
318  buffer.write_u32(kWasmVersion);
319 
320  // == Emit signatures ========================================================
321  if (signatures_.size() > 0) {
322  size_t start = EmitSection(kTypeSectionCode, buffer);
323  buffer.write_size(signatures_.size());
324 
325  for (FunctionSig* sig : signatures_) {
326  buffer.write_u8(kWasmFunctionTypeCode);
327  buffer.write_size(sig->parameter_count());
328  for (auto param : sig->parameters()) {
329  buffer.write_u8(ValueTypes::ValueTypeCodeFor(param));
330  }
331  buffer.write_size(sig->return_count());
332  for (auto ret : sig->returns()) {
333  buffer.write_u8(ValueTypes::ValueTypeCodeFor(ret));
334  }
335  }
336  FixupSection(buffer, start);
337  }
338 
339  // == Emit imports ===========================================================
340  if (global_imports_.size() + function_imports_.size() > 0) {
341  size_t start = EmitSection(kImportSectionCode, buffer);
342  buffer.write_size(global_imports_.size() + function_imports_.size());
343  for (auto import : global_imports_) {
344  buffer.write_u32v(0); // module name (length)
345  buffer.write_string(import.name); // field name
346  buffer.write_u8(kExternalGlobal);
347  buffer.write_u8(import.type_code);
348  buffer.write_u8(0); // immutable
349  }
350  for (auto import : function_imports_) {
351  buffer.write_u32v(0); // module name (length)
352  buffer.write_string(import.name); // field name
353  buffer.write_u8(kExternalFunction);
354  buffer.write_u32v(import.sig_index);
355  }
356  FixupSection(buffer, start);
357  }
358 
359  // == Emit function signatures ===============================================
360  uint32_t num_function_names = 0;
361  if (functions_.size() > 0) {
362  size_t start = EmitSection(kFunctionSectionCode, buffer);
363  buffer.write_size(functions_.size());
364  for (auto function : functions_) {
365  function->WriteSignature(buffer);
366  if (!function->name_.is_empty()) ++num_function_names;
367  }
368  FixupSection(buffer, start);
369  }
370 
371  // == emit function table ====================================================
372  if (indirect_functions_.size() > 0) {
373  size_t start = EmitSection(kTableSectionCode, buffer);
374  buffer.write_u8(1); // table count
375  buffer.write_u8(kLocalAnyFunc);
376  buffer.write_u8(kHasMaximumFlag);
377  buffer.write_size(indirect_functions_.size());
378  buffer.write_size(indirect_functions_.size());
379  FixupSection(buffer, start);
380  }
381 
382  // == emit memory declaration ================================================
383  {
384  size_t start = EmitSection(kMemorySectionCode, buffer);
385  buffer.write_u8(1); // memory count
386  if (has_shared_memory_) {
387  buffer.write_u8(has_max_memory_size_ ? MemoryFlags::kSharedAndMaximum
388  : MemoryFlags::kSharedNoMaximum);
389  } else {
390  buffer.write_u8(has_max_memory_size_ ? MemoryFlags::kMaximum
391  : MemoryFlags::kNoMaximum);
392  }
393  buffer.write_u32v(min_memory_size_);
394  if (has_max_memory_size_) {
395  buffer.write_u32v(max_memory_size_);
396  }
397  FixupSection(buffer, start);
398  }
399 
400  // == Emit globals ===========================================================
401  if (globals_.size() > 0) {
402  size_t start = EmitSection(kGlobalSectionCode, buffer);
403  buffer.write_size(globals_.size());
404 
405  for (auto global : globals_) {
406  buffer.write_u8(ValueTypes::ValueTypeCodeFor(global.type));
407  buffer.write_u8(global.mutability ? 1 : 0);
408  switch (global.init.kind) {
409  case WasmInitExpr::kI32Const:
410  DCHECK_EQ(kWasmI32, global.type);
411  buffer.write_u8(kExprI32Const);
412  buffer.write_i32v(global.init.val.i32_const);
413  break;
414  case WasmInitExpr::kI64Const:
415  DCHECK_EQ(kWasmI64, global.type);
416  buffer.write_u8(kExprI64Const);
417  buffer.write_i64v(global.init.val.i64_const);
418  break;
419  case WasmInitExpr::kF32Const:
420  DCHECK_EQ(kWasmF32, global.type);
421  buffer.write_u8(kExprF32Const);
422  buffer.write_f32(global.init.val.f32_const);
423  break;
424  case WasmInitExpr::kF64Const:
425  DCHECK_EQ(kWasmF64, global.type);
426  buffer.write_u8(kExprF64Const);
427  buffer.write_f64(global.init.val.f64_const);
428  break;
429  case WasmInitExpr::kGlobalIndex:
430  buffer.write_u8(kExprGetGlobal);
431  buffer.write_u32v(global.init.val.global_index);
432  break;
433  default: {
434  // No initializer, emit a default value.
435  switch (global.type) {
436  case kWasmI32:
437  buffer.write_u8(kExprI32Const);
438  // LEB encoding of 0.
439  buffer.write_u8(0);
440  break;
441  case kWasmI64:
442  buffer.write_u8(kExprI64Const);
443  // LEB encoding of 0.
444  buffer.write_u8(0);
445  break;
446  case kWasmF32:
447  buffer.write_u8(kExprF32Const);
448  buffer.write_f32(0.f);
449  break;
450  case kWasmF64:
451  buffer.write_u8(kExprF64Const);
452  buffer.write_f64(0.);
453  break;
454  default:
455  UNREACHABLE();
456  }
457  }
458  }
459  buffer.write_u8(kExprEnd);
460  }
461  FixupSection(buffer, start);
462  }
463 
464  // == emit exports ===========================================================
465  if (!function_exports_.empty()) {
466  size_t start = EmitSection(kExportSectionCode, buffer);
467  buffer.write_size(function_exports_.size());
468  for (auto function_export : function_exports_) {
469  buffer.write_string(function_export.name);
470  buffer.write_u8(kExternalFunction);
471  buffer.write_size(function_export.function_index +
472  function_imports_.size());
473  }
474  FixupSection(buffer, start);
475  }
476 
477  // == emit start function index ==============================================
478  if (start_function_index_ >= 0) {
479  size_t start = EmitSection(kStartSectionCode, buffer);
480  buffer.write_size(start_function_index_ + function_imports_.size());
481  FixupSection(buffer, start);
482  }
483 
484  // == emit function table elements ===========================================
485  if (indirect_functions_.size() > 0) {
486  size_t start = EmitSection(kElementSectionCode, buffer);
487  buffer.write_u8(1); // count of entries
488  buffer.write_u8(0); // table index
489  buffer.write_u8(kExprI32Const); // offset
490  buffer.write_u32v(0);
491  buffer.write_u8(kExprEnd);
492  buffer.write_size(indirect_functions_.size()); // element count
493 
494  for (auto index : indirect_functions_) {
495  buffer.write_size(index + function_imports_.size());
496  }
497 
498  FixupSection(buffer, start);
499  }
500 
501  // == emit code ==============================================================
502  if (functions_.size() > 0) {
503  size_t start = EmitSection(kCodeSectionCode, buffer);
504  buffer.write_size(functions_.size());
505  for (auto function : functions_) {
506  function->WriteBody(buffer);
507  }
508  FixupSection(buffer, start);
509  }
510 
511  // == emit data segments =====================================================
512  if (data_segments_.size() > 0) {
513  size_t start = EmitSection(kDataSectionCode, buffer);
514  buffer.write_size(data_segments_.size());
515 
516  for (auto segment : data_segments_) {
517  buffer.write_u8(0); // linear memory segment
518  buffer.write_u8(kExprI32Const); // initializer expression for dest
519  buffer.write_u32v(segment.dest);
520  buffer.write_u8(kExprEnd);
521  buffer.write_u32v(static_cast<uint32_t>(segment.data.size()));
522  buffer.write(&segment.data[0], segment.data.size());
523  }
524  FixupSection(buffer, start);
525  }
526 
527  // == Emit names =============================================================
528  if (num_function_names > 0 || !function_imports_.empty()) {
529  // Emit the section code.
530  buffer.write_u8(kUnknownSectionCode);
531  // Emit a placeholder for the length.
532  size_t start = buffer.reserve_u32v();
533  // Emit the section string.
534  buffer.write_size(4);
535  buffer.write(reinterpret_cast<const byte*>("name"), 4);
536  // Emit a subsection for the function names.
537  buffer.write_u8(NameSectionKindCode::kFunction);
538  // Emit a placeholder for the subsection length.
539  size_t functions_start = buffer.reserve_u32v();
540  // Emit the function names.
541  // Imports are always named.
542  uint32_t num_imports = static_cast<uint32_t>(function_imports_.size());
543  buffer.write_size(num_imports + num_function_names);
544  uint32_t function_index = 0;
545  for (; function_index < num_imports; ++function_index) {
546  const WasmFunctionImport* import = &function_imports_[function_index];
547  DCHECK(!import->name.is_empty());
548  buffer.write_u32v(function_index);
549  buffer.write_string(import->name);
550  }
551  if (num_function_names > 0) {
552  for (auto function : functions_) {
553  DCHECK_EQ(function_index,
554  function->func_index() + function_imports_.size());
555  if (!function->name_.is_empty()) {
556  buffer.write_u32v(function_index);
557  buffer.write_string(function->name_);
558  }
559  ++function_index;
560  }
561  }
562  FixupSection(buffer, functions_start);
563  FixupSection(buffer, start);
564  }
565 }
566 
567 void WasmModuleBuilder::WriteAsmJsOffsetTable(ZoneBuffer& buffer) const {
568  // == Emit asm.js offset table ===============================================
569  buffer.write_size(functions_.size());
570  // Emit the offset table per function.
571  for (auto function : functions_) {
572  function->WriteAsmWasmOffsetTable(buffer);
573  }
574  // Append a 0 to indicate that this is an encoded table.
575  buffer.write_u8(0);
576 }
577 } // namespace wasm
578 } // namespace internal
579 } // namespace v8
Definition: libplatform.h:13