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