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/v8.h"
9 #include "src/zone/zone-containers.h"
10
11 #include "src/wasm/ast-decoder.h"
12 #include "src/wasm/leb-helper.h"
13 #include "src/wasm/wasm-macro-gen.h"
14 #include "src/wasm/wasm-module-builder.h"
15 #include "src/wasm/wasm-module.h"
16 #include "src/wasm/wasm-opcodes.h"
17
18 #include "src/v8memory.h"
19
20 #if DEBUG
21 #define TRACE(...) \
22 do { \
23 if (FLAG_trace_wasm_encoder) PrintF(__VA_ARGS__); \
24 } while (false)
25 #else
26 #define TRACE(...)
27 #endif
28
29 namespace v8 {
30 namespace internal {
31 namespace wasm {
32
33 // Emit a section code and the size as a padded varint that can be patched
34 // later.
EmitSection(WasmSectionCode code,ZoneBuffer & buffer)35 size_t EmitSection(WasmSectionCode code, ZoneBuffer& buffer) {
36 // Emit the section code.
37 buffer.write_u8(code);
38
39 // Emit a placeholder for the length.
40 return buffer.reserve_u32v();
41 }
42
43 // Patch the size of a section after it's finished.
FixupSection(ZoneBuffer & buffer,size_t start)44 void FixupSection(ZoneBuffer& buffer, size_t start) {
45 buffer.patch_u32v(start, static_cast<uint32_t>(buffer.offset() - start -
46 kPaddedVarInt32Size));
47 }
48
WasmFunctionBuilder(WasmModuleBuilder * builder)49 WasmFunctionBuilder::WasmFunctionBuilder(WasmModuleBuilder* builder)
50 : builder_(builder),
51 locals_(builder->zone()),
52 signature_index_(0),
53 exported_(0),
54 func_index_(static_cast<uint32_t>(builder->functions_.size())),
55 body_(builder->zone()),
56 name_(builder->zone()),
57 exported_name_(builder->zone()),
58 i32_temps_(builder->zone()),
59 i64_temps_(builder->zone()),
60 f32_temps_(builder->zone()),
61 f64_temps_(builder->zone()),
62 direct_calls_(builder->zone()),
63 asm_offsets_(builder->zone(), 8) {}
64
EmitVarInt(uint32_t val)65 void WasmFunctionBuilder::EmitVarInt(uint32_t val) {
66 byte buffer[8];
67 byte* ptr = buffer;
68 LEBHelper::write_u32v(&ptr, val);
69 for (byte* p = buffer; p < ptr; p++) {
70 body_.push_back(*p);
71 }
72 }
73
SetSignature(FunctionSig * sig)74 void WasmFunctionBuilder::SetSignature(FunctionSig* sig) {
75 DCHECK(!locals_.has_sig());
76 locals_.set_sig(sig);
77 signature_index_ = builder_->AddSignature(sig);
78 }
79
AddLocal(LocalType type)80 uint32_t WasmFunctionBuilder::AddLocal(LocalType type) {
81 DCHECK(locals_.has_sig());
82 return locals_.AddLocals(1, type);
83 }
84
EmitGetLocal(uint32_t local_index)85 void WasmFunctionBuilder::EmitGetLocal(uint32_t local_index) {
86 EmitWithVarInt(kExprGetLocal, local_index);
87 }
88
EmitSetLocal(uint32_t local_index)89 void WasmFunctionBuilder::EmitSetLocal(uint32_t local_index) {
90 EmitWithVarInt(kExprSetLocal, local_index);
91 }
92
EmitTeeLocal(uint32_t local_index)93 void WasmFunctionBuilder::EmitTeeLocal(uint32_t local_index) {
94 EmitWithVarInt(kExprTeeLocal, local_index);
95 }
96
EmitCode(const byte * code,uint32_t code_size)97 void WasmFunctionBuilder::EmitCode(const byte* code, uint32_t code_size) {
98 for (size_t i = 0; i < code_size; ++i) {
99 body_.push_back(code[i]);
100 }
101 }
102
Emit(WasmOpcode opcode)103 void WasmFunctionBuilder::Emit(WasmOpcode opcode) {
104 body_.push_back(static_cast<byte>(opcode));
105 }
106
EmitWithU8(WasmOpcode opcode,const byte immediate)107 void WasmFunctionBuilder::EmitWithU8(WasmOpcode opcode, const byte immediate) {
108 body_.push_back(static_cast<byte>(opcode));
109 body_.push_back(immediate);
110 }
111
EmitWithU8U8(WasmOpcode opcode,const byte imm1,const byte imm2)112 void WasmFunctionBuilder::EmitWithU8U8(WasmOpcode opcode, const byte imm1,
113 const byte imm2) {
114 body_.push_back(static_cast<byte>(opcode));
115 body_.push_back(imm1);
116 body_.push_back(imm2);
117 }
118
EmitWithVarInt(WasmOpcode opcode,uint32_t immediate)119 void WasmFunctionBuilder::EmitWithVarInt(WasmOpcode opcode,
120 uint32_t immediate) {
121 body_.push_back(static_cast<byte>(opcode));
122 EmitVarInt(immediate);
123 }
124
EmitI32Const(int32_t value)125 void WasmFunctionBuilder::EmitI32Const(int32_t value) {
126 // TODO(titzer): variable-length signed and unsigned i32 constants.
127 if (-128 <= value && value <= 127) {
128 EmitWithU8(kExprI8Const, static_cast<byte>(value));
129 } else {
130 byte code[] = {WASM_I32V_5(value)};
131 EmitCode(code, sizeof(code));
132 }
133 }
134
EmitDirectCallIndex(uint32_t index)135 void WasmFunctionBuilder::EmitDirectCallIndex(uint32_t index) {
136 DirectCallIndex call;
137 call.offset = body_.size();
138 call.direct_index = index;
139 direct_calls_.push_back(call);
140 byte code[] = {U32V_5(0)};
141 EmitCode(code, sizeof(code));
142 }
143
Export()144 void WasmFunctionBuilder::Export() { exported_ = true; }
145
ExportAs(Vector<const char> name)146 void WasmFunctionBuilder::ExportAs(Vector<const char> name) {
147 exported_ = true;
148 exported_name_.resize(name.length());
149 memcpy(exported_name_.data(), name.start(), name.length());
150 }
151
SetName(Vector<const char> name)152 void WasmFunctionBuilder::SetName(Vector<const char> name) {
153 name_.resize(name.length());
154 memcpy(name_.data(), name.start(), name.length());
155 }
156
AddAsmWasmOffset(int asm_position)157 void WasmFunctionBuilder::AddAsmWasmOffset(int asm_position) {
158 // We only want to emit one mapping per byte offset:
159 DCHECK(asm_offsets_.size() == 0 || body_.size() > last_asm_byte_offset_);
160
161 DCHECK_LE(body_.size(), kMaxUInt32);
162 uint32_t byte_offset = static_cast<uint32_t>(body_.size());
163 asm_offsets_.write_u32v(byte_offset - last_asm_byte_offset_);
164 last_asm_byte_offset_ = byte_offset;
165
166 DCHECK_GE(asm_position, 0);
167 asm_offsets_.write_i32v(asm_position - last_asm_source_position_);
168 last_asm_source_position_ = asm_position;
169 }
170
WriteSignature(ZoneBuffer & buffer) const171 void WasmFunctionBuilder::WriteSignature(ZoneBuffer& buffer) const {
172 buffer.write_u32v(signature_index_);
173 }
174
WriteExport(ZoneBuffer & buffer) const175 void WasmFunctionBuilder::WriteExport(ZoneBuffer& buffer) const {
176 if (exported_) {
177 const ZoneVector<char>* exported_name =
178 exported_name_.size() == 0 ? &name_ : &exported_name_;
179 buffer.write_size(exported_name->size());
180 buffer.write(reinterpret_cast<const byte*>(exported_name->data()),
181 exported_name->size());
182 buffer.write_u8(kExternalFunction);
183 buffer.write_u32v(func_index_ +
184 static_cast<uint32_t>(builder_->imports_.size()));
185 }
186 }
187
WriteBody(ZoneBuffer & buffer) const188 void WasmFunctionBuilder::WriteBody(ZoneBuffer& buffer) const {
189 size_t locals_size = locals_.Size();
190 buffer.write_size(locals_size + body_.size());
191 buffer.EnsureSpace(locals_size);
192 byte** ptr = buffer.pos_ptr();
193 locals_.Emit(*ptr);
194 (*ptr) += locals_size; // UGLY: manual bump of position pointer
195 if (body_.size() > 0) {
196 size_t base = buffer.offset();
197 buffer.write(&body_[0], body_.size());
198 for (DirectCallIndex call : direct_calls_) {
199 buffer.patch_u32v(
200 base + call.offset,
201 call.direct_index + static_cast<uint32_t>(builder_->imports_.size()));
202 }
203 }
204 }
205
WriteAsmWasmOffsetTable(ZoneBuffer & buffer) const206 void WasmFunctionBuilder::WriteAsmWasmOffsetTable(ZoneBuffer& buffer) const {
207 if (asm_offsets_.size() == 0) {
208 buffer.write_size(0);
209 return;
210 }
211 buffer.write_size(asm_offsets_.size() + kInt32Size);
212 // Offset of the recorded byte offsets.
213 DCHECK_GE(kMaxUInt32, locals_.Size());
214 buffer.write_u32(static_cast<uint32_t>(locals_.Size()));
215 buffer.write(asm_offsets_.begin(), asm_offsets_.size());
216 }
217
WasmModuleBuilder(Zone * zone)218 WasmModuleBuilder::WasmModuleBuilder(Zone* zone)
219 : zone_(zone),
220 signatures_(zone),
221 imports_(zone),
222 functions_(zone),
223 data_segments_(zone),
224 indirect_functions_(zone),
225 globals_(zone),
226 signature_map_(zone),
227 start_function_index_(-1) {}
228
AddFunction(FunctionSig * sig)229 WasmFunctionBuilder* WasmModuleBuilder::AddFunction(FunctionSig* sig) {
230 functions_.push_back(new (zone_) WasmFunctionBuilder(this));
231 // Add the signature if one was provided here.
232 if (sig) functions_.back()->SetSignature(sig);
233 return functions_.back();
234 }
235
AddDataSegment(const byte * data,uint32_t size,uint32_t dest)236 void WasmModuleBuilder::AddDataSegment(const byte* data, uint32_t size,
237 uint32_t dest) {
238 data_segments_.push_back({ZoneVector<byte>(zone()), dest});
239 ZoneVector<byte>& vec = data_segments_.back().data;
240 for (uint32_t i = 0; i < size; i++) {
241 vec.push_back(data[i]);
242 }
243 }
244
operator ()(FunctionSig * a,FunctionSig * b) const245 bool WasmModuleBuilder::CompareFunctionSigs::operator()(FunctionSig* a,
246 FunctionSig* b) const {
247 if (a->return_count() < b->return_count()) return true;
248 if (a->return_count() > b->return_count()) return false;
249 if (a->parameter_count() < b->parameter_count()) return true;
250 if (a->parameter_count() > b->parameter_count()) return false;
251 for (size_t r = 0; r < a->return_count(); r++) {
252 if (a->GetReturn(r) < b->GetReturn(r)) return true;
253 if (a->GetReturn(r) > b->GetReturn(r)) return false;
254 }
255 for (size_t p = 0; p < a->parameter_count(); p++) {
256 if (a->GetParam(p) < b->GetParam(p)) return true;
257 if (a->GetParam(p) > b->GetParam(p)) return false;
258 }
259 return false;
260 }
261
AddSignature(FunctionSig * sig)262 uint32_t WasmModuleBuilder::AddSignature(FunctionSig* sig) {
263 SignatureMap::iterator pos = signature_map_.find(sig);
264 if (pos != signature_map_.end()) {
265 return pos->second;
266 } else {
267 uint32_t index = static_cast<uint32_t>(signatures_.size());
268 signature_map_[sig] = index;
269 signatures_.push_back(sig);
270 return index;
271 }
272 }
273
AddIndirectFunction(uint32_t index)274 void WasmModuleBuilder::AddIndirectFunction(uint32_t index) {
275 indirect_functions_.push_back(index);
276 }
277
AddImport(const char * name,int name_length,FunctionSig * sig)278 uint32_t WasmModuleBuilder::AddImport(const char* name, int name_length,
279 FunctionSig* sig) {
280 imports_.push_back({AddSignature(sig), name, name_length});
281 return static_cast<uint32_t>(imports_.size() - 1);
282 }
283
MarkStartFunction(WasmFunctionBuilder * function)284 void WasmModuleBuilder::MarkStartFunction(WasmFunctionBuilder* function) {
285 start_function_index_ = function->func_index();
286 }
287
AddGlobal(LocalType type,bool exported,bool mutability,const WasmInitExpr & init)288 uint32_t WasmModuleBuilder::AddGlobal(LocalType type, bool exported,
289 bool mutability,
290 const WasmInitExpr& init) {
291 globals_.push_back({type, exported, mutability, init});
292 return static_cast<uint32_t>(globals_.size() - 1);
293 }
294
WriteTo(ZoneBuffer & buffer) const295 void WasmModuleBuilder::WriteTo(ZoneBuffer& buffer) const {
296 uint32_t exports = 0;
297
298 // == Emit magic =============================================================
299 TRACE("emit magic\n");
300 buffer.write_u32(kWasmMagic);
301 buffer.write_u32(kWasmVersion);
302
303 // == Emit signatures ========================================================
304 if (signatures_.size() > 0) {
305 size_t start = EmitSection(kTypeSectionCode, buffer);
306 buffer.write_size(signatures_.size());
307
308 for (FunctionSig* sig : signatures_) {
309 buffer.write_u8(kWasmFunctionTypeForm);
310 buffer.write_size(sig->parameter_count());
311 for (size_t j = 0; j < sig->parameter_count(); j++) {
312 buffer.write_u8(WasmOpcodes::LocalTypeCodeFor(sig->GetParam(j)));
313 }
314 buffer.write_size(sig->return_count());
315 for (size_t j = 0; j < sig->return_count(); j++) {
316 buffer.write_u8(WasmOpcodes::LocalTypeCodeFor(sig->GetReturn(j)));
317 }
318 }
319 FixupSection(buffer, start);
320 }
321
322 // == Emit imports ===========================================================
323 if (imports_.size() > 0) {
324 size_t start = EmitSection(kImportSectionCode, buffer);
325 buffer.write_size(imports_.size());
326 for (auto import : imports_) {
327 buffer.write_u32v(import.name_length); // module name length
328 buffer.write(reinterpret_cast<const byte*>(import.name), // module name
329 import.name_length);
330 buffer.write_u32v(0); // field name length
331 buffer.write_u8(kExternalFunction);
332 buffer.write_u32v(import.sig_index);
333 }
334 FixupSection(buffer, start);
335 }
336
337 // == Emit function signatures ===============================================
338 bool has_names = false;
339 if (functions_.size() > 0) {
340 size_t start = EmitSection(kFunctionSectionCode, buffer);
341 buffer.write_size(functions_.size());
342 for (auto function : functions_) {
343 function->WriteSignature(buffer);
344 if (function->exported()) exports++;
345 if (function->name_.size() > 0) has_names = true;
346 }
347 FixupSection(buffer, start);
348 }
349
350 // == emit function table ====================================================
351 if (indirect_functions_.size() > 0) {
352 size_t start = EmitSection(kTableSectionCode, buffer);
353 buffer.write_u8(1); // table count
354 buffer.write_u8(kWasmAnyFunctionTypeForm);
355 buffer.write_u8(kResizableMaximumFlag);
356 buffer.write_size(indirect_functions_.size());
357 buffer.write_size(indirect_functions_.size());
358 FixupSection(buffer, start);
359 }
360
361 // == emit memory declaration ================================================
362 {
363 size_t start = EmitSection(kMemorySectionCode, buffer);
364 buffer.write_u8(1); // memory count
365 buffer.write_u32v(kResizableMaximumFlag);
366 buffer.write_u32v(16); // min memory size
367 buffer.write_u32v(32); // max memory size
368 FixupSection(buffer, start);
369 }
370
371 // == Emit globals ===========================================================
372 if (globals_.size() > 0) {
373 size_t start = EmitSection(kGlobalSectionCode, buffer);
374 buffer.write_size(globals_.size());
375
376 for (auto global : globals_) {
377 buffer.write_u8(WasmOpcodes::LocalTypeCodeFor(global.type));
378 buffer.write_u8(global.mutability ? 1 : 0);
379 switch (global.init.kind) {
380 case WasmInitExpr::kI32Const: {
381 DCHECK_EQ(kAstI32, global.type);
382 const byte code[] = {WASM_I32V_5(global.init.val.i32_const)};
383 buffer.write(code, sizeof(code));
384 break;
385 }
386 case WasmInitExpr::kI64Const: {
387 DCHECK_EQ(kAstI64, global.type);
388 const byte code[] = {WASM_I64V_10(global.init.val.i64_const)};
389 buffer.write(code, sizeof(code));
390 break;
391 }
392 case WasmInitExpr::kF32Const: {
393 DCHECK_EQ(kAstF32, global.type);
394 const byte code[] = {WASM_F32(global.init.val.f32_const)};
395 buffer.write(code, sizeof(code));
396 break;
397 }
398 case WasmInitExpr::kF64Const: {
399 DCHECK_EQ(kAstF64, global.type);
400 const byte code[] = {WASM_F64(global.init.val.f64_const)};
401 buffer.write(code, sizeof(code));
402 break;
403 }
404 case WasmInitExpr::kGlobalIndex: {
405 const byte code[] = {kExprGetGlobal,
406 U32V_5(global.init.val.global_index)};
407 buffer.write(code, sizeof(code));
408 break;
409 }
410 default: {
411 // No initializer, emit a default value.
412 switch (global.type) {
413 case kAstI32: {
414 const byte code[] = {WASM_I32V_1(0)};
415 buffer.write(code, sizeof(code));
416 break;
417 }
418 case kAstI64: {
419 const byte code[] = {WASM_I64V_1(0)};
420 buffer.write(code, sizeof(code));
421 break;
422 }
423 case kAstF32: {
424 const byte code[] = {WASM_F32(0.0)};
425 buffer.write(code, sizeof(code));
426 break;
427 }
428 case kAstF64: {
429 const byte code[] = {WASM_F64(0.0)};
430 buffer.write(code, sizeof(code));
431 break;
432 }
433 default:
434 UNREACHABLE();
435 }
436 }
437 }
438 buffer.write_u8(kExprEnd);
439 }
440 FixupSection(buffer, start);
441 }
442
443 // == emit exports ===========================================================
444 if (exports > 0) {
445 size_t start = EmitSection(kExportSectionCode, buffer);
446 buffer.write_u32v(exports);
447 for (auto function : functions_) function->WriteExport(buffer);
448 FixupSection(buffer, start);
449 }
450
451 // == emit start function index ==============================================
452 if (start_function_index_ >= 0) {
453 size_t start = EmitSection(kStartSectionCode, buffer);
454 buffer.write_u32v(start_function_index_ +
455 static_cast<uint32_t>(imports_.size()));
456 FixupSection(buffer, start);
457 }
458
459 // == emit function table elements ===========================================
460 if (indirect_functions_.size() > 0) {
461 size_t start = EmitSection(kElementSectionCode, buffer);
462 buffer.write_u8(1); // count of entries
463 buffer.write_u8(0); // table index
464 buffer.write_u8(kExprI32Const); // offset
465 buffer.write_u32v(0);
466 buffer.write_u8(kExprEnd);
467 buffer.write_size(indirect_functions_.size()); // element count
468
469 for (auto index : indirect_functions_) {
470 buffer.write_u32v(index + static_cast<uint32_t>(imports_.size()));
471 }
472
473 FixupSection(buffer, start);
474 }
475
476 // == emit code ==============================================================
477 if (functions_.size() > 0) {
478 size_t start = EmitSection(kCodeSectionCode, buffer);
479 buffer.write_size(functions_.size());
480 for (auto function : functions_) {
481 function->WriteBody(buffer);
482 }
483 FixupSection(buffer, start);
484 }
485
486 // == emit data segments =====================================================
487 if (data_segments_.size() > 0) {
488 size_t start = EmitSection(kDataSectionCode, buffer);
489 buffer.write_size(data_segments_.size());
490
491 for (auto segment : data_segments_) {
492 buffer.write_u8(0); // linear memory segment
493 buffer.write_u8(kExprI32Const); // initializer expression for dest
494 buffer.write_u32v(segment.dest);
495 buffer.write_u8(kExprEnd);
496 buffer.write_u32v(static_cast<uint32_t>(segment.data.size()));
497 buffer.write(&segment.data[0], segment.data.size());
498 }
499 FixupSection(buffer, start);
500 }
501
502 // == Emit names =============================================================
503 if (has_names) {
504 // Emit the section code.
505 buffer.write_u8(kUnknownSectionCode);
506 // Emit a placeholder for the length.
507 size_t start = buffer.reserve_u32v();
508 // Emit the section string.
509 buffer.write_size(4);
510 buffer.write(reinterpret_cast<const byte*>("name"), 4);
511 // Emit the names.
512 size_t count = functions_.size() + imports_.size();
513 buffer.write_size(count);
514 for (size_t i = 0; i < imports_.size(); i++) {
515 buffer.write_u8(0); // empty name for import
516 buffer.write_u8(0); // no local variables
517 }
518 for (auto function : functions_) {
519 buffer.write_size(function->name_.size());
520 if (function->name_.size() > 0) {
521 buffer.write(reinterpret_cast<const byte*>(&function->name_[0]),
522 function->name_.size());
523 }
524 buffer.write_u8(0);
525 }
526 FixupSection(buffer, start);
527 }
528 }
529
WriteAsmJsOffsetTable(ZoneBuffer & buffer) const530 void WasmModuleBuilder::WriteAsmJsOffsetTable(ZoneBuffer& buffer) const {
531 // == Emit asm.js offset table ===============================================
532 buffer.write_size(functions_.size());
533 // Emit the offset table per function.
534 for (auto function : functions_) {
535 function->WriteAsmWasmOffsetTable(buffer);
536 }
537 }
538 } // namespace wasm
539 } // namespace internal
540 } // namespace v8
541