1 /*
2 * Copyright 2016 WebAssembly Community Group participants
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include <algorithm>
18 #include <fstream>
19
20 #include "ir/module-utils.h"
21 #include "support/bits.h"
22 #include "support/debug.h"
23 #include "wasm-binary.h"
24 #include "wasm-debug.h"
25 #include "wasm-stack.h"
26
27 #define DEBUG_TYPE "binary"
28
29 namespace wasm {
30
prepare()31 void WasmBinaryWriter::prepare() {
32 // Collect function types and their frequencies. Collect information in each
33 // function in parallel, then merge.
34 ModuleUtils::collectSignatures(*wasm, types, typeIndices);
35 importInfo = wasm::make_unique<ImportInfo>(*wasm);
36 }
37
write()38 void WasmBinaryWriter::write() {
39 writeHeader();
40
41 writeDylinkSection();
42
43 initializeDebugInfo();
44 if (sourceMap) {
45 writeSourceMapProlog();
46 }
47
48 writeTypes();
49 writeImports();
50 writeFunctionSignatures();
51 writeFunctionTableDeclaration();
52 writeMemory();
53 writeEvents();
54 writeGlobals();
55 writeExports();
56 writeStart();
57 writeTableElements();
58 writeDataCount();
59 writeFunctions();
60 writeDataSegments();
61 if (debugInfo) {
62 writeNames();
63 }
64 if (sourceMap && !sourceMapUrl.empty()) {
65 writeSourceMapUrl();
66 }
67 if (symbolMap.size() > 0) {
68 writeSymbolMap();
69 }
70
71 if (sourceMap) {
72 writeSourceMapEpilog();
73 }
74
75 #ifdef BUILD_LLVM_DWARF
76 // Update DWARF user sections after writing the data they refer to
77 // (function bodies), and before writing the user sections themselves.
78 if (Debug::hasDWARFSections(*wasm)) {
79 Debug::writeDWARFSections(*wasm, binaryLocations);
80 }
81 #endif
82
83 writeLateUserSections();
84 writeFeaturesSection();
85
86 finishUp();
87 }
88
writeHeader()89 void WasmBinaryWriter::writeHeader() {
90 BYN_TRACE("== writeHeader\n");
91 o << int32_t(BinaryConsts::Magic); // magic number \0asm
92 o << int32_t(BinaryConsts::Version);
93 }
94
writeU32LEBPlaceholder()95 int32_t WasmBinaryWriter::writeU32LEBPlaceholder() {
96 int32_t ret = o.size();
97 o << int32_t(0);
98 o << int8_t(0);
99 return ret;
100 }
101
writeResizableLimits(Address initial,Address maximum,bool hasMaximum,bool shared,bool is64)102 void WasmBinaryWriter::writeResizableLimits(
103 Address initial, Address maximum, bool hasMaximum, bool shared, bool is64) {
104 uint32_t flags = (hasMaximum ? (uint32_t)BinaryConsts::HasMaximum : 0U) |
105 (shared ? (uint32_t)BinaryConsts::IsShared : 0U) |
106 (is64 ? (uint32_t)BinaryConsts::Is64 : 0U);
107 o << U32LEB(flags);
108 o << U32LEB(initial);
109 if (hasMaximum) {
110 o << U32LEB(maximum);
111 }
112 }
113
startSection(T code)114 template<typename T> int32_t WasmBinaryWriter::startSection(T code) {
115 o << uint8_t(code);
116 if (sourceMap) {
117 sourceMapLocationsSizeAtSectionStart = sourceMapLocations.size();
118 }
119 binaryLocationsSizeAtSectionStart = binaryLocations.expressions.size();
120 return writeU32LEBPlaceholder(); // section size to be filled in later
121 }
122
finishSection(int32_t start)123 void WasmBinaryWriter::finishSection(int32_t start) {
124 // section size does not include the reserved bytes of the size field itself
125 int32_t size = o.size() - start - MaxLEB32Bytes;
126 auto sizeFieldSize = o.writeAt(start, U32LEB(size));
127 // We can move things back if the actual LEB for the size doesn't use the
128 // maximum 5 bytes. In that case we need to adjust offsets after we move
129 // things backwards.
130 auto adjustmentForLEBShrinking = MaxLEB32Bytes - sizeFieldSize;
131 if (adjustmentForLEBShrinking) {
132 // we can save some room, nice
133 assert(sizeFieldSize < MaxLEB32Bytes);
134 std::move(&o[start] + MaxLEB32Bytes,
135 &o[start] + MaxLEB32Bytes + size,
136 &o[start] + sizeFieldSize);
137 o.resize(o.size() - adjustmentForLEBShrinking);
138 if (sourceMap) {
139 for (auto i = sourceMapLocationsSizeAtSectionStart;
140 i < sourceMapLocations.size();
141 ++i) {
142 sourceMapLocations[i].first -= adjustmentForLEBShrinking;
143 }
144 }
145 }
146
147 if (binaryLocationsSizeAtSectionStart != binaryLocations.expressions.size()) {
148 // We added the binary locations, adjust them: they must be relative
149 // to the code section.
150 assert(binaryLocationsSizeAtSectionStart == 0);
151 // The section type byte is right before the LEB for the size; we want
152 // offsets that are relative to the body, which is after that section type
153 // byte and the the size LEB.
154 auto body = start + sizeFieldSize;
155 // Offsets are relative to the body of the code section: after the
156 // section type byte and the size.
157 // Everything was moved by the adjustment, track that. After this,
158 // we are at the right absolute address.
159 // We are relative to the section start.
160 auto totalAdjustment = adjustmentForLEBShrinking + body;
161 for (auto& pair : binaryLocations.expressions) {
162 pair.second.start -= totalAdjustment;
163 pair.second.end -= totalAdjustment;
164 }
165 for (auto& pair : binaryLocations.functions) {
166 pair.second.start -= totalAdjustment;
167 pair.second.declarations -= totalAdjustment;
168 pair.second.end -= totalAdjustment;
169 }
170 for (auto& pair : binaryLocations.delimiters) {
171 for (auto& item : pair.second) {
172 item -= totalAdjustment;
173 }
174 }
175 }
176 }
177
178 int32_t
startSubsection(BinaryConsts::UserSections::Subsection code)179 WasmBinaryWriter::startSubsection(BinaryConsts::UserSections::Subsection code) {
180 return startSection(code);
181 }
182
finishSubsection(int32_t start)183 void WasmBinaryWriter::finishSubsection(int32_t start) { finishSection(start); }
184
writeStart()185 void WasmBinaryWriter::writeStart() {
186 if (!wasm->start.is()) {
187 return;
188 }
189 BYN_TRACE("== writeStart\n");
190 auto start = startSection(BinaryConsts::Section::Start);
191 o << U32LEB(getFunctionIndex(wasm->start.str));
192 finishSection(start);
193 }
194
writeMemory()195 void WasmBinaryWriter::writeMemory() {
196 if (!wasm->memory.exists || wasm->memory.imported()) {
197 return;
198 }
199 BYN_TRACE("== writeMemory\n");
200 auto start = startSection(BinaryConsts::Section::Memory);
201 o << U32LEB(1); // Define 1 memory
202 writeResizableLimits(wasm->memory.initial,
203 wasm->memory.max,
204 wasm->memory.hasMax(),
205 wasm->memory.shared,
206 wasm->memory.is64());
207 finishSection(start);
208 }
209
writeTypes()210 void WasmBinaryWriter::writeTypes() {
211 if (types.size() == 0) {
212 return;
213 }
214 BYN_TRACE("== writeTypes\n");
215 auto start = startSection(BinaryConsts::Section::Type);
216 o << U32LEB(types.size());
217 for (Index i = 0; i < types.size(); ++i) {
218 Signature& sig = types[i];
219 BYN_TRACE("write " << sig.params << " -> " << sig.results << std::endl);
220 o << S32LEB(BinaryConsts::EncodedType::Func);
221 for (auto& sigType : {sig.params, sig.results}) {
222 o << U32LEB(sigType.size());
223 for (const auto& type : sigType) {
224 o << binaryType(type);
225 }
226 }
227 }
228 finishSection(start);
229 }
230
writeImports()231 void WasmBinaryWriter::writeImports() {
232 auto num = importInfo->getNumImports();
233 if (num == 0) {
234 return;
235 }
236 BYN_TRACE("== writeImports\n");
237 auto start = startSection(BinaryConsts::Section::Import);
238 o << U32LEB(num);
239 auto writeImportHeader = [&](Importable* import) {
240 writeInlineString(import->module.str);
241 writeInlineString(import->base.str);
242 };
243 ModuleUtils::iterImportedFunctions(*wasm, [&](Function* func) {
244 BYN_TRACE("write one function\n");
245 writeImportHeader(func);
246 o << U32LEB(int32_t(ExternalKind::Function));
247 o << U32LEB(getTypeIndex(func->sig));
248 });
249 ModuleUtils::iterImportedGlobals(*wasm, [&](Global* global) {
250 BYN_TRACE("write one global\n");
251 writeImportHeader(global);
252 o << U32LEB(int32_t(ExternalKind::Global));
253 o << binaryType(global->type);
254 o << U32LEB(global->mutable_);
255 });
256 ModuleUtils::iterImportedEvents(*wasm, [&](Event* event) {
257 BYN_TRACE("write one event\n");
258 writeImportHeader(event);
259 o << U32LEB(int32_t(ExternalKind::Event));
260 o << U32LEB(event->attribute);
261 o << U32LEB(getTypeIndex(event->sig));
262 });
263 if (wasm->memory.imported()) {
264 BYN_TRACE("write one memory\n");
265 writeImportHeader(&wasm->memory);
266 o << U32LEB(int32_t(ExternalKind::Memory));
267 writeResizableLimits(wasm->memory.initial,
268 wasm->memory.max,
269 wasm->memory.hasMax(),
270 wasm->memory.shared,
271 wasm->memory.is64());
272 }
273 if (wasm->table.imported()) {
274 BYN_TRACE("write one table\n");
275 writeImportHeader(&wasm->table);
276 o << U32LEB(int32_t(ExternalKind::Table));
277 o << S32LEB(BinaryConsts::EncodedType::funcref);
278 writeResizableLimits(wasm->table.initial,
279 wasm->table.max,
280 wasm->table.hasMax(),
281 /*shared=*/false,
282 /*is64*/ false);
283 }
284 finishSection(start);
285 }
286
writeFunctionSignatures()287 void WasmBinaryWriter::writeFunctionSignatures() {
288 if (importInfo->getNumDefinedFunctions() == 0) {
289 return;
290 }
291 BYN_TRACE("== writeFunctionSignatures\n");
292 auto start = startSection(BinaryConsts::Section::Function);
293 o << U32LEB(importInfo->getNumDefinedFunctions());
294 ModuleUtils::iterDefinedFunctions(*wasm, [&](Function* func) {
295 BYN_TRACE("write one\n");
296 o << U32LEB(getTypeIndex(func->sig));
297 });
298 finishSection(start);
299 }
300
writeExpression(Expression * curr)301 void WasmBinaryWriter::writeExpression(Expression* curr) {
302 BinaryenIRToBinaryWriter(*this, o).visit(curr);
303 }
304
writeFunctions()305 void WasmBinaryWriter::writeFunctions() {
306 if (importInfo->getNumDefinedFunctions() == 0) {
307 return;
308 }
309 BYN_TRACE("== writeFunctions\n");
310 auto sectionStart = startSection(BinaryConsts::Section::Code);
311 o << U32LEB(importInfo->getNumDefinedFunctions());
312 bool DWARF = Debug::hasDWARFSections(*getModule());
313 ModuleUtils::iterDefinedFunctions(*wasm, [&](Function* func) {
314 assert(binaryLocationTrackedExpressionsForFunc.empty());
315 size_t sourceMapLocationsSizeAtFunctionStart = sourceMapLocations.size();
316 BYN_TRACE("write one at" << o.size() << std::endl);
317 size_t sizePos = writeU32LEBPlaceholder();
318 size_t start = o.size();
319 BYN_TRACE("writing" << func->name << std::endl);
320 // Emit Stack IR if present, and if we can
321 if (func->stackIR && !sourceMap && !DWARF) {
322 BYN_TRACE("write Stack IR\n");
323 StackIRToBinaryWriter(*this, o, func).write();
324 } else {
325 BYN_TRACE("write Binaryen IR\n");
326 BinaryenIRToBinaryWriter(*this, o, func, sourceMap, DWARF).write();
327 }
328 size_t size = o.size() - start;
329 assert(size <= std::numeric_limits<uint32_t>::max());
330 BYN_TRACE("body size: " << size << ", writing at " << sizePos
331 << ", next starts at " << o.size() << "\n");
332 auto sizeFieldSize = o.writeAt(sizePos, U32LEB(size));
333 // We can move things back if the actual LEB for the size doesn't use the
334 // maximum 5 bytes. In that case we need to adjust offsets after we move
335 // things backwards.
336 auto adjustmentForLEBShrinking = MaxLEB32Bytes - sizeFieldSize;
337 if (adjustmentForLEBShrinking) {
338 // we can save some room, nice
339 assert(sizeFieldSize < MaxLEB32Bytes);
340 std::move(&o[start], &o[start] + size, &o[sizePos] + sizeFieldSize);
341 o.resize(o.size() - adjustmentForLEBShrinking);
342 if (sourceMap) {
343 for (auto i = sourceMapLocationsSizeAtFunctionStart;
344 i < sourceMapLocations.size();
345 ++i) {
346 sourceMapLocations[i].first -= adjustmentForLEBShrinking;
347 }
348 }
349 for (auto* curr : binaryLocationTrackedExpressionsForFunc) {
350 // We added the binary locations, adjust them: they must be relative
351 // to the code section.
352 auto& span = binaryLocations.expressions[curr];
353 span.start -= adjustmentForLEBShrinking;
354 span.end -= adjustmentForLEBShrinking;
355 auto iter = binaryLocations.delimiters.find(curr);
356 if (iter != binaryLocations.delimiters.end()) {
357 for (auto& item : iter->second) {
358 item -= adjustmentForLEBShrinking;
359 }
360 }
361 }
362 }
363 if (!binaryLocationTrackedExpressionsForFunc.empty()) {
364 binaryLocations.functions[func] = BinaryLocations::FunctionLocations{
365 BinaryLocation(sizePos),
366 BinaryLocation(start - adjustmentForLEBShrinking),
367 BinaryLocation(o.size())};
368 }
369 tableOfContents.functionBodies.emplace_back(
370 func->name, sizePos + sizeFieldSize, size);
371 binaryLocationTrackedExpressionsForFunc.clear();
372 });
373 finishSection(sectionStart);
374 }
375
writeGlobals()376 void WasmBinaryWriter::writeGlobals() {
377 if (importInfo->getNumDefinedGlobals() == 0) {
378 return;
379 }
380 BYN_TRACE("== writeglobals\n");
381 auto start = startSection(BinaryConsts::Section::Global);
382 // Count and emit the total number of globals after tuple globals have been
383 // expanded into their constituent parts.
384 Index num = 0;
385 ModuleUtils::iterDefinedGlobals(
386 *wasm, [&num](Global* global) { num += global->type.size(); });
387 o << U32LEB(num);
388 ModuleUtils::iterDefinedGlobals(*wasm, [&](Global* global) {
389 BYN_TRACE("write one\n");
390 size_t i = 0;
391 for (const auto& t : global->type) {
392 o << binaryType(t);
393 o << U32LEB(global->mutable_);
394 if (global->type.size() == 1) {
395 writeExpression(global->init);
396 } else {
397 writeExpression(global->init->cast<TupleMake>()->operands[i]);
398 }
399 o << int8_t(BinaryConsts::End);
400 ++i;
401 }
402 });
403 finishSection(start);
404 }
405
writeExports()406 void WasmBinaryWriter::writeExports() {
407 if (wasm->exports.size() == 0) {
408 return;
409 }
410 BYN_TRACE("== writeexports\n");
411 auto start = startSection(BinaryConsts::Section::Export);
412 o << U32LEB(wasm->exports.size());
413 for (auto& curr : wasm->exports) {
414 BYN_TRACE("write one\n");
415 writeInlineString(curr->name.str);
416 o << U32LEB(int32_t(curr->kind));
417 switch (curr->kind) {
418 case ExternalKind::Function:
419 o << U32LEB(getFunctionIndex(curr->value));
420 break;
421 case ExternalKind::Table:
422 o << U32LEB(0);
423 break;
424 case ExternalKind::Memory:
425 o << U32LEB(0);
426 break;
427 case ExternalKind::Global:
428 o << U32LEB(getGlobalIndex(curr->value));
429 break;
430 case ExternalKind::Event:
431 o << U32LEB(getEventIndex(curr->value));
432 break;
433 default:
434 WASM_UNREACHABLE("unexpected extern kind");
435 }
436 }
437 finishSection(start);
438 }
439
writeDataCount()440 void WasmBinaryWriter::writeDataCount() {
441 if (!wasm->features.hasBulkMemory() || !wasm->memory.segments.size()) {
442 return;
443 }
444 auto start = startSection(BinaryConsts::Section::DataCount);
445 o << U32LEB(wasm->memory.segments.size());
446 finishSection(start);
447 }
448
writeDataSegments()449 void WasmBinaryWriter::writeDataSegments() {
450 if (wasm->memory.segments.size() == 0) {
451 return;
452 }
453 if (wasm->memory.segments.size() > WebLimitations::MaxDataSegments) {
454 std::cerr << "Some VMs may not accept this binary because it has a large "
455 << "number of data segments. Run the limit-segments pass to "
456 << "merge segments.\n";
457 }
458 auto start = startSection(BinaryConsts::Section::Data);
459 o << U32LEB(wasm->memory.segments.size());
460 for (auto& segment : wasm->memory.segments) {
461 uint32_t flags = 0;
462 if (segment.isPassive) {
463 flags |= BinaryConsts::IsPassive;
464 }
465 o << U32LEB(flags);
466 if (!segment.isPassive) {
467 writeExpression(segment.offset);
468 o << int8_t(BinaryConsts::End);
469 }
470 writeInlineBuffer(segment.data.data(), segment.data.size());
471 }
472 finishSection(start);
473 }
474
getFunctionIndex(Name name) const475 uint32_t WasmBinaryWriter::getFunctionIndex(Name name) const {
476 auto it = indexes.functionIndexes.find(name);
477 assert(it != indexes.functionIndexes.end());
478 return it->second;
479 }
480
getGlobalIndex(Name name) const481 uint32_t WasmBinaryWriter::getGlobalIndex(Name name) const {
482 auto it = indexes.globalIndexes.find(name);
483 assert(it != indexes.globalIndexes.end());
484 return it->second;
485 }
486
getEventIndex(Name name) const487 uint32_t WasmBinaryWriter::getEventIndex(Name name) const {
488 auto it = indexes.eventIndexes.find(name);
489 assert(it != indexes.eventIndexes.end());
490 return it->second;
491 }
492
getTypeIndex(Signature sig) const493 uint32_t WasmBinaryWriter::getTypeIndex(Signature sig) const {
494 auto it = typeIndices.find(sig);
495 assert(it != typeIndices.end());
496 return it->second;
497 }
498
writeFunctionTableDeclaration()499 void WasmBinaryWriter::writeFunctionTableDeclaration() {
500 if (!wasm->table.exists || wasm->table.imported()) {
501 return;
502 }
503 BYN_TRACE("== writeFunctionTableDeclaration\n");
504 auto start = startSection(BinaryConsts::Section::Table);
505 o << U32LEB(1); // Declare 1 table.
506 o << S32LEB(BinaryConsts::EncodedType::funcref);
507 writeResizableLimits(wasm->table.initial,
508 wasm->table.max,
509 wasm->table.hasMax(),
510 /*shared=*/false,
511 /*is64*/ false);
512 finishSection(start);
513 }
514
writeTableElements()515 void WasmBinaryWriter::writeTableElements() {
516 if (!wasm->table.exists || wasm->table.segments.size() == 0) {
517 return;
518 }
519 BYN_TRACE("== writeTableElements\n");
520 auto start = startSection(BinaryConsts::Section::Element);
521
522 o << U32LEB(wasm->table.segments.size());
523 for (auto& segment : wasm->table.segments) {
524 // Table index; 0 in the MVP (and binaryen IR only has 1 table)
525 o << U32LEB(0);
526 writeExpression(segment.offset);
527 o << int8_t(BinaryConsts::End);
528 o << U32LEB(segment.data.size());
529 for (auto name : segment.data) {
530 o << U32LEB(getFunctionIndex(name));
531 }
532 }
533 finishSection(start);
534 }
535
writeEvents()536 void WasmBinaryWriter::writeEvents() {
537 if (importInfo->getNumDefinedEvents() == 0) {
538 return;
539 }
540 BYN_TRACE("== writeEvents\n");
541 auto start = startSection(BinaryConsts::Section::Event);
542 auto num = importInfo->getNumDefinedEvents();
543 o << U32LEB(num);
544 ModuleUtils::iterDefinedEvents(*wasm, [&](Event* event) {
545 BYN_TRACE("write one\n");
546 o << U32LEB(event->attribute);
547 o << U32LEB(getTypeIndex(event->sig));
548 });
549
550 finishSection(start);
551 }
552
writeNames()553 void WasmBinaryWriter::writeNames() {
554 BYN_TRACE("== writeNames\n");
555 auto start = startSection(BinaryConsts::Section::User);
556 writeInlineString(BinaryConsts::UserSections::Name);
557
558 // module name
559 if (wasm->name.is()) {
560 auto substart =
561 startSubsection(BinaryConsts::UserSections::Subsection::NameModule);
562 writeEscapedName(wasm->name.str);
563 finishSubsection(substart);
564 }
565
566 // function names
567 {
568 auto substart =
569 startSubsection(BinaryConsts::UserSections::Subsection::NameFunction);
570 o << U32LEB(indexes.functionIndexes.size());
571 Index emitted = 0;
572 auto add = [&](Function* curr) {
573 o << U32LEB(emitted);
574 writeEscapedName(curr->name.str);
575 emitted++;
576 };
577 ModuleUtils::iterImportedFunctions(*wasm, add);
578 ModuleUtils::iterDefinedFunctions(*wasm, add);
579 assert(emitted == indexes.functionIndexes.size());
580 finishSubsection(substart);
581 }
582
583 // local names
584 {
585 // Find all functions with at least one local name and only emit the
586 // subsection if there is at least one.
587 std::vector<std::pair<Index, Function*>> functionsWithLocalNames;
588 Index checked = 0;
589 auto check = [&](Function* curr) {
590 auto numLocals = curr->getNumLocals();
591 for (Index i = 0; i < numLocals; ++i) {
592 if (curr->hasLocalName(i)) {
593 functionsWithLocalNames.push_back({checked, curr});
594 break;
595 }
596 }
597 checked++;
598 };
599 ModuleUtils::iterImportedFunctions(*wasm, check);
600 ModuleUtils::iterDefinedFunctions(*wasm, check);
601 assert(checked == indexes.functionIndexes.size());
602 if (functionsWithLocalNames.size() > 0) {
603 // Otherwise emit those functions but only include locals with a name.
604 auto substart =
605 startSubsection(BinaryConsts::UserSections::Subsection::NameLocal);
606 o << U32LEB(functionsWithLocalNames.size());
607 Index emitted = 0;
608 for (auto& indexedFunc : functionsWithLocalNames) {
609 std::vector<std::pair<Index, Name>> localsWithNames;
610 auto numLocals = indexedFunc.second->getNumLocals();
611 for (Index i = 0; i < numLocals; ++i) {
612 if (indexedFunc.second->hasLocalName(i)) {
613 localsWithNames.push_back({i, indexedFunc.second->getLocalName(i)});
614 }
615 }
616 assert(localsWithNames.size());
617 o << U32LEB(indexedFunc.first);
618 o << U32LEB(localsWithNames.size());
619 for (auto& indexedLocal : localsWithNames) {
620 o << U32LEB(indexedLocal.first);
621 writeEscapedName(indexedLocal.second.str);
622 }
623 emitted++;
624 }
625 assert(emitted == functionsWithLocalNames.size());
626 finishSubsection(substart);
627 }
628 }
629
630 // table names
631 if (wasm->table.exists && wasm->table.name.is()) {
632 auto substart =
633 startSubsection(BinaryConsts::UserSections::Subsection::NameTable);
634 o << U32LEB(1) << U32LEB(0); // currently exactly 1 table at index 0
635 writeEscapedName(wasm->table.name.str);
636 finishSubsection(substart);
637 }
638
639 // memory names
640 if (wasm->memory.exists && wasm->memory.name.is()) {
641 auto substart =
642 startSubsection(BinaryConsts::UserSections::Subsection::NameMemory);
643 o << U32LEB(1) << U32LEB(0); // currently exactly 1 memory at index 0
644 writeEscapedName(wasm->memory.name.str);
645 finishSubsection(substart);
646 }
647
648 // global names
649 {
650 std::vector<std::pair<Index, Global*>> globalsWithNames;
651 Index checked = 0;
652 auto check = [&](Global* curr) {
653 if (curr->name.is()) {
654 globalsWithNames.push_back({checked, curr});
655 }
656 checked++;
657 };
658 ModuleUtils::iterImportedGlobals(*wasm, check);
659 ModuleUtils::iterDefinedGlobals(*wasm, check);
660 assert(checked == indexes.globalIndexes.size());
661 if (globalsWithNames.size() > 0) {
662 auto substart =
663 startSubsection(BinaryConsts::UserSections::Subsection::NameGlobal);
664 o << U32LEB(globalsWithNames.size());
665 for (auto& indexedGlobal : globalsWithNames) {
666 o << U32LEB(indexedGlobal.first);
667 writeEscapedName(indexedGlobal.second->name.str);
668 }
669 finishSubsection(substart);
670 }
671 }
672
673 // TODO: label, type, element and data names
674 // see: https://github.com/WebAssembly/extended-name-section
675
676 finishSection(start);
677 }
678
writeSourceMapUrl()679 void WasmBinaryWriter::writeSourceMapUrl() {
680 BYN_TRACE("== writeSourceMapUrl\n");
681 auto start = startSection(BinaryConsts::Section::User);
682 writeInlineString(BinaryConsts::UserSections::SourceMapUrl);
683 writeInlineString(sourceMapUrl.c_str());
684 finishSection(start);
685 }
686
writeSymbolMap()687 void WasmBinaryWriter::writeSymbolMap() {
688 std::ofstream file(symbolMap);
689 auto write = [&](Function* func) {
690 file << getFunctionIndex(func->name) << ":" << func->name.str << std::endl;
691 };
692 ModuleUtils::iterImportedFunctions(*wasm, write);
693 ModuleUtils::iterDefinedFunctions(*wasm, write);
694 file.close();
695 }
696
initializeDebugInfo()697 void WasmBinaryWriter::initializeDebugInfo() {
698 lastDebugLocation = {0, /* lineNumber = */ 1, 0};
699 }
700
writeSourceMapProlog()701 void WasmBinaryWriter::writeSourceMapProlog() {
702 *sourceMap << "{\"version\":3,\"sources\":[";
703 for (size_t i = 0; i < wasm->debugInfoFileNames.size(); i++) {
704 if (i > 0) {
705 *sourceMap << ",";
706 }
707 // TODO respect JSON string encoding, e.g. quotes and control chars.
708 *sourceMap << "\"" << wasm->debugInfoFileNames[i] << "\"";
709 }
710 *sourceMap << "],\"names\":[],\"mappings\":\"";
711 }
712
writeBase64VLQ(std::ostream & out,int32_t n)713 static void writeBase64VLQ(std::ostream& out, int32_t n) {
714 uint32_t value = n >= 0 ? n << 1 : ((-n) << 1) | 1;
715 while (1) {
716 uint32_t digit = value & 0x1F;
717 value >>= 5;
718 if (!value) {
719 // last VLQ digit -- base64 codes 'A'..'Z', 'a'..'f'
720 out << char(digit < 26 ? 'A' + digit : 'a' + digit - 26);
721 break;
722 }
723 // more VLG digit will follow -- add continuation bit (0x20),
724 // base64 codes 'g'..'z', '0'..'9', '+', '/'
725 out << char(digit < 20
726 ? 'g' + digit
727 : digit < 30 ? '0' + digit - 20 : digit == 30 ? '+' : '/');
728 }
729 }
730
writeSourceMapEpilog()731 void WasmBinaryWriter::writeSourceMapEpilog() {
732 // write source map entries
733 size_t lastOffset = 0;
734 Function::DebugLocation lastLoc = {0, /* lineNumber = */ 1, 0};
735 for (const auto& offsetAndlocPair : sourceMapLocations) {
736 if (lastOffset > 0) {
737 *sourceMap << ",";
738 }
739 size_t offset = offsetAndlocPair.first;
740 const Function::DebugLocation& loc = *offsetAndlocPair.second;
741 writeBase64VLQ(*sourceMap, int32_t(offset - lastOffset));
742 writeBase64VLQ(*sourceMap, int32_t(loc.fileIndex - lastLoc.fileIndex));
743 writeBase64VLQ(*sourceMap, int32_t(loc.lineNumber - lastLoc.lineNumber));
744 writeBase64VLQ(*sourceMap,
745 int32_t(loc.columnNumber - lastLoc.columnNumber));
746 lastLoc = loc;
747 lastOffset = offset;
748 }
749 *sourceMap << "\"}";
750 }
751
writeLateUserSections()752 void WasmBinaryWriter::writeLateUserSections() {
753 for (auto& section : wasm->userSections) {
754 if (section.name != BinaryConsts::UserSections::Dylink) {
755 writeUserSection(section);
756 }
757 }
758 }
759
writeUserSection(const UserSection & section)760 void WasmBinaryWriter::writeUserSection(const UserSection& section) {
761 auto start = startSection(BinaryConsts::User);
762 writeInlineString(section.name.c_str());
763 for (size_t i = 0; i < section.data.size(); i++) {
764 o << uint8_t(section.data[i]);
765 }
766 finishSection(start);
767 }
768
writeFeaturesSection()769 void WasmBinaryWriter::writeFeaturesSection() {
770 if (!wasm->hasFeaturesSection || wasm->features.isMVP()) {
771 return;
772 }
773
774 // TODO(tlively): unify feature names with rest of toolchain and use
775 // FeatureSet::toString()
776 auto toString = [](FeatureSet::Feature f) {
777 switch (f) {
778 case FeatureSet::Atomics:
779 return BinaryConsts::UserSections::AtomicsFeature;
780 case FeatureSet::MutableGlobals:
781 return BinaryConsts::UserSections::MutableGlobalsFeature;
782 case FeatureSet::TruncSat:
783 return BinaryConsts::UserSections::TruncSatFeature;
784 case FeatureSet::SIMD:
785 return BinaryConsts::UserSections::SIMD128Feature;
786 case FeatureSet::BulkMemory:
787 return BinaryConsts::UserSections::BulkMemoryFeature;
788 case FeatureSet::SignExt:
789 return BinaryConsts::UserSections::SignExtFeature;
790 case FeatureSet::ExceptionHandling:
791 return BinaryConsts::UserSections::ExceptionHandlingFeature;
792 case FeatureSet::TailCall:
793 return BinaryConsts::UserSections::TailCallFeature;
794 case FeatureSet::ReferenceTypes:
795 return BinaryConsts::UserSections::ReferenceTypesFeature;
796 case FeatureSet::Multivalue:
797 return BinaryConsts::UserSections::MultivalueFeature;
798 case FeatureSet::GC:
799 return BinaryConsts::UserSections::GCFeature;
800 case FeatureSet::Memory64:
801 return BinaryConsts::UserSections::Memory64Feature;
802 default:
803 WASM_UNREACHABLE("unexpected feature flag");
804 }
805 };
806
807 std::vector<const char*> features;
808 wasm->features.iterFeatures(
809 [&](FeatureSet::Feature f) { features.push_back(toString(f)); });
810
811 auto start = startSection(BinaryConsts::User);
812 writeInlineString(BinaryConsts::UserSections::TargetFeatures);
813 o << U32LEB(features.size());
814 for (auto& f : features) {
815 o << uint8_t(BinaryConsts::FeatureUsed);
816 writeInlineString(f);
817 }
818 finishSection(start);
819 }
820
writeDylinkSection()821 void WasmBinaryWriter::writeDylinkSection() {
822 if (!wasm->dylinkSection) {
823 return;
824 }
825
826 auto start = startSection(BinaryConsts::User);
827 writeInlineString(BinaryConsts::UserSections::Dylink);
828 o << U32LEB(wasm->dylinkSection->memorySize);
829 o << U32LEB(wasm->dylinkSection->memoryAlignment);
830 o << U32LEB(wasm->dylinkSection->tableSize);
831 o << U32LEB(wasm->dylinkSection->tableAlignment);
832 o << U32LEB(wasm->dylinkSection->neededDynlibs.size());
833 for (auto& neededDynlib : wasm->dylinkSection->neededDynlibs) {
834 writeInlineString(neededDynlib.c_str());
835 }
836 finishSection(start);
837 }
838
writeDebugLocation(const Function::DebugLocation & loc)839 void WasmBinaryWriter::writeDebugLocation(const Function::DebugLocation& loc) {
840 if (loc == lastDebugLocation) {
841 return;
842 }
843 auto offset = o.size();
844 sourceMapLocations.emplace_back(offset, &loc);
845 lastDebugLocation = loc;
846 }
847
writeDebugLocation(Expression * curr,Function * func)848 void WasmBinaryWriter::writeDebugLocation(Expression* curr, Function* func) {
849 if (sourceMap) {
850 auto& debugLocations = func->debugLocations;
851 auto iter = debugLocations.find(curr);
852 if (iter != debugLocations.end()) {
853 writeDebugLocation(iter->second);
854 }
855 }
856 // If this is an instruction in a function, and if the original wasm had
857 // binary locations tracked, then track it in the output as well.
858 if (func && !func->expressionLocations.empty()) {
859 binaryLocations.expressions[curr] =
860 BinaryLocations::Span{BinaryLocation(o.size()), 0};
861 binaryLocationTrackedExpressionsForFunc.push_back(curr);
862 }
863 }
864
writeDebugLocationEnd(Expression * curr,Function * func)865 void WasmBinaryWriter::writeDebugLocationEnd(Expression* curr, Function* func) {
866 if (func && !func->expressionLocations.empty()) {
867 auto& span = binaryLocations.expressions.at(curr);
868 assert(span.end == 0);
869 span.end = o.size();
870 }
871 }
872
writeExtraDebugLocation(Expression * curr,Function * func,BinaryLocations::DelimiterId id)873 void WasmBinaryWriter::writeExtraDebugLocation(
874 Expression* curr, Function* func, BinaryLocations::DelimiterId id) {
875 if (func && !func->expressionLocations.empty()) {
876 binaryLocations.delimiters[curr][id] = o.size();
877 }
878 }
879
writeInlineString(const char * name)880 void WasmBinaryWriter::writeInlineString(const char* name) {
881 int32_t size = strlen(name);
882 o << U32LEB(size);
883 for (int32_t i = 0; i < size; i++) {
884 o << int8_t(name[i]);
885 }
886 }
887
isHexDigit(char ch)888 static bool isHexDigit(char ch) {
889 return (ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f') ||
890 (ch >= 'A' && ch <= 'F');
891 }
892
decodeHexNibble(char ch)893 static int decodeHexNibble(char ch) {
894 return ch <= '9' ? ch & 15 : (ch & 15) + 9;
895 }
896
writeEscapedName(const char * name)897 void WasmBinaryWriter::writeEscapedName(const char* name) {
898 if (!strpbrk(name, "\\")) {
899 writeInlineString(name);
900 return;
901 }
902 // decode escaped by escapeName (see below) function names
903 std::string unescaped;
904 int32_t size = strlen(name);
905 for (int32_t i = 0; i < size;) {
906 char ch = name[i++];
907 // support only `\xx` escapes; ignore invalid or unsupported escapes
908 if (ch != '\\' || i + 1 >= size || !isHexDigit(name[i]) ||
909 !isHexDigit(name[i + 1])) {
910 unescaped.push_back(ch);
911 continue;
912 }
913 unescaped.push_back(
914 char((decodeHexNibble(name[i]) << 4) | decodeHexNibble(name[i + 1])));
915 i += 2;
916 }
917 writeInlineString(unescaped.c_str());
918 }
919
writeInlineBuffer(const char * data,size_t size)920 void WasmBinaryWriter::writeInlineBuffer(const char* data, size_t size) {
921 o << U32LEB(size);
922 for (size_t i = 0; i < size; i++) {
923 o << int8_t(data[i]);
924 }
925 }
926
emitBuffer(const char * data,size_t size)927 void WasmBinaryWriter::emitBuffer(const char* data, size_t size) {
928 assert(size > 0);
929 buffersToWrite.emplace_back(data, size, o.size());
930 // placeholder, we'll fill in the pointer to the buffer later when we have it
931 o << uint32_t(0);
932 }
933
emitString(const char * str)934 void WasmBinaryWriter::emitString(const char* str) {
935 BYN_TRACE("emitString " << str << std::endl);
936 emitBuffer(str, strlen(str) + 1);
937 }
938
finishUp()939 void WasmBinaryWriter::finishUp() {
940 BYN_TRACE("finishUp\n");
941 // finish buffers
942 for (const auto& buffer : buffersToWrite) {
943 BYN_TRACE("writing buffer"
944 << (int)buffer.data[0] << "," << (int)buffer.data[1] << " at "
945 << o.size() << " and pointer is at " << buffer.pointerLocation
946 << "\n");
947 o.writeAt(buffer.pointerLocation, (uint32_t)o.size());
948 for (size_t i = 0; i < buffer.size; i++) {
949 o << (uint8_t)buffer.data[i];
950 }
951 }
952 }
953
954 // reader
955
hasDWARFSections()956 bool WasmBinaryBuilder::hasDWARFSections() {
957 assert(pos == 0);
958 getInt32(); // magic
959 getInt32(); // version
960 bool has = false;
961 while (more()) {
962 uint8_t sectionCode = getInt8();
963 uint32_t payloadLen = getU32LEB();
964 if (uint64_t(pos) + uint64_t(payloadLen) > input.size()) {
965 throwError("Section extends beyond end of input");
966 }
967 auto oldPos = pos;
968 if (sectionCode == BinaryConsts::Section::User) {
969 auto sectionName = getInlineString();
970 if (Debug::isDWARFSection(sectionName)) {
971 has = true;
972 break;
973 }
974 }
975 pos = oldPos + payloadLen;
976 }
977 pos = 0;
978 return has;
979 }
980
read()981 void WasmBinaryBuilder::read() {
982 if (DWARF) {
983 // In order to update dwarf, we must store info about each IR node's
984 // binary position. This has noticeable memory overhead, so we don't do it
985 // by default: the user must request it by setting "DWARF", and even if so
986 // we scan ahead to see that there actually *are* DWARF sections, so that
987 // we don't do unnecessary work.
988 if (!hasDWARFSections()) {
989 DWARF = false;
990 }
991 }
992
993 readHeader();
994 readSourceMapHeader();
995
996 // read sections until the end
997 while (more()) {
998 uint8_t sectionCode = getInt8();
999 uint32_t payloadLen = getU32LEB();
1000 if (uint64_t(pos) + uint64_t(payloadLen) > input.size()) {
1001 throwError("Section extends beyond end of input");
1002 }
1003
1004 auto oldPos = pos;
1005
1006 // note the section in the list of seen sections, as almost no sections can
1007 // appear more than once, and verify those that shouldn't do not.
1008 if (sectionCode != BinaryConsts::Section::User &&
1009 sectionCode != BinaryConsts::Section::Code) {
1010 if (!seenSections.insert(BinaryConsts::Section(sectionCode)).second) {
1011 throwError("section seen more than once: " +
1012 std::to_string(sectionCode));
1013 }
1014 }
1015
1016 switch (sectionCode) {
1017 case BinaryConsts::Section::Start:
1018 readStart();
1019 break;
1020 case BinaryConsts::Section::Memory:
1021 readMemory();
1022 break;
1023 case BinaryConsts::Section::Type:
1024 readSignatures();
1025 break;
1026 case BinaryConsts::Section::Import:
1027 readImports();
1028 break;
1029 case BinaryConsts::Section::Function:
1030 readFunctionSignatures();
1031 break;
1032 case BinaryConsts::Section::Code:
1033 if (DWARF) {
1034 codeSectionLocation = pos;
1035 }
1036 readFunctions();
1037 break;
1038 case BinaryConsts::Section::Export:
1039 readExports();
1040 break;
1041 case BinaryConsts::Section::Element:
1042 readTableElements();
1043 break;
1044 case BinaryConsts::Section::Global:
1045 readGlobals();
1046 break;
1047 case BinaryConsts::Section::Data:
1048 readDataSegments();
1049 break;
1050 case BinaryConsts::Section::DataCount:
1051 readDataCount();
1052 break;
1053 case BinaryConsts::Section::Table:
1054 readFunctionTableDeclaration();
1055 break;
1056 case BinaryConsts::Section::Event:
1057 readEvents();
1058 break;
1059 default: {
1060 readUserSection(payloadLen);
1061 if (pos > oldPos + payloadLen) {
1062 throwError("bad user section size, started at " +
1063 std::to_string(oldPos) + " plus payload " +
1064 std::to_string(payloadLen) +
1065 " not being equal to new position " + std::to_string(pos));
1066 }
1067 pos = oldPos + payloadLen;
1068 }
1069 }
1070
1071 // make sure we advanced exactly past this section
1072 if (pos != oldPos + payloadLen) {
1073 throwError("bad section size, started at " + std::to_string(oldPos) +
1074 " plus payload " + std::to_string(payloadLen) +
1075 " not being equal to new position " + std::to_string(pos));
1076 }
1077 }
1078
1079 validateBinary();
1080 processNames();
1081 }
1082
readUserSection(size_t payloadLen)1083 void WasmBinaryBuilder::readUserSection(size_t payloadLen) {
1084 auto oldPos = pos;
1085 Name sectionName = getInlineString();
1086 size_t read = pos - oldPos;
1087 if (read > payloadLen) {
1088 throwError("bad user section size");
1089 }
1090 payloadLen -= read;
1091 if (sectionName.equals(BinaryConsts::UserSections::Name)) {
1092 readNames(payloadLen);
1093 } else if (sectionName.equals(BinaryConsts::UserSections::TargetFeatures)) {
1094 readFeatures(payloadLen);
1095 } else if (sectionName.equals(BinaryConsts::UserSections::Dylink)) {
1096 readDylink(payloadLen);
1097 } else {
1098 // an unfamiliar custom section
1099 if (sectionName.equals(BinaryConsts::UserSections::Linking)) {
1100 std::cerr
1101 << "warning: linking section is present, so this is not a standard "
1102 "wasm file - binaryen cannot handle this properly!\n";
1103 }
1104 wasm.userSections.resize(wasm.userSections.size() + 1);
1105 auto& section = wasm.userSections.back();
1106 section.name = sectionName.str;
1107 auto sectionSize = payloadLen;
1108 section.data.resize(sectionSize);
1109 for (size_t i = 0; i < sectionSize; i++) {
1110 section.data[i] = getInt8();
1111 }
1112 }
1113 }
1114
getInt8()1115 uint8_t WasmBinaryBuilder::getInt8() {
1116 if (!more()) {
1117 throwError("unexpected end of input");
1118 }
1119 BYN_TRACE("getInt8: " << (int)(uint8_t)input[pos] << " (at " << pos << ")\n");
1120 return input[pos++];
1121 }
1122
getInt16()1123 uint16_t WasmBinaryBuilder::getInt16() {
1124 BYN_TRACE("<==\n");
1125 auto ret = uint16_t(getInt8());
1126 ret |= uint16_t(getInt8()) << 8;
1127 BYN_TRACE("getInt16: " << ret << "/0x" << std::hex << ret << std::dec
1128 << " ==>\n");
1129 return ret;
1130 }
1131
getInt32()1132 uint32_t WasmBinaryBuilder::getInt32() {
1133 BYN_TRACE("<==\n");
1134 auto ret = uint32_t(getInt16());
1135 ret |= uint32_t(getInt16()) << 16;
1136 BYN_TRACE("getInt32: " << ret << "/0x" << std::hex << ret << std::dec
1137 << " ==>\n");
1138 return ret;
1139 }
1140
getInt64()1141 uint64_t WasmBinaryBuilder::getInt64() {
1142 BYN_TRACE("<==\n");
1143 auto ret = uint64_t(getInt32());
1144 ret |= uint64_t(getInt32()) << 32;
1145 BYN_TRACE("getInt64: " << ret << "/0x" << std::hex << ret << std::dec
1146 << " ==>\n");
1147 return ret;
1148 }
1149
getLaneIndex(size_t lanes)1150 uint8_t WasmBinaryBuilder::getLaneIndex(size_t lanes) {
1151 BYN_TRACE("<==\n");
1152 auto ret = getInt8();
1153 if (ret >= lanes) {
1154 throwError("Illegal lane index");
1155 }
1156 BYN_TRACE("getLaneIndex(" << lanes << "): " << ret << " ==>" << std::endl);
1157 return ret;
1158 }
1159
getFloat32Literal()1160 Literal WasmBinaryBuilder::getFloat32Literal() {
1161 BYN_TRACE("<==\n");
1162 auto ret = Literal(getInt32());
1163 ret = ret.castToF32();
1164 BYN_TRACE("getFloat32: " << ret << " ==>\n");
1165 return ret;
1166 }
1167
getFloat64Literal()1168 Literal WasmBinaryBuilder::getFloat64Literal() {
1169 BYN_TRACE("<==\n");
1170 auto ret = Literal(getInt64());
1171 ret = ret.castToF64();
1172 BYN_TRACE("getFloat64: " << ret << " ==>\n");
1173 return ret;
1174 }
1175
getVec128Literal()1176 Literal WasmBinaryBuilder::getVec128Literal() {
1177 BYN_TRACE("<==\n");
1178 std::array<uint8_t, 16> bytes;
1179 for (auto i = 0; i < 16; ++i) {
1180 bytes[i] = getInt8();
1181 }
1182 auto ret = Literal(bytes.data());
1183 BYN_TRACE("getVec128: " << ret << " ==>\n");
1184 return ret;
1185 }
1186
getU32LEB()1187 uint32_t WasmBinaryBuilder::getU32LEB() {
1188 BYN_TRACE("<==\n");
1189 U32LEB ret;
1190 ret.read([&]() { return getInt8(); });
1191 BYN_TRACE("getU32LEB: " << ret.value << " ==>\n");
1192 return ret.value;
1193 }
1194
getU64LEB()1195 uint64_t WasmBinaryBuilder::getU64LEB() {
1196 BYN_TRACE("<==\n");
1197 U64LEB ret;
1198 ret.read([&]() { return getInt8(); });
1199 BYN_TRACE("getU64LEB: " << ret.value << " ==>\n");
1200 return ret.value;
1201 }
1202
getS32LEB()1203 int32_t WasmBinaryBuilder::getS32LEB() {
1204 BYN_TRACE("<==\n");
1205 S32LEB ret;
1206 ret.read([&]() { return (int8_t)getInt8(); });
1207 BYN_TRACE("getS32LEB: " << ret.value << " ==>\n");
1208 return ret.value;
1209 }
1210
getS64LEB()1211 int64_t WasmBinaryBuilder::getS64LEB() {
1212 BYN_TRACE("<==\n");
1213 S64LEB ret;
1214 ret.read([&]() { return (int8_t)getInt8(); });
1215 BYN_TRACE("getS64LEB: " << ret.value << " ==>\n");
1216 return ret.value;
1217 }
1218
getType()1219 Type WasmBinaryBuilder::getType() {
1220 int type = getS32LEB();
1221 // Single value types are negative; signature indices are non-negative
1222 if (type >= 0) {
1223 // TODO: Handle block input types properly
1224 if (size_t(type) >= signatures.size()) {
1225 throwError("invalid signature index: " + std::to_string(type));
1226 }
1227 return signatures[type].results;
1228 }
1229 switch (type) {
1230 // None only used for block signatures. TODO: Separate out?
1231 case BinaryConsts::EncodedType::Empty:
1232 return Type::none;
1233 case BinaryConsts::EncodedType::i32:
1234 return Type::i32;
1235 case BinaryConsts::EncodedType::i64:
1236 return Type::i64;
1237 case BinaryConsts::EncodedType::f32:
1238 return Type::f32;
1239 case BinaryConsts::EncodedType::f64:
1240 return Type::f64;
1241 case BinaryConsts::EncodedType::v128:
1242 return Type::v128;
1243 case BinaryConsts::EncodedType::funcref:
1244 return Type::funcref;
1245 case BinaryConsts::EncodedType::externref:
1246 return Type::externref;
1247 case BinaryConsts::EncodedType::exnref:
1248 return Type::exnref;
1249 case BinaryConsts::EncodedType::anyref:
1250 return Type::anyref;
1251 case BinaryConsts::EncodedType::eqref:
1252 return Type::eqref;
1253 case BinaryConsts::EncodedType::i31ref:
1254 return Type::i31ref;
1255 default:
1256 throwError("invalid wasm type: " + std::to_string(type));
1257 }
1258 WASM_UNREACHABLE("unexpected type");
1259 }
1260
getHeapType()1261 HeapType WasmBinaryBuilder::getHeapType() {
1262 int type = getS32LEB(); // TODO: Actually encoded as s33
1263 // Single heap types are negative; heap type indices are non-negative
1264 if (type >= 0) {
1265 if (size_t(type) >= signatures.size()) {
1266 throwError("invalid signature index: " + std::to_string(type));
1267 }
1268 return HeapType(signatures[type]);
1269 }
1270 switch (type) {
1271 case BinaryConsts::EncodedHeapType::func:
1272 return HeapType::FuncKind;
1273 case BinaryConsts::EncodedHeapType::extern_:
1274 return HeapType::ExternKind;
1275 case BinaryConsts::EncodedHeapType::exn:
1276 return HeapType::ExnKind;
1277 case BinaryConsts::EncodedHeapType::any:
1278 return HeapType::AnyKind;
1279 case BinaryConsts::EncodedHeapType::eq:
1280 return HeapType::EqKind;
1281 case BinaryConsts::EncodedHeapType::i31:
1282 return HeapType::I31Kind;
1283 default:
1284 throwError("invalid wasm heap type: " + std::to_string(type));
1285 }
1286 WASM_UNREACHABLE("unexpected type");
1287 }
1288
getConcreteType()1289 Type WasmBinaryBuilder::getConcreteType() {
1290 auto type = getType();
1291 if (!type.isConcrete()) {
1292 throw ParseException("non-concrete type when one expected");
1293 }
1294 return type;
1295 }
1296
getInlineString()1297 Name WasmBinaryBuilder::getInlineString() {
1298 BYN_TRACE("<==\n");
1299 auto len = getU32LEB();
1300 std::string str;
1301 for (size_t i = 0; i < len; i++) {
1302 auto curr = char(getInt8());
1303 if (curr == 0) {
1304 throwError(
1305 "inline string contains NULL (0). that is technically valid in wasm, "
1306 "but you shouldn't do it, and it's not supported in binaryen");
1307 }
1308 str = str + curr;
1309 }
1310 BYN_TRACE("getInlineString: " << str << " ==>\n");
1311 return Name(str);
1312 }
1313
verifyInt8(int8_t x)1314 void WasmBinaryBuilder::verifyInt8(int8_t x) {
1315 int8_t y = getInt8();
1316 if (x != y) {
1317 throwError("surprising value");
1318 }
1319 }
1320
verifyInt16(int16_t x)1321 void WasmBinaryBuilder::verifyInt16(int16_t x) {
1322 int16_t y = getInt16();
1323 if (x != y) {
1324 throwError("surprising value");
1325 }
1326 }
1327
verifyInt32(int32_t x)1328 void WasmBinaryBuilder::verifyInt32(int32_t x) {
1329 int32_t y = getInt32();
1330 if (x != y) {
1331 throwError("surprising value");
1332 }
1333 }
1334
verifyInt64(int64_t x)1335 void WasmBinaryBuilder::verifyInt64(int64_t x) {
1336 int64_t y = getInt64();
1337 if (x != y) {
1338 throwError("surprising value");
1339 }
1340 }
1341
ungetInt8()1342 void WasmBinaryBuilder::ungetInt8() {
1343 assert(pos > 0);
1344 BYN_TRACE("ungetInt8 (at " << pos << ")\n");
1345 pos--;
1346 }
1347
readHeader()1348 void WasmBinaryBuilder::readHeader() {
1349 BYN_TRACE("== readHeader\n");
1350 verifyInt32(BinaryConsts::Magic);
1351 verifyInt32(BinaryConsts::Version);
1352 }
1353
readStart()1354 void WasmBinaryBuilder::readStart() {
1355 BYN_TRACE("== readStart\n");
1356 startIndex = getU32LEB();
1357 }
1358
readMemory()1359 void WasmBinaryBuilder::readMemory() {
1360 BYN_TRACE("== readMemory\n");
1361 auto numMemories = getU32LEB();
1362 if (!numMemories) {
1363 return;
1364 }
1365 if (numMemories != 1) {
1366 throwError("Must be exactly 1 memory");
1367 }
1368 if (wasm.memory.exists) {
1369 throwError("Memory cannot be both imported and defined");
1370 }
1371 wasm.memory.exists = true;
1372 getResizableLimits(wasm.memory.initial,
1373 wasm.memory.max,
1374 wasm.memory.shared,
1375 wasm.memory.indexType,
1376 Memory::kUnlimitedSize);
1377 }
1378
readSignatures()1379 void WasmBinaryBuilder::readSignatures() {
1380 BYN_TRACE("== readSignatures\n");
1381 size_t numTypes = getU32LEB();
1382 BYN_TRACE("num: " << numTypes << std::endl);
1383 for (size_t i = 0; i < numTypes; i++) {
1384 BYN_TRACE("read one\n");
1385 std::vector<Type> params;
1386 std::vector<Type> results;
1387 auto form = getS32LEB();
1388 if (form != BinaryConsts::EncodedType::Func) {
1389 throwError("bad signature form " + std::to_string(form));
1390 }
1391 size_t numParams = getU32LEB();
1392 BYN_TRACE("num params: " << numParams << std::endl);
1393 for (size_t j = 0; j < numParams; j++) {
1394 params.push_back(getConcreteType());
1395 }
1396 auto numResults = getU32LEB();
1397 BYN_TRACE("num results: " << numResults << std::endl);
1398 for (size_t j = 0; j < numResults; j++) {
1399 results.push_back(getConcreteType());
1400 }
1401 signatures.emplace_back(Type(params), Type(results));
1402 }
1403 }
1404
getFunctionName(Index index)1405 Name WasmBinaryBuilder::getFunctionName(Index index) {
1406 if (index >= wasm.functions.size()) {
1407 throwError("invalid function index");
1408 }
1409 return wasm.functions[index]->name;
1410 }
1411
getGlobalName(Index index)1412 Name WasmBinaryBuilder::getGlobalName(Index index) {
1413 if (index >= wasm.globals.size()) {
1414 throwError("invalid global index");
1415 }
1416 return wasm.globals[index]->name;
1417 }
1418
getEventName(Index index)1419 Name WasmBinaryBuilder::getEventName(Index index) {
1420 if (index >= wasm.events.size()) {
1421 throwError("invalid event index");
1422 }
1423 return wasm.events[index]->name;
1424 }
1425
getResizableLimits(Address & initial,Address & max,bool & shared,Type & indexType,Address defaultIfNoMax)1426 void WasmBinaryBuilder::getResizableLimits(Address& initial,
1427 Address& max,
1428 bool& shared,
1429 Type& indexType,
1430 Address defaultIfNoMax) {
1431 auto flags = getU32LEB();
1432 initial = getU32LEB();
1433 bool hasMax = (flags & BinaryConsts::HasMaximum) != 0;
1434 bool isShared = (flags & BinaryConsts::IsShared) != 0;
1435 bool is64 = (flags & BinaryConsts::Is64) != 0;
1436 if (isShared && !hasMax) {
1437 throwError("shared memory must have max size");
1438 }
1439 shared = isShared;
1440 indexType = is64 ? Type::i64 : Type::i32;
1441 if (hasMax) {
1442 max = getU32LEB();
1443 } else {
1444 max = defaultIfNoMax;
1445 }
1446 }
1447
readImports()1448 void WasmBinaryBuilder::readImports() {
1449 BYN_TRACE("== readImports\n");
1450 size_t num = getU32LEB();
1451 BYN_TRACE("num: " << num << std::endl);
1452 Builder builder(wasm);
1453 for (size_t i = 0; i < num; i++) {
1454 BYN_TRACE("read one\n");
1455 auto module = getInlineString();
1456 auto base = getInlineString();
1457 auto kind = (ExternalKind)getU32LEB();
1458 // We set a unique prefix for the name based on the kind. This ensures no
1459 // collisions between them, which can't occur here (due to the index i) but
1460 // could occur later due to the names section.
1461 switch (kind) {
1462 case ExternalKind::Function: {
1463 auto name = Name(std::string("fimport$") + std::to_string(i));
1464 auto index = getU32LEB();
1465 if (index >= signatures.size()) {
1466 throwError("invalid function index " + std::to_string(index) + " / " +
1467 std::to_string(signatures.size()));
1468 }
1469 auto* curr = builder.makeFunction(name, signatures[index], {});
1470 curr->module = module;
1471 curr->base = base;
1472 wasm.addFunction(curr);
1473 functionImports.push_back(curr);
1474 break;
1475 }
1476 case ExternalKind::Table: {
1477 wasm.table.module = module;
1478 wasm.table.base = base;
1479 wasm.table.name = Name(std::string("timport$") + std::to_string(i));
1480 auto elementType = getS32LEB();
1481 WASM_UNUSED(elementType);
1482 if (elementType != BinaryConsts::EncodedType::funcref) {
1483 throwError("Imported table type is not funcref");
1484 }
1485 wasm.table.exists = true;
1486 bool is_shared;
1487 Type indexType;
1488 getResizableLimits(wasm.table.initial,
1489 wasm.table.max,
1490 is_shared,
1491 indexType,
1492 Table::kUnlimitedSize);
1493 if (is_shared) {
1494 throwError("Tables may not be shared");
1495 }
1496 if (indexType == Type::i64) {
1497 throwError("Tables may not be 64-bit");
1498 }
1499 break;
1500 }
1501 case ExternalKind::Memory: {
1502 wasm.memory.module = module;
1503 wasm.memory.base = base;
1504 wasm.memory.name = Name(std::string("mimport$") + std::to_string(i));
1505 wasm.memory.exists = true;
1506 getResizableLimits(wasm.memory.initial,
1507 wasm.memory.max,
1508 wasm.memory.shared,
1509 wasm.memory.indexType,
1510 Memory::kUnlimitedSize);
1511 break;
1512 }
1513 case ExternalKind::Global: {
1514 auto name = Name(std::string("gimport$") + std::to_string(i));
1515 auto type = getConcreteType();
1516 auto mutable_ = getU32LEB();
1517 auto* curr =
1518 builder.makeGlobal(name,
1519 type,
1520 nullptr,
1521 mutable_ ? Builder::Mutable : Builder::Immutable);
1522 curr->module = module;
1523 curr->base = base;
1524 wasm.addGlobal(curr);
1525 globalImports.push_back(curr);
1526 break;
1527 }
1528 case ExternalKind::Event: {
1529 auto name = Name(std::string("eimport$") + std::to_string(i));
1530 auto attribute = getU32LEB();
1531 auto index = getU32LEB();
1532 if (index >= signatures.size()) {
1533 throwError("invalid event index " + std::to_string(index) + " / " +
1534 std::to_string(signatures.size()));
1535 }
1536 auto* curr = builder.makeEvent(name, attribute, signatures[index]);
1537 curr->module = module;
1538 curr->base = base;
1539 wasm.addEvent(curr);
1540 break;
1541 }
1542 default: {
1543 throwError("bad import kind");
1544 }
1545 }
1546 }
1547 }
1548
getNextLabel()1549 Name WasmBinaryBuilder::getNextLabel() {
1550 requireFunctionContext("getting a label");
1551 return Name("label$" + std::to_string(nextLabel++));
1552 }
1553
requireFunctionContext(const char * error)1554 void WasmBinaryBuilder::requireFunctionContext(const char* error) {
1555 if (!currFunction) {
1556 throwError(std::string("in a non-function context: ") + error);
1557 }
1558 }
1559
readFunctionSignatures()1560 void WasmBinaryBuilder::readFunctionSignatures() {
1561 BYN_TRACE("== readFunctionSignatures\n");
1562 size_t num = getU32LEB();
1563 BYN_TRACE("num: " << num << std::endl);
1564 for (size_t i = 0; i < num; i++) {
1565 BYN_TRACE("read one\n");
1566 auto index = getU32LEB();
1567 if (index >= signatures.size()) {
1568 throwError("invalid function type index for function");
1569 }
1570 functionSignatures.push_back(signatures[index]);
1571 }
1572 }
1573
readFunctions()1574 void WasmBinaryBuilder::readFunctions() {
1575 BYN_TRACE("== readFunctions\n");
1576 size_t total = getU32LEB();
1577 if (total != functionSignatures.size()) {
1578 throwError("invalid function section size, must equal types");
1579 }
1580 for (size_t i = 0; i < total; i++) {
1581 BYN_TRACE("read one at " << pos << std::endl);
1582 auto sizePos = pos;
1583 size_t size = getU32LEB();
1584 if (size == 0) {
1585 throwError("empty function size");
1586 }
1587 endOfFunction = pos + size;
1588
1589 auto* func = new Function;
1590 func->name = Name::fromInt(i);
1591 func->sig = functionSignatures[i];
1592 currFunction = func;
1593
1594 if (DWARF) {
1595 func->funcLocation = BinaryLocations::FunctionLocations{
1596 BinaryLocation(sizePos - codeSectionLocation),
1597 BinaryLocation(pos - codeSectionLocation),
1598 BinaryLocation(pos - codeSectionLocation + size)};
1599 }
1600
1601 readNextDebugLocation();
1602
1603 BYN_TRACE("reading " << i << std::endl);
1604 size_t numLocalTypes = getU32LEB();
1605 for (size_t t = 0; t < numLocalTypes; t++) {
1606 auto num = getU32LEB();
1607 auto type = getConcreteType();
1608 while (num > 0) {
1609 func->vars.push_back(type);
1610 num--;
1611 }
1612 }
1613 std::swap(func->prologLocation, debugLocation);
1614 {
1615 // process the function body
1616 BYN_TRACE("processing function: " << i << std::endl);
1617 nextLabel = 0;
1618 debugLocation.clear();
1619 willBeIgnored = false;
1620 // process body
1621 assert(breakTargetNames.size() == 0);
1622 assert(breakStack.empty());
1623 assert(expressionStack.empty());
1624 assert(controlFlowStack.empty());
1625 assert(depth == 0);
1626 func->body = getBlockOrSingleton(func->sig.results);
1627 assert(depth == 0);
1628 assert(breakStack.size() == 0);
1629 assert(breakTargetNames.size() == 0);
1630 if (!expressionStack.empty()) {
1631 throwError("stack not empty on function exit");
1632 }
1633 assert(controlFlowStack.empty());
1634 if (pos != endOfFunction) {
1635 throwError("binary offset at function exit not at expected location");
1636 }
1637 }
1638 std::swap(func->epilogLocation, debugLocation);
1639 currFunction = nullptr;
1640 debugLocation.clear();
1641 functions.push_back(func);
1642 }
1643 BYN_TRACE(" end function bodies\n");
1644 }
1645
readExports()1646 void WasmBinaryBuilder::readExports() {
1647 BYN_TRACE("== readExports\n");
1648 size_t num = getU32LEB();
1649 BYN_TRACE("num: " << num << std::endl);
1650 std::set<Name> names;
1651 for (size_t i = 0; i < num; i++) {
1652 BYN_TRACE("read one\n");
1653 auto curr = new Export;
1654 curr->name = getInlineString();
1655 if (names.count(curr->name) > 0) {
1656 throwError("duplicate export name");
1657 }
1658 names.insert(curr->name);
1659 curr->kind = (ExternalKind)getU32LEB();
1660 auto index = getU32LEB();
1661 exportIndices[curr] = index;
1662 exportOrder.push_back(curr);
1663 }
1664 }
1665
readBase64VLQ(std::istream & in)1666 static int32_t readBase64VLQ(std::istream& in) {
1667 uint32_t value = 0;
1668 uint32_t shift = 0;
1669 while (1) {
1670 auto ch = in.get();
1671 if (ch == EOF) {
1672 throw MapParseException("unexpected EOF in the middle of VLQ");
1673 }
1674 if ((ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch < 'g')) {
1675 // last number digit
1676 uint32_t digit = ch < 'a' ? ch - 'A' : ch - 'a' + 26;
1677 value |= digit << shift;
1678 break;
1679 }
1680 if (!(ch >= 'g' && ch <= 'z') && !(ch >= '0' && ch <= '9') && ch != '+' &&
1681 ch != '/') {
1682 throw MapParseException("invalid VLQ digit");
1683 }
1684 uint32_t digit =
1685 ch > '9' ? ch - 'g' : (ch >= '0' ? ch - '0' + 20 : (ch == '+' ? 30 : 31));
1686 value |= digit << shift;
1687 shift += 5;
1688 }
1689 return value & 1 ? -int32_t(value >> 1) : int32_t(value >> 1);
1690 }
1691
readSourceMapHeader()1692 void WasmBinaryBuilder::readSourceMapHeader() {
1693 if (!sourceMap) {
1694 return;
1695 }
1696
1697 auto skipWhitespace = [&]() {
1698 while (sourceMap->peek() == ' ' || sourceMap->peek() == '\n') {
1699 sourceMap->get();
1700 }
1701 };
1702
1703 auto maybeReadChar = [&](char expected) {
1704 if (sourceMap->peek() != expected) {
1705 return false;
1706 }
1707 sourceMap->get();
1708 return true;
1709 };
1710
1711 auto mustReadChar = [&](char expected) {
1712 char c = sourceMap->get();
1713 if (c != expected) {
1714 throw MapParseException(std::string("Unexpected char: expected '") +
1715 expected + "' got '" + c + "'");
1716 }
1717 };
1718
1719 auto findField = [&](const char* name) {
1720 bool matching = false;
1721 size_t len = strlen(name);
1722 size_t pos;
1723 while (1) {
1724 int ch = sourceMap->get();
1725 if (ch == EOF) {
1726 return false;
1727 }
1728 if (ch == '\"') {
1729 if (matching) {
1730 // we matched a terminating quote.
1731 if (pos == len) {
1732 break;
1733 }
1734 matching = false;
1735 } else {
1736 matching = true;
1737 pos = 0;
1738 }
1739 } else if (matching && name[pos] == ch) {
1740 ++pos;
1741 } else if (matching) {
1742 matching = false;
1743 }
1744 }
1745 skipWhitespace();
1746 mustReadChar(':');
1747 skipWhitespace();
1748 return true;
1749 };
1750
1751 auto readString = [&](std::string& str) {
1752 std::vector<char> vec;
1753 skipWhitespace();
1754 mustReadChar('\"');
1755 if (!maybeReadChar('\"')) {
1756 while (1) {
1757 int ch = sourceMap->get();
1758 if (ch == EOF) {
1759 throw MapParseException("unexpected EOF in the middle of string");
1760 }
1761 if (ch == '\"') {
1762 break;
1763 }
1764 vec.push_back(ch);
1765 }
1766 }
1767 skipWhitespace();
1768 str = std::string(vec.begin(), vec.end());
1769 };
1770
1771 if (!findField("sources")) {
1772 throw MapParseException("cannot find the 'sources' field in map");
1773 }
1774
1775 skipWhitespace();
1776 mustReadChar('[');
1777 if (!maybeReadChar(']')) {
1778 do {
1779 std::string file;
1780 readString(file);
1781 Index index = wasm.debugInfoFileNames.size();
1782 wasm.debugInfoFileNames.push_back(file);
1783 debugInfoFileIndices[file] = index;
1784 } while (maybeReadChar(','));
1785 mustReadChar(']');
1786 }
1787
1788 if (!findField("mappings")) {
1789 throw MapParseException("cannot find the 'mappings' field in map");
1790 }
1791
1792 mustReadChar('\"');
1793 if (maybeReadChar('\"')) { // empty mappings
1794 nextDebugLocation.first = 0;
1795 return;
1796 }
1797 // read first debug location
1798 uint32_t position = readBase64VLQ(*sourceMap);
1799 uint32_t fileIndex = readBase64VLQ(*sourceMap);
1800 uint32_t lineNumber =
1801 readBase64VLQ(*sourceMap) + 1; // adjust zero-based line number
1802 uint32_t columnNumber = readBase64VLQ(*sourceMap);
1803 nextDebugLocation = {position, {fileIndex, lineNumber, columnNumber}};
1804 }
1805
readNextDebugLocation()1806 void WasmBinaryBuilder::readNextDebugLocation() {
1807 if (!sourceMap) {
1808 return;
1809 }
1810
1811 while (nextDebugLocation.first && nextDebugLocation.first <= pos) {
1812 debugLocation.clear();
1813 // use debugLocation only for function expressions
1814 if (currFunction) {
1815 debugLocation.insert(nextDebugLocation.second);
1816 }
1817
1818 char ch;
1819 *sourceMap >> ch;
1820 if (ch == '\"') { // end of records
1821 nextDebugLocation.first = 0;
1822 break;
1823 }
1824 if (ch != ',') {
1825 throw MapParseException("Unexpected delimiter");
1826 }
1827
1828 int32_t positionDelta = readBase64VLQ(*sourceMap);
1829 uint32_t position = nextDebugLocation.first + positionDelta;
1830 int32_t fileIndexDelta = readBase64VLQ(*sourceMap);
1831 uint32_t fileIndex = nextDebugLocation.second.fileIndex + fileIndexDelta;
1832 int32_t lineNumberDelta = readBase64VLQ(*sourceMap);
1833 uint32_t lineNumber = nextDebugLocation.second.lineNumber + lineNumberDelta;
1834 int32_t columnNumberDelta = readBase64VLQ(*sourceMap);
1835 uint32_t columnNumber =
1836 nextDebugLocation.second.columnNumber + columnNumberDelta;
1837
1838 nextDebugLocation = {position, {fileIndex, lineNumber, columnNumber}};
1839 }
1840 }
1841
readExpression()1842 Expression* WasmBinaryBuilder::readExpression() {
1843 assert(depth == 0);
1844 processExpressions();
1845 if (expressionStack.size() != 1) {
1846 throwError("expected to read a single expression");
1847 }
1848 auto* ret = popExpression();
1849 assert(depth == 0);
1850 return ret;
1851 }
1852
readGlobals()1853 void WasmBinaryBuilder::readGlobals() {
1854 BYN_TRACE("== readGlobals\n");
1855 size_t num = getU32LEB();
1856 BYN_TRACE("num: " << num << std::endl);
1857 for (size_t i = 0; i < num; i++) {
1858 BYN_TRACE("read one\n");
1859 auto type = getConcreteType();
1860 auto mutable_ = getU32LEB();
1861 if (mutable_ & ~1) {
1862 throwError("Global mutability must be 0 or 1");
1863 }
1864 auto* init = readExpression();
1865 globals.push_back(
1866 Builder::makeGlobal("global$" + std::to_string(i),
1867 type,
1868 init,
1869 mutable_ ? Builder::Mutable : Builder::Immutable));
1870 }
1871 }
1872
processExpressions()1873 void WasmBinaryBuilder::processExpressions() {
1874 BYN_TRACE("== processExpressions\n");
1875 unreachableInTheWasmSense = false;
1876 while (1) {
1877 Expression* curr;
1878 auto ret = readExpression(curr);
1879 if (!curr) {
1880 lastSeparator = ret;
1881 BYN_TRACE("== processExpressions finished\n");
1882 return;
1883 }
1884 pushExpression(curr);
1885 if (curr->type == Type::unreachable) {
1886 // Once we see something unreachable, we don't want to add anything else
1887 // to the stack, as it could be stacky code that is non-representable in
1888 // our AST. but we do need to skip it.
1889 // If there is nothing else here, just stop. Otherwise, go into
1890 // unreachable mode. peek to see what to do.
1891 if (pos == endOfFunction) {
1892 throwError("Reached function end without seeing End opcode");
1893 }
1894 if (!more()) {
1895 throwError("unexpected end of input");
1896 }
1897 auto peek = input[pos];
1898 if (peek == BinaryConsts::End || peek == BinaryConsts::Else ||
1899 peek == BinaryConsts::Catch) {
1900 BYN_TRACE("== processExpressions finished with unreachable"
1901 << std::endl);
1902 lastSeparator = BinaryConsts::ASTNodes(peek);
1903 // Read the byte we peeked at. No new instruction is generated for it.
1904 Expression* dummy = nullptr;
1905 readExpression(dummy);
1906 assert(!dummy);
1907 return;
1908 } else {
1909 skipUnreachableCode();
1910 return;
1911 }
1912 }
1913 }
1914 }
1915
skipUnreachableCode()1916 void WasmBinaryBuilder::skipUnreachableCode() {
1917 BYN_TRACE("== skipUnreachableCode\n");
1918 // preserve the stack, and restore it. it contains the instruction that made
1919 // us unreachable, and we can ignore anything after it. things after it may
1920 // pop, we want to undo that
1921 auto savedStack = expressionStack;
1922 // note we are entering unreachable code, and note what the state as before so
1923 // we can restore it
1924 auto before = willBeIgnored;
1925 willBeIgnored = true;
1926 // clear the stack. nothing should be popped from there anyhow, just stuff
1927 // can be pushed and then popped. Popping past the top of the stack will
1928 // result in uneachables being returned
1929 expressionStack.clear();
1930 while (1) {
1931 // set the unreachableInTheWasmSense flag each time, as sub-blocks may set
1932 // and unset it
1933 unreachableInTheWasmSense = true;
1934 Expression* curr;
1935 auto ret = readExpression(curr);
1936 if (!curr) {
1937 BYN_TRACE("== skipUnreachableCode finished\n");
1938 lastSeparator = ret;
1939 unreachableInTheWasmSense = false;
1940 willBeIgnored = before;
1941 expressionStack = savedStack;
1942 return;
1943 }
1944 pushExpression(curr);
1945 }
1946 }
1947
pushExpression(Expression * curr)1948 void WasmBinaryBuilder::pushExpression(Expression* curr) {
1949 if (curr->type.isTuple()) {
1950 // Store tuple to local and push individual extracted values
1951 Builder builder(wasm);
1952 Index tuple = builder.addVar(currFunction, curr->type);
1953 expressionStack.push_back(builder.makeLocalSet(tuple, curr));
1954 for (Index i = 0; i < curr->type.size(); ++i) {
1955 expressionStack.push_back(
1956 builder.makeTupleExtract(builder.makeLocalGet(tuple, curr->type), i));
1957 }
1958 } else {
1959 expressionStack.push_back(curr);
1960 }
1961 }
1962
popExpression()1963 Expression* WasmBinaryBuilder::popExpression() {
1964 BYN_TRACE("== popExpression\n");
1965 if (expressionStack.empty()) {
1966 if (unreachableInTheWasmSense) {
1967 // in unreachable code, trying to pop past the polymorphic stack
1968 // area results in receiving unreachables
1969 BYN_TRACE("== popping unreachable from polymorphic stack" << std::endl);
1970 return allocator.alloc<Unreachable>();
1971 }
1972 throwError(
1973 "attempted pop from empty stack / beyond block start boundary at " +
1974 std::to_string(pos));
1975 }
1976 // the stack is not empty, and we would not be going out of the current block
1977 auto ret = expressionStack.back();
1978 assert(!ret->type.isTuple());
1979 expressionStack.pop_back();
1980 return ret;
1981 }
1982
popNonVoidExpression()1983 Expression* WasmBinaryBuilder::popNonVoidExpression() {
1984 auto* ret = popExpression();
1985 if (ret->type != Type::none) {
1986 return ret;
1987 }
1988 // we found a void, so this is stacky code that we must handle carefully
1989 Builder builder(wasm);
1990 // add elements until we find a non-void
1991 std::vector<Expression*> expressions;
1992 expressions.push_back(ret);
1993 while (1) {
1994 auto* curr = popExpression();
1995 expressions.push_back(curr);
1996 if (curr->type != Type::none) {
1997 break;
1998 }
1999 }
2000 auto* block = builder.makeBlock();
2001 while (!expressions.empty()) {
2002 block->list.push_back(expressions.back());
2003 expressions.pop_back();
2004 }
2005 requireFunctionContext("popping void where we need a new local");
2006 auto type = block->list[0]->type;
2007 if (type.isConcrete()) {
2008 auto local = builder.addVar(currFunction, type);
2009 block->list[0] = builder.makeLocalSet(local, block->list[0]);
2010 block->list.push_back(builder.makeLocalGet(local, type));
2011 } else {
2012 assert(type == Type::unreachable);
2013 // nothing to do here - unreachable anyhow
2014 }
2015 block->finalize();
2016 return block;
2017 }
2018
popTuple(size_t numElems)2019 Expression* WasmBinaryBuilder::popTuple(size_t numElems) {
2020 Builder builder(wasm);
2021 std::vector<Expression*> elements;
2022 elements.resize(numElems);
2023 for (size_t i = 0; i < numElems; i++) {
2024 auto* elem = popNonVoidExpression();
2025 if (elem->type == Type::unreachable) {
2026 // All the previously-popped items cannot be reached, so ignore them. We
2027 // cannot continue popping because there might not be enough items on the
2028 // expression stack after an unreachable expression. Any remaining
2029 // elements can stay unperturbed on the stack and will be explicitly
2030 // dropped by some parent call to pushBlockElements.
2031 return elem;
2032 }
2033 elements[numElems - i - 1] = elem;
2034 }
2035 return Builder(wasm).makeTupleMake(std::move(elements));
2036 }
2037
popTypedExpression(Type type)2038 Expression* WasmBinaryBuilder::popTypedExpression(Type type) {
2039 if (type.isSingle()) {
2040 return popNonVoidExpression();
2041 } else if (type.isTuple()) {
2042 return popTuple(type.size());
2043 } else {
2044 WASM_UNREACHABLE("Invalid popped type");
2045 }
2046 }
2047
validateBinary()2048 void WasmBinaryBuilder::validateBinary() {
2049 if (hasDataCount && wasm.memory.segments.size() != dataCount) {
2050 throwError("Number of segments does not agree with DataCount section");
2051 }
2052 }
2053
processNames()2054 void WasmBinaryBuilder::processNames() {
2055 for (auto* func : functions) {
2056 wasm.addFunction(func);
2057 }
2058 for (auto* global : globals) {
2059 wasm.addGlobal(global);
2060 }
2061
2062 // now that we have names, apply things
2063
2064 if (startIndex != static_cast<Index>(-1)) {
2065 wasm.start = getFunctionName(startIndex);
2066 }
2067
2068 for (auto* curr : exportOrder) {
2069 auto index = exportIndices[curr];
2070 switch (curr->kind) {
2071 case ExternalKind::Function: {
2072 curr->value = getFunctionName(index);
2073 break;
2074 }
2075 case ExternalKind::Table:
2076 curr->value = wasm.table.name;
2077 break;
2078 case ExternalKind::Memory:
2079 curr->value = wasm.memory.name;
2080 break;
2081 case ExternalKind::Global:
2082 curr->value = getGlobalName(index);
2083 break;
2084 case ExternalKind::Event:
2085 curr->value = getEventName(index);
2086 break;
2087 default:
2088 throwError("bad export kind");
2089 }
2090 wasm.addExport(curr);
2091 }
2092
2093 for (auto& iter : functionRefs) {
2094 size_t index = iter.first;
2095 auto& refs = iter.second;
2096 for (auto* ref : refs) {
2097 if (auto* call = ref->dynCast<Call>()) {
2098 call->target = getFunctionName(index);
2099 } else if (auto* refFunc = ref->dynCast<RefFunc>()) {
2100 refFunc->func = getFunctionName(index);
2101 } else {
2102 WASM_UNREACHABLE("Invalid type in function references");
2103 }
2104 }
2105 }
2106
2107 for (auto& pair : functionTable) {
2108 auto i = pair.first;
2109 auto& indices = pair.second;
2110 for (auto j : indices) {
2111 wasm.table.segments[i].data.push_back(getFunctionName(j));
2112 }
2113 }
2114
2115 for (auto& iter : globalRefs) {
2116 size_t index = iter.first;
2117 auto& refs = iter.second;
2118 for (auto* ref : refs) {
2119 if (auto* get = ref->dynCast<GlobalGet>()) {
2120 get->name = getGlobalName(index);
2121 } else if (auto* set = ref->dynCast<GlobalSet>()) {
2122 set->name = getGlobalName(index);
2123 } else {
2124 WASM_UNREACHABLE("Invalid type in global references");
2125 }
2126 }
2127 }
2128
2129 // Everything now has its proper name.
2130
2131 wasm.updateMaps();
2132 }
2133
readDataCount()2134 void WasmBinaryBuilder::readDataCount() {
2135 BYN_TRACE("== readDataCount\n");
2136 hasDataCount = true;
2137 dataCount = getU32LEB();
2138 }
2139
readDataSegments()2140 void WasmBinaryBuilder::readDataSegments() {
2141 BYN_TRACE("== readDataSegments\n");
2142 auto num = getU32LEB();
2143 for (size_t i = 0; i < num; i++) {
2144 Memory::Segment curr;
2145 uint32_t flags = getU32LEB();
2146 if (flags > 2) {
2147 throwError("bad segment flags, must be 0, 1, or 2, not " +
2148 std::to_string(flags));
2149 }
2150 curr.isPassive = flags & BinaryConsts::IsPassive;
2151 if (flags & BinaryConsts::HasMemIndex) {
2152 auto memIndex = getU32LEB();
2153 if (memIndex != 0) {
2154 throwError("nonzero memory index");
2155 }
2156 }
2157 if (!curr.isPassive) {
2158 curr.offset = readExpression();
2159 }
2160 auto size = getU32LEB();
2161 curr.data.resize(size);
2162 for (size_t j = 0; j < size; j++) {
2163 curr.data[j] = char(getInt8());
2164 }
2165 wasm.memory.segments.push_back(curr);
2166 }
2167 }
2168
readFunctionTableDeclaration()2169 void WasmBinaryBuilder::readFunctionTableDeclaration() {
2170 BYN_TRACE("== readFunctionTableDeclaration\n");
2171 auto numTables = getU32LEB();
2172 if (numTables != 1) {
2173 throwError("Only 1 table definition allowed in MVP");
2174 }
2175 if (wasm.table.exists) {
2176 throwError("Table cannot be both imported and defined");
2177 }
2178 wasm.table.exists = true;
2179 auto elemType = getS32LEB();
2180 if (elemType != BinaryConsts::EncodedType::funcref) {
2181 throwError("ElementType must be funcref in MVP");
2182 }
2183 bool is_shared;
2184 Type indexType;
2185 getResizableLimits(wasm.table.initial,
2186 wasm.table.max,
2187 is_shared,
2188 indexType,
2189 Table::kUnlimitedSize);
2190 if (is_shared) {
2191 throwError("Tables may not be shared");
2192 }
2193 if (indexType == Type::i64) {
2194 throwError("Tables may not be 64-bit");
2195 }
2196 }
2197
readTableElements()2198 void WasmBinaryBuilder::readTableElements() {
2199 BYN_TRACE("== readTableElements\n");
2200 auto numSegments = getU32LEB();
2201 if (numSegments >= Table::kMaxSize) {
2202 throwError("Too many segments");
2203 }
2204 for (size_t i = 0; i < numSegments; i++) {
2205 auto tableIndex = getU32LEB();
2206 if (tableIndex != 0) {
2207 throwError("Table elements must refer to table 0 in MVP");
2208 }
2209 wasm.table.segments.emplace_back(readExpression());
2210
2211 auto& indexSegment = functionTable[i];
2212 auto size = getU32LEB();
2213 for (Index j = 0; j < size; j++) {
2214 indexSegment.push_back(getU32LEB());
2215 }
2216 }
2217 }
2218
readEvents()2219 void WasmBinaryBuilder::readEvents() {
2220 BYN_TRACE("== readEvents\n");
2221 size_t numEvents = getU32LEB();
2222 BYN_TRACE("num: " << numEvents << std::endl);
2223 for (size_t i = 0; i < numEvents; i++) {
2224 BYN_TRACE("read one\n");
2225 auto attribute = getU32LEB();
2226 auto typeIndex = getU32LEB();
2227 if (typeIndex >= signatures.size()) {
2228 throwError("invalid event index " + std::to_string(typeIndex) + " / " +
2229 std::to_string(signatures.size()));
2230 }
2231 wasm.addEvent(Builder::makeEvent(
2232 "event$" + std::to_string(i), attribute, signatures[typeIndex]));
2233 }
2234 }
2235
isIdChar(char ch)2236 static bool isIdChar(char ch) {
2237 return (ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'Z') ||
2238 (ch >= 'a' && ch <= 'z') || ch == '!' || ch == '#' || ch == '$' ||
2239 ch == '%' || ch == '&' || ch == '\'' || ch == '*' || ch == '+' ||
2240 ch == '-' || ch == '.' || ch == '/' || ch == ':' || ch == '<' ||
2241 ch == '=' || ch == '>' || ch == '?' || ch == '@' || ch == '^' ||
2242 ch == '_' || ch == '`' || ch == '|' || ch == '~';
2243 }
2244
formatNibble(int nibble)2245 static char formatNibble(int nibble) {
2246 return nibble < 10 ? '0' + nibble : 'a' - 10 + nibble;
2247 }
2248
escape(Name name)2249 Name WasmBinaryBuilder::escape(Name name) {
2250 bool allIdChars = true;
2251 for (const char* p = name.str; allIdChars && *p; p++) {
2252 allIdChars = isIdChar(*p);
2253 }
2254 if (allIdChars) {
2255 return name;
2256 }
2257 // encode name, if at least one non-idchar (per WebAssembly spec) was found
2258 std::string escaped;
2259 for (const char* p = name.str; *p; p++) {
2260 char ch = *p;
2261 if (isIdChar(ch)) {
2262 escaped.push_back(ch);
2263 continue;
2264 }
2265 // replace non-idchar with `\xx` escape
2266 escaped.push_back('\\');
2267 escaped.push_back(formatNibble(ch >> 4));
2268 escaped.push_back(formatNibble(ch & 15));
2269 }
2270 return escaped;
2271 }
2272
readNames(size_t payloadLen)2273 void WasmBinaryBuilder::readNames(size_t payloadLen) {
2274 BYN_TRACE("== readNames\n");
2275 auto sectionPos = pos;
2276 while (pos < sectionPos + payloadLen) {
2277 auto nameType = getU32LEB();
2278 auto subsectionSize = getU32LEB();
2279 auto subsectionPos = pos;
2280 if (nameType == BinaryConsts::UserSections::Subsection::NameModule) {
2281 wasm.name = getInlineString();
2282 } else if (nameType ==
2283 BinaryConsts::UserSections::Subsection::NameFunction) {
2284 auto num = getU32LEB();
2285 std::set<Name> usedNames;
2286 for (size_t i = 0; i < num; i++) {
2287 auto index = getU32LEB();
2288 auto rawName = getInlineString();
2289 auto name = escape(rawName);
2290 // De-duplicate names by appending .1, .2, etc.
2291 for (int i = 1; !usedNames.insert(name).second; ++i) {
2292 name = std::string(escape(rawName).str) + std::string(".") +
2293 std::to_string(i);
2294 }
2295 auto numFunctionImports = functionImports.size();
2296 if (index < numFunctionImports) {
2297 functionImports[index]->name = name;
2298 } else if (index - numFunctionImports < functions.size()) {
2299 functions[index - numFunctionImports]->name = name;
2300 } else {
2301 std::cerr << "warning: function index out of bounds in name section, "
2302 "function subsection: "
2303 << std::string(rawName.str) << " at index "
2304 << std::to_string(index) << std::endl;
2305 }
2306 }
2307 } else if (nameType == BinaryConsts::UserSections::Subsection::NameLocal) {
2308 auto numFuncs = getU32LEB();
2309 auto numFunctionImports = functionImports.size();
2310 for (size_t i = 0; i < numFuncs; i++) {
2311 auto funcIndex = getU32LEB();
2312 Function* func = nullptr;
2313 if (funcIndex < numFunctionImports) {
2314 func = functionImports[funcIndex];
2315 } else if (funcIndex - numFunctionImports < functions.size()) {
2316 func = functions[funcIndex - numFunctionImports];
2317 } else {
2318 std::cerr
2319 << "warning: function index out of bounds in name section, local "
2320 "subsection: "
2321 << std::to_string(funcIndex) << std::endl;
2322 }
2323 auto numLocals = getU32LEB();
2324 std::set<Name> usedNames;
2325 for (size_t j = 0; j < numLocals; j++) {
2326 auto localIndex = getU32LEB();
2327 auto rawLocalName = getInlineString();
2328 if (!func) {
2329 continue; // read and discard in case of prior error
2330 }
2331 auto localName = escape(rawLocalName);
2332 // De-duplicate names by appending .1, .2, etc.
2333 for (int i = 1; !usedNames.insert(localName).second; ++i) {
2334 localName = std::string(escape(rawLocalName).str) +
2335 std::string(".") + std::to_string(i);
2336 }
2337 if (localIndex < func->getNumLocals()) {
2338 func->localNames[localIndex] = localName;
2339 } else {
2340 std::cerr << "warning: local index out of bounds in name "
2341 "section, local subsection: "
2342 << std::string(rawLocalName.str) << " at index "
2343 << std::to_string(localIndex) << " in function "
2344 << std::string(func->name.str) << std::endl;
2345 }
2346 }
2347 }
2348 } else if (nameType == BinaryConsts::UserSections::Subsection::NameTable) {
2349 auto num = getU32LEB();
2350 for (size_t i = 0; i < num; i++) {
2351 auto index = getU32LEB();
2352 auto rawName = getInlineString();
2353 if (index == 0) {
2354 wasm.table.name = escape(rawName);
2355 } else {
2356 std::cerr << "warning: table index out of bounds in name section, "
2357 "table subsection: "
2358 << std::string(rawName.str) << " at index "
2359 << std::to_string(index) << std::endl;
2360 }
2361 }
2362 } else if (nameType == BinaryConsts::UserSections::Subsection::NameMemory) {
2363 auto num = getU32LEB();
2364 for (size_t i = 0; i < num; i++) {
2365 auto index = getU32LEB();
2366 auto rawName = getInlineString();
2367 if (index == 0) {
2368 wasm.memory.name = escape(rawName);
2369 } else {
2370 std::cerr << "warning: memory index out of bounds in name section, "
2371 "memory subsection: "
2372 << std::string(rawName.str) << " at index "
2373 << std::to_string(index) << std::endl;
2374 }
2375 }
2376 } else if (nameType == BinaryConsts::UserSections::Subsection::NameGlobal) {
2377 auto num = getU32LEB();
2378 std::set<Name> usedNames;
2379 for (size_t i = 0; i < num; i++) {
2380 auto index = getU32LEB();
2381 auto rawName = getInlineString();
2382 auto name = escape(rawName);
2383 // De-duplicate names by appending .1, .2, etc.
2384 for (int i = 1; !usedNames.insert(name).second; ++i) {
2385 name = std::string(escape(rawName).str) + std::string(".") +
2386 std::to_string(i);
2387 }
2388 auto numGlobalImports = globalImports.size();
2389 if (index < numGlobalImports) {
2390 globalImports[index]->name = name;
2391 } else if (index - numGlobalImports < globals.size()) {
2392 globals[index - numGlobalImports]->name = name;
2393 } else {
2394 std::cerr << "warning: global index out of bounds in name section, "
2395 "global subsection: "
2396 << std::string(rawName.str) << " at index "
2397 << std::to_string(index) << std::endl;
2398 }
2399 }
2400 } else {
2401 std::cerr << "warning: unknown name subsection with id "
2402 << std::to_string(nameType) << " at " << pos << std::endl;
2403 pos = subsectionPos + subsectionSize;
2404 }
2405 if (pos != subsectionPos + subsectionSize) {
2406 throwError("bad names subsection position change");
2407 }
2408 }
2409 if (pos != sectionPos + payloadLen) {
2410 throwError("bad names section position change");
2411 }
2412 }
2413
readFeatures(size_t payloadLen)2414 void WasmBinaryBuilder::readFeatures(size_t payloadLen) {
2415 wasm.hasFeaturesSection = true;
2416 wasm.features = FeatureSet::MVP;
2417
2418 auto sectionPos = pos;
2419 size_t numFeatures = getU32LEB();
2420 for (size_t i = 0; i < numFeatures; ++i) {
2421 uint8_t prefix = getInt8();
2422 if (prefix != BinaryConsts::FeatureUsed) {
2423 if (prefix == BinaryConsts::FeatureRequired) {
2424 std::cerr
2425 << "warning: required features in feature section are ignored";
2426 } else if (prefix == BinaryConsts::FeatureDisallowed) {
2427 std::cerr
2428 << "warning: disallowed features in feature section are ignored";
2429 } else {
2430 throwError("Unrecognized feature policy prefix");
2431 }
2432 }
2433
2434 Name name = getInlineString();
2435 if (pos > sectionPos + payloadLen) {
2436 throwError("ill-formed string extends beyond section");
2437 }
2438
2439 if (prefix != BinaryConsts::FeatureDisallowed) {
2440 if (name == BinaryConsts::UserSections::AtomicsFeature) {
2441 wasm.features.setAtomics();
2442 } else if (name == BinaryConsts::UserSections::BulkMemoryFeature) {
2443 wasm.features.setBulkMemory();
2444 } else if (name == BinaryConsts::UserSections::ExceptionHandlingFeature) {
2445 wasm.features.setExceptionHandling();
2446 } else if (name == BinaryConsts::UserSections::MutableGlobalsFeature) {
2447 wasm.features.setMutableGlobals();
2448 } else if (name == BinaryConsts::UserSections::TruncSatFeature) {
2449 wasm.features.setTruncSat();
2450 } else if (name == BinaryConsts::UserSections::SignExtFeature) {
2451 wasm.features.setSignExt();
2452 } else if (name == BinaryConsts::UserSections::SIMD128Feature) {
2453 wasm.features.setSIMD();
2454 } else if (name == BinaryConsts::UserSections::TailCallFeature) {
2455 wasm.features.setTailCall();
2456 } else if (name == BinaryConsts::UserSections::ReferenceTypesFeature) {
2457 wasm.features.setReferenceTypes();
2458 } else if (name == BinaryConsts::UserSections::MultivalueFeature) {
2459 wasm.features.setMultivalue();
2460 } else if (name == BinaryConsts::UserSections::GCFeature) {
2461 wasm.features.setGC();
2462 } else if (name == BinaryConsts::UserSections::Memory64Feature) {
2463 wasm.features.setMemory64();
2464 }
2465 }
2466 }
2467 if (pos != sectionPos + payloadLen) {
2468 throwError("bad features section size");
2469 }
2470 }
2471
readDylink(size_t payloadLen)2472 void WasmBinaryBuilder::readDylink(size_t payloadLen) {
2473 wasm.dylinkSection = make_unique<DylinkSection>();
2474
2475 auto sectionPos = pos;
2476
2477 wasm.dylinkSection->memorySize = getU32LEB();
2478 wasm.dylinkSection->memoryAlignment = getU32LEB();
2479 wasm.dylinkSection->tableSize = getU32LEB();
2480 wasm.dylinkSection->tableAlignment = getU32LEB();
2481
2482 size_t numNeededDynlibs = getU32LEB();
2483 for (size_t i = 0; i < numNeededDynlibs; ++i) {
2484 wasm.dylinkSection->neededDynlibs.push_back(getInlineString());
2485 }
2486
2487 if (pos != sectionPos + payloadLen) {
2488 throwError("bad features section size");
2489 }
2490 }
2491
readExpression(Expression * & curr)2492 BinaryConsts::ASTNodes WasmBinaryBuilder::readExpression(Expression*& curr) {
2493 if (pos == endOfFunction) {
2494 throwError("Reached function end without seeing End opcode");
2495 }
2496 BYN_TRACE("zz recurse into " << ++depth << " at " << pos << std::endl);
2497 readNextDebugLocation();
2498 std::set<Function::DebugLocation> currDebugLocation;
2499 if (debugLocation.size()) {
2500 currDebugLocation.insert(*debugLocation.begin());
2501 }
2502 size_t startPos = pos;
2503 uint8_t code = getInt8();
2504 BYN_TRACE("readExpression seeing " << (int)code << std::endl);
2505 switch (code) {
2506 case BinaryConsts::Block:
2507 visitBlock((curr = allocator.alloc<Block>())->cast<Block>());
2508 break;
2509 case BinaryConsts::If:
2510 visitIf((curr = allocator.alloc<If>())->cast<If>());
2511 break;
2512 case BinaryConsts::Loop:
2513 visitLoop((curr = allocator.alloc<Loop>())->cast<Loop>());
2514 break;
2515 case BinaryConsts::Br:
2516 case BinaryConsts::BrIf:
2517 visitBreak((curr = allocator.alloc<Break>())->cast<Break>(), code);
2518 break; // code distinguishes br from br_if
2519 case BinaryConsts::BrTable:
2520 visitSwitch((curr = allocator.alloc<Switch>())->cast<Switch>());
2521 break;
2522 case BinaryConsts::CallFunction:
2523 visitCall((curr = allocator.alloc<Call>())->cast<Call>());
2524 break;
2525 case BinaryConsts::CallIndirect:
2526 visitCallIndirect(
2527 (curr = allocator.alloc<CallIndirect>())->cast<CallIndirect>());
2528 break;
2529 case BinaryConsts::RetCallFunction: {
2530 auto call = allocator.alloc<Call>();
2531 call->isReturn = true;
2532 curr = call;
2533 visitCall(call);
2534 break;
2535 }
2536 case BinaryConsts::RetCallIndirect: {
2537 auto call = allocator.alloc<CallIndirect>();
2538 call->isReturn = true;
2539 curr = call;
2540 visitCallIndirect(call);
2541 break;
2542 }
2543 case BinaryConsts::LocalGet:
2544 visitLocalGet((curr = allocator.alloc<LocalGet>())->cast<LocalGet>());
2545 break;
2546 case BinaryConsts::LocalTee:
2547 case BinaryConsts::LocalSet:
2548 visitLocalSet((curr = allocator.alloc<LocalSet>())->cast<LocalSet>(),
2549 code);
2550 break;
2551 case BinaryConsts::GlobalGet:
2552 visitGlobalGet((curr = allocator.alloc<GlobalGet>())->cast<GlobalGet>());
2553 break;
2554 case BinaryConsts::GlobalSet:
2555 visitGlobalSet((curr = allocator.alloc<GlobalSet>())->cast<GlobalSet>());
2556 break;
2557 case BinaryConsts::Select:
2558 case BinaryConsts::SelectWithType:
2559 visitSelect((curr = allocator.alloc<Select>())->cast<Select>(), code);
2560 break;
2561 case BinaryConsts::Return:
2562 visitReturn((curr = allocator.alloc<Return>())->cast<Return>());
2563 break;
2564 case BinaryConsts::Nop:
2565 visitNop((curr = allocator.alloc<Nop>())->cast<Nop>());
2566 break;
2567 case BinaryConsts::Unreachable:
2568 visitUnreachable(
2569 (curr = allocator.alloc<Unreachable>())->cast<Unreachable>());
2570 break;
2571 case BinaryConsts::Drop:
2572 visitDrop((curr = allocator.alloc<Drop>())->cast<Drop>());
2573 break;
2574 case BinaryConsts::End:
2575 curr = nullptr;
2576 continueControlFlow(BinaryLocations::End, startPos);
2577 break;
2578 case BinaryConsts::Else:
2579 curr = nullptr;
2580 continueControlFlow(BinaryLocations::Else, startPos);
2581 break;
2582 case BinaryConsts::Catch:
2583 curr = nullptr;
2584 continueControlFlow(BinaryLocations::Catch, startPos);
2585 break;
2586 case BinaryConsts::RefNull:
2587 visitRefNull((curr = allocator.alloc<RefNull>())->cast<RefNull>());
2588 break;
2589 case BinaryConsts::RefIsNull:
2590 visitRefIsNull((curr = allocator.alloc<RefIsNull>())->cast<RefIsNull>());
2591 break;
2592 case BinaryConsts::RefFunc:
2593 visitRefFunc((curr = allocator.alloc<RefFunc>())->cast<RefFunc>());
2594 break;
2595 case BinaryConsts::RefEq:
2596 visitRefEq((curr = allocator.alloc<RefEq>())->cast<RefEq>());
2597 break;
2598 case BinaryConsts::Try:
2599 visitTryOrTryInBlock(curr);
2600 break;
2601 case BinaryConsts::Throw:
2602 visitThrow((curr = allocator.alloc<Throw>())->cast<Throw>());
2603 break;
2604 case BinaryConsts::Rethrow:
2605 visitRethrow((curr = allocator.alloc<Rethrow>())->cast<Rethrow>());
2606 break;
2607 case BinaryConsts::BrOnExn:
2608 visitBrOnExn((curr = allocator.alloc<BrOnExn>())->cast<BrOnExn>());
2609 break;
2610 case BinaryConsts::MemorySize: {
2611 auto size = allocator.alloc<MemorySize>();
2612 if (wasm.memory.is64()) {
2613 size->make64();
2614 }
2615 curr = size;
2616 visitMemorySize(size);
2617 break;
2618 }
2619 case BinaryConsts::MemoryGrow: {
2620 auto grow = allocator.alloc<MemoryGrow>();
2621 if (wasm.memory.is64()) {
2622 grow->make64();
2623 }
2624 curr = grow;
2625 visitMemoryGrow(grow);
2626 break;
2627 }
2628 case BinaryConsts::AtomicPrefix: {
2629 code = static_cast<uint8_t>(getU32LEB());
2630 if (maybeVisitLoad(curr, code, /*isAtomic=*/true)) {
2631 break;
2632 }
2633 if (maybeVisitStore(curr, code, /*isAtomic=*/true)) {
2634 break;
2635 }
2636 if (maybeVisitAtomicRMW(curr, code)) {
2637 break;
2638 }
2639 if (maybeVisitAtomicCmpxchg(curr, code)) {
2640 break;
2641 }
2642 if (maybeVisitAtomicWait(curr, code)) {
2643 break;
2644 }
2645 if (maybeVisitAtomicNotify(curr, code)) {
2646 break;
2647 }
2648 if (maybeVisitAtomicFence(curr, code)) {
2649 break;
2650 }
2651 throwError("invalid code after atomic prefix: " + std::to_string(code));
2652 break;
2653 }
2654 case BinaryConsts::MiscPrefix: {
2655 auto opcode = getU32LEB();
2656 if (maybeVisitTruncSat(curr, opcode)) {
2657 break;
2658 }
2659 if (maybeVisitMemoryInit(curr, opcode)) {
2660 break;
2661 }
2662 if (maybeVisitDataDrop(curr, opcode)) {
2663 break;
2664 }
2665 if (maybeVisitMemoryCopy(curr, opcode)) {
2666 break;
2667 }
2668 if (maybeVisitMemoryFill(curr, opcode)) {
2669 break;
2670 }
2671 throwError("invalid code after nontrapping float-to-int prefix: " +
2672 std::to_string(opcode));
2673 break;
2674 }
2675 case BinaryConsts::SIMDPrefix: {
2676 auto opcode = getU32LEB();
2677 if (maybeVisitSIMDBinary(curr, opcode)) {
2678 break;
2679 }
2680 if (maybeVisitSIMDUnary(curr, opcode)) {
2681 break;
2682 }
2683 if (maybeVisitSIMDConst(curr, opcode)) {
2684 break;
2685 }
2686 if (maybeVisitSIMDStore(curr, opcode)) {
2687 break;
2688 }
2689 if (maybeVisitSIMDExtract(curr, opcode)) {
2690 break;
2691 }
2692 if (maybeVisitSIMDReplace(curr, opcode)) {
2693 break;
2694 }
2695 if (maybeVisitSIMDShuffle(curr, opcode)) {
2696 break;
2697 }
2698 if (maybeVisitSIMDTernary(curr, opcode)) {
2699 break;
2700 }
2701 if (maybeVisitSIMDShift(curr, opcode)) {
2702 break;
2703 }
2704 if (maybeVisitSIMDLoad(curr, opcode)) {
2705 break;
2706 }
2707 throwError("invalid code after SIMD prefix: " + std::to_string(opcode));
2708 break;
2709 }
2710 case BinaryConsts::GCPrefix: {
2711 auto opcode = getU32LEB();
2712 if (maybeVisitI31New(curr, opcode)) {
2713 break;
2714 }
2715 if (maybeVisitI31Get(curr, opcode)) {
2716 break;
2717 }
2718 if (maybeVisitRefTest(curr, opcode)) {
2719 break;
2720 }
2721 if (maybeVisitRefCast(curr, opcode)) {
2722 break;
2723 }
2724 if (maybeVisitBrOnCast(curr, opcode)) {
2725 break;
2726 }
2727 if (maybeVisitRttCanon(curr, opcode)) {
2728 break;
2729 }
2730 if (maybeVisitRttSub(curr, opcode)) {
2731 break;
2732 }
2733 if (maybeVisitStructNew(curr, opcode)) {
2734 break;
2735 }
2736 if (maybeVisitStructGet(curr, opcode)) {
2737 break;
2738 }
2739 if (maybeVisitStructSet(curr, opcode)) {
2740 break;
2741 }
2742 if (maybeVisitArrayNew(curr, opcode)) {
2743 break;
2744 }
2745 if (maybeVisitArrayGet(curr, opcode)) {
2746 break;
2747 }
2748 if (maybeVisitArraySet(curr, opcode)) {
2749 break;
2750 }
2751 if (maybeVisitArrayLen(curr, opcode)) {
2752 break;
2753 }
2754 throwError("invalid code after GC prefix: " + std::to_string(opcode));
2755 break;
2756 }
2757 default: {
2758 // otherwise, the code is a subcode TODO: optimize
2759 if (maybeVisitBinary(curr, code)) {
2760 break;
2761 }
2762 if (maybeVisitUnary(curr, code)) {
2763 break;
2764 }
2765 if (maybeVisitConst(curr, code)) {
2766 break;
2767 }
2768 if (maybeVisitLoad(curr, code, /*isAtomic=*/false)) {
2769 break;
2770 }
2771 if (maybeVisitStore(curr, code, /*isAtomic=*/false)) {
2772 break;
2773 }
2774 throwError("bad node code " + std::to_string(code));
2775 break;
2776 }
2777 }
2778 if (curr) {
2779 if (currDebugLocation.size()) {
2780 currFunction->debugLocations[curr] = *currDebugLocation.begin();
2781 }
2782 if (DWARF && currFunction) {
2783 currFunction->expressionLocations[curr] =
2784 BinaryLocations::Span{BinaryLocation(startPos - codeSectionLocation),
2785 BinaryLocation(pos - codeSectionLocation)};
2786 }
2787 }
2788 BYN_TRACE("zz recurse from " << depth-- << " at " << pos << std::endl);
2789 return BinaryConsts::ASTNodes(code);
2790 }
2791
startControlFlow(Expression * curr)2792 void WasmBinaryBuilder::startControlFlow(Expression* curr) {
2793 if (DWARF && currFunction) {
2794 controlFlowStack.push_back(curr);
2795 }
2796 }
2797
continueControlFlow(BinaryLocations::DelimiterId id,BinaryLocation pos)2798 void WasmBinaryBuilder::continueControlFlow(BinaryLocations::DelimiterId id,
2799 BinaryLocation pos) {
2800 if (DWARF && currFunction) {
2801 if (controlFlowStack.empty()) {
2802 // We reached the end of the function, which is also marked with an
2803 // "end", like a control flow structure.
2804 assert(id == BinaryLocations::End);
2805 assert(pos + 1 == endOfFunction);
2806 return;
2807 }
2808 assert(!controlFlowStack.empty());
2809 auto currControlFlow = controlFlowStack.back();
2810 // We are called after parsing the byte, so we need to subtract one to
2811 // get its position.
2812 currFunction->delimiterLocations[currControlFlow][id] =
2813 pos - codeSectionLocation;
2814 if (id == BinaryLocations::End) {
2815 controlFlowStack.pop_back();
2816 }
2817 }
2818 }
2819
pushBlockElements(Block * curr,Type type,size_t start)2820 void WasmBinaryBuilder::pushBlockElements(Block* curr,
2821 Type type,
2822 size_t start) {
2823 assert(start <= expressionStack.size());
2824 // The results of this block are the last values pushed to the expressionStack
2825 Expression* results = nullptr;
2826 if (type.isConcrete()) {
2827 results = popTypedExpression(type);
2828 }
2829 if (expressionStack.size() < start) {
2830 throwError("Block requires more values than are available");
2831 }
2832 // Everything else on the stack after `start` is either a none-type expression
2833 // or a concretely-type expression that is implicitly dropped due to
2834 // unreachability at the end of the block, like this:
2835 //
2836 // block i32
2837 // i32.const 1
2838 // i32.const 2
2839 // i32.const 3
2840 // return
2841 // end
2842 //
2843 // The first two const elements will be emitted as drops in the block (the
2844 // optimizer can remove them, of course, but in general we may need dropped
2845 // items here as they may have side effects).
2846 //
2847 for (size_t i = start; i < expressionStack.size(); ++i) {
2848 auto* item = expressionStack[i];
2849 if (item->type.isConcrete()) {
2850 item = Builder(wasm).makeDrop(item);
2851 }
2852 curr->list.push_back(item);
2853 }
2854 expressionStack.resize(start);
2855 if (results != nullptr) {
2856 curr->list.push_back(results);
2857 }
2858 }
2859
visitBlock(Block * curr)2860 void WasmBinaryBuilder::visitBlock(Block* curr) {
2861 BYN_TRACE("zz node: Block\n");
2862 startControlFlow(curr);
2863 // special-case Block and de-recurse nested blocks in their first position, as
2864 // that is a common pattern that can be very highly nested.
2865 std::vector<Block*> stack;
2866 while (1) {
2867 curr->type = getType();
2868 curr->name = getNextLabel();
2869 breakStack.push_back({curr->name, curr->type});
2870 stack.push_back(curr);
2871 if (more() && input[pos] == BinaryConsts::Block) {
2872 // a recursion
2873 readNextDebugLocation();
2874 curr = allocator.alloc<Block>();
2875 startControlFlow(curr);
2876 pos++;
2877 if (debugLocation.size()) {
2878 currFunction->debugLocations[curr] = *debugLocation.begin();
2879 }
2880 continue;
2881 } else {
2882 // end of recursion
2883 break;
2884 }
2885 }
2886 Block* last = nullptr;
2887 while (stack.size() > 0) {
2888 curr = stack.back();
2889 stack.pop_back();
2890 // everything after this, that is left when we see the marker, is ours
2891 size_t start = expressionStack.size();
2892 if (last) {
2893 // the previous block is our first-position element
2894 pushExpression(last);
2895 }
2896 last = curr;
2897 processExpressions();
2898 size_t end = expressionStack.size();
2899 if (end < start) {
2900 throwError("block cannot pop from outside");
2901 }
2902 pushBlockElements(curr, curr->type, start);
2903 curr->finalize(curr->type,
2904 breakTargetNames.find(curr->name) !=
2905 breakTargetNames.end() /* hasBreak */);
2906 breakStack.pop_back();
2907 breakTargetNames.erase(curr->name);
2908 }
2909 }
2910
2911 // Gets a block of expressions. If it's just one, return that singleton.
getBlockOrSingleton(Type type)2912 Expression* WasmBinaryBuilder::getBlockOrSingleton(Type type) {
2913 Name label = getNextLabel();
2914 breakStack.push_back({label, type});
2915 auto start = expressionStack.size();
2916
2917 processExpressions();
2918 size_t end = expressionStack.size();
2919 if (end < start) {
2920 throwError("block cannot pop from outside");
2921 }
2922 breakStack.pop_back();
2923 auto* block = allocator.alloc<Block>();
2924 pushBlockElements(block, type, start);
2925 block->name = label;
2926 block->finalize(type);
2927 // maybe we don't need a block here?
2928 if (breakTargetNames.find(block->name) == breakTargetNames.end()) {
2929 block->name = Name();
2930 if (block->list.size() == 1) {
2931 return block->list[0];
2932 }
2933 }
2934 breakTargetNames.erase(block->name);
2935 return block;
2936 }
2937
visitIf(If * curr)2938 void WasmBinaryBuilder::visitIf(If* curr) {
2939 BYN_TRACE("zz node: If\n");
2940 startControlFlow(curr);
2941 curr->type = getType();
2942 curr->condition = popNonVoidExpression();
2943 curr->ifTrue = getBlockOrSingleton(curr->type);
2944 if (lastSeparator == BinaryConsts::Else) {
2945 curr->ifFalse = getBlockOrSingleton(curr->type);
2946 }
2947 curr->finalize(curr->type);
2948 if (lastSeparator != BinaryConsts::End) {
2949 throwError("if should end with End");
2950 }
2951 }
2952
visitLoop(Loop * curr)2953 void WasmBinaryBuilder::visitLoop(Loop* curr) {
2954 BYN_TRACE("zz node: Loop\n");
2955 startControlFlow(curr);
2956 curr->type = getType();
2957 curr->name = getNextLabel();
2958 breakStack.push_back({curr->name, Type::none});
2959 // find the expressions in the block, and create the body
2960 // a loop may have a list of instructions in wasm, much like
2961 // a block, but it only has a label at the top of the loop,
2962 // so even if we need a block (if there is more than 1
2963 // expression) we never need a label on the block.
2964 auto start = expressionStack.size();
2965 processExpressions();
2966 size_t end = expressionStack.size();
2967 if (start > end) {
2968 throwError("block cannot pop from outside");
2969 }
2970 if (end - start == 1) {
2971 curr->body = popExpression();
2972 } else {
2973 auto* block = allocator.alloc<Block>();
2974 pushBlockElements(block, curr->type, start);
2975 block->finalize(curr->type);
2976 curr->body = block;
2977 }
2978 breakStack.pop_back();
2979 breakTargetNames.erase(curr->name);
2980 curr->finalize(curr->type);
2981 }
2982
2983 WasmBinaryBuilder::BreakTarget
getBreakTarget(int32_t offset)2984 WasmBinaryBuilder::getBreakTarget(int32_t offset) {
2985 BYN_TRACE("getBreakTarget " << offset << std::endl);
2986 if (breakStack.size() < 1 + size_t(offset)) {
2987 throwError("bad breakindex (low)");
2988 }
2989 size_t index = breakStack.size() - 1 - offset;
2990 if (index >= breakStack.size()) {
2991 throwError("bad breakindex (high)");
2992 }
2993 BYN_TRACE("breaktarget " << breakStack[index].name << " type "
2994 << breakStack[index].type << std::endl);
2995 auto& ret = breakStack[index];
2996 // if the break is in literally unreachable code, then we will not emit it
2997 // anyhow, so do not note that the target has breaks to it
2998 if (!willBeIgnored) {
2999 breakTargetNames.insert(ret.name);
3000 }
3001 return ret;
3002 }
3003
visitBreak(Break * curr,uint8_t code)3004 void WasmBinaryBuilder::visitBreak(Break* curr, uint8_t code) {
3005 BYN_TRACE("zz node: Break, code " << int32_t(code) << std::endl);
3006 BreakTarget target = getBreakTarget(getU32LEB());
3007 curr->name = target.name;
3008 if (code == BinaryConsts::BrIf) {
3009 curr->condition = popNonVoidExpression();
3010 }
3011 if (target.type.isConcrete()) {
3012 curr->value = popTypedExpression(target.type);
3013 }
3014 curr->finalize();
3015 }
3016
visitSwitch(Switch * curr)3017 void WasmBinaryBuilder::visitSwitch(Switch* curr) {
3018 BYN_TRACE("zz node: Switch\n");
3019 curr->condition = popNonVoidExpression();
3020 auto numTargets = getU32LEB();
3021 BYN_TRACE("targets: " << numTargets << std::endl);
3022 for (size_t i = 0; i < numTargets; i++) {
3023 curr->targets.push_back(getBreakTarget(getU32LEB()).name);
3024 }
3025 auto defaultTarget = getBreakTarget(getU32LEB());
3026 curr->default_ = defaultTarget.name;
3027 BYN_TRACE("default: " << curr->default_ << "\n");
3028 if (defaultTarget.type.isConcrete()) {
3029 curr->value = popTypedExpression(defaultTarget.type);
3030 }
3031 curr->finalize();
3032 }
3033
visitCall(Call * curr)3034 void WasmBinaryBuilder::visitCall(Call* curr) {
3035 BYN_TRACE("zz node: Call\n");
3036 auto index = getU32LEB();
3037 Signature sig;
3038 if (index < functionImports.size()) {
3039 auto* import = functionImports[index];
3040 sig = import->sig;
3041 } else {
3042 Index adjustedIndex = index - functionImports.size();
3043 if (adjustedIndex >= functionSignatures.size()) {
3044 throwError("invalid call index");
3045 }
3046 sig = functionSignatures[adjustedIndex];
3047 }
3048 auto num = sig.params.size();
3049 curr->operands.resize(num);
3050 for (size_t i = 0; i < num; i++) {
3051 curr->operands[num - i - 1] = popNonVoidExpression();
3052 }
3053 curr->type = sig.results;
3054 functionRefs[index].push_back(curr); // we don't know function names yet
3055 curr->finalize();
3056 }
3057
visitCallIndirect(CallIndirect * curr)3058 void WasmBinaryBuilder::visitCallIndirect(CallIndirect* curr) {
3059 BYN_TRACE("zz node: CallIndirect\n");
3060 auto index = getU32LEB();
3061 if (index >= signatures.size()) {
3062 throwError("bad call_indirect function index");
3063 }
3064 curr->sig = signatures[index];
3065 auto reserved = getU32LEB();
3066 if (reserved != 0) {
3067 throwError("Invalid flags field in call_indirect");
3068 }
3069 auto num = curr->sig.params.size();
3070 curr->operands.resize(num);
3071 curr->target = popNonVoidExpression();
3072 for (size_t i = 0; i < num; i++) {
3073 curr->operands[num - i - 1] = popNonVoidExpression();
3074 }
3075 curr->finalize();
3076 }
3077
visitLocalGet(LocalGet * curr)3078 void WasmBinaryBuilder::visitLocalGet(LocalGet* curr) {
3079 BYN_TRACE("zz node: LocalGet " << pos << std::endl);
3080 ;
3081 requireFunctionContext("local.get");
3082 curr->index = getU32LEB();
3083 if (curr->index >= currFunction->getNumLocals()) {
3084 throwError("bad local.get index");
3085 }
3086 curr->type = currFunction->getLocalType(curr->index);
3087 curr->finalize();
3088 }
3089
visitLocalSet(LocalSet * curr,uint8_t code)3090 void WasmBinaryBuilder::visitLocalSet(LocalSet* curr, uint8_t code) {
3091 BYN_TRACE("zz node: Set|LocalTee\n");
3092 requireFunctionContext("local.set outside of function");
3093 curr->index = getU32LEB();
3094 if (curr->index >= currFunction->getNumLocals()) {
3095 throwError("bad local.set index");
3096 }
3097 curr->value = popNonVoidExpression();
3098 if (code == BinaryConsts::LocalTee) {
3099 curr->makeTee(currFunction->getLocalType(curr->index));
3100 } else {
3101 curr->makeSet();
3102 }
3103 curr->finalize();
3104 }
3105
visitGlobalGet(GlobalGet * curr)3106 void WasmBinaryBuilder::visitGlobalGet(GlobalGet* curr) {
3107 BYN_TRACE("zz node: GlobalGet " << pos << std::endl);
3108 auto index = getU32LEB();
3109 if (index < globalImports.size()) {
3110 auto* import = globalImports[index];
3111 curr->name = import->name;
3112 curr->type = import->type;
3113 } else {
3114 Index adjustedIndex = index - globalImports.size();
3115 if (adjustedIndex >= globals.size()) {
3116 throwError("invalid global index");
3117 }
3118 auto* glob = globals[adjustedIndex];
3119 curr->name = glob->name;
3120 curr->type = glob->type;
3121 }
3122 globalRefs[index].push_back(curr); // we don't know the final name yet
3123 }
3124
visitGlobalSet(GlobalSet * curr)3125 void WasmBinaryBuilder::visitGlobalSet(GlobalSet* curr) {
3126 BYN_TRACE("zz node: GlobalSet\n");
3127 auto index = getU32LEB();
3128 if (index < globalImports.size()) {
3129 auto* import = globalImports[index];
3130 curr->name = import->name;
3131 } else {
3132 Index adjustedIndex = index - globalImports.size();
3133 if (adjustedIndex >= globals.size()) {
3134 throwError("invalid global index");
3135 }
3136 curr->name = globals[adjustedIndex]->name;
3137 }
3138 curr->value = popNonVoidExpression();
3139 globalRefs[index].push_back(curr); // we don't know the final name yet
3140 curr->finalize();
3141 }
3142
readMemoryAccess(Address & alignment,Address & offset)3143 void WasmBinaryBuilder::readMemoryAccess(Address& alignment, Address& offset) {
3144 auto rawAlignment = getU32LEB();
3145 if (rawAlignment > 4) {
3146 throwError("Alignment must be of a reasonable size");
3147 }
3148 alignment = Bits::pow2(rawAlignment);
3149 offset = getU32LEB();
3150 }
3151
maybeVisitLoad(Expression * & out,uint8_t code,bool isAtomic)3152 bool WasmBinaryBuilder::maybeVisitLoad(Expression*& out,
3153 uint8_t code,
3154 bool isAtomic) {
3155 Load* curr;
3156 if (!isAtomic) {
3157 switch (code) {
3158 case BinaryConsts::I32LoadMem8S:
3159 curr = allocator.alloc<Load>();
3160 curr->bytes = 1;
3161 curr->type = Type::i32;
3162 curr->signed_ = true;
3163 break;
3164 case BinaryConsts::I32LoadMem8U:
3165 curr = allocator.alloc<Load>();
3166 curr->bytes = 1;
3167 curr->type = Type::i32;
3168 curr->signed_ = false;
3169 break;
3170 case BinaryConsts::I32LoadMem16S:
3171 curr = allocator.alloc<Load>();
3172 curr->bytes = 2;
3173 curr->type = Type::i32;
3174 curr->signed_ = true;
3175 break;
3176 case BinaryConsts::I32LoadMem16U:
3177 curr = allocator.alloc<Load>();
3178 curr->bytes = 2;
3179 curr->type = Type::i32;
3180 curr->signed_ = false;
3181 break;
3182 case BinaryConsts::I32LoadMem:
3183 curr = allocator.alloc<Load>();
3184 curr->bytes = 4;
3185 curr->type = Type::i32;
3186 break;
3187 case BinaryConsts::I64LoadMem8S:
3188 curr = allocator.alloc<Load>();
3189 curr->bytes = 1;
3190 curr->type = Type::i64;
3191 curr->signed_ = true;
3192 break;
3193 case BinaryConsts::I64LoadMem8U:
3194 curr = allocator.alloc<Load>();
3195 curr->bytes = 1;
3196 curr->type = Type::i64;
3197 curr->signed_ = false;
3198 break;
3199 case BinaryConsts::I64LoadMem16S:
3200 curr = allocator.alloc<Load>();
3201 curr->bytes = 2;
3202 curr->type = Type::i64;
3203 curr->signed_ = true;
3204 break;
3205 case BinaryConsts::I64LoadMem16U:
3206 curr = allocator.alloc<Load>();
3207 curr->bytes = 2;
3208 curr->type = Type::i64;
3209 curr->signed_ = false;
3210 break;
3211 case BinaryConsts::I64LoadMem32S:
3212 curr = allocator.alloc<Load>();
3213 curr->bytes = 4;
3214 curr->type = Type::i64;
3215 curr->signed_ = true;
3216 break;
3217 case BinaryConsts::I64LoadMem32U:
3218 curr = allocator.alloc<Load>();
3219 curr->bytes = 4;
3220 curr->type = Type::i64;
3221 curr->signed_ = false;
3222 break;
3223 case BinaryConsts::I64LoadMem:
3224 curr = allocator.alloc<Load>();
3225 curr->bytes = 8;
3226 curr->type = Type::i64;
3227 break;
3228 case BinaryConsts::F32LoadMem:
3229 curr = allocator.alloc<Load>();
3230 curr->bytes = 4;
3231 curr->type = Type::f32;
3232 break;
3233 case BinaryConsts::F64LoadMem:
3234 curr = allocator.alloc<Load>();
3235 curr->bytes = 8;
3236 curr->type = Type::f64;
3237 break;
3238 default:
3239 return false;
3240 }
3241 BYN_TRACE("zz node: Load\n");
3242 } else {
3243 switch (code) {
3244 case BinaryConsts::I32AtomicLoad8U:
3245 curr = allocator.alloc<Load>();
3246 curr->bytes = 1;
3247 curr->type = Type::i32;
3248 break;
3249 case BinaryConsts::I32AtomicLoad16U:
3250 curr = allocator.alloc<Load>();
3251 curr->bytes = 2;
3252 curr->type = Type::i32;
3253 break;
3254 case BinaryConsts::I32AtomicLoad:
3255 curr = allocator.alloc<Load>();
3256 curr->bytes = 4;
3257 curr->type = Type::i32;
3258 break;
3259 case BinaryConsts::I64AtomicLoad8U:
3260 curr = allocator.alloc<Load>();
3261 curr->bytes = 1;
3262 curr->type = Type::i64;
3263 break;
3264 case BinaryConsts::I64AtomicLoad16U:
3265 curr = allocator.alloc<Load>();
3266 curr->bytes = 2;
3267 curr->type = Type::i64;
3268 break;
3269 case BinaryConsts::I64AtomicLoad32U:
3270 curr = allocator.alloc<Load>();
3271 curr->bytes = 4;
3272 curr->type = Type::i64;
3273 break;
3274 case BinaryConsts::I64AtomicLoad:
3275 curr = allocator.alloc<Load>();
3276 curr->bytes = 8;
3277 curr->type = Type::i64;
3278 break;
3279 default:
3280 return false;
3281 }
3282 curr->signed_ = false;
3283 BYN_TRACE("zz node: AtomicLoad\n");
3284 }
3285
3286 curr->isAtomic = isAtomic;
3287 readMemoryAccess(curr->align, curr->offset);
3288 curr->ptr = popNonVoidExpression();
3289 curr->finalize();
3290 out = curr;
3291 return true;
3292 }
3293
maybeVisitStore(Expression * & out,uint8_t code,bool isAtomic)3294 bool WasmBinaryBuilder::maybeVisitStore(Expression*& out,
3295 uint8_t code,
3296 bool isAtomic) {
3297 Store* curr;
3298 if (!isAtomic) {
3299 switch (code) {
3300 case BinaryConsts::I32StoreMem8:
3301 curr = allocator.alloc<Store>();
3302 curr->bytes = 1;
3303 curr->valueType = Type::i32;
3304 break;
3305 case BinaryConsts::I32StoreMem16:
3306 curr = allocator.alloc<Store>();
3307 curr->bytes = 2;
3308 curr->valueType = Type::i32;
3309 break;
3310 case BinaryConsts::I32StoreMem:
3311 curr = allocator.alloc<Store>();
3312 curr->bytes = 4;
3313 curr->valueType = Type::i32;
3314 break;
3315 case BinaryConsts::I64StoreMem8:
3316 curr = allocator.alloc<Store>();
3317 curr->bytes = 1;
3318 curr->valueType = Type::i64;
3319 break;
3320 case BinaryConsts::I64StoreMem16:
3321 curr = allocator.alloc<Store>();
3322 curr->bytes = 2;
3323 curr->valueType = Type::i64;
3324 break;
3325 case BinaryConsts::I64StoreMem32:
3326 curr = allocator.alloc<Store>();
3327 curr->bytes = 4;
3328 curr->valueType = Type::i64;
3329 break;
3330 case BinaryConsts::I64StoreMem:
3331 curr = allocator.alloc<Store>();
3332 curr->bytes = 8;
3333 curr->valueType = Type::i64;
3334 break;
3335 case BinaryConsts::F32StoreMem:
3336 curr = allocator.alloc<Store>();
3337 curr->bytes = 4;
3338 curr->valueType = Type::f32;
3339 break;
3340 case BinaryConsts::F64StoreMem:
3341 curr = allocator.alloc<Store>();
3342 curr->bytes = 8;
3343 curr->valueType = Type::f64;
3344 break;
3345 default:
3346 return false;
3347 }
3348 } else {
3349 switch (code) {
3350 case BinaryConsts::I32AtomicStore8:
3351 curr = allocator.alloc<Store>();
3352 curr->bytes = 1;
3353 curr->valueType = Type::i32;
3354 break;
3355 case BinaryConsts::I32AtomicStore16:
3356 curr = allocator.alloc<Store>();
3357 curr->bytes = 2;
3358 curr->valueType = Type::i32;
3359 break;
3360 case BinaryConsts::I32AtomicStore:
3361 curr = allocator.alloc<Store>();
3362 curr->bytes = 4;
3363 curr->valueType = Type::i32;
3364 break;
3365 case BinaryConsts::I64AtomicStore8:
3366 curr = allocator.alloc<Store>();
3367 curr->bytes = 1;
3368 curr->valueType = Type::i64;
3369 break;
3370 case BinaryConsts::I64AtomicStore16:
3371 curr = allocator.alloc<Store>();
3372 curr->bytes = 2;
3373 curr->valueType = Type::i64;
3374 break;
3375 case BinaryConsts::I64AtomicStore32:
3376 curr = allocator.alloc<Store>();
3377 curr->bytes = 4;
3378 curr->valueType = Type::i64;
3379 break;
3380 case BinaryConsts::I64AtomicStore:
3381 curr = allocator.alloc<Store>();
3382 curr->bytes = 8;
3383 curr->valueType = Type::i64;
3384 break;
3385 default:
3386 return false;
3387 }
3388 }
3389
3390 curr->isAtomic = isAtomic;
3391 BYN_TRACE("zz node: Store\n");
3392 readMemoryAccess(curr->align, curr->offset);
3393 curr->value = popNonVoidExpression();
3394 curr->ptr = popNonVoidExpression();
3395 curr->finalize();
3396 out = curr;
3397 return true;
3398 }
3399
maybeVisitAtomicRMW(Expression * & out,uint8_t code)3400 bool WasmBinaryBuilder::maybeVisitAtomicRMW(Expression*& out, uint8_t code) {
3401 if (code < BinaryConsts::AtomicRMWOps_Begin ||
3402 code > BinaryConsts::AtomicRMWOps_End) {
3403 return false;
3404 }
3405 auto* curr = allocator.alloc<AtomicRMW>();
3406
3407 // Set curr to the given opcode, type and size.
3408 #define SET(opcode, optype, size) \
3409 curr->op = opcode; \
3410 curr->type = optype; \
3411 curr->bytes = size
3412
3413 // Handle the cases for all the valid types for a particular opcode
3414 #define SET_FOR_OP(Op) \
3415 case BinaryConsts::I32AtomicRMW##Op: \
3416 SET(Op, Type::i32, 4); \
3417 break; \
3418 case BinaryConsts::I32AtomicRMW##Op##8U: \
3419 SET(Op, Type::i32, 1); \
3420 break; \
3421 case BinaryConsts::I32AtomicRMW##Op##16U: \
3422 SET(Op, Type::i32, 2); \
3423 break; \
3424 case BinaryConsts::I64AtomicRMW##Op: \
3425 SET(Op, Type::i64, 8); \
3426 break; \
3427 case BinaryConsts::I64AtomicRMW##Op##8U: \
3428 SET(Op, Type::i64, 1); \
3429 break; \
3430 case BinaryConsts::I64AtomicRMW##Op##16U: \
3431 SET(Op, Type::i64, 2); \
3432 break; \
3433 case BinaryConsts::I64AtomicRMW##Op##32U: \
3434 SET(Op, Type::i64, 4); \
3435 break;
3436
3437 switch (code) {
3438 SET_FOR_OP(Add);
3439 SET_FOR_OP(Sub);
3440 SET_FOR_OP(And);
3441 SET_FOR_OP(Or);
3442 SET_FOR_OP(Xor);
3443 SET_FOR_OP(Xchg);
3444 default:
3445 WASM_UNREACHABLE("unexpected opcode");
3446 }
3447 #undef SET_FOR_OP
3448 #undef SET
3449
3450 BYN_TRACE("zz node: AtomicRMW\n");
3451 Address readAlign;
3452 readMemoryAccess(readAlign, curr->offset);
3453 if (readAlign != curr->bytes) {
3454 throwError("Align of AtomicRMW must match size");
3455 }
3456 curr->value = popNonVoidExpression();
3457 curr->ptr = popNonVoidExpression();
3458 curr->finalize();
3459 out = curr;
3460 return true;
3461 }
3462
maybeVisitAtomicCmpxchg(Expression * & out,uint8_t code)3463 bool WasmBinaryBuilder::maybeVisitAtomicCmpxchg(Expression*& out,
3464 uint8_t code) {
3465 if (code < BinaryConsts::AtomicCmpxchgOps_Begin ||
3466 code > BinaryConsts::AtomicCmpxchgOps_End) {
3467 return false;
3468 }
3469 auto* curr = allocator.alloc<AtomicCmpxchg>();
3470
3471 // Set curr to the given type and size.
3472 #define SET(optype, size) \
3473 curr->type = optype; \
3474 curr->bytes = size
3475
3476 switch (code) {
3477 case BinaryConsts::I32AtomicCmpxchg:
3478 SET(Type::i32, 4);
3479 break;
3480 case BinaryConsts::I64AtomicCmpxchg:
3481 SET(Type::i64, 8);
3482 break;
3483 case BinaryConsts::I32AtomicCmpxchg8U:
3484 SET(Type::i32, 1);
3485 break;
3486 case BinaryConsts::I32AtomicCmpxchg16U:
3487 SET(Type::i32, 2);
3488 break;
3489 case BinaryConsts::I64AtomicCmpxchg8U:
3490 SET(Type::i64, 1);
3491 break;
3492 case BinaryConsts::I64AtomicCmpxchg16U:
3493 SET(Type::i64, 2);
3494 break;
3495 case BinaryConsts::I64AtomicCmpxchg32U:
3496 SET(Type::i64, 4);
3497 break;
3498 default:
3499 WASM_UNREACHABLE("unexpected opcode");
3500 }
3501
3502 BYN_TRACE("zz node: AtomicCmpxchg\n");
3503 Address readAlign;
3504 readMemoryAccess(readAlign, curr->offset);
3505 if (readAlign != curr->bytes) {
3506 throwError("Align of AtomicCpxchg must match size");
3507 }
3508 curr->replacement = popNonVoidExpression();
3509 curr->expected = popNonVoidExpression();
3510 curr->ptr = popNonVoidExpression();
3511 curr->finalize();
3512 out = curr;
3513 return true;
3514 }
3515
maybeVisitAtomicWait(Expression * & out,uint8_t code)3516 bool WasmBinaryBuilder::maybeVisitAtomicWait(Expression*& out, uint8_t code) {
3517 if (code < BinaryConsts::I32AtomicWait ||
3518 code > BinaryConsts::I64AtomicWait) {
3519 return false;
3520 }
3521 auto* curr = allocator.alloc<AtomicWait>();
3522
3523 switch (code) {
3524 case BinaryConsts::I32AtomicWait:
3525 curr->expectedType = Type::i32;
3526 break;
3527 case BinaryConsts::I64AtomicWait:
3528 curr->expectedType = Type::i64;
3529 break;
3530 default:
3531 WASM_UNREACHABLE("unexpected opcode");
3532 }
3533 curr->type = Type::i32;
3534 BYN_TRACE("zz node: AtomicWait\n");
3535 curr->timeout = popNonVoidExpression();
3536 curr->expected = popNonVoidExpression();
3537 curr->ptr = popNonVoidExpression();
3538 Address readAlign;
3539 readMemoryAccess(readAlign, curr->offset);
3540 if (readAlign != curr->expectedType.getByteSize()) {
3541 throwError("Align of AtomicWait must match size");
3542 }
3543 curr->finalize();
3544 out = curr;
3545 return true;
3546 }
3547
maybeVisitAtomicNotify(Expression * & out,uint8_t code)3548 bool WasmBinaryBuilder::maybeVisitAtomicNotify(Expression*& out, uint8_t code) {
3549 if (code != BinaryConsts::AtomicNotify) {
3550 return false;
3551 }
3552 auto* curr = allocator.alloc<AtomicNotify>();
3553 BYN_TRACE("zz node: AtomicNotify\n");
3554
3555 curr->type = Type::i32;
3556 curr->notifyCount = popNonVoidExpression();
3557 curr->ptr = popNonVoidExpression();
3558 Address readAlign;
3559 readMemoryAccess(readAlign, curr->offset);
3560 if (readAlign != curr->type.getByteSize()) {
3561 throwError("Align of AtomicNotify must match size");
3562 }
3563 curr->finalize();
3564 out = curr;
3565 return true;
3566 }
3567
maybeVisitAtomicFence(Expression * & out,uint8_t code)3568 bool WasmBinaryBuilder::maybeVisitAtomicFence(Expression*& out, uint8_t code) {
3569 if (code != BinaryConsts::AtomicFence) {
3570 return false;
3571 }
3572 auto* curr = allocator.alloc<AtomicFence>();
3573 BYN_TRACE("zz node: AtomicFence\n");
3574 curr->order = getU32LEB();
3575 curr->finalize();
3576 out = curr;
3577 return true;
3578 }
3579
maybeVisitConst(Expression * & out,uint8_t code)3580 bool WasmBinaryBuilder::maybeVisitConst(Expression*& out, uint8_t code) {
3581 Const* curr;
3582 BYN_TRACE("zz node: Const, code " << code << std::endl);
3583 switch (code) {
3584 case BinaryConsts::I32Const:
3585 curr = allocator.alloc<Const>();
3586 curr->value = Literal(getS32LEB());
3587 break;
3588 case BinaryConsts::I64Const:
3589 curr = allocator.alloc<Const>();
3590 curr->value = Literal(getS64LEB());
3591 break;
3592 case BinaryConsts::F32Const:
3593 curr = allocator.alloc<Const>();
3594 curr->value = getFloat32Literal();
3595 break;
3596 case BinaryConsts::F64Const:
3597 curr = allocator.alloc<Const>();
3598 curr->value = getFloat64Literal();
3599 break;
3600 default:
3601 return false;
3602 }
3603 curr->type = curr->value.type;
3604 out = curr;
3605
3606 return true;
3607 }
3608
maybeVisitUnary(Expression * & out,uint8_t code)3609 bool WasmBinaryBuilder::maybeVisitUnary(Expression*& out, uint8_t code) {
3610 Unary* curr;
3611 switch (code) {
3612 case BinaryConsts::I32Clz:
3613 curr = allocator.alloc<Unary>();
3614 curr->op = ClzInt32;
3615 break;
3616 case BinaryConsts::I64Clz:
3617 curr = allocator.alloc<Unary>();
3618 curr->op = ClzInt64;
3619 break;
3620 case BinaryConsts::I32Ctz:
3621 curr = allocator.alloc<Unary>();
3622 curr->op = CtzInt32;
3623 break;
3624 case BinaryConsts::I64Ctz:
3625 curr = allocator.alloc<Unary>();
3626 curr->op = CtzInt64;
3627 break;
3628 case BinaryConsts::I32Popcnt:
3629 curr = allocator.alloc<Unary>();
3630 curr->op = PopcntInt32;
3631 break;
3632 case BinaryConsts::I64Popcnt:
3633 curr = allocator.alloc<Unary>();
3634 curr->op = PopcntInt64;
3635 break;
3636 case BinaryConsts::I32EqZ:
3637 curr = allocator.alloc<Unary>();
3638 curr->op = EqZInt32;
3639 break;
3640 case BinaryConsts::I64EqZ:
3641 curr = allocator.alloc<Unary>();
3642 curr->op = EqZInt64;
3643 break;
3644 case BinaryConsts::F32Neg:
3645 curr = allocator.alloc<Unary>();
3646 curr->op = NegFloat32;
3647 break;
3648 case BinaryConsts::F64Neg:
3649 curr = allocator.alloc<Unary>();
3650 curr->op = NegFloat64;
3651 break;
3652 case BinaryConsts::F32Abs:
3653 curr = allocator.alloc<Unary>();
3654 curr->op = AbsFloat32;
3655 break;
3656 case BinaryConsts::F64Abs:
3657 curr = allocator.alloc<Unary>();
3658 curr->op = AbsFloat64;
3659 break;
3660 case BinaryConsts::F32Ceil:
3661 curr = allocator.alloc<Unary>();
3662 curr->op = CeilFloat32;
3663 break;
3664 case BinaryConsts::F64Ceil:
3665 curr = allocator.alloc<Unary>();
3666 curr->op = CeilFloat64;
3667 break;
3668 case BinaryConsts::F32Floor:
3669 curr = allocator.alloc<Unary>();
3670 curr->op = FloorFloat32;
3671 break;
3672 case BinaryConsts::F64Floor:
3673 curr = allocator.alloc<Unary>();
3674 curr->op = FloorFloat64;
3675 break;
3676 case BinaryConsts::F32NearestInt:
3677 curr = allocator.alloc<Unary>();
3678 curr->op = NearestFloat32;
3679 break;
3680 case BinaryConsts::F64NearestInt:
3681 curr = allocator.alloc<Unary>();
3682 curr->op = NearestFloat64;
3683 break;
3684 case BinaryConsts::F32Sqrt:
3685 curr = allocator.alloc<Unary>();
3686 curr->op = SqrtFloat32;
3687 break;
3688 case BinaryConsts::F64Sqrt:
3689 curr = allocator.alloc<Unary>();
3690 curr->op = SqrtFloat64;
3691 break;
3692 case BinaryConsts::F32UConvertI32:
3693 curr = allocator.alloc<Unary>();
3694 curr->op = ConvertUInt32ToFloat32;
3695 break;
3696 case BinaryConsts::F64UConvertI32:
3697 curr = allocator.alloc<Unary>();
3698 curr->op = ConvertUInt32ToFloat64;
3699 break;
3700 case BinaryConsts::F32SConvertI32:
3701 curr = allocator.alloc<Unary>();
3702 curr->op = ConvertSInt32ToFloat32;
3703 break;
3704 case BinaryConsts::F64SConvertI32:
3705 curr = allocator.alloc<Unary>();
3706 curr->op = ConvertSInt32ToFloat64;
3707 break;
3708 case BinaryConsts::F32UConvertI64:
3709 curr = allocator.alloc<Unary>();
3710 curr->op = ConvertUInt64ToFloat32;
3711 break;
3712 case BinaryConsts::F64UConvertI64:
3713 curr = allocator.alloc<Unary>();
3714 curr->op = ConvertUInt64ToFloat64;
3715 break;
3716 case BinaryConsts::F32SConvertI64:
3717 curr = allocator.alloc<Unary>();
3718 curr->op = ConvertSInt64ToFloat32;
3719 break;
3720 case BinaryConsts::F64SConvertI64:
3721 curr = allocator.alloc<Unary>();
3722 curr->op = ConvertSInt64ToFloat64;
3723 break;
3724
3725 case BinaryConsts::I64SExtendI32:
3726 curr = allocator.alloc<Unary>();
3727 curr->op = ExtendSInt32;
3728 break;
3729 case BinaryConsts::I64UExtendI32:
3730 curr = allocator.alloc<Unary>();
3731 curr->op = ExtendUInt32;
3732 break;
3733 case BinaryConsts::I32WrapI64:
3734 curr = allocator.alloc<Unary>();
3735 curr->op = WrapInt64;
3736 break;
3737
3738 case BinaryConsts::I32UTruncF32:
3739 curr = allocator.alloc<Unary>();
3740 curr->op = TruncUFloat32ToInt32;
3741 break;
3742 case BinaryConsts::I32UTruncF64:
3743 curr = allocator.alloc<Unary>();
3744 curr->op = TruncUFloat64ToInt32;
3745 break;
3746 case BinaryConsts::I32STruncF32:
3747 curr = allocator.alloc<Unary>();
3748 curr->op = TruncSFloat32ToInt32;
3749 break;
3750 case BinaryConsts::I32STruncF64:
3751 curr = allocator.alloc<Unary>();
3752 curr->op = TruncSFloat64ToInt32;
3753 break;
3754 case BinaryConsts::I64UTruncF32:
3755 curr = allocator.alloc<Unary>();
3756 curr->op = TruncUFloat32ToInt64;
3757 break;
3758 case BinaryConsts::I64UTruncF64:
3759 curr = allocator.alloc<Unary>();
3760 curr->op = TruncUFloat64ToInt64;
3761 break;
3762 case BinaryConsts::I64STruncF32:
3763 curr = allocator.alloc<Unary>();
3764 curr->op = TruncSFloat32ToInt64;
3765 break;
3766 case BinaryConsts::I64STruncF64:
3767 curr = allocator.alloc<Unary>();
3768 curr->op = TruncSFloat64ToInt64;
3769 break;
3770
3771 case BinaryConsts::F32Trunc:
3772 curr = allocator.alloc<Unary>();
3773 curr->op = TruncFloat32;
3774 break;
3775 case BinaryConsts::F64Trunc:
3776 curr = allocator.alloc<Unary>();
3777 curr->op = TruncFloat64;
3778 break;
3779
3780 case BinaryConsts::F32DemoteI64:
3781 curr = allocator.alloc<Unary>();
3782 curr->op = DemoteFloat64;
3783 break;
3784 case BinaryConsts::F64PromoteF32:
3785 curr = allocator.alloc<Unary>();
3786 curr->op = PromoteFloat32;
3787 break;
3788 case BinaryConsts::I32ReinterpretF32:
3789 curr = allocator.alloc<Unary>();
3790 curr->op = ReinterpretFloat32;
3791 break;
3792 case BinaryConsts::I64ReinterpretF64:
3793 curr = allocator.alloc<Unary>();
3794 curr->op = ReinterpretFloat64;
3795 break;
3796 case BinaryConsts::F32ReinterpretI32:
3797 curr = allocator.alloc<Unary>();
3798 curr->op = ReinterpretInt32;
3799 break;
3800 case BinaryConsts::F64ReinterpretI64:
3801 curr = allocator.alloc<Unary>();
3802 curr->op = ReinterpretInt64;
3803 break;
3804
3805 case BinaryConsts::I32ExtendS8:
3806 curr = allocator.alloc<Unary>();
3807 curr->op = ExtendS8Int32;
3808 break;
3809 case BinaryConsts::I32ExtendS16:
3810 curr = allocator.alloc<Unary>();
3811 curr->op = ExtendS16Int32;
3812 break;
3813 case BinaryConsts::I64ExtendS8:
3814 curr = allocator.alloc<Unary>();
3815 curr->op = ExtendS8Int64;
3816 break;
3817 case BinaryConsts::I64ExtendS16:
3818 curr = allocator.alloc<Unary>();
3819 curr->op = ExtendS16Int64;
3820 break;
3821 case BinaryConsts::I64ExtendS32:
3822 curr = allocator.alloc<Unary>();
3823 curr->op = ExtendS32Int64;
3824 break;
3825
3826 default:
3827 return false;
3828 }
3829 BYN_TRACE("zz node: Unary\n");
3830 curr->value = popNonVoidExpression();
3831 curr->finalize();
3832 out = curr;
3833 return true;
3834 }
3835
maybeVisitTruncSat(Expression * & out,uint32_t code)3836 bool WasmBinaryBuilder::maybeVisitTruncSat(Expression*& out, uint32_t code) {
3837 Unary* curr;
3838 switch (code) {
3839 case BinaryConsts::I32STruncSatF32:
3840 curr = allocator.alloc<Unary>();
3841 curr->op = TruncSatSFloat32ToInt32;
3842 break;
3843 case BinaryConsts::I32UTruncSatF32:
3844 curr = allocator.alloc<Unary>();
3845 curr->op = TruncSatUFloat32ToInt32;
3846 break;
3847 case BinaryConsts::I32STruncSatF64:
3848 curr = allocator.alloc<Unary>();
3849 curr->op = TruncSatSFloat64ToInt32;
3850 break;
3851 case BinaryConsts::I32UTruncSatF64:
3852 curr = allocator.alloc<Unary>();
3853 curr->op = TruncSatUFloat64ToInt32;
3854 break;
3855 case BinaryConsts::I64STruncSatF32:
3856 curr = allocator.alloc<Unary>();
3857 curr->op = TruncSatSFloat32ToInt64;
3858 break;
3859 case BinaryConsts::I64UTruncSatF32:
3860 curr = allocator.alloc<Unary>();
3861 curr->op = TruncSatUFloat32ToInt64;
3862 break;
3863 case BinaryConsts::I64STruncSatF64:
3864 curr = allocator.alloc<Unary>();
3865 curr->op = TruncSatSFloat64ToInt64;
3866 break;
3867 case BinaryConsts::I64UTruncSatF64:
3868 curr = allocator.alloc<Unary>();
3869 curr->op = TruncSatUFloat64ToInt64;
3870 break;
3871 default:
3872 return false;
3873 }
3874 BYN_TRACE("zz node: Unary (nontrapping float-to-int)\n");
3875 curr->value = popNonVoidExpression();
3876 curr->finalize();
3877 out = curr;
3878 return true;
3879 }
3880
maybeVisitMemoryInit(Expression * & out,uint32_t code)3881 bool WasmBinaryBuilder::maybeVisitMemoryInit(Expression*& out, uint32_t code) {
3882 if (code != BinaryConsts::MemoryInit) {
3883 return false;
3884 }
3885 auto* curr = allocator.alloc<MemoryInit>();
3886 curr->size = popNonVoidExpression();
3887 curr->offset = popNonVoidExpression();
3888 curr->dest = popNonVoidExpression();
3889 curr->segment = getU32LEB();
3890 if (getInt8() != 0) {
3891 throwError("Unexpected nonzero memory index");
3892 }
3893 curr->finalize();
3894 out = curr;
3895 return true;
3896 }
3897
maybeVisitDataDrop(Expression * & out,uint32_t code)3898 bool WasmBinaryBuilder::maybeVisitDataDrop(Expression*& out, uint32_t code) {
3899 if (code != BinaryConsts::DataDrop) {
3900 return false;
3901 }
3902 auto* curr = allocator.alloc<DataDrop>();
3903 curr->segment = getU32LEB();
3904 curr->finalize();
3905 out = curr;
3906 return true;
3907 }
3908
maybeVisitMemoryCopy(Expression * & out,uint32_t code)3909 bool WasmBinaryBuilder::maybeVisitMemoryCopy(Expression*& out, uint32_t code) {
3910 if (code != BinaryConsts::MemoryCopy) {
3911 return false;
3912 }
3913 auto* curr = allocator.alloc<MemoryCopy>();
3914 curr->size = popNonVoidExpression();
3915 curr->source = popNonVoidExpression();
3916 curr->dest = popNonVoidExpression();
3917 if (getInt8() != 0 || getInt8() != 0) {
3918 throwError("Unexpected nonzero memory index");
3919 }
3920 curr->finalize();
3921 out = curr;
3922 return true;
3923 }
3924
maybeVisitMemoryFill(Expression * & out,uint32_t code)3925 bool WasmBinaryBuilder::maybeVisitMemoryFill(Expression*& out, uint32_t code) {
3926 if (code != BinaryConsts::MemoryFill) {
3927 return false;
3928 }
3929 auto* curr = allocator.alloc<MemoryFill>();
3930 curr->size = popNonVoidExpression();
3931 curr->value = popNonVoidExpression();
3932 curr->dest = popNonVoidExpression();
3933 if (getInt8() != 0) {
3934 throwError("Unexpected nonzero memory index");
3935 }
3936 curr->finalize();
3937 out = curr;
3938 return true;
3939 }
3940
maybeVisitBinary(Expression * & out,uint8_t code)3941 bool WasmBinaryBuilder::maybeVisitBinary(Expression*& out, uint8_t code) {
3942 Binary* curr;
3943 #define INT_TYPED_CODE(code) \
3944 { \
3945 case BinaryConsts::I32##code: \
3946 curr = allocator.alloc<Binary>(); \
3947 curr->op = code##Int32; \
3948 break; \
3949 case BinaryConsts::I64##code: \
3950 curr = allocator.alloc<Binary>(); \
3951 curr->op = code##Int64; \
3952 break; \
3953 }
3954 #define FLOAT_TYPED_CODE(code) \
3955 { \
3956 case BinaryConsts::F32##code: \
3957 curr = allocator.alloc<Binary>(); \
3958 curr->op = code##Float32; \
3959 break; \
3960 case BinaryConsts::F64##code: \
3961 curr = allocator.alloc<Binary>(); \
3962 curr->op = code##Float64; \
3963 break; \
3964 }
3965 #define TYPED_CODE(code) \
3966 { \
3967 INT_TYPED_CODE(code) \
3968 FLOAT_TYPED_CODE(code) \
3969 }
3970
3971 switch (code) {
3972 TYPED_CODE(Add);
3973 TYPED_CODE(Sub);
3974 TYPED_CODE(Mul);
3975 INT_TYPED_CODE(DivS);
3976 INT_TYPED_CODE(DivU);
3977 INT_TYPED_CODE(RemS);
3978 INT_TYPED_CODE(RemU);
3979 INT_TYPED_CODE(And);
3980 INT_TYPED_CODE(Or);
3981 INT_TYPED_CODE(Xor);
3982 INT_TYPED_CODE(Shl);
3983 INT_TYPED_CODE(ShrU);
3984 INT_TYPED_CODE(ShrS);
3985 INT_TYPED_CODE(RotL);
3986 INT_TYPED_CODE(RotR);
3987 FLOAT_TYPED_CODE(Div);
3988 FLOAT_TYPED_CODE(CopySign);
3989 FLOAT_TYPED_CODE(Min);
3990 FLOAT_TYPED_CODE(Max);
3991 TYPED_CODE(Eq);
3992 TYPED_CODE(Ne);
3993 INT_TYPED_CODE(LtS);
3994 INT_TYPED_CODE(LtU);
3995 INT_TYPED_CODE(LeS);
3996 INT_TYPED_CODE(LeU);
3997 INT_TYPED_CODE(GtS);
3998 INT_TYPED_CODE(GtU);
3999 INT_TYPED_CODE(GeS);
4000 INT_TYPED_CODE(GeU);
4001 FLOAT_TYPED_CODE(Lt);
4002 FLOAT_TYPED_CODE(Le);
4003 FLOAT_TYPED_CODE(Gt);
4004 FLOAT_TYPED_CODE(Ge);
4005 default:
4006 return false;
4007 }
4008 BYN_TRACE("zz node: Binary\n");
4009 curr->right = popNonVoidExpression();
4010 curr->left = popNonVoidExpression();
4011 curr->finalize();
4012 out = curr;
4013 return true;
4014 #undef TYPED_CODE
4015 #undef INT_TYPED_CODE
4016 #undef FLOAT_TYPED_CODE
4017 }
4018
maybeVisitSIMDBinary(Expression * & out,uint32_t code)4019 bool WasmBinaryBuilder::maybeVisitSIMDBinary(Expression*& out, uint32_t code) {
4020 Binary* curr;
4021 switch (code) {
4022 case BinaryConsts::I8x16Eq:
4023 curr = allocator.alloc<Binary>();
4024 curr->op = EqVecI8x16;
4025 break;
4026 case BinaryConsts::I8x16Ne:
4027 curr = allocator.alloc<Binary>();
4028 curr->op = NeVecI8x16;
4029 break;
4030 case BinaryConsts::I8x16LtS:
4031 curr = allocator.alloc<Binary>();
4032 curr->op = LtSVecI8x16;
4033 break;
4034 case BinaryConsts::I8x16LtU:
4035 curr = allocator.alloc<Binary>();
4036 curr->op = LtUVecI8x16;
4037 break;
4038 case BinaryConsts::I8x16GtS:
4039 curr = allocator.alloc<Binary>();
4040 curr->op = GtSVecI8x16;
4041 break;
4042 case BinaryConsts::I8x16GtU:
4043 curr = allocator.alloc<Binary>();
4044 curr->op = GtUVecI8x16;
4045 break;
4046 case BinaryConsts::I8x16LeS:
4047 curr = allocator.alloc<Binary>();
4048 curr->op = LeSVecI8x16;
4049 break;
4050 case BinaryConsts::I8x16LeU:
4051 curr = allocator.alloc<Binary>();
4052 curr->op = LeUVecI8x16;
4053 break;
4054 case BinaryConsts::I8x16GeS:
4055 curr = allocator.alloc<Binary>();
4056 curr->op = GeSVecI8x16;
4057 break;
4058 case BinaryConsts::I8x16GeU:
4059 curr = allocator.alloc<Binary>();
4060 curr->op = GeUVecI8x16;
4061 break;
4062 case BinaryConsts::I16x8Eq:
4063 curr = allocator.alloc<Binary>();
4064 curr->op = EqVecI16x8;
4065 break;
4066 case BinaryConsts::I16x8Ne:
4067 curr = allocator.alloc<Binary>();
4068 curr->op = NeVecI16x8;
4069 break;
4070 case BinaryConsts::I16x8LtS:
4071 curr = allocator.alloc<Binary>();
4072 curr->op = LtSVecI16x8;
4073 break;
4074 case BinaryConsts::I16x8LtU:
4075 curr = allocator.alloc<Binary>();
4076 curr->op = LtUVecI16x8;
4077 break;
4078 case BinaryConsts::I16x8GtS:
4079 curr = allocator.alloc<Binary>();
4080 curr->op = GtSVecI16x8;
4081 break;
4082 case BinaryConsts::I16x8GtU:
4083 curr = allocator.alloc<Binary>();
4084 curr->op = GtUVecI16x8;
4085 break;
4086 case BinaryConsts::I16x8LeS:
4087 curr = allocator.alloc<Binary>();
4088 curr->op = LeSVecI16x8;
4089 break;
4090 case BinaryConsts::I16x8LeU:
4091 curr = allocator.alloc<Binary>();
4092 curr->op = LeUVecI16x8;
4093 break;
4094 case BinaryConsts::I16x8GeS:
4095 curr = allocator.alloc<Binary>();
4096 curr->op = GeSVecI16x8;
4097 break;
4098 case BinaryConsts::I16x8GeU:
4099 curr = allocator.alloc<Binary>();
4100 curr->op = GeUVecI16x8;
4101 break;
4102 case BinaryConsts::I32x4Eq:
4103 curr = allocator.alloc<Binary>();
4104 curr->op = EqVecI32x4;
4105 break;
4106 case BinaryConsts::I32x4Ne:
4107 curr = allocator.alloc<Binary>();
4108 curr->op = NeVecI32x4;
4109 break;
4110 case BinaryConsts::I32x4LtS:
4111 curr = allocator.alloc<Binary>();
4112 curr->op = LtSVecI32x4;
4113 break;
4114 case BinaryConsts::I32x4LtU:
4115 curr = allocator.alloc<Binary>();
4116 curr->op = LtUVecI32x4;
4117 break;
4118 case BinaryConsts::I32x4GtS:
4119 curr = allocator.alloc<Binary>();
4120 curr->op = GtSVecI32x4;
4121 break;
4122 case BinaryConsts::I32x4GtU:
4123 curr = allocator.alloc<Binary>();
4124 curr->op = GtUVecI32x4;
4125 break;
4126 case BinaryConsts::I32x4LeS:
4127 curr = allocator.alloc<Binary>();
4128 curr->op = LeSVecI32x4;
4129 break;
4130 case BinaryConsts::I32x4LeU:
4131 curr = allocator.alloc<Binary>();
4132 curr->op = LeUVecI32x4;
4133 break;
4134 case BinaryConsts::I32x4GeS:
4135 curr = allocator.alloc<Binary>();
4136 curr->op = GeSVecI32x4;
4137 break;
4138 case BinaryConsts::I32x4GeU:
4139 curr = allocator.alloc<Binary>();
4140 curr->op = GeUVecI32x4;
4141 break;
4142 case BinaryConsts::F32x4Eq:
4143 curr = allocator.alloc<Binary>();
4144 curr->op = EqVecF32x4;
4145 break;
4146 case BinaryConsts::F32x4Ne:
4147 curr = allocator.alloc<Binary>();
4148 curr->op = NeVecF32x4;
4149 break;
4150 case BinaryConsts::F32x4Lt:
4151 curr = allocator.alloc<Binary>();
4152 curr->op = LtVecF32x4;
4153 break;
4154 case BinaryConsts::F32x4Gt:
4155 curr = allocator.alloc<Binary>();
4156 curr->op = GtVecF32x4;
4157 break;
4158 case BinaryConsts::F32x4Le:
4159 curr = allocator.alloc<Binary>();
4160 curr->op = LeVecF32x4;
4161 break;
4162 case BinaryConsts::F32x4Ge:
4163 curr = allocator.alloc<Binary>();
4164 curr->op = GeVecF32x4;
4165 break;
4166 case BinaryConsts::F64x2Eq:
4167 curr = allocator.alloc<Binary>();
4168 curr->op = EqVecF64x2;
4169 break;
4170 case BinaryConsts::F64x2Ne:
4171 curr = allocator.alloc<Binary>();
4172 curr->op = NeVecF64x2;
4173 break;
4174 case BinaryConsts::F64x2Lt:
4175 curr = allocator.alloc<Binary>();
4176 curr->op = LtVecF64x2;
4177 break;
4178 case BinaryConsts::F64x2Gt:
4179 curr = allocator.alloc<Binary>();
4180 curr->op = GtVecF64x2;
4181 break;
4182 case BinaryConsts::F64x2Le:
4183 curr = allocator.alloc<Binary>();
4184 curr->op = LeVecF64x2;
4185 break;
4186 case BinaryConsts::F64x2Ge:
4187 curr = allocator.alloc<Binary>();
4188 curr->op = GeVecF64x2;
4189 break;
4190 case BinaryConsts::V128And:
4191 curr = allocator.alloc<Binary>();
4192 curr->op = AndVec128;
4193 break;
4194 case BinaryConsts::V128Or:
4195 curr = allocator.alloc<Binary>();
4196 curr->op = OrVec128;
4197 break;
4198 case BinaryConsts::V128Xor:
4199 curr = allocator.alloc<Binary>();
4200 curr->op = XorVec128;
4201 break;
4202 case BinaryConsts::V128AndNot:
4203 curr = allocator.alloc<Binary>();
4204 curr->op = AndNotVec128;
4205 break;
4206 case BinaryConsts::I8x16Add:
4207 curr = allocator.alloc<Binary>();
4208 curr->op = AddVecI8x16;
4209 break;
4210 case BinaryConsts::I8x16AddSatS:
4211 curr = allocator.alloc<Binary>();
4212 curr->op = AddSatSVecI8x16;
4213 break;
4214 case BinaryConsts::I8x16AddSatU:
4215 curr = allocator.alloc<Binary>();
4216 curr->op = AddSatUVecI8x16;
4217 break;
4218 case BinaryConsts::I8x16Sub:
4219 curr = allocator.alloc<Binary>();
4220 curr->op = SubVecI8x16;
4221 break;
4222 case BinaryConsts::I8x16SubSatS:
4223 curr = allocator.alloc<Binary>();
4224 curr->op = SubSatSVecI8x16;
4225 break;
4226 case BinaryConsts::I8x16SubSatU:
4227 curr = allocator.alloc<Binary>();
4228 curr->op = SubSatUVecI8x16;
4229 break;
4230 case BinaryConsts::I8x16Mul:
4231 curr = allocator.alloc<Binary>();
4232 curr->op = MulVecI8x16;
4233 break;
4234 case BinaryConsts::I8x16MinS:
4235 curr = allocator.alloc<Binary>();
4236 curr->op = MinSVecI8x16;
4237 break;
4238 case BinaryConsts::I8x16MinU:
4239 curr = allocator.alloc<Binary>();
4240 curr->op = MinUVecI8x16;
4241 break;
4242 case BinaryConsts::I8x16MaxS:
4243 curr = allocator.alloc<Binary>();
4244 curr->op = MaxSVecI8x16;
4245 break;
4246 case BinaryConsts::I8x16MaxU:
4247 curr = allocator.alloc<Binary>();
4248 curr->op = MaxUVecI8x16;
4249 break;
4250 case BinaryConsts::I8x16AvgrU:
4251 curr = allocator.alloc<Binary>();
4252 curr->op = AvgrUVecI8x16;
4253 break;
4254 case BinaryConsts::I16x8Add:
4255 curr = allocator.alloc<Binary>();
4256 curr->op = AddVecI16x8;
4257 break;
4258 case BinaryConsts::I16x8AddSatS:
4259 curr = allocator.alloc<Binary>();
4260 curr->op = AddSatSVecI16x8;
4261 break;
4262 case BinaryConsts::I16x8AddSatU:
4263 curr = allocator.alloc<Binary>();
4264 curr->op = AddSatUVecI16x8;
4265 break;
4266 case BinaryConsts::I16x8Sub:
4267 curr = allocator.alloc<Binary>();
4268 curr->op = SubVecI16x8;
4269 break;
4270 case BinaryConsts::I16x8SubSatS:
4271 curr = allocator.alloc<Binary>();
4272 curr->op = SubSatSVecI16x8;
4273 break;
4274 case BinaryConsts::I16x8SubSatU:
4275 curr = allocator.alloc<Binary>();
4276 curr->op = SubSatUVecI16x8;
4277 break;
4278 case BinaryConsts::I16x8Mul:
4279 curr = allocator.alloc<Binary>();
4280 curr->op = MulVecI16x8;
4281 break;
4282 case BinaryConsts::I16x8MinS:
4283 curr = allocator.alloc<Binary>();
4284 curr->op = MinSVecI16x8;
4285 break;
4286 case BinaryConsts::I16x8MinU:
4287 curr = allocator.alloc<Binary>();
4288 curr->op = MinUVecI16x8;
4289 break;
4290 case BinaryConsts::I16x8MaxS:
4291 curr = allocator.alloc<Binary>();
4292 curr->op = MaxSVecI16x8;
4293 break;
4294 case BinaryConsts::I16x8MaxU:
4295 curr = allocator.alloc<Binary>();
4296 curr->op = MaxUVecI16x8;
4297 break;
4298 case BinaryConsts::I16x8AvgrU:
4299 curr = allocator.alloc<Binary>();
4300 curr->op = AvgrUVecI16x8;
4301 break;
4302 case BinaryConsts::I32x4Add:
4303 curr = allocator.alloc<Binary>();
4304 curr->op = AddVecI32x4;
4305 break;
4306 case BinaryConsts::I32x4Sub:
4307 curr = allocator.alloc<Binary>();
4308 curr->op = SubVecI32x4;
4309 break;
4310 case BinaryConsts::I32x4Mul:
4311 curr = allocator.alloc<Binary>();
4312 curr->op = MulVecI32x4;
4313 break;
4314 case BinaryConsts::I32x4MinS:
4315 curr = allocator.alloc<Binary>();
4316 curr->op = MinSVecI32x4;
4317 break;
4318 case BinaryConsts::I32x4MinU:
4319 curr = allocator.alloc<Binary>();
4320 curr->op = MinUVecI32x4;
4321 break;
4322 case BinaryConsts::I32x4MaxS:
4323 curr = allocator.alloc<Binary>();
4324 curr->op = MaxSVecI32x4;
4325 break;
4326 case BinaryConsts::I32x4MaxU:
4327 curr = allocator.alloc<Binary>();
4328 curr->op = MaxUVecI32x4;
4329 break;
4330 case BinaryConsts::I32x4DotSVecI16x8:
4331 curr = allocator.alloc<Binary>();
4332 curr->op = DotSVecI16x8ToVecI32x4;
4333 break;
4334 case BinaryConsts::I64x2Add:
4335 curr = allocator.alloc<Binary>();
4336 curr->op = AddVecI64x2;
4337 break;
4338 case BinaryConsts::I64x2Sub:
4339 curr = allocator.alloc<Binary>();
4340 curr->op = SubVecI64x2;
4341 break;
4342 case BinaryConsts::I64x2Mul:
4343 curr = allocator.alloc<Binary>();
4344 curr->op = MulVecI64x2;
4345 break;
4346 case BinaryConsts::F32x4Add:
4347 curr = allocator.alloc<Binary>();
4348 curr->op = AddVecF32x4;
4349 break;
4350 case BinaryConsts::F32x4Sub:
4351 curr = allocator.alloc<Binary>();
4352 curr->op = SubVecF32x4;
4353 break;
4354 case BinaryConsts::F32x4Mul:
4355 curr = allocator.alloc<Binary>();
4356 curr->op = MulVecF32x4;
4357 break;
4358 case BinaryConsts::F32x4Div:
4359 curr = allocator.alloc<Binary>();
4360 curr->op = DivVecF32x4;
4361 break;
4362 case BinaryConsts::F32x4Min:
4363 curr = allocator.alloc<Binary>();
4364 curr->op = MinVecF32x4;
4365 break;
4366 case BinaryConsts::F32x4Max:
4367 curr = allocator.alloc<Binary>();
4368 curr->op = MaxVecF32x4;
4369 break;
4370 case BinaryConsts::F32x4PMin:
4371 curr = allocator.alloc<Binary>();
4372 curr->op = PMinVecF32x4;
4373 break;
4374 case BinaryConsts::F32x4PMax:
4375 curr = allocator.alloc<Binary>();
4376 curr->op = PMaxVecF32x4;
4377 break;
4378 case BinaryConsts::F64x2Add:
4379 curr = allocator.alloc<Binary>();
4380 curr->op = AddVecF64x2;
4381 break;
4382 case BinaryConsts::F64x2Sub:
4383 curr = allocator.alloc<Binary>();
4384 curr->op = SubVecF64x2;
4385 break;
4386 case BinaryConsts::F64x2Mul:
4387 curr = allocator.alloc<Binary>();
4388 curr->op = MulVecF64x2;
4389 break;
4390 case BinaryConsts::F64x2Div:
4391 curr = allocator.alloc<Binary>();
4392 curr->op = DivVecF64x2;
4393 break;
4394 case BinaryConsts::F64x2Min:
4395 curr = allocator.alloc<Binary>();
4396 curr->op = MinVecF64x2;
4397 break;
4398 case BinaryConsts::F64x2Max:
4399 curr = allocator.alloc<Binary>();
4400 curr->op = MaxVecF64x2;
4401 break;
4402 case BinaryConsts::F64x2PMin:
4403 curr = allocator.alloc<Binary>();
4404 curr->op = PMinVecF64x2;
4405 break;
4406 case BinaryConsts::F64x2PMax:
4407 curr = allocator.alloc<Binary>();
4408 curr->op = PMaxVecF64x2;
4409 break;
4410 case BinaryConsts::I8x16NarrowSI16x8:
4411 curr = allocator.alloc<Binary>();
4412 curr->op = NarrowSVecI16x8ToVecI8x16;
4413 break;
4414 case BinaryConsts::I8x16NarrowUI16x8:
4415 curr = allocator.alloc<Binary>();
4416 curr->op = NarrowUVecI16x8ToVecI8x16;
4417 break;
4418 case BinaryConsts::I16x8NarrowSI32x4:
4419 curr = allocator.alloc<Binary>();
4420 curr->op = NarrowSVecI32x4ToVecI16x8;
4421 break;
4422 case BinaryConsts::I16x8NarrowUI32x4:
4423 curr = allocator.alloc<Binary>();
4424 curr->op = NarrowUVecI32x4ToVecI16x8;
4425 break;
4426 case BinaryConsts::V8x16Swizzle:
4427 curr = allocator.alloc<Binary>();
4428 curr->op = SwizzleVec8x16;
4429 break;
4430 default:
4431 return false;
4432 }
4433 BYN_TRACE("zz node: Binary\n");
4434 curr->right = popNonVoidExpression();
4435 curr->left = popNonVoidExpression();
4436 curr->finalize();
4437 out = curr;
4438 return true;
4439 }
maybeVisitSIMDUnary(Expression * & out,uint32_t code)4440 bool WasmBinaryBuilder::maybeVisitSIMDUnary(Expression*& out, uint32_t code) {
4441 Unary* curr;
4442 switch (code) {
4443 case BinaryConsts::I8x16Splat:
4444 curr = allocator.alloc<Unary>();
4445 curr->op = SplatVecI8x16;
4446 break;
4447 case BinaryConsts::I16x8Splat:
4448 curr = allocator.alloc<Unary>();
4449 curr->op = SplatVecI16x8;
4450 break;
4451 case BinaryConsts::I32x4Splat:
4452 curr = allocator.alloc<Unary>();
4453 curr->op = SplatVecI32x4;
4454 break;
4455 case BinaryConsts::I64x2Splat:
4456 curr = allocator.alloc<Unary>();
4457 curr->op = SplatVecI64x2;
4458 break;
4459 case BinaryConsts::F32x4Splat:
4460 curr = allocator.alloc<Unary>();
4461 curr->op = SplatVecF32x4;
4462 break;
4463 case BinaryConsts::F64x2Splat:
4464 curr = allocator.alloc<Unary>();
4465 curr->op = SplatVecF64x2;
4466 break;
4467 case BinaryConsts::V128Not:
4468 curr = allocator.alloc<Unary>();
4469 curr->op = NotVec128;
4470 break;
4471 case BinaryConsts::I8x16Abs:
4472 curr = allocator.alloc<Unary>();
4473 curr->op = AbsVecI8x16;
4474 break;
4475 case BinaryConsts::I8x16Neg:
4476 curr = allocator.alloc<Unary>();
4477 curr->op = NegVecI8x16;
4478 break;
4479 case BinaryConsts::I8x16AnyTrue:
4480 curr = allocator.alloc<Unary>();
4481 curr->op = AnyTrueVecI8x16;
4482 break;
4483 case BinaryConsts::I8x16AllTrue:
4484 curr = allocator.alloc<Unary>();
4485 curr->op = AllTrueVecI8x16;
4486 break;
4487 case BinaryConsts::I8x16Bitmask:
4488 curr = allocator.alloc<Unary>();
4489 curr->op = BitmaskVecI8x16;
4490 break;
4491 case BinaryConsts::I16x8Abs:
4492 curr = allocator.alloc<Unary>();
4493 curr->op = AbsVecI16x8;
4494 break;
4495 case BinaryConsts::I16x8Neg:
4496 curr = allocator.alloc<Unary>();
4497 curr->op = NegVecI16x8;
4498 break;
4499 case BinaryConsts::I16x8AnyTrue:
4500 curr = allocator.alloc<Unary>();
4501 curr->op = AnyTrueVecI16x8;
4502 break;
4503 case BinaryConsts::I16x8AllTrue:
4504 curr = allocator.alloc<Unary>();
4505 curr->op = AllTrueVecI16x8;
4506 break;
4507 case BinaryConsts::I16x8Bitmask:
4508 curr = allocator.alloc<Unary>();
4509 curr->op = BitmaskVecI16x8;
4510 break;
4511 case BinaryConsts::I32x4Abs:
4512 curr = allocator.alloc<Unary>();
4513 curr->op = AbsVecI32x4;
4514 break;
4515 case BinaryConsts::I32x4Neg:
4516 curr = allocator.alloc<Unary>();
4517 curr->op = NegVecI32x4;
4518 break;
4519 case BinaryConsts::I32x4AnyTrue:
4520 curr = allocator.alloc<Unary>();
4521 curr->op = AnyTrueVecI32x4;
4522 break;
4523 case BinaryConsts::I32x4AllTrue:
4524 curr = allocator.alloc<Unary>();
4525 curr->op = AllTrueVecI32x4;
4526 break;
4527 case BinaryConsts::I32x4Bitmask:
4528 curr = allocator.alloc<Unary>();
4529 curr->op = BitmaskVecI32x4;
4530 break;
4531 case BinaryConsts::I64x2Neg:
4532 curr = allocator.alloc<Unary>();
4533 curr->op = NegVecI64x2;
4534 break;
4535 case BinaryConsts::I64x2AnyTrue:
4536 curr = allocator.alloc<Unary>();
4537 curr->op = AnyTrueVecI64x2;
4538 break;
4539 case BinaryConsts::I64x2AllTrue:
4540 curr = allocator.alloc<Unary>();
4541 curr->op = AllTrueVecI64x2;
4542 break;
4543 case BinaryConsts::F32x4Abs:
4544 curr = allocator.alloc<Unary>();
4545 curr->op = AbsVecF32x4;
4546 break;
4547 case BinaryConsts::F32x4Neg:
4548 curr = allocator.alloc<Unary>();
4549 curr->op = NegVecF32x4;
4550 break;
4551 case BinaryConsts::F32x4Sqrt:
4552 curr = allocator.alloc<Unary>();
4553 curr->op = SqrtVecF32x4;
4554 break;
4555 case BinaryConsts::F32x4Ceil:
4556 curr = allocator.alloc<Unary>();
4557 curr->op = CeilVecF32x4;
4558 break;
4559 case BinaryConsts::F32x4Floor:
4560 curr = allocator.alloc<Unary>();
4561 curr->op = FloorVecF32x4;
4562 break;
4563 case BinaryConsts::F32x4Trunc:
4564 curr = allocator.alloc<Unary>();
4565 curr->op = TruncVecF32x4;
4566 break;
4567 case BinaryConsts::F32x4Nearest:
4568 curr = allocator.alloc<Unary>();
4569 curr->op = NearestVecF32x4;
4570 break;
4571 case BinaryConsts::F64x2Abs:
4572 curr = allocator.alloc<Unary>();
4573 curr->op = AbsVecF64x2;
4574 break;
4575 case BinaryConsts::F64x2Neg:
4576 curr = allocator.alloc<Unary>();
4577 curr->op = NegVecF64x2;
4578 break;
4579 case BinaryConsts::F64x2Sqrt:
4580 curr = allocator.alloc<Unary>();
4581 curr->op = SqrtVecF64x2;
4582 break;
4583 case BinaryConsts::F64x2Ceil:
4584 curr = allocator.alloc<Unary>();
4585 curr->op = CeilVecF64x2;
4586 break;
4587 case BinaryConsts::F64x2Floor:
4588 curr = allocator.alloc<Unary>();
4589 curr->op = FloorVecF64x2;
4590 break;
4591 case BinaryConsts::F64x2Trunc:
4592 curr = allocator.alloc<Unary>();
4593 curr->op = TruncVecF64x2;
4594 break;
4595 case BinaryConsts::F64x2Nearest:
4596 curr = allocator.alloc<Unary>();
4597 curr->op = NearestVecF64x2;
4598 break;
4599 case BinaryConsts::I32x4TruncSatSF32x4:
4600 curr = allocator.alloc<Unary>();
4601 curr->op = TruncSatSVecF32x4ToVecI32x4;
4602 break;
4603 case BinaryConsts::I32x4TruncSatUF32x4:
4604 curr = allocator.alloc<Unary>();
4605 curr->op = TruncSatUVecF32x4ToVecI32x4;
4606 break;
4607 case BinaryConsts::I64x2TruncSatSF64x2:
4608 curr = allocator.alloc<Unary>();
4609 curr->op = TruncSatSVecF64x2ToVecI64x2;
4610 break;
4611 case BinaryConsts::I64x2TruncSatUF64x2:
4612 curr = allocator.alloc<Unary>();
4613 curr->op = TruncSatUVecF64x2ToVecI64x2;
4614 break;
4615 case BinaryConsts::F32x4ConvertSI32x4:
4616 curr = allocator.alloc<Unary>();
4617 curr->op = ConvertSVecI32x4ToVecF32x4;
4618 break;
4619 case BinaryConsts::F32x4ConvertUI32x4:
4620 curr = allocator.alloc<Unary>();
4621 curr->op = ConvertUVecI32x4ToVecF32x4;
4622 break;
4623 case BinaryConsts::F64x2ConvertSI64x2:
4624 curr = allocator.alloc<Unary>();
4625 curr->op = ConvertSVecI64x2ToVecF64x2;
4626 break;
4627 case BinaryConsts::F64x2ConvertUI64x2:
4628 curr = allocator.alloc<Unary>();
4629 curr->op = ConvertUVecI64x2ToVecF64x2;
4630 break;
4631 case BinaryConsts::I16x8WidenLowSI8x16:
4632 curr = allocator.alloc<Unary>();
4633 curr->op = WidenLowSVecI8x16ToVecI16x8;
4634 break;
4635 case BinaryConsts::I16x8WidenHighSI8x16:
4636 curr = allocator.alloc<Unary>();
4637 curr->op = WidenHighSVecI8x16ToVecI16x8;
4638 break;
4639 case BinaryConsts::I16x8WidenLowUI8x16:
4640 curr = allocator.alloc<Unary>();
4641 curr->op = WidenLowUVecI8x16ToVecI16x8;
4642 break;
4643 case BinaryConsts::I16x8WidenHighUI8x16:
4644 curr = allocator.alloc<Unary>();
4645 curr->op = WidenHighUVecI8x16ToVecI16x8;
4646 break;
4647 case BinaryConsts::I32x4WidenLowSI16x8:
4648 curr = allocator.alloc<Unary>();
4649 curr->op = WidenLowSVecI16x8ToVecI32x4;
4650 break;
4651 case BinaryConsts::I32x4WidenHighSI16x8:
4652 curr = allocator.alloc<Unary>();
4653 curr->op = WidenHighSVecI16x8ToVecI32x4;
4654 break;
4655 case BinaryConsts::I32x4WidenLowUI16x8:
4656 curr = allocator.alloc<Unary>();
4657 curr->op = WidenLowUVecI16x8ToVecI32x4;
4658 break;
4659 case BinaryConsts::I32x4WidenHighUI16x8:
4660 curr = allocator.alloc<Unary>();
4661 curr->op = WidenHighUVecI16x8ToVecI32x4;
4662 break;
4663 default:
4664 return false;
4665 }
4666 curr->value = popNonVoidExpression();
4667 curr->finalize();
4668 out = curr;
4669 return true;
4670 }
4671
maybeVisitSIMDConst(Expression * & out,uint32_t code)4672 bool WasmBinaryBuilder::maybeVisitSIMDConst(Expression*& out, uint32_t code) {
4673 if (code != BinaryConsts::V128Const) {
4674 return false;
4675 }
4676 auto* curr = allocator.alloc<Const>();
4677 curr->value = getVec128Literal();
4678 curr->finalize();
4679 out = curr;
4680 return true;
4681 }
4682
maybeVisitSIMDStore(Expression * & out,uint32_t code)4683 bool WasmBinaryBuilder::maybeVisitSIMDStore(Expression*& out, uint32_t code) {
4684 if (code != BinaryConsts::V128Store) {
4685 return false;
4686 }
4687 auto* curr = allocator.alloc<Store>();
4688 curr->bytes = 16;
4689 curr->valueType = Type::v128;
4690 readMemoryAccess(curr->align, curr->offset);
4691 curr->isAtomic = false;
4692 curr->value = popNonVoidExpression();
4693 curr->ptr = popNonVoidExpression();
4694 curr->finalize();
4695 out = curr;
4696 return true;
4697 }
4698
maybeVisitSIMDExtract(Expression * & out,uint32_t code)4699 bool WasmBinaryBuilder::maybeVisitSIMDExtract(Expression*& out, uint32_t code) {
4700 SIMDExtract* curr;
4701 switch (code) {
4702 case BinaryConsts::I8x16ExtractLaneS:
4703 curr = allocator.alloc<SIMDExtract>();
4704 curr->op = ExtractLaneSVecI8x16;
4705 curr->index = getLaneIndex(16);
4706 break;
4707 case BinaryConsts::I8x16ExtractLaneU:
4708 curr = allocator.alloc<SIMDExtract>();
4709 curr->op = ExtractLaneUVecI8x16;
4710 curr->index = getLaneIndex(16);
4711 break;
4712 case BinaryConsts::I16x8ExtractLaneS:
4713 curr = allocator.alloc<SIMDExtract>();
4714 curr->op = ExtractLaneSVecI16x8;
4715 curr->index = getLaneIndex(8);
4716 break;
4717 case BinaryConsts::I16x8ExtractLaneU:
4718 curr = allocator.alloc<SIMDExtract>();
4719 curr->op = ExtractLaneUVecI16x8;
4720 curr->index = getLaneIndex(8);
4721 break;
4722 case BinaryConsts::I32x4ExtractLane:
4723 curr = allocator.alloc<SIMDExtract>();
4724 curr->op = ExtractLaneVecI32x4;
4725 curr->index = getLaneIndex(4);
4726 break;
4727 case BinaryConsts::I64x2ExtractLane:
4728 curr = allocator.alloc<SIMDExtract>();
4729 curr->op = ExtractLaneVecI64x2;
4730 curr->index = getLaneIndex(2);
4731 break;
4732 case BinaryConsts::F32x4ExtractLane:
4733 curr = allocator.alloc<SIMDExtract>();
4734 curr->op = ExtractLaneVecF32x4;
4735 curr->index = getLaneIndex(4);
4736 break;
4737 case BinaryConsts::F64x2ExtractLane:
4738 curr = allocator.alloc<SIMDExtract>();
4739 curr->op = ExtractLaneVecF64x2;
4740 curr->index = getLaneIndex(2);
4741 break;
4742 default:
4743 return false;
4744 }
4745 curr->vec = popNonVoidExpression();
4746 curr->finalize();
4747 out = curr;
4748 return true;
4749 }
4750
maybeVisitSIMDReplace(Expression * & out,uint32_t code)4751 bool WasmBinaryBuilder::maybeVisitSIMDReplace(Expression*& out, uint32_t code) {
4752 SIMDReplace* curr;
4753 switch (code) {
4754 case BinaryConsts::I8x16ReplaceLane:
4755 curr = allocator.alloc<SIMDReplace>();
4756 curr->op = ReplaceLaneVecI8x16;
4757 curr->index = getLaneIndex(16);
4758 break;
4759 case BinaryConsts::I16x8ReplaceLane:
4760 curr = allocator.alloc<SIMDReplace>();
4761 curr->op = ReplaceLaneVecI16x8;
4762 curr->index = getLaneIndex(8);
4763 break;
4764 case BinaryConsts::I32x4ReplaceLane:
4765 curr = allocator.alloc<SIMDReplace>();
4766 curr->op = ReplaceLaneVecI32x4;
4767 curr->index = getLaneIndex(4);
4768 break;
4769 case BinaryConsts::I64x2ReplaceLane:
4770 curr = allocator.alloc<SIMDReplace>();
4771 curr->op = ReplaceLaneVecI64x2;
4772 curr->index = getLaneIndex(2);
4773 break;
4774 case BinaryConsts::F32x4ReplaceLane:
4775 curr = allocator.alloc<SIMDReplace>();
4776 curr->op = ReplaceLaneVecF32x4;
4777 curr->index = getLaneIndex(4);
4778 break;
4779 case BinaryConsts::F64x2ReplaceLane:
4780 curr = allocator.alloc<SIMDReplace>();
4781 curr->op = ReplaceLaneVecF64x2;
4782 curr->index = getLaneIndex(2);
4783 break;
4784 default:
4785 return false;
4786 }
4787 curr->value = popNonVoidExpression();
4788 curr->vec = popNonVoidExpression();
4789 curr->finalize();
4790 out = curr;
4791 return true;
4792 }
4793
maybeVisitSIMDShuffle(Expression * & out,uint32_t code)4794 bool WasmBinaryBuilder::maybeVisitSIMDShuffle(Expression*& out, uint32_t code) {
4795 if (code != BinaryConsts::V8x16Shuffle) {
4796 return false;
4797 }
4798 auto* curr = allocator.alloc<SIMDShuffle>();
4799 for (auto i = 0; i < 16; ++i) {
4800 curr->mask[i] = getLaneIndex(32);
4801 }
4802 curr->right = popNonVoidExpression();
4803 curr->left = popNonVoidExpression();
4804 curr->finalize();
4805 out = curr;
4806 return true;
4807 }
4808
maybeVisitSIMDTernary(Expression * & out,uint32_t code)4809 bool WasmBinaryBuilder::maybeVisitSIMDTernary(Expression*& out, uint32_t code) {
4810 SIMDTernary* curr;
4811 switch (code) {
4812 case BinaryConsts::V128Bitselect:
4813 curr = allocator.alloc<SIMDTernary>();
4814 curr->op = Bitselect;
4815 break;
4816 case BinaryConsts::F32x4QFMA:
4817 curr = allocator.alloc<SIMDTernary>();
4818 curr->op = QFMAF32x4;
4819 break;
4820 case BinaryConsts::F32x4QFMS:
4821 curr = allocator.alloc<SIMDTernary>();
4822 curr->op = QFMSF32x4;
4823 break;
4824 case BinaryConsts::F64x2QFMA:
4825 curr = allocator.alloc<SIMDTernary>();
4826 curr->op = QFMAF64x2;
4827 break;
4828 case BinaryConsts::F64x2QFMS:
4829 curr = allocator.alloc<SIMDTernary>();
4830 curr->op = QFMSF64x2;
4831 break;
4832 default:
4833 return false;
4834 }
4835 curr->c = popNonVoidExpression();
4836 curr->b = popNonVoidExpression();
4837 curr->a = popNonVoidExpression();
4838 curr->finalize();
4839 out = curr;
4840 return true;
4841 }
4842
maybeVisitSIMDShift(Expression * & out,uint32_t code)4843 bool WasmBinaryBuilder::maybeVisitSIMDShift(Expression*& out, uint32_t code) {
4844 SIMDShift* curr;
4845 switch (code) {
4846 case BinaryConsts::I8x16Shl:
4847 curr = allocator.alloc<SIMDShift>();
4848 curr->op = ShlVecI8x16;
4849 break;
4850 case BinaryConsts::I8x16ShrS:
4851 curr = allocator.alloc<SIMDShift>();
4852 curr->op = ShrSVecI8x16;
4853 break;
4854 case BinaryConsts::I8x16ShrU:
4855 curr = allocator.alloc<SIMDShift>();
4856 curr->op = ShrUVecI8x16;
4857 break;
4858 case BinaryConsts::I16x8Shl:
4859 curr = allocator.alloc<SIMDShift>();
4860 curr->op = ShlVecI16x8;
4861 break;
4862 case BinaryConsts::I16x8ShrS:
4863 curr = allocator.alloc<SIMDShift>();
4864 curr->op = ShrSVecI16x8;
4865 break;
4866 case BinaryConsts::I16x8ShrU:
4867 curr = allocator.alloc<SIMDShift>();
4868 curr->op = ShrUVecI16x8;
4869 break;
4870 case BinaryConsts::I32x4Shl:
4871 curr = allocator.alloc<SIMDShift>();
4872 curr->op = ShlVecI32x4;
4873 break;
4874 case BinaryConsts::I32x4ShrS:
4875 curr = allocator.alloc<SIMDShift>();
4876 curr->op = ShrSVecI32x4;
4877 break;
4878 case BinaryConsts::I32x4ShrU:
4879 curr = allocator.alloc<SIMDShift>();
4880 curr->op = ShrUVecI32x4;
4881 break;
4882 case BinaryConsts::I64x2Shl:
4883 curr = allocator.alloc<SIMDShift>();
4884 curr->op = ShlVecI64x2;
4885 break;
4886 case BinaryConsts::I64x2ShrS:
4887 curr = allocator.alloc<SIMDShift>();
4888 curr->op = ShrSVecI64x2;
4889 break;
4890 case BinaryConsts::I64x2ShrU:
4891 curr = allocator.alloc<SIMDShift>();
4892 curr->op = ShrUVecI64x2;
4893 break;
4894 default:
4895 return false;
4896 }
4897 curr->shift = popNonVoidExpression();
4898 curr->vec = popNonVoidExpression();
4899 curr->finalize();
4900 out = curr;
4901 return true;
4902 }
4903
maybeVisitSIMDLoad(Expression * & out,uint32_t code)4904 bool WasmBinaryBuilder::maybeVisitSIMDLoad(Expression*& out, uint32_t code) {
4905 if (code == BinaryConsts::V128Load) {
4906 auto* curr = allocator.alloc<Load>();
4907 curr->type = Type::v128;
4908 curr->bytes = 16;
4909 readMemoryAccess(curr->align, curr->offset);
4910 curr->isAtomic = false;
4911 curr->ptr = popNonVoidExpression();
4912 curr->finalize();
4913 out = curr;
4914 return true;
4915 }
4916 SIMDLoad* curr;
4917 switch (code) {
4918 case BinaryConsts::V8x16LoadSplat:
4919 curr = allocator.alloc<SIMDLoad>();
4920 curr->op = LoadSplatVec8x16;
4921 break;
4922 case BinaryConsts::V16x8LoadSplat:
4923 curr = allocator.alloc<SIMDLoad>();
4924 curr->op = LoadSplatVec16x8;
4925 break;
4926 case BinaryConsts::V32x4LoadSplat:
4927 curr = allocator.alloc<SIMDLoad>();
4928 curr->op = LoadSplatVec32x4;
4929 break;
4930 case BinaryConsts::V64x2LoadSplat:
4931 curr = allocator.alloc<SIMDLoad>();
4932 curr->op = LoadSplatVec64x2;
4933 break;
4934 case BinaryConsts::I16x8LoadExtSVec8x8:
4935 curr = allocator.alloc<SIMDLoad>();
4936 curr->op = LoadExtSVec8x8ToVecI16x8;
4937 break;
4938 case BinaryConsts::I16x8LoadExtUVec8x8:
4939 curr = allocator.alloc<SIMDLoad>();
4940 curr->op = LoadExtUVec8x8ToVecI16x8;
4941 break;
4942 case BinaryConsts::I32x4LoadExtSVec16x4:
4943 curr = allocator.alloc<SIMDLoad>();
4944 curr->op = LoadExtSVec16x4ToVecI32x4;
4945 break;
4946 case BinaryConsts::I32x4LoadExtUVec16x4:
4947 curr = allocator.alloc<SIMDLoad>();
4948 curr->op = LoadExtUVec16x4ToVecI32x4;
4949 break;
4950 case BinaryConsts::I64x2LoadExtSVec32x2:
4951 curr = allocator.alloc<SIMDLoad>();
4952 curr->op = LoadExtSVec32x2ToVecI64x2;
4953 break;
4954 case BinaryConsts::I64x2LoadExtUVec32x2:
4955 curr = allocator.alloc<SIMDLoad>();
4956 curr->op = LoadExtUVec32x2ToVecI64x2;
4957 break;
4958 case BinaryConsts::V128Load32Zero:
4959 curr = allocator.alloc<SIMDLoad>();
4960 curr->op = Load32Zero;
4961 break;
4962 case BinaryConsts::V128Load64Zero:
4963 curr = allocator.alloc<SIMDLoad>();
4964 curr->op = Load64Zero;
4965 break;
4966 default:
4967 return false;
4968 }
4969 readMemoryAccess(curr->align, curr->offset);
4970 curr->ptr = popNonVoidExpression();
4971 curr->finalize();
4972 out = curr;
4973 return true;
4974 }
4975
visitSelect(Select * curr,uint8_t code)4976 void WasmBinaryBuilder::visitSelect(Select* curr, uint8_t code) {
4977 BYN_TRACE("zz node: Select, code " << int32_t(code) << std::endl);
4978 if (code == BinaryConsts::SelectWithType) {
4979 size_t numTypes = getU32LEB();
4980 std::vector<Type> types;
4981 for (size_t i = 0; i < numTypes; i++) {
4982 types.push_back(getType());
4983 }
4984 curr->type = Type(types);
4985 }
4986 curr->condition = popNonVoidExpression();
4987 curr->ifFalse = popNonVoidExpression();
4988 curr->ifTrue = popNonVoidExpression();
4989 if (code == BinaryConsts::SelectWithType) {
4990 curr->finalize(curr->type);
4991 } else {
4992 curr->finalize();
4993 }
4994 }
4995
visitReturn(Return * curr)4996 void WasmBinaryBuilder::visitReturn(Return* curr) {
4997 BYN_TRACE("zz node: Return\n");
4998 requireFunctionContext("return");
4999 if (currFunction->sig.results.isConcrete()) {
5000 curr->value = popTypedExpression(currFunction->sig.results);
5001 }
5002 curr->finalize();
5003 }
5004
visitMemorySize(MemorySize * curr)5005 void WasmBinaryBuilder::visitMemorySize(MemorySize* curr) {
5006 BYN_TRACE("zz node: MemorySize\n");
5007 auto reserved = getU32LEB();
5008 if (reserved != 0) {
5009 throwError("Invalid reserved field on memory.size");
5010 }
5011 curr->finalize();
5012 }
5013
visitMemoryGrow(MemoryGrow * curr)5014 void WasmBinaryBuilder::visitMemoryGrow(MemoryGrow* curr) {
5015 BYN_TRACE("zz node: MemoryGrow\n");
5016 curr->delta = popNonVoidExpression();
5017 auto reserved = getU32LEB();
5018 if (reserved != 0) {
5019 throwError("Invalid reserved field on memory.grow");
5020 }
5021 curr->finalize();
5022 }
5023
visitNop(Nop * curr)5024 void WasmBinaryBuilder::visitNop(Nop* curr) { BYN_TRACE("zz node: Nop\n"); }
5025
visitUnreachable(Unreachable * curr)5026 void WasmBinaryBuilder::visitUnreachable(Unreachable* curr) {
5027 BYN_TRACE("zz node: Unreachable\n");
5028 }
5029
visitDrop(Drop * curr)5030 void WasmBinaryBuilder::visitDrop(Drop* curr) {
5031 BYN_TRACE("zz node: Drop\n");
5032 curr->value = popNonVoidExpression();
5033 curr->finalize();
5034 }
5035
visitRefNull(RefNull * curr)5036 void WasmBinaryBuilder::visitRefNull(RefNull* curr) {
5037 BYN_TRACE("zz node: RefNull\n");
5038 curr->finalize(getHeapType());
5039 }
5040
visitRefIsNull(RefIsNull * curr)5041 void WasmBinaryBuilder::visitRefIsNull(RefIsNull* curr) {
5042 BYN_TRACE("zz node: RefIsNull\n");
5043 curr->value = popNonVoidExpression();
5044 curr->finalize();
5045 }
5046
visitRefFunc(RefFunc * curr)5047 void WasmBinaryBuilder::visitRefFunc(RefFunc* curr) {
5048 BYN_TRACE("zz node: RefFunc\n");
5049 Index index = getU32LEB();
5050 if (index >= functionImports.size() + functionSignatures.size()) {
5051 throwError("ref.func: invalid call index");
5052 }
5053 functionRefs[index].push_back(curr); // we don't know function names yet
5054 curr->finalize();
5055 }
5056
visitRefEq(RefEq * curr)5057 void WasmBinaryBuilder::visitRefEq(RefEq* curr) {
5058 BYN_TRACE("zz node: RefEq\n");
5059 curr->right = popNonVoidExpression();
5060 curr->left = popNonVoidExpression();
5061 curr->finalize();
5062 }
5063
visitTryOrTryInBlock(Expression * & out)5064 void WasmBinaryBuilder::visitTryOrTryInBlock(Expression*& out) {
5065 BYN_TRACE("zz node: Try\n");
5066 auto* curr = allocator.alloc<Try>();
5067 startControlFlow(curr);
5068 // For simplicity of implementation, like if scopes, we create a hidden block
5069 // within each try-body and catch-body, and let branches target those inner
5070 // blocks instead.
5071 curr->type = getType();
5072 curr->body = getBlockOrSingleton(curr->type);
5073 if (lastSeparator != BinaryConsts::Catch) {
5074 throwError("No catch instruction within a try scope");
5075 }
5076
5077 // For simplicity, we create an inner block within the catch body too, but the
5078 // one within the 'catch' *must* be omitted when we write out the binary back
5079 // later, because the 'catch' instruction pushes a value onto the stack and
5080 // the inner block does not support block input parameters without multivalue
5081 // support.
5082 // try
5083 // ...
5084 // catch ;; Pushes a value onto the stack
5085 // block ;; Inner block. Should be deleted when writing binary!
5086 // use the pushed value
5087 // end
5088 // end
5089 //
5090 // But when input binary code is like
5091 // try
5092 // ...
5093 // catch
5094 // br 0
5095 // end
5096 //
5097 // 'br 0' accidentally happens to target the inner block, creating code like
5098 // this in Binaryen IR, making the inner block not deletable, resulting in a
5099 // validation error:
5100 // (try
5101 // ...
5102 // (catch
5103 // (block $label0 ;; Cannot be deleted, because there's a branch to this
5104 // ...
5105 // (br $label0)
5106 // )
5107 // )
5108 // )
5109 //
5110 // When this happens, we fix this by creating a block that wraps the whole
5111 // try-catch, and making the branches target that block instead, like this:
5112 // (block $label ;; New enclosing block, new target for the branch
5113 // (try
5114 // ...
5115 // (catch
5116 // (block ;; Now this can be deleted when writing binary
5117 // ...
5118 // (br $label0)
5119 // )
5120 // )
5121 // )
5122 // )
5123 Name catchLabel = getNextLabel();
5124 breakStack.push_back({catchLabel, curr->type});
5125 auto start = expressionStack.size();
5126
5127 Builder builder(wasm);
5128 pushExpression(builder.makePop(Type::exnref));
5129
5130 processExpressions();
5131 size_t end = expressionStack.size();
5132 if (start > end) {
5133 throwError("block cannot pop from outside");
5134 }
5135 if (end - start == 1) {
5136 curr->catchBody = popExpression();
5137 } else {
5138 auto* block = allocator.alloc<Block>();
5139 pushBlockElements(block, curr->type, start);
5140 block->finalize(curr->type);
5141 curr->catchBody = block;
5142 }
5143 curr->finalize(curr->type);
5144
5145 if (breakTargetNames.find(catchLabel) == breakTargetNames.end()) {
5146 out = curr;
5147 } else {
5148 // Create a new block that encloses the whole try-catch
5149 auto* block = builder.makeBlock(catchLabel, curr);
5150 out = block;
5151 }
5152 breakStack.pop_back();
5153 breakTargetNames.erase(catchLabel);
5154 }
5155
visitThrow(Throw * curr)5156 void WasmBinaryBuilder::visitThrow(Throw* curr) {
5157 BYN_TRACE("zz node: Throw\n");
5158 auto index = getU32LEB();
5159 if (index >= wasm.events.size()) {
5160 throwError("bad event index");
5161 }
5162 auto* event = wasm.events[index].get();
5163 curr->event = event->name;
5164 size_t num = event->sig.params.size();
5165 curr->operands.resize(num);
5166 for (size_t i = 0; i < num; i++) {
5167 curr->operands[num - i - 1] = popNonVoidExpression();
5168 }
5169 curr->finalize();
5170 }
5171
visitRethrow(Rethrow * curr)5172 void WasmBinaryBuilder::visitRethrow(Rethrow* curr) {
5173 BYN_TRACE("zz node: Rethrow\n");
5174 curr->exnref = popNonVoidExpression();
5175 curr->finalize();
5176 }
5177
visitBrOnExn(BrOnExn * curr)5178 void WasmBinaryBuilder::visitBrOnExn(BrOnExn* curr) {
5179 BYN_TRACE("zz node: BrOnExn\n");
5180 BreakTarget target = getBreakTarget(getU32LEB());
5181 curr->name = target.name;
5182 auto index = getU32LEB();
5183 if (index >= wasm.events.size()) {
5184 throwError("bad event index");
5185 }
5186 curr->event = wasm.events[index]->name;
5187 curr->exnref = popNonVoidExpression();
5188
5189 Event* event = wasm.getEventOrNull(curr->event);
5190 assert(event && "br_on_exn's event must exist");
5191
5192 // Copy params info into BrOnExn, because it is necessary when BrOnExn is
5193 // refinalized without the module.
5194 curr->sent = event->sig.params;
5195 curr->finalize();
5196 }
5197
maybeVisitI31New(Expression * & out,uint32_t code)5198 bool WasmBinaryBuilder::maybeVisitI31New(Expression*& out, uint32_t code) {
5199 if (code != BinaryConsts::I31New) {
5200 return false;
5201 }
5202 auto* curr = allocator.alloc<I31New>();
5203 curr->value = popNonVoidExpression();
5204 curr->finalize();
5205 out = curr;
5206 return true;
5207 }
5208
maybeVisitI31Get(Expression * & out,uint32_t code)5209 bool WasmBinaryBuilder::maybeVisitI31Get(Expression*& out, uint32_t code) {
5210 I31Get* curr;
5211 switch (code) {
5212 case BinaryConsts::I31GetS:
5213 curr = allocator.alloc<I31Get>();
5214 curr->signed_ = true;
5215 break;
5216 case BinaryConsts::I31GetU:
5217 curr = allocator.alloc<I31Get>();
5218 curr->signed_ = false;
5219 break;
5220 default:
5221 return false;
5222 }
5223 curr->i31 = popNonVoidExpression();
5224 curr->finalize();
5225 out = curr;
5226 return true;
5227 }
5228
maybeVisitRefTest(Expression * & out,uint32_t code)5229 bool WasmBinaryBuilder::maybeVisitRefTest(Expression*& out, uint32_t code) {
5230 if (code != BinaryConsts::RefTest) {
5231 return false;
5232 }
5233 auto* curr = allocator.alloc<RefTest>();
5234 WASM_UNREACHABLE("TODO (gc): ref.test");
5235 curr->finalize();
5236 out = curr;
5237 return true;
5238 }
5239
maybeVisitRefCast(Expression * & out,uint32_t code)5240 bool WasmBinaryBuilder::maybeVisitRefCast(Expression*& out, uint32_t code) {
5241 if (code != BinaryConsts::RefCast) {
5242 return false;
5243 }
5244 auto* curr = allocator.alloc<RefCast>();
5245 WASM_UNREACHABLE("TODO (gc): ref.cast");
5246 curr->finalize();
5247 out = curr;
5248 return true;
5249 }
5250
maybeVisitBrOnCast(Expression * & out,uint32_t code)5251 bool WasmBinaryBuilder::maybeVisitBrOnCast(Expression*& out, uint32_t code) {
5252 if (code != BinaryConsts::BrOnCast) {
5253 return false;
5254 }
5255 auto* curr = allocator.alloc<BrOnCast>();
5256 WASM_UNREACHABLE("TODO (gc): br_on_cast");
5257 curr->finalize();
5258 out = curr;
5259 return true;
5260 }
5261
maybeVisitRttCanon(Expression * & out,uint32_t code)5262 bool WasmBinaryBuilder::maybeVisitRttCanon(Expression*& out, uint32_t code) {
5263 if (code != BinaryConsts::RttCanon) {
5264 return false;
5265 }
5266 auto* curr = allocator.alloc<RttCanon>();
5267 WASM_UNREACHABLE("TODO (gc): rtt.canon");
5268 curr->finalize();
5269 out = curr;
5270 return true;
5271 }
5272
maybeVisitRttSub(Expression * & out,uint32_t code)5273 bool WasmBinaryBuilder::maybeVisitRttSub(Expression*& out, uint32_t code) {
5274 if (code != BinaryConsts::RttSub) {
5275 return false;
5276 }
5277 auto* curr = allocator.alloc<RttSub>();
5278 WASM_UNREACHABLE("TODO (gc): rtt.sub");
5279 curr->finalize();
5280 out = curr;
5281 return true;
5282 }
5283
maybeVisitStructNew(Expression * & out,uint32_t code)5284 bool WasmBinaryBuilder::maybeVisitStructNew(Expression*& out, uint32_t code) {
5285 StructNew* curr;
5286 switch (code) {
5287 case BinaryConsts::StructNewWithRtt:
5288 curr = allocator.alloc<StructNew>();
5289 // ...
5290 break;
5291 case BinaryConsts::StructNewDefaultWithRtt:
5292 curr = allocator.alloc<StructNew>();
5293 // ...
5294 break;
5295 default:
5296 return false;
5297 }
5298 WASM_UNREACHABLE("TODO (gc): struct.new");
5299 curr->finalize();
5300 out = curr;
5301 return true;
5302 }
5303
maybeVisitStructGet(Expression * & out,uint32_t code)5304 bool WasmBinaryBuilder::maybeVisitStructGet(Expression*& out, uint32_t code) {
5305 StructGet* curr;
5306 switch (code) {
5307 case BinaryConsts::StructGet:
5308 curr = allocator.alloc<StructGet>();
5309 // ...
5310 break;
5311 case BinaryConsts::StructGetS:
5312 curr = allocator.alloc<StructGet>();
5313 // ...
5314 break;
5315 case BinaryConsts::StructGetU:
5316 curr = allocator.alloc<StructGet>();
5317 // ...
5318 break;
5319 default:
5320 return false;
5321 }
5322 WASM_UNREACHABLE("TODO (gc): struct.get");
5323 curr->finalize();
5324 out = curr;
5325 return true;
5326 }
5327
maybeVisitStructSet(Expression * & out,uint32_t code)5328 bool WasmBinaryBuilder::maybeVisitStructSet(Expression*& out, uint32_t code) {
5329 if (code != BinaryConsts::StructSet) {
5330 return false;
5331 }
5332 auto* curr = allocator.alloc<StructSet>();
5333 WASM_UNREACHABLE("TODO (gc): struct.set");
5334 curr->finalize();
5335 out = curr;
5336 return true;
5337 }
5338
maybeVisitArrayNew(Expression * & out,uint32_t code)5339 bool WasmBinaryBuilder::maybeVisitArrayNew(Expression*& out, uint32_t code) {
5340 ArrayNew* curr;
5341 switch (code) {
5342 case BinaryConsts::ArrayNewWithRtt:
5343 curr = allocator.alloc<ArrayNew>();
5344 // ...
5345 break;
5346 case BinaryConsts::ArrayNewDefaultWithRtt:
5347 curr = allocator.alloc<ArrayNew>();
5348 // ...
5349 break;
5350 default:
5351 return false;
5352 }
5353 WASM_UNREACHABLE("TODO (gc): array.new");
5354 curr->finalize();
5355 out = curr;
5356 return true;
5357 }
5358
maybeVisitArrayGet(Expression * & out,uint32_t code)5359 bool WasmBinaryBuilder::maybeVisitArrayGet(Expression*& out, uint32_t code) {
5360 ArrayGet* curr;
5361 switch (code) {
5362 case BinaryConsts::ArrayGet:
5363 curr = allocator.alloc<ArrayGet>();
5364 // ...
5365 break;
5366 case BinaryConsts::ArrayGetS:
5367 curr = allocator.alloc<ArrayGet>();
5368 // ...
5369 break;
5370 case BinaryConsts::ArrayGetU:
5371 curr = allocator.alloc<ArrayGet>();
5372 // ...
5373 break;
5374 default:
5375 return false;
5376 }
5377 WASM_UNREACHABLE("TODO (gc): array.get");
5378 curr->finalize();
5379 out = curr;
5380 return true;
5381 }
5382
maybeVisitArraySet(Expression * & out,uint32_t code)5383 bool WasmBinaryBuilder::maybeVisitArraySet(Expression*& out, uint32_t code) {
5384 if (code != BinaryConsts::ArraySet) {
5385 return false;
5386 }
5387 auto* curr = allocator.alloc<ArraySet>();
5388 WASM_UNREACHABLE("TODO (gc): array.set");
5389 curr->finalize();
5390 out = curr;
5391 return true;
5392 }
5393
maybeVisitArrayLen(Expression * & out,uint32_t code)5394 bool WasmBinaryBuilder::maybeVisitArrayLen(Expression*& out, uint32_t code) {
5395 if (code != BinaryConsts::ArrayLen) {
5396 return false;
5397 }
5398 auto* curr = allocator.alloc<ArrayLen>();
5399 WASM_UNREACHABLE("TODO (gc): array.len");
5400 curr->finalize();
5401 out = curr;
5402 return true;
5403 }
5404
throwError(std::string text)5405 void WasmBinaryBuilder::throwError(std::string text) {
5406 throw ParseException(text, 0, pos);
5407 }
5408
5409 } // namespace wasm
5410