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