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 //
18 // Print out text in s-expression format
19 //
20 
21 #include <ir/module-utils.h>
22 #include <pass.h>
23 #include <pretty_printing.h>
24 #include <wasm-printing.h>
25 #include <wasm-stack.h>
26 #include <wasm.h>
27 
28 namespace wasm {
29 
30 namespace {
31 
isFullForced()32 bool isFullForced() {
33   if (getenv("BINARYEN_PRINT_FULL")) {
34     return std::stoi(getenv("BINARYEN_PRINT_FULL")) != 0;
35   }
36   return false;
37 }
38 
printName(Name name,std::ostream & o)39 std::ostream& printName(Name name, std::ostream& o) {
40   // we need to quote names if they have tricky chars
41   if (!name.str || !strpbrk(name.str, "()")) {
42     o << '$' << name.str;
43   } else {
44     o << "\"$" << name.str << '"';
45   }
46   return o;
47 }
48 
printLocal(Index index,Function * func,std::ostream & o)49 static std::ostream& printLocal(Index index, Function* func, std::ostream& o) {
50   Name name;
51   if (func) {
52     name = func->getLocalNameOrDefault(index);
53   }
54   if (!name) {
55     name = Name::fromInt(index);
56   }
57   return printName(name, o);
58 }
59 
60 // Unlike the default format, tuple types in s-expressions should not have
61 // commas.
62 struct SExprType {
63   Type type;
SExprTypewasm::__anon9b9eebad0111::SExprType64   SExprType(Type type) : type(type){};
65 };
66 
operator <<(std::ostream & o,const SExprType & localType)67 static std::ostream& operator<<(std::ostream& o, const SExprType& localType) {
68   Type type = localType.type;
69   if (type.isTuple()) {
70     o << '(';
71     auto sep = "";
72     for (const auto& t : type) {
73       o << sep << t;
74       sep = " ";
75     }
76     o << ')';
77   } else {
78     o << type;
79   }
80   return o;
81 }
82 
83 // Wrapper for printing signature names
84 struct SigName {
85   Signature sig;
SigNamewasm::__anon9b9eebad0111::SigName86   SigName(Signature sig) : sig(sig) {}
87 };
88 
operator <<(std::ostream & os,SigName sigName)89 std::ostream& operator<<(std::ostream& os, SigName sigName) {
90   auto printType = [&](Type type) {
91     if (type == Type::none) {
92       os << "none";
93     } else {
94       auto sep = "";
95       for (const auto& t : type) {
96         os << sep << t;
97         sep = "_";
98       }
99     }
100   };
101 
102   os << '$';
103   printType(sigName.sig.params);
104   os << "_=>_";
105   printType(sigName.sig.results);
106   return os;
107 }
108 
109 } // anonymous namespace
110 
111 // Printing "unreachable" as a instruction prefix type is not valid in wasm text
112 // format. Print something else to make it pass.
forceConcrete(Type type)113 static Type forceConcrete(Type type) {
114   return type.isConcrete() ? type : Type::i32;
115 }
116 
117 // Prints the internal contents of an expression: everything but
118 // the children.
119 struct PrintExpressionContents
120   : public OverriddenVisitor<PrintExpressionContents> {
121   Function* currFunction = nullptr;
122   std::ostream& o;
123 
PrintExpressionContentswasm::PrintExpressionContents124   PrintExpressionContents(Function* currFunction, std::ostream& o)
125     : currFunction(currFunction), o(o) {}
126 
visitBlockwasm::PrintExpressionContents127   void visitBlock(Block* curr) {
128     printMedium(o, "block");
129     if (curr->name.is()) {
130       o << ' ';
131       printName(curr->name, o);
132     }
133     if (curr->type.isConcrete()) {
134       o << ' ' << ResultType(curr->type);
135     }
136   }
visitIfwasm::PrintExpressionContents137   void visitIf(If* curr) {
138     printMedium(o, "if");
139     if (curr->type.isConcrete()) {
140       o << ' ' << ResultType(curr->type);
141     }
142   }
visitLoopwasm::PrintExpressionContents143   void visitLoop(Loop* curr) {
144     printMedium(o, "loop");
145     if (curr->name.is()) {
146       o << ' ';
147       printName(curr->name, o);
148     }
149     if (curr->type.isConcrete()) {
150       o << ' ' << ResultType(curr->type);
151     }
152   }
visitBreakwasm::PrintExpressionContents153   void visitBreak(Break* curr) {
154     if (curr->condition) {
155       printMedium(o, "br_if ");
156     } else {
157       printMedium(o, "br ");
158     }
159     printName(curr->name, o);
160   }
visitSwitchwasm::PrintExpressionContents161   void visitSwitch(Switch* curr) {
162     printMedium(o, "br_table");
163     for (auto& t : curr->targets) {
164       o << ' ';
165       printName(t, o);
166     }
167     o << ' ';
168     printName(curr->default_, o);
169   }
visitCallwasm::PrintExpressionContents170   void visitCall(Call* curr) {
171     if (curr->isReturn) {
172       printMedium(o, "return_call ");
173     } else {
174       printMedium(o, "call ");
175     }
176     printName(curr->target, o);
177   }
visitCallIndirectwasm::PrintExpressionContents178   void visitCallIndirect(CallIndirect* curr) {
179     if (curr->isReturn) {
180       printMedium(o, "return_call_indirect (type ");
181     } else {
182       printMedium(o, "call_indirect (type ");
183     }
184     o << SigName(curr->sig) << ')';
185   }
visitLocalGetwasm::PrintExpressionContents186   void visitLocalGet(LocalGet* curr) {
187     printMedium(o, "local.get ");
188     printLocal(curr->index, currFunction, o);
189   }
visitLocalSetwasm::PrintExpressionContents190   void visitLocalSet(LocalSet* curr) {
191     if (curr->isTee()) {
192       printMedium(o, "local.tee ");
193     } else {
194       printMedium(o, "local.set ");
195     }
196     printLocal(curr->index, currFunction, o);
197   }
visitGlobalGetwasm::PrintExpressionContents198   void visitGlobalGet(GlobalGet* curr) {
199     printMedium(o, "global.get ");
200     printName(curr->name, o);
201   }
visitGlobalSetwasm::PrintExpressionContents202   void visitGlobalSet(GlobalSet* curr) {
203     printMedium(o, "global.set ");
204     printName(curr->name, o);
205   }
visitLoadwasm::PrintExpressionContents206   void visitLoad(Load* curr) {
207     prepareColor(o) << forceConcrete(curr->type);
208     if (curr->isAtomic) {
209       o << ".atomic";
210     }
211     o << ".load";
212     if (curr->type != Type::unreachable &&
213         curr->bytes < curr->type.getByteSize()) {
214       if (curr->bytes == 1) {
215         o << '8';
216       } else if (curr->bytes == 2) {
217         o << "16";
218       } else if (curr->bytes == 4) {
219         o << "32";
220       } else {
221         abort();
222       }
223       o << (curr->signed_ ? "_s" : "_u");
224     }
225     restoreNormalColor(o);
226     if (curr->offset) {
227       o << " offset=" << curr->offset;
228     }
229     if (curr->align != curr->bytes) {
230       o << " align=" << curr->align;
231     }
232   }
visitStorewasm::PrintExpressionContents233   void visitStore(Store* curr) {
234     prepareColor(o) << forceConcrete(curr->valueType);
235     if (curr->isAtomic) {
236       o << ".atomic";
237     }
238     o << ".store";
239     if (curr->bytes < 4 || (curr->valueType == Type::i64 && curr->bytes < 8)) {
240       if (curr->bytes == 1) {
241         o << '8';
242       } else if (curr->bytes == 2) {
243         o << "16";
244       } else if (curr->bytes == 4) {
245         o << "32";
246       } else {
247         abort();
248       }
249     }
250     restoreNormalColor(o);
251     if (curr->offset) {
252       o << " offset=" << curr->offset;
253     }
254     if (curr->align != curr->bytes) {
255       o << " align=" << curr->align;
256     }
257   }
printRMWSizewasm::PrintExpressionContents258   static void printRMWSize(std::ostream& o, Type type, uint8_t bytes) {
259     prepareColor(o) << forceConcrete(type) << ".atomic.rmw";
260     if (type != Type::unreachable && bytes != type.getByteSize()) {
261       if (bytes == 1) {
262         o << '8';
263       } else if (bytes == 2) {
264         o << "16";
265       } else if (bytes == 4) {
266         o << "32";
267       } else {
268         WASM_UNREACHABLE("invalid RMW byte length");
269       }
270     }
271     o << '.';
272   }
visitAtomicRMWwasm::PrintExpressionContents273   void visitAtomicRMW(AtomicRMW* curr) {
274     prepareColor(o);
275     printRMWSize(o, curr->type, curr->bytes);
276     switch (curr->op) {
277       case Add:
278         o << "add";
279         break;
280       case Sub:
281         o << "sub";
282         break;
283       case And:
284         o << "and";
285         break;
286       case Or:
287         o << "or";
288         break;
289       case Xor:
290         o << "xor";
291         break;
292       case Xchg:
293         o << "xchg";
294         break;
295     }
296     if (curr->type != Type::unreachable &&
297         curr->bytes != curr->type.getByteSize()) {
298       o << "_u";
299     }
300     restoreNormalColor(o);
301     if (curr->offset) {
302       o << " offset=" << curr->offset;
303     }
304   }
visitAtomicCmpxchgwasm::PrintExpressionContents305   void visitAtomicCmpxchg(AtomicCmpxchg* curr) {
306     prepareColor(o);
307     printRMWSize(o, curr->type, curr->bytes);
308     o << "cmpxchg";
309     if (curr->type != Type::unreachable &&
310         curr->bytes != curr->type.getByteSize()) {
311       o << "_u";
312     }
313     restoreNormalColor(o);
314     if (curr->offset) {
315       o << " offset=" << curr->offset;
316     }
317   }
visitAtomicWaitwasm::PrintExpressionContents318   void visitAtomicWait(AtomicWait* curr) {
319     prepareColor(o);
320     o << forceConcrete(curr->expectedType) << ".atomic.wait";
321     if (curr->offset) {
322       o << " offset=" << curr->offset;
323     }
324   }
visitAtomicNotifywasm::PrintExpressionContents325   void visitAtomicNotify(AtomicNotify* curr) {
326     printMedium(o, "atomic.notify");
327     if (curr->offset) {
328       o << " offset=" << curr->offset;
329     }
330   }
visitAtomicFencewasm::PrintExpressionContents331   void visitAtomicFence(AtomicFence* curr) { printMedium(o, "atomic.fence"); }
visitSIMDExtractwasm::PrintExpressionContents332   void visitSIMDExtract(SIMDExtract* curr) {
333     prepareColor(o);
334     switch (curr->op) {
335       case ExtractLaneSVecI8x16:
336         o << "i8x16.extract_lane_s";
337         break;
338       case ExtractLaneUVecI8x16:
339         o << "i8x16.extract_lane_u";
340         break;
341       case ExtractLaneSVecI16x8:
342         o << "i16x8.extract_lane_s";
343         break;
344       case ExtractLaneUVecI16x8:
345         o << "i16x8.extract_lane_u";
346         break;
347       case ExtractLaneVecI32x4:
348         o << "i32x4.extract_lane";
349         break;
350       case ExtractLaneVecI64x2:
351         o << "i64x2.extract_lane";
352         break;
353       case ExtractLaneVecF32x4:
354         o << "f32x4.extract_lane";
355         break;
356       case ExtractLaneVecF64x2:
357         o << "f64x2.extract_lane";
358         break;
359     }
360     o << " " << int(curr->index);
361   }
visitSIMDReplacewasm::PrintExpressionContents362   void visitSIMDReplace(SIMDReplace* curr) {
363     prepareColor(o);
364     switch (curr->op) {
365       case ReplaceLaneVecI8x16:
366         o << "i8x16.replace_lane";
367         break;
368       case ReplaceLaneVecI16x8:
369         o << "i16x8.replace_lane";
370         break;
371       case ReplaceLaneVecI32x4:
372         o << "i32x4.replace_lane";
373         break;
374       case ReplaceLaneVecI64x2:
375         o << "i64x2.replace_lane";
376         break;
377       case ReplaceLaneVecF32x4:
378         o << "f32x4.replace_lane";
379         break;
380       case ReplaceLaneVecF64x2:
381         o << "f64x2.replace_lane";
382         break;
383     }
384     o << " " << int(curr->index);
385   }
visitSIMDShufflewasm::PrintExpressionContents386   void visitSIMDShuffle(SIMDShuffle* curr) {
387     prepareColor(o);
388     o << "v8x16.shuffle";
389     for (uint8_t mask_index : curr->mask) {
390       o << " " << std::to_string(mask_index);
391     }
392   }
visitSIMDTernarywasm::PrintExpressionContents393   void visitSIMDTernary(SIMDTernary* curr) {
394     prepareColor(o);
395     switch (curr->op) {
396       case Bitselect:
397         o << "v128.bitselect";
398         break;
399       case QFMAF32x4:
400         o << "f32x4.qfma";
401         break;
402       case QFMSF32x4:
403         o << "f32x4.qfms";
404         break;
405       case QFMAF64x2:
406         o << "f64x2.qfma";
407         break;
408       case QFMSF64x2:
409         o << "f64x2.qfms";
410         break;
411     }
412   }
visitSIMDShiftwasm::PrintExpressionContents413   void visitSIMDShift(SIMDShift* curr) {
414     prepareColor(o);
415     switch (curr->op) {
416       case ShlVecI8x16:
417         o << "i8x16.shl";
418         break;
419       case ShrSVecI8x16:
420         o << "i8x16.shr_s";
421         break;
422       case ShrUVecI8x16:
423         o << "i8x16.shr_u";
424         break;
425       case ShlVecI16x8:
426         o << "i16x8.shl";
427         break;
428       case ShrSVecI16x8:
429         o << "i16x8.shr_s";
430         break;
431       case ShrUVecI16x8:
432         o << "i16x8.shr_u";
433         break;
434       case ShlVecI32x4:
435         o << "i32x4.shl";
436         break;
437       case ShrSVecI32x4:
438         o << "i32x4.shr_s";
439         break;
440       case ShrUVecI32x4:
441         o << "i32x4.shr_u";
442         break;
443       case ShlVecI64x2:
444         o << "i64x2.shl";
445         break;
446       case ShrSVecI64x2:
447         o << "i64x2.shr_s";
448         break;
449       case ShrUVecI64x2:
450         o << "i64x2.shr_u";
451         break;
452     }
453   }
visitSIMDLoadwasm::PrintExpressionContents454   void visitSIMDLoad(SIMDLoad* curr) {
455     prepareColor(o);
456     switch (curr->op) {
457       case LoadSplatVec8x16:
458         o << "v8x16.load_splat";
459         break;
460       case LoadSplatVec16x8:
461         o << "v16x8.load_splat";
462         break;
463       case LoadSplatVec32x4:
464         o << "v32x4.load_splat";
465         break;
466       case LoadSplatVec64x2:
467         o << "v64x2.load_splat";
468         break;
469       case LoadExtSVec8x8ToVecI16x8:
470         o << "i16x8.load8x8_s";
471         break;
472       case LoadExtUVec8x8ToVecI16x8:
473         o << "i16x8.load8x8_u";
474         break;
475       case LoadExtSVec16x4ToVecI32x4:
476         o << "i32x4.load16x4_s";
477         break;
478       case LoadExtUVec16x4ToVecI32x4:
479         o << "i32x4.load16x4_u";
480         break;
481       case LoadExtSVec32x2ToVecI64x2:
482         o << "i64x2.load32x2_s";
483         break;
484       case LoadExtUVec32x2ToVecI64x2:
485         o << "i64x2.load32x2_u";
486         break;
487       case Load32Zero:
488         o << "v128.load32_zero";
489         break;
490       case Load64Zero:
491         o << "v128.load64_zero";
492         break;
493     }
494     restoreNormalColor(o);
495     if (curr->offset) {
496       o << " offset=" << curr->offset;
497     }
498     if (curr->align != curr->getMemBytes()) {
499       o << " align=" << curr->align;
500     }
501   }
visitMemoryInitwasm::PrintExpressionContents502   void visitMemoryInit(MemoryInit* curr) {
503     prepareColor(o);
504     o << "memory.init " << curr->segment;
505   }
visitDataDropwasm::PrintExpressionContents506   void visitDataDrop(DataDrop* curr) {
507     prepareColor(o);
508     o << "data.drop " << curr->segment;
509   }
visitMemoryCopywasm::PrintExpressionContents510   void visitMemoryCopy(MemoryCopy* curr) {
511     prepareColor(o);
512     o << "memory.copy";
513   }
visitMemoryFillwasm::PrintExpressionContents514   void visitMemoryFill(MemoryFill* curr) {
515     prepareColor(o);
516     o << "memory.fill";
517   }
visitConstwasm::PrintExpressionContents518   void visitConst(Const* curr) {
519     o << curr->value.type << ".const " << curr->value;
520   }
visitUnarywasm::PrintExpressionContents521   void visitUnary(Unary* curr) {
522     prepareColor(o);
523     switch (curr->op) {
524       case ClzInt32:
525         o << "i32.clz";
526         break;
527       case CtzInt32:
528         o << "i32.ctz";
529         break;
530       case PopcntInt32:
531         o << "i32.popcnt";
532         break;
533       case EqZInt32:
534         o << "i32.eqz";
535         break;
536       case ClzInt64:
537         o << "i64.clz";
538         break;
539       case CtzInt64:
540         o << "i64.ctz";
541         break;
542       case PopcntInt64:
543         o << "i64.popcnt";
544         break;
545       case EqZInt64:
546         o << "i64.eqz";
547         break;
548       case NegFloat32:
549         o << "f32.neg";
550         break;
551       case AbsFloat32:
552         o << "f32.abs";
553         break;
554       case CeilFloat32:
555         o << "f32.ceil";
556         break;
557       case FloorFloat32:
558         o << "f32.floor";
559         break;
560       case TruncFloat32:
561         o << "f32.trunc";
562         break;
563       case NearestFloat32:
564         o << "f32.nearest";
565         break;
566       case SqrtFloat32:
567         o << "f32.sqrt";
568         break;
569       case NegFloat64:
570         o << "f64.neg";
571         break;
572       case AbsFloat64:
573         o << "f64.abs";
574         break;
575       case CeilFloat64:
576         o << "f64.ceil";
577         break;
578       case FloorFloat64:
579         o << "f64.floor";
580         break;
581       case TruncFloat64:
582         o << "f64.trunc";
583         break;
584       case NearestFloat64:
585         o << "f64.nearest";
586         break;
587       case SqrtFloat64:
588         o << "f64.sqrt";
589         break;
590       case ExtendSInt32:
591         o << "i64.extend_i32_s";
592         break;
593       case ExtendUInt32:
594         o << "i64.extend_i32_u";
595         break;
596       case WrapInt64:
597         o << "i32.wrap_i64";
598         break;
599       case TruncSFloat32ToInt32:
600         o << "i32.trunc_f32_s";
601         break;
602       case TruncSFloat32ToInt64:
603         o << "i64.trunc_f32_s";
604         break;
605       case TruncUFloat32ToInt32:
606         o << "i32.trunc_f32_u";
607         break;
608       case TruncUFloat32ToInt64:
609         o << "i64.trunc_f32_u";
610         break;
611       case TruncSFloat64ToInt32:
612         o << "i32.trunc_f64_s";
613         break;
614       case TruncSFloat64ToInt64:
615         o << "i64.trunc_f64_s";
616         break;
617       case TruncUFloat64ToInt32:
618         o << "i32.trunc_f64_u";
619         break;
620       case TruncUFloat64ToInt64:
621         o << "i64.trunc_f64_u";
622         break;
623       case ReinterpretFloat32:
624         o << "i32.reinterpret_f32";
625         break;
626       case ReinterpretFloat64:
627         o << "i64.reinterpret_f64";
628         break;
629       case ConvertUInt32ToFloat32:
630         o << "f32.convert_i32_u";
631         break;
632       case ConvertUInt32ToFloat64:
633         o << "f64.convert_i32_u";
634         break;
635       case ConvertSInt32ToFloat32:
636         o << "f32.convert_i32_s";
637         break;
638       case ConvertSInt32ToFloat64:
639         o << "f64.convert_i32_s";
640         break;
641       case ConvertUInt64ToFloat32:
642         o << "f32.convert_i64_u";
643         break;
644       case ConvertUInt64ToFloat64:
645         o << "f64.convert_i64_u";
646         break;
647       case ConvertSInt64ToFloat32:
648         o << "f32.convert_i64_s";
649         break;
650       case ConvertSInt64ToFloat64:
651         o << "f64.convert_i64_s";
652         break;
653       case PromoteFloat32:
654         o << "f64.promote_f32";
655         break;
656       case DemoteFloat64:
657         o << "f32.demote_f64";
658         break;
659       case ReinterpretInt32:
660         o << "f32.reinterpret_i32";
661         break;
662       case ReinterpretInt64:
663         o << "f64.reinterpret_i64";
664         break;
665       case ExtendS8Int32:
666         o << "i32.extend8_s";
667         break;
668       case ExtendS16Int32:
669         o << "i32.extend16_s";
670         break;
671       case ExtendS8Int64:
672         o << "i64.extend8_s";
673         break;
674       case ExtendS16Int64:
675         o << "i64.extend16_s";
676         break;
677       case ExtendS32Int64:
678         o << "i64.extend32_s";
679         break;
680       case TruncSatSFloat32ToInt32:
681         o << "i32.trunc_sat_f32_s";
682         break;
683       case TruncSatUFloat32ToInt32:
684         o << "i32.trunc_sat_f32_u";
685         break;
686       case TruncSatSFloat64ToInt32:
687         o << "i32.trunc_sat_f64_s";
688         break;
689       case TruncSatUFloat64ToInt32:
690         o << "i32.trunc_sat_f64_u";
691         break;
692       case TruncSatSFloat32ToInt64:
693         o << "i64.trunc_sat_f32_s";
694         break;
695       case TruncSatUFloat32ToInt64:
696         o << "i64.trunc_sat_f32_u";
697         break;
698       case TruncSatSFloat64ToInt64:
699         o << "i64.trunc_sat_f64_s";
700         break;
701       case TruncSatUFloat64ToInt64:
702         o << "i64.trunc_sat_f64_u";
703         break;
704       case SplatVecI8x16:
705         o << "i8x16.splat";
706         break;
707       case SplatVecI16x8:
708         o << "i16x8.splat";
709         break;
710       case SplatVecI32x4:
711         o << "i32x4.splat";
712         break;
713       case SplatVecI64x2:
714         o << "i64x2.splat";
715         break;
716       case SplatVecF32x4:
717         o << "f32x4.splat";
718         break;
719       case SplatVecF64x2:
720         o << "f64x2.splat";
721         break;
722       case NotVec128:
723         o << "v128.not";
724         break;
725       case AbsVecI8x16:
726         o << "i8x16.abs";
727         break;
728       case NegVecI8x16:
729         o << "i8x16.neg";
730         break;
731       case AnyTrueVecI8x16:
732         o << "i8x16.any_true";
733         break;
734       case AllTrueVecI8x16:
735         o << "i8x16.all_true";
736         break;
737       case BitmaskVecI8x16:
738         o << "i8x16.bitmask";
739         break;
740       case AbsVecI16x8:
741         o << "i16x8.abs";
742         break;
743       case NegVecI16x8:
744         o << "i16x8.neg";
745         break;
746       case AnyTrueVecI16x8:
747         o << "i16x8.any_true";
748         break;
749       case AllTrueVecI16x8:
750         o << "i16x8.all_true";
751         break;
752       case BitmaskVecI16x8:
753         o << "i16x8.bitmask";
754         break;
755       case AbsVecI32x4:
756         o << "i32x4.abs";
757         break;
758       case NegVecI32x4:
759         o << "i32x4.neg";
760         break;
761       case AnyTrueVecI32x4:
762         o << "i32x4.any_true";
763         break;
764       case AllTrueVecI32x4:
765         o << "i32x4.all_true";
766         break;
767       case BitmaskVecI32x4:
768         o << "i32x4.bitmask";
769         break;
770       case NegVecI64x2:
771         o << "i64x2.neg";
772         break;
773       case AnyTrueVecI64x2:
774         o << "i64x2.any_true";
775         break;
776       case AllTrueVecI64x2:
777         o << "i64x2.all_true";
778         break;
779       case AbsVecF32x4:
780         o << "f32x4.abs";
781         break;
782       case NegVecF32x4:
783         o << "f32x4.neg";
784         break;
785       case SqrtVecF32x4:
786         o << "f32x4.sqrt";
787         break;
788       case CeilVecF32x4:
789         o << "f32x4.ceil";
790         break;
791       case FloorVecF32x4:
792         o << "f32x4.floor";
793         break;
794       case TruncVecF32x4:
795         o << "f32x4.trunc";
796         break;
797       case NearestVecF32x4:
798         o << "f32x4.nearest";
799         break;
800       case AbsVecF64x2:
801         o << "f64x2.abs";
802         break;
803       case NegVecF64x2:
804         o << "f64x2.neg";
805         break;
806       case SqrtVecF64x2:
807         o << "f64x2.sqrt";
808         break;
809       case CeilVecF64x2:
810         o << "f64x2.ceil";
811         break;
812       case FloorVecF64x2:
813         o << "f64x2.floor";
814         break;
815       case TruncVecF64x2:
816         o << "f64x2.trunc";
817         break;
818       case NearestVecF64x2:
819         o << "f64x2.nearest";
820         break;
821       case TruncSatSVecF32x4ToVecI32x4:
822         o << "i32x4.trunc_sat_f32x4_s";
823         break;
824       case TruncSatUVecF32x4ToVecI32x4:
825         o << "i32x4.trunc_sat_f32x4_u";
826         break;
827       case TruncSatSVecF64x2ToVecI64x2:
828         o << "i64x2.trunc_sat_f64x2_s";
829         break;
830       case TruncSatUVecF64x2ToVecI64x2:
831         o << "i64x2.trunc_sat_f64x2_u";
832         break;
833       case ConvertSVecI32x4ToVecF32x4:
834         o << "f32x4.convert_i32x4_s";
835         break;
836       case ConvertUVecI32x4ToVecF32x4:
837         o << "f32x4.convert_i32x4_u";
838         break;
839       case ConvertSVecI64x2ToVecF64x2:
840         o << "f64x2.convert_i64x2_s";
841         break;
842       case ConvertUVecI64x2ToVecF64x2:
843         o << "f64x2.convert_i64x2_u";
844         break;
845       case WidenLowSVecI8x16ToVecI16x8:
846         o << "i16x8.widen_low_i8x16_s";
847         break;
848       case WidenHighSVecI8x16ToVecI16x8:
849         o << "i16x8.widen_high_i8x16_s";
850         break;
851       case WidenLowUVecI8x16ToVecI16x8:
852         o << "i16x8.widen_low_i8x16_u";
853         break;
854       case WidenHighUVecI8x16ToVecI16x8:
855         o << "i16x8.widen_high_i8x16_u";
856         break;
857       case WidenLowSVecI16x8ToVecI32x4:
858         o << "i32x4.widen_low_i16x8_s";
859         break;
860       case WidenHighSVecI16x8ToVecI32x4:
861         o << "i32x4.widen_high_i16x8_s";
862         break;
863       case WidenLowUVecI16x8ToVecI32x4:
864         o << "i32x4.widen_low_i16x8_u";
865         break;
866       case WidenHighUVecI16x8ToVecI32x4:
867         o << "i32x4.widen_high_i16x8_u";
868         break;
869       case InvalidUnary:
870         WASM_UNREACHABLE("unvalid unary operator");
871     }
872   }
visitBinarywasm::PrintExpressionContents873   void visitBinary(Binary* curr) {
874     prepareColor(o);
875     switch (curr->op) {
876       case AddInt32:
877         o << "i32.add";
878         break;
879       case SubInt32:
880         o << "i32.sub";
881         break;
882       case MulInt32:
883         o << "i32.mul";
884         break;
885       case DivSInt32:
886         o << "i32.div_s";
887         break;
888       case DivUInt32:
889         o << "i32.div_u";
890         break;
891       case RemSInt32:
892         o << "i32.rem_s";
893         break;
894       case RemUInt32:
895         o << "i32.rem_u";
896         break;
897       case AndInt32:
898         o << "i32.and";
899         break;
900       case OrInt32:
901         o << "i32.or";
902         break;
903       case XorInt32:
904         o << "i32.xor";
905         break;
906       case ShlInt32:
907         o << "i32.shl";
908         break;
909       case ShrUInt32:
910         o << "i32.shr_u";
911         break;
912       case ShrSInt32:
913         o << "i32.shr_s";
914         break;
915       case RotLInt32:
916         o << "i32.rotl";
917         break;
918       case RotRInt32:
919         o << "i32.rotr";
920         break;
921       case EqInt32:
922         o << "i32.eq";
923         break;
924       case NeInt32:
925         o << "i32.ne";
926         break;
927       case LtSInt32:
928         o << "i32.lt_s";
929         break;
930       case LtUInt32:
931         o << "i32.lt_u";
932         break;
933       case LeSInt32:
934         o << "i32.le_s";
935         break;
936       case LeUInt32:
937         o << "i32.le_u";
938         break;
939       case GtSInt32:
940         o << "i32.gt_s";
941         break;
942       case GtUInt32:
943         o << "i32.gt_u";
944         break;
945       case GeSInt32:
946         o << "i32.ge_s";
947         break;
948       case GeUInt32:
949         o << "i32.ge_u";
950         break;
951 
952       case AddInt64:
953         o << "i64.add";
954         break;
955       case SubInt64:
956         o << "i64.sub";
957         break;
958       case MulInt64:
959         o << "i64.mul";
960         break;
961       case DivSInt64:
962         o << "i64.div_s";
963         break;
964       case DivUInt64:
965         o << "i64.div_u";
966         break;
967       case RemSInt64:
968         o << "i64.rem_s";
969         break;
970       case RemUInt64:
971         o << "i64.rem_u";
972         break;
973       case AndInt64:
974         o << "i64.and";
975         break;
976       case OrInt64:
977         o << "i64.or";
978         break;
979       case XorInt64:
980         o << "i64.xor";
981         break;
982       case ShlInt64:
983         o << "i64.shl";
984         break;
985       case ShrUInt64:
986         o << "i64.shr_u";
987         break;
988       case ShrSInt64:
989         o << "i64.shr_s";
990         break;
991       case RotLInt64:
992         o << "i64.rotl";
993         break;
994       case RotRInt64:
995         o << "i64.rotr";
996         break;
997       case EqInt64:
998         o << "i64.eq";
999         break;
1000       case NeInt64:
1001         o << "i64.ne";
1002         break;
1003       case LtSInt64:
1004         o << "i64.lt_s";
1005         break;
1006       case LtUInt64:
1007         o << "i64.lt_u";
1008         break;
1009       case LeSInt64:
1010         o << "i64.le_s";
1011         break;
1012       case LeUInt64:
1013         o << "i64.le_u";
1014         break;
1015       case GtSInt64:
1016         o << "i64.gt_s";
1017         break;
1018       case GtUInt64:
1019         o << "i64.gt_u";
1020         break;
1021       case GeSInt64:
1022         o << "i64.ge_s";
1023         break;
1024       case GeUInt64:
1025         o << "i64.ge_u";
1026         break;
1027 
1028       case AddFloat32:
1029         o << "f32.add";
1030         break;
1031       case SubFloat32:
1032         o << "f32.sub";
1033         break;
1034       case MulFloat32:
1035         o << "f32.mul";
1036         break;
1037       case DivFloat32:
1038         o << "f32.div";
1039         break;
1040       case CopySignFloat32:
1041         o << "f32.copysign";
1042         break;
1043       case MinFloat32:
1044         o << "f32.min";
1045         break;
1046       case MaxFloat32:
1047         o << "f32.max";
1048         break;
1049       case EqFloat32:
1050         o << "f32.eq";
1051         break;
1052       case NeFloat32:
1053         o << "f32.ne";
1054         break;
1055       case LtFloat32:
1056         o << "f32.lt";
1057         break;
1058       case LeFloat32:
1059         o << "f32.le";
1060         break;
1061       case GtFloat32:
1062         o << "f32.gt";
1063         break;
1064       case GeFloat32:
1065         o << "f32.ge";
1066         break;
1067 
1068       case AddFloat64:
1069         o << "f64.add";
1070         break;
1071       case SubFloat64:
1072         o << "f64.sub";
1073         break;
1074       case MulFloat64:
1075         o << "f64.mul";
1076         break;
1077       case DivFloat64:
1078         o << "f64.div";
1079         break;
1080       case CopySignFloat64:
1081         o << "f64.copysign";
1082         break;
1083       case MinFloat64:
1084         o << "f64.min";
1085         break;
1086       case MaxFloat64:
1087         o << "f64.max";
1088         break;
1089       case EqFloat64:
1090         o << "f64.eq";
1091         break;
1092       case NeFloat64:
1093         o << "f64.ne";
1094         break;
1095       case LtFloat64:
1096         o << "f64.lt";
1097         break;
1098       case LeFloat64:
1099         o << "f64.le";
1100         break;
1101       case GtFloat64:
1102         o << "f64.gt";
1103         break;
1104       case GeFloat64:
1105         o << "f64.ge";
1106         break;
1107 
1108       case EqVecI8x16:
1109         o << "i8x16.eq";
1110         break;
1111       case NeVecI8x16:
1112         o << "i8x16.ne";
1113         break;
1114       case LtSVecI8x16:
1115         o << "i8x16.lt_s";
1116         break;
1117       case LtUVecI8x16:
1118         o << "i8x16.lt_u";
1119         break;
1120       case GtSVecI8x16:
1121         o << "i8x16.gt_s";
1122         break;
1123       case GtUVecI8x16:
1124         o << "i8x16.gt_u";
1125         break;
1126       case LeSVecI8x16:
1127         o << "i8x16.le_s";
1128         break;
1129       case LeUVecI8x16:
1130         o << "i8x16.le_u";
1131         break;
1132       case GeSVecI8x16:
1133         o << "i8x16.ge_s";
1134         break;
1135       case GeUVecI8x16:
1136         o << "i8x16.ge_u";
1137         break;
1138       case EqVecI16x8:
1139         o << "i16x8.eq";
1140         break;
1141       case NeVecI16x8:
1142         o << "i16x8.ne";
1143         break;
1144       case LtSVecI16x8:
1145         o << "i16x8.lt_s";
1146         break;
1147       case LtUVecI16x8:
1148         o << "i16x8.lt_u";
1149         break;
1150       case GtSVecI16x8:
1151         o << "i16x8.gt_s";
1152         break;
1153       case GtUVecI16x8:
1154         o << "i16x8.gt_u";
1155         break;
1156       case LeSVecI16x8:
1157         o << "i16x8.le_s";
1158         break;
1159       case LeUVecI16x8:
1160         o << "i16x8.le_u";
1161         break;
1162       case GeSVecI16x8:
1163         o << "i16x8.ge_s";
1164         break;
1165       case GeUVecI16x8:
1166         o << "i16x8.ge_u";
1167         break;
1168       case EqVecI32x4:
1169         o << "i32x4.eq";
1170         break;
1171       case NeVecI32x4:
1172         o << "i32x4.ne";
1173         break;
1174       case LtSVecI32x4:
1175         o << "i32x4.lt_s";
1176         break;
1177       case LtUVecI32x4:
1178         o << "i32x4.lt_u";
1179         break;
1180       case GtSVecI32x4:
1181         o << "i32x4.gt_s";
1182         break;
1183       case GtUVecI32x4:
1184         o << "i32x4.gt_u";
1185         break;
1186       case LeSVecI32x4:
1187         o << "i32x4.le_s";
1188         break;
1189       case LeUVecI32x4:
1190         o << "i32x4.le_u";
1191         break;
1192       case GeSVecI32x4:
1193         o << "i32x4.ge_s";
1194         break;
1195       case GeUVecI32x4:
1196         o << "i32x4.ge_u";
1197         break;
1198       case EqVecF32x4:
1199         o << "f32x4.eq";
1200         break;
1201       case NeVecF32x4:
1202         o << "f32x4.ne";
1203         break;
1204       case LtVecF32x4:
1205         o << "f32x4.lt";
1206         break;
1207       case GtVecF32x4:
1208         o << "f32x4.gt";
1209         break;
1210       case LeVecF32x4:
1211         o << "f32x4.le";
1212         break;
1213       case GeVecF32x4:
1214         o << "f32x4.ge";
1215         break;
1216       case EqVecF64x2:
1217         o << "f64x2.eq";
1218         break;
1219       case NeVecF64x2:
1220         o << "f64x2.ne";
1221         break;
1222       case LtVecF64x2:
1223         o << "f64x2.lt";
1224         break;
1225       case GtVecF64x2:
1226         o << "f64x2.gt";
1227         break;
1228       case LeVecF64x2:
1229         o << "f64x2.le";
1230         break;
1231       case GeVecF64x2:
1232         o << "f64x2.ge";
1233         break;
1234 
1235       case AndVec128:
1236         o << "v128.and";
1237         break;
1238       case OrVec128:
1239         o << "v128.or";
1240         break;
1241       case XorVec128:
1242         o << "v128.xor";
1243         break;
1244       case AndNotVec128:
1245         o << "v128.andnot";
1246         break;
1247 
1248       case AddVecI8x16:
1249         o << "i8x16.add";
1250         break;
1251       case AddSatSVecI8x16:
1252         o << "i8x16.add_saturate_s";
1253         break;
1254       case AddSatUVecI8x16:
1255         o << "i8x16.add_saturate_u";
1256         break;
1257       case SubVecI8x16:
1258         o << "i8x16.sub";
1259         break;
1260       case SubSatSVecI8x16:
1261         o << "i8x16.sub_saturate_s";
1262         break;
1263       case SubSatUVecI8x16:
1264         o << "i8x16.sub_saturate_u";
1265         break;
1266       case MulVecI8x16:
1267         o << "i8x16.mul";
1268         break;
1269       case MinSVecI8x16:
1270         o << "i8x16.min_s";
1271         break;
1272       case MinUVecI8x16:
1273         o << "i8x16.min_u";
1274         break;
1275       case MaxSVecI8x16:
1276         o << "i8x16.max_s";
1277         break;
1278       case MaxUVecI8x16:
1279         o << "i8x16.max_u";
1280         break;
1281       case AvgrUVecI8x16:
1282         o << "i8x16.avgr_u";
1283         break;
1284       case AddVecI16x8:
1285         o << "i16x8.add";
1286         break;
1287       case AddSatSVecI16x8:
1288         o << "i16x8.add_saturate_s";
1289         break;
1290       case AddSatUVecI16x8:
1291         o << "i16x8.add_saturate_u";
1292         break;
1293       case SubVecI16x8:
1294         o << "i16x8.sub";
1295         break;
1296       case SubSatSVecI16x8:
1297         o << "i16x8.sub_saturate_s";
1298         break;
1299       case SubSatUVecI16x8:
1300         o << "i16x8.sub_saturate_u";
1301         break;
1302       case MulVecI16x8:
1303         o << "i16x8.mul";
1304         break;
1305       case MinSVecI16x8:
1306         o << "i16x8.min_s";
1307         break;
1308       case MinUVecI16x8:
1309         o << "i16x8.min_u";
1310         break;
1311       case MaxSVecI16x8:
1312         o << "i16x8.max_s";
1313         break;
1314       case MaxUVecI16x8:
1315         o << "i16x8.max_u";
1316         break;
1317       case AvgrUVecI16x8:
1318         o << "i16x8.avgr_u";
1319         break;
1320       case AddVecI32x4:
1321         o << "i32x4.add";
1322         break;
1323       case SubVecI32x4:
1324         o << "i32x4.sub";
1325         break;
1326       case MulVecI32x4:
1327         o << "i32x4.mul";
1328         break;
1329       case MinSVecI32x4:
1330         o << "i32x4.min_s";
1331         break;
1332       case MinUVecI32x4:
1333         o << "i32x4.min_u";
1334         break;
1335       case MaxSVecI32x4:
1336         o << "i32x4.max_s";
1337         break;
1338       case MaxUVecI32x4:
1339         o << "i32x4.max_u";
1340         break;
1341       case DotSVecI16x8ToVecI32x4:
1342         o << "i32x4.dot_i16x8_s";
1343         break;
1344       case AddVecI64x2:
1345         o << "i64x2.add";
1346         break;
1347       case SubVecI64x2:
1348         o << "i64x2.sub";
1349         break;
1350       case MulVecI64x2:
1351         o << "i64x2.mul";
1352         break;
1353 
1354       case AddVecF32x4:
1355         o << "f32x4.add";
1356         break;
1357       case SubVecF32x4:
1358         o << "f32x4.sub";
1359         break;
1360       case MulVecF32x4:
1361         o << "f32x4.mul";
1362         break;
1363       case DivVecF32x4:
1364         o << "f32x4.div";
1365         break;
1366       case MinVecF32x4:
1367         o << "f32x4.min";
1368         break;
1369       case MaxVecF32x4:
1370         o << "f32x4.max";
1371         break;
1372       case PMinVecF32x4:
1373         o << "f32x4.pmin";
1374         break;
1375       case PMaxVecF32x4:
1376         o << "f32x4.pmax";
1377         break;
1378       case AddVecF64x2:
1379         o << "f64x2.add";
1380         break;
1381       case SubVecF64x2:
1382         o << "f64x2.sub";
1383         break;
1384       case MulVecF64x2:
1385         o << "f64x2.mul";
1386         break;
1387       case DivVecF64x2:
1388         o << "f64x2.div";
1389         break;
1390       case MinVecF64x2:
1391         o << "f64x2.min";
1392         break;
1393       case MaxVecF64x2:
1394         o << "f64x2.max";
1395         break;
1396       case PMinVecF64x2:
1397         o << "f64x2.pmin";
1398         break;
1399       case PMaxVecF64x2:
1400         o << "f64x2.pmax";
1401         break;
1402 
1403       case NarrowSVecI16x8ToVecI8x16:
1404         o << "i8x16.narrow_i16x8_s";
1405         break;
1406       case NarrowUVecI16x8ToVecI8x16:
1407         o << "i8x16.narrow_i16x8_u";
1408         break;
1409       case NarrowSVecI32x4ToVecI16x8:
1410         o << "i16x8.narrow_i32x4_s";
1411         break;
1412       case NarrowUVecI32x4ToVecI16x8:
1413         o << "i16x8.narrow_i32x4_u";
1414         break;
1415 
1416       case SwizzleVec8x16:
1417         o << "v8x16.swizzle";
1418         break;
1419 
1420       case InvalidBinary:
1421         WASM_UNREACHABLE("unvalid binary operator");
1422     }
1423     restoreNormalColor(o);
1424   }
visitSelectwasm::PrintExpressionContents1425   void visitSelect(Select* curr) {
1426     prepareColor(o) << "select";
1427     if (curr->type.isRef()) {
1428       o << " (result " << curr->type << ')';
1429     }
1430   }
visitDropwasm::PrintExpressionContents1431   void visitDrop(Drop* curr) { printMedium(o, "drop"); }
visitReturnwasm::PrintExpressionContents1432   void visitReturn(Return* curr) { printMedium(o, "return"); }
visitMemorySizewasm::PrintExpressionContents1433   void visitMemorySize(MemorySize* curr) { printMedium(o, "memory.size"); }
visitMemoryGrowwasm::PrintExpressionContents1434   void visitMemoryGrow(MemoryGrow* curr) { printMedium(o, "memory.grow"); }
visitRefNullwasm::PrintExpressionContents1435   void visitRefNull(RefNull* curr) {
1436     printMedium(o, "ref.null ");
1437     o << curr->type.getHeapType();
1438   }
visitRefIsNullwasm::PrintExpressionContents1439   void visitRefIsNull(RefIsNull* curr) { printMedium(o, "ref.is_null"); }
visitRefFuncwasm::PrintExpressionContents1440   void visitRefFunc(RefFunc* curr) {
1441     printMedium(o, "ref.func ");
1442     printName(curr->func, o);
1443   }
visitRefEqwasm::PrintExpressionContents1444   void visitRefEq(RefEq* curr) { printMedium(o, "ref.eq"); }
visitTrywasm::PrintExpressionContents1445   void visitTry(Try* curr) {
1446     printMedium(o, "try");
1447     if (curr->type.isConcrete()) {
1448       o << ' ' << ResultType(curr->type);
1449     }
1450   }
visitThrowwasm::PrintExpressionContents1451   void visitThrow(Throw* curr) {
1452     printMedium(o, "throw ");
1453     printName(curr->event, o);
1454   }
visitRethrowwasm::PrintExpressionContents1455   void visitRethrow(Rethrow* curr) { printMedium(o, "rethrow"); }
visitBrOnExnwasm::PrintExpressionContents1456   void visitBrOnExn(BrOnExn* curr) {
1457     printMedium(o, "br_on_exn ");
1458     printName(curr->name, o);
1459     o << " ";
1460     printName(curr->event, o);
1461   }
visitNopwasm::PrintExpressionContents1462   void visitNop(Nop* curr) { printMinor(o, "nop"); }
visitUnreachablewasm::PrintExpressionContents1463   void visitUnreachable(Unreachable* curr) { printMinor(o, "unreachable"); }
visitPopwasm::PrintExpressionContents1464   void visitPop(Pop* curr) {
1465     prepareColor(o) << "pop";
1466     for (auto type : curr->type) {
1467       assert(type.isBasic() && "TODO: print and parse compound types");
1468       o << " " << type;
1469     }
1470     restoreNormalColor(o);
1471   }
visitTupleMakewasm::PrintExpressionContents1472   void visitTupleMake(TupleMake* curr) { printMedium(o, "tuple.make"); }
visitTupleExtractwasm::PrintExpressionContents1473   void visitTupleExtract(TupleExtract* curr) {
1474     printMedium(o, "tuple.extract ");
1475     o << curr->index;
1476   }
visitI31Newwasm::PrintExpressionContents1477   void visitI31New(I31New* curr) { printMedium(o, "i31.new"); }
visitI31Getwasm::PrintExpressionContents1478   void visitI31Get(I31Get* curr) {
1479     printMedium(o, curr->signed_ ? "i31.get_s" : "i31.get_u");
1480   }
visitRefTestwasm::PrintExpressionContents1481   void visitRefTest(RefTest* curr) {
1482     printMedium(o, "ref.test");
1483     WASM_UNREACHABLE("TODO (gc): ref.test");
1484   }
visitRefCastwasm::PrintExpressionContents1485   void visitRefCast(RefCast* curr) {
1486     printMedium(o, "ref.cast");
1487     WASM_UNREACHABLE("TODO (gc): ref.cast");
1488   }
visitBrOnCastwasm::PrintExpressionContents1489   void visitBrOnCast(BrOnCast* curr) {
1490     printMedium(o, "br_on_cast");
1491     WASM_UNREACHABLE("TODO (gc): br_on_cast");
1492   }
visitRttCanonwasm::PrintExpressionContents1493   void visitRttCanon(RttCanon* curr) {
1494     printMedium(o, "rtt.canon");
1495     WASM_UNREACHABLE("TODO (gc): rtt.canon");
1496   }
visitRttSubwasm::PrintExpressionContents1497   void visitRttSub(RttSub* curr) {
1498     printMedium(o, "rtt.sub");
1499     WASM_UNREACHABLE("TODO (gc): rtt.sub");
1500   }
visitStructNewwasm::PrintExpressionContents1501   void visitStructNew(StructNew* curr) {
1502     WASM_UNREACHABLE("TODO (gc): struct.new");
1503   }
visitStructGetwasm::PrintExpressionContents1504   void visitStructGet(StructGet* curr) {
1505     WASM_UNREACHABLE("TODO (gc): struct.get");
1506   }
visitStructSetwasm::PrintExpressionContents1507   void visitStructSet(StructSet* curr) {
1508     printMedium(o, "struct.set");
1509     WASM_UNREACHABLE("TODO (gc): struct.set");
1510   }
visitArrayNewwasm::PrintExpressionContents1511   void visitArrayNew(ArrayNew* curr) {
1512     WASM_UNREACHABLE("TODO (gc): array.new");
1513   }
visitArrayGetwasm::PrintExpressionContents1514   void visitArrayGet(ArrayGet* curr) {
1515     WASM_UNREACHABLE("TODO (gc): array.get");
1516   }
visitArraySetwasm::PrintExpressionContents1517   void visitArraySet(ArraySet* curr) {
1518     printMedium(o, "array.set");
1519     WASM_UNREACHABLE("TODO (gc): array.set");
1520   }
visitArrayLenwasm::PrintExpressionContents1521   void visitArrayLen(ArrayLen* curr) {
1522     printMedium(o, "array.len");
1523     WASM_UNREACHABLE("TODO (gc): array.len");
1524   }
1525 };
1526 
1527 // Prints an expression in s-expr format, including both the
1528 // internal contents and the nested children.
1529 struct PrintSExpression : public OverriddenVisitor<PrintSExpression> {
1530   std::ostream& o;
1531   unsigned indent = 0;
1532 
1533   bool minify;
1534   const char* maybeSpace;
1535   const char* maybeNewLine;
1536 
1537   bool full = false; // whether to not elide nodes in output when possible
1538                      // (like implicit blocks) and to emit types
1539   bool printStackIR = false; // whether to print stack IR if it is present
1540                              // (if false, and Stack IR is there, we just
1541                              // note it exists)
1542 
1543   Module* currModule = nullptr;
1544   Function* currFunction = nullptr;
1545   Function::DebugLocation lastPrintedLocation;
1546   bool debugInfo;
1547 
PrintSExpressionwasm::PrintSExpression1548   PrintSExpression(std::ostream& o) : o(o) {
1549     setMinify(false);
1550     if (!full) {
1551       full = isFullForced();
1552     }
1553   }
1554 
printDebugLocationwasm::PrintSExpression1555   void printDebugLocation(const Function::DebugLocation& location) {
1556     if (lastPrintedLocation == location) {
1557       return;
1558     }
1559     lastPrintedLocation = location;
1560     auto fileName = currModule->debugInfoFileNames[location.fileIndex];
1561     o << ";;@ " << fileName << ":" << location.lineNumber << ":"
1562       << location.columnNumber << '\n';
1563     doIndent(o, indent);
1564   }
1565 
printDebugLocationwasm::PrintSExpression1566   void printDebugLocation(Expression* curr) {
1567     if (currFunction) {
1568       // show an annotation, if there is one
1569       auto& debugLocations = currFunction->debugLocations;
1570       auto iter = debugLocations.find(curr);
1571       if (iter != debugLocations.end()) {
1572         printDebugLocation(iter->second);
1573       }
1574       // show a binary position, if there is one
1575       if (debugInfo) {
1576         auto iter = currFunction->expressionLocations.find(curr);
1577         if (iter != currFunction->expressionLocations.end()) {
1578           Colors::grey(o);
1579           o << ";; code offset: 0x" << std::hex << iter->second.start
1580             << std::dec << '\n';
1581           restoreNormalColor(o);
1582           doIndent(o, indent);
1583         }
1584       }
1585     }
1586   }
1587 
visitwasm::PrintSExpression1588   void visit(Expression* curr) {
1589     printDebugLocation(curr);
1590     OverriddenVisitor<PrintSExpression>::visit(curr);
1591   }
1592 
setMinifywasm::PrintSExpression1593   void setMinify(bool minify_) {
1594     minify = minify_;
1595     maybeSpace = minify ? "" : " ";
1596     maybeNewLine = minify ? "" : "\n";
1597   }
1598 
setFullwasm::PrintSExpression1599   void setFull(bool full_) { full = full_; }
1600 
setPrintStackIRwasm::PrintSExpression1601   void setPrintStackIR(bool printStackIR_) { printStackIR = printStackIR_; }
1602 
setDebugInfowasm::PrintSExpression1603   void setDebugInfo(bool debugInfo_) { debugInfo = debugInfo_; }
1604 
incIndentwasm::PrintSExpression1605   void incIndent() {
1606     if (minify) {
1607       return;
1608     }
1609     o << '\n';
1610     indent++;
1611   }
decIndentwasm::PrintSExpression1612   void decIndent() {
1613     if (!minify) {
1614       assert(indent > 0);
1615       indent--;
1616       doIndent(o, indent);
1617     }
1618     o << ')';
1619   }
printFullLinewasm::PrintSExpression1620   void printFullLine(Expression* expression) {
1621     if (!minify) {
1622       doIndent(o, indent);
1623     }
1624     if (full) {
1625       o << "[" << expression->type << "] ";
1626     }
1627     visit(expression);
1628     o << maybeNewLine;
1629   }
1630 
1631   // loop, if, and try can contain implicit blocks. But they are not needed to
1632   // be printed in some cases.
maybePrintImplicitBlockwasm::PrintSExpression1633   void maybePrintImplicitBlock(Expression* curr, bool allowMultipleInsts) {
1634     auto block = curr->dynCast<Block>();
1635     if (!full && block && block->name.isNull() &&
1636         (allowMultipleInsts || block->list.size() == 1)) {
1637       for (auto expression : block->list) {
1638         printFullLine(expression);
1639       }
1640     } else {
1641       printFullLine(curr);
1642     }
1643   }
1644 
visitBlockwasm::PrintSExpression1645   void visitBlock(Block* curr) {
1646     // special-case Block, because Block nesting (in their first element) can be
1647     // incredibly deep
1648     std::vector<Block*> stack;
1649     while (1) {
1650       if (stack.size() > 0) {
1651         doIndent(o, indent);
1652         printDebugLocation(curr);
1653       }
1654       stack.push_back(curr);
1655       if (full) {
1656         o << "[" << curr->type << "] ";
1657       }
1658       o << '(';
1659       PrintExpressionContents(currFunction, o).visit(curr);
1660       incIndent();
1661       if (curr->list.size() > 0 && curr->list[0]->is<Block>()) {
1662         // recurse into the first element
1663         curr = curr->list[0]->cast<Block>();
1664         continue;
1665       } else {
1666         break; // that's all we can recurse, start to unwind
1667       }
1668     }
1669     auto* top = stack.back();
1670     while (stack.size() > 0) {
1671       curr = stack.back();
1672       stack.pop_back();
1673       auto& list = curr->list;
1674       for (size_t i = 0; i < list.size(); i++) {
1675         if (curr != top && i == 0) {
1676           // one of the block recursions we already handled
1677           decIndent();
1678           if (full) {
1679             o << " ;; end block";
1680             auto* child = list[0]->cast<Block>();
1681             if (child->name.is()) {
1682               o << ' ' << child->name;
1683             }
1684           }
1685           o << '\n';
1686           continue;
1687         }
1688         printFullLine(list[i]);
1689       }
1690     }
1691     decIndent();
1692     if (full) {
1693       o << " ;; end block";
1694       if (curr->name.is()) {
1695         o << ' ' << curr->name;
1696       }
1697     }
1698   }
visitIfwasm::PrintSExpression1699   void visitIf(If* curr) {
1700     o << '(';
1701     PrintExpressionContents(currFunction, o).visit(curr);
1702     incIndent();
1703     printFullLine(curr->condition);
1704     maybePrintImplicitBlock(curr->ifTrue, false);
1705     if (curr->ifFalse) {
1706       maybePrintImplicitBlock(curr->ifFalse, false);
1707     }
1708     decIndent();
1709     if (full) {
1710       o << " ;; end if";
1711     }
1712   }
visitLoopwasm::PrintSExpression1713   void visitLoop(Loop* curr) {
1714     o << '(';
1715     PrintExpressionContents(currFunction, o).visit(curr);
1716     incIndent();
1717     maybePrintImplicitBlock(curr->body, true);
1718     decIndent();
1719     if (full) {
1720       o << " ;; end loop";
1721       if (curr->name.is()) {
1722         o << ' ' << curr->name;
1723       }
1724     }
1725   }
visitBreakwasm::PrintSExpression1726   void visitBreak(Break* curr) {
1727     o << '(';
1728     PrintExpressionContents(currFunction, o).visit(curr);
1729     if (curr->condition) {
1730       incIndent();
1731     } else {
1732       if (!curr->value || curr->value->is<Nop>()) {
1733         // avoid a new line just for the parens
1734         o << ')';
1735         return;
1736       }
1737       incIndent();
1738     }
1739     if (curr->value && !curr->value->is<Nop>()) {
1740       printFullLine(curr->value);
1741     }
1742     if (curr->condition) {
1743       printFullLine(curr->condition);
1744     }
1745     decIndent();
1746   }
visitSwitchwasm::PrintSExpression1747   void visitSwitch(Switch* curr) {
1748     o << '(';
1749     PrintExpressionContents(currFunction, o).visit(curr);
1750     incIndent();
1751     if (curr->value && !curr->value->is<Nop>()) {
1752       printFullLine(curr->value);
1753     }
1754     printFullLine(curr->condition);
1755     decIndent();
1756   }
1757 
printCallOperandswasm::PrintSExpression1758   template<typename CallBase> void printCallOperands(CallBase* curr) {
1759     if (curr->operands.size() > 0) {
1760       incIndent();
1761       for (auto operand : curr->operands) {
1762         printFullLine(operand);
1763       }
1764       decIndent();
1765     } else {
1766       o << ')';
1767     }
1768   }
1769 
visitCallwasm::PrintSExpression1770   void visitCall(Call* curr) {
1771     o << '(';
1772     PrintExpressionContents(currFunction, o).visit(curr);
1773     printCallOperands(curr);
1774   }
visitCallIndirectwasm::PrintSExpression1775   void visitCallIndirect(CallIndirect* curr) {
1776     o << '(';
1777     PrintExpressionContents(currFunction, o).visit(curr);
1778     incIndent();
1779     for (auto operand : curr->operands) {
1780       printFullLine(operand);
1781     }
1782     printFullLine(curr->target);
1783     decIndent();
1784   }
visitLocalGetwasm::PrintSExpression1785   void visitLocalGet(LocalGet* curr) {
1786     o << '(';
1787     PrintExpressionContents(currFunction, o).visit(curr);
1788     o << ')';
1789   }
visitLocalSetwasm::PrintSExpression1790   void visitLocalSet(LocalSet* curr) {
1791     o << '(';
1792     PrintExpressionContents(currFunction, o).visit(curr);
1793     incIndent();
1794     printFullLine(curr->value);
1795     decIndent();
1796   }
visitGlobalGetwasm::PrintSExpression1797   void visitGlobalGet(GlobalGet* curr) {
1798     o << '(';
1799     PrintExpressionContents(currFunction, o).visit(curr);
1800     o << ')';
1801   }
visitGlobalSetwasm::PrintSExpression1802   void visitGlobalSet(GlobalSet* curr) {
1803     o << '(';
1804     PrintExpressionContents(currFunction, o).visit(curr);
1805     incIndent();
1806     printFullLine(curr->value);
1807     decIndent();
1808   }
visitLoadwasm::PrintSExpression1809   void visitLoad(Load* curr) {
1810     o << '(';
1811     PrintExpressionContents(currFunction, o).visit(curr);
1812     incIndent();
1813     printFullLine(curr->ptr);
1814     decIndent();
1815   }
visitStorewasm::PrintSExpression1816   void visitStore(Store* curr) {
1817     o << '(';
1818     PrintExpressionContents(currFunction, o).visit(curr);
1819     incIndent();
1820     printFullLine(curr->ptr);
1821     printFullLine(curr->value);
1822     decIndent();
1823   }
visitAtomicRMWwasm::PrintSExpression1824   void visitAtomicRMW(AtomicRMW* curr) {
1825     o << '(';
1826     PrintExpressionContents(currFunction, o).visit(curr);
1827     incIndent();
1828     printFullLine(curr->ptr);
1829     printFullLine(curr->value);
1830     decIndent();
1831   }
visitAtomicCmpxchgwasm::PrintSExpression1832   void visitAtomicCmpxchg(AtomicCmpxchg* curr) {
1833     o << '(';
1834     PrintExpressionContents(currFunction, o).visit(curr);
1835     incIndent();
1836     printFullLine(curr->ptr);
1837     printFullLine(curr->expected);
1838     printFullLine(curr->replacement);
1839     decIndent();
1840   }
visitAtomicWaitwasm::PrintSExpression1841   void visitAtomicWait(AtomicWait* curr) {
1842     o << '(';
1843     PrintExpressionContents(currFunction, o).visit(curr);
1844     restoreNormalColor(o);
1845     incIndent();
1846     printFullLine(curr->ptr);
1847     printFullLine(curr->expected);
1848     printFullLine(curr->timeout);
1849     decIndent();
1850   }
visitAtomicNotifywasm::PrintSExpression1851   void visitAtomicNotify(AtomicNotify* curr) {
1852     o << '(';
1853     PrintExpressionContents(currFunction, o).visit(curr);
1854     incIndent();
1855     printFullLine(curr->ptr);
1856     printFullLine(curr->notifyCount);
1857     decIndent();
1858   }
visitAtomicFencewasm::PrintSExpression1859   void visitAtomicFence(AtomicFence* curr) {
1860     o << '(';
1861     PrintExpressionContents(currFunction, o).visit(curr);
1862     o << ')';
1863   }
visitSIMDExtractwasm::PrintSExpression1864   void visitSIMDExtract(SIMDExtract* curr) {
1865     o << '(';
1866     PrintExpressionContents(currFunction, o).visit(curr);
1867     incIndent();
1868     printFullLine(curr->vec);
1869     decIndent();
1870   }
visitSIMDReplacewasm::PrintSExpression1871   void visitSIMDReplace(SIMDReplace* curr) {
1872     o << '(';
1873     PrintExpressionContents(currFunction, o).visit(curr);
1874     incIndent();
1875     printFullLine(curr->vec);
1876     printFullLine(curr->value);
1877     decIndent();
1878   }
visitSIMDShufflewasm::PrintSExpression1879   void visitSIMDShuffle(SIMDShuffle* curr) {
1880     o << '(';
1881     PrintExpressionContents(currFunction, o).visit(curr);
1882     incIndent();
1883     printFullLine(curr->left);
1884     printFullLine(curr->right);
1885     decIndent();
1886   }
visitSIMDTernarywasm::PrintSExpression1887   void visitSIMDTernary(SIMDTernary* curr) {
1888     o << '(';
1889     PrintExpressionContents(currFunction, o).visit(curr);
1890     incIndent();
1891     printFullLine(curr->a);
1892     printFullLine(curr->b);
1893     printFullLine(curr->c);
1894     decIndent();
1895   }
visitSIMDShiftwasm::PrintSExpression1896   void visitSIMDShift(SIMDShift* curr) {
1897     o << '(';
1898     PrintExpressionContents(currFunction, o).visit(curr);
1899     incIndent();
1900     printFullLine(curr->vec);
1901     printFullLine(curr->shift);
1902     decIndent();
1903   }
visitSIMDLoadwasm::PrintSExpression1904   void visitSIMDLoad(SIMDLoad* curr) {
1905     o << '(';
1906     PrintExpressionContents(currFunction, o).visit(curr);
1907     incIndent();
1908     printFullLine(curr->ptr);
1909     decIndent();
1910   }
visitMemoryInitwasm::PrintSExpression1911   void visitMemoryInit(MemoryInit* curr) {
1912     o << '(';
1913     PrintExpressionContents(currFunction, o).visit(curr);
1914     incIndent();
1915     printFullLine(curr->dest);
1916     printFullLine(curr->offset);
1917     printFullLine(curr->size);
1918     decIndent();
1919   }
visitDataDropwasm::PrintSExpression1920   void visitDataDrop(DataDrop* curr) {
1921     o << '(';
1922     PrintExpressionContents(currFunction, o).visit(curr);
1923     o << ')';
1924   }
visitMemoryCopywasm::PrintSExpression1925   void visitMemoryCopy(MemoryCopy* curr) {
1926     o << '(';
1927     PrintExpressionContents(currFunction, o).visit(curr);
1928     incIndent();
1929     printFullLine(curr->dest);
1930     printFullLine(curr->source);
1931     printFullLine(curr->size);
1932     decIndent();
1933   }
visitMemoryFillwasm::PrintSExpression1934   void visitMemoryFill(MemoryFill* curr) {
1935     o << '(';
1936     PrintExpressionContents(currFunction, o).visit(curr);
1937     incIndent();
1938     printFullLine(curr->dest);
1939     printFullLine(curr->value);
1940     printFullLine(curr->size);
1941     decIndent();
1942   }
visitConstwasm::PrintSExpression1943   void visitConst(Const* curr) {
1944     o << '(';
1945     PrintExpressionContents(currFunction, o).visit(curr);
1946     o << ')';
1947   }
visitUnarywasm::PrintSExpression1948   void visitUnary(Unary* curr) {
1949     o << '(';
1950     PrintExpressionContents(currFunction, o).visit(curr);
1951     incIndent();
1952     printFullLine(curr->value);
1953     decIndent();
1954   }
visitBinarywasm::PrintSExpression1955   void visitBinary(Binary* curr) {
1956     o << '(';
1957     PrintExpressionContents(currFunction, o).visit(curr);
1958     incIndent();
1959     printFullLine(curr->left);
1960     printFullLine(curr->right);
1961     decIndent();
1962   }
visitSelectwasm::PrintSExpression1963   void visitSelect(Select* curr) {
1964     o << '(';
1965     PrintExpressionContents(currFunction, o).visit(curr);
1966     incIndent();
1967     printFullLine(curr->ifTrue);
1968     printFullLine(curr->ifFalse);
1969     printFullLine(curr->condition);
1970     decIndent();
1971   }
visitDropwasm::PrintSExpression1972   void visitDrop(Drop* curr) {
1973     o << '(';
1974     PrintExpressionContents(currFunction, o).visit(curr);
1975     incIndent();
1976     printFullLine(curr->value);
1977     decIndent();
1978   }
visitReturnwasm::PrintSExpression1979   void visitReturn(Return* curr) {
1980     o << '(';
1981     PrintExpressionContents(currFunction, o).visit(curr);
1982     if (!curr->value) {
1983       // avoid a new line just for the parens
1984       o << ')';
1985       return;
1986     }
1987     incIndent();
1988     printFullLine(curr->value);
1989     decIndent();
1990   }
visitMemorySizewasm::PrintSExpression1991   void visitMemorySize(MemorySize* curr) {
1992     o << '(';
1993     PrintExpressionContents(currFunction, o).visit(curr);
1994     o << ')';
1995   }
visitMemoryGrowwasm::PrintSExpression1996   void visitMemoryGrow(MemoryGrow* curr) {
1997     o << '(';
1998     PrintExpressionContents(currFunction, o).visit(curr);
1999     incIndent();
2000     printFullLine(curr->delta);
2001     decIndent();
2002   }
visitRefNullwasm::PrintSExpression2003   void visitRefNull(RefNull* curr) {
2004     o << '(';
2005     PrintExpressionContents(currFunction, o).visit(curr);
2006     o << ')';
2007   }
visitRefIsNullwasm::PrintSExpression2008   void visitRefIsNull(RefIsNull* curr) {
2009     o << '(';
2010     PrintExpressionContents(currFunction, o).visit(curr);
2011     incIndent();
2012     printFullLine(curr->value);
2013     decIndent();
2014   }
visitRefFuncwasm::PrintSExpression2015   void visitRefFunc(RefFunc* curr) {
2016     o << '(';
2017     PrintExpressionContents(currFunction, o).visit(curr);
2018     o << ')';
2019   }
visitRefEqwasm::PrintSExpression2020   void visitRefEq(RefEq* curr) {
2021     o << '(';
2022     PrintExpressionContents(currFunction, o).visit(curr);
2023     incIndent();
2024     printFullLine(curr->left);
2025     printFullLine(curr->right);
2026     decIndent();
2027   }
2028   // try-catch-end is written in the folded wat format as
2029   // (try
2030   //  (do
2031   //   ...
2032   //  )
2033   //  (catch
2034   //   ...
2035   //  )
2036   // )
2037   // The parenthesis wrapping 'catch' is just a syntax and does not affect
2038   // nested depths of instructions within.
visitTrywasm::PrintSExpression2039   void visitTry(Try* curr) {
2040     o << '(';
2041     PrintExpressionContents(currFunction, o).visit(curr);
2042     incIndent();
2043     doIndent(o, indent);
2044     o << "(do";
2045     incIndent();
2046     maybePrintImplicitBlock(curr->body, true);
2047     decIndent();
2048     o << "\n";
2049     doIndent(o, indent);
2050     o << "(catch";
2051     incIndent();
2052     maybePrintImplicitBlock(curr->catchBody, true);
2053     decIndent();
2054     o << "\n";
2055     decIndent();
2056     if (full) {
2057       o << " ;; end try";
2058     }
2059   }
visitThrowwasm::PrintSExpression2060   void visitThrow(Throw* curr) {
2061     o << '(';
2062     PrintExpressionContents(currFunction, o).visit(curr);
2063     incIndent();
2064     for (auto operand : curr->operands) {
2065       printFullLine(operand);
2066     }
2067     decIndent();
2068   }
visitRethrowwasm::PrintSExpression2069   void visitRethrow(Rethrow* curr) {
2070     o << '(';
2071     PrintExpressionContents(currFunction, o).visit(curr);
2072     incIndent();
2073     printFullLine(curr->exnref);
2074     decIndent();
2075   }
visitBrOnExnwasm::PrintSExpression2076   void visitBrOnExn(BrOnExn* curr) {
2077     o << '(';
2078     PrintExpressionContents(currFunction, o).visit(curr);
2079     incIndent();
2080     printFullLine(curr->exnref);
2081     decIndent();
2082   }
visitNopwasm::PrintSExpression2083   void visitNop(Nop* curr) {
2084     o << '(';
2085     PrintExpressionContents(currFunction, o).visit(curr);
2086     o << ')';
2087   }
visitUnreachablewasm::PrintSExpression2088   void visitUnreachable(Unreachable* curr) {
2089     o << '(';
2090     PrintExpressionContents(currFunction, o).visit(curr);
2091     o << ')';
2092   }
visitPopwasm::PrintSExpression2093   void visitPop(Pop* curr) {
2094     o << '(';
2095     PrintExpressionContents(currFunction, o).visit(curr);
2096     o << ')';
2097   }
visitTupleMakewasm::PrintSExpression2098   void visitTupleMake(TupleMake* curr) {
2099     o << '(';
2100     PrintExpressionContents(currFunction, o).visit(curr);
2101     incIndent();
2102     for (auto operand : curr->operands) {
2103       printFullLine(operand);
2104     }
2105     decIndent();
2106   }
visitTupleExtractwasm::PrintSExpression2107   void visitTupleExtract(TupleExtract* curr) {
2108     o << '(';
2109     PrintExpressionContents(currFunction, o).visit(curr);
2110     incIndent();
2111     printFullLine(curr->tuple);
2112     decIndent();
2113   }
visitI31Newwasm::PrintSExpression2114   void visitI31New(I31New* curr) {
2115     o << '(';
2116     PrintExpressionContents(currFunction, o).visit(curr);
2117     incIndent();
2118     printFullLine(curr->value);
2119     decIndent();
2120   }
visitI31Getwasm::PrintSExpression2121   void visitI31Get(I31Get* curr) {
2122     o << '(';
2123     PrintExpressionContents(currFunction, o).visit(curr);
2124     incIndent();
2125     printFullLine(curr->i31);
2126     decIndent();
2127   }
visitRefTestwasm::PrintSExpression2128   void visitRefTest(RefTest* curr) {
2129     o << '(';
2130     PrintExpressionContents(currFunction, o).visit(curr);
2131     WASM_UNREACHABLE("TODO (gc): ref.test");
2132   }
visitRefCastwasm::PrintSExpression2133   void visitRefCast(RefCast* curr) {
2134     o << '(';
2135     PrintExpressionContents(currFunction, o).visit(curr);
2136     WASM_UNREACHABLE("TODO (gc): ref.cast");
2137   }
visitBrOnCastwasm::PrintSExpression2138   void visitBrOnCast(BrOnCast* curr) {
2139     o << '(';
2140     PrintExpressionContents(currFunction, o).visit(curr);
2141     WASM_UNREACHABLE("TODO (gc): br_on_cast");
2142   }
visitRttCanonwasm::PrintSExpression2143   void visitRttCanon(RttCanon* curr) {
2144     o << '(';
2145     PrintExpressionContents(currFunction, o).visit(curr);
2146     WASM_UNREACHABLE("TODO (gc): rtt.canon");
2147   }
visitRttSubwasm::PrintSExpression2148   void visitRttSub(RttSub* curr) {
2149     o << '(';
2150     PrintExpressionContents(currFunction, o).visit(curr);
2151     WASM_UNREACHABLE("TODO (gc): rtt.sub");
2152   }
visitStructNewwasm::PrintSExpression2153   void visitStructNew(StructNew* curr) {
2154     o << '(';
2155     PrintExpressionContents(currFunction, o).visit(curr);
2156     WASM_UNREACHABLE("TODO (gc): struct.new");
2157   }
visitStructGetwasm::PrintSExpression2158   void visitStructGet(StructGet* curr) {
2159     o << '(';
2160     PrintExpressionContents(currFunction, o).visit(curr);
2161     WASM_UNREACHABLE("TODO (gc): struct.get");
2162   }
visitStructSetwasm::PrintSExpression2163   void visitStructSet(StructSet* curr) {
2164     o << '(';
2165     PrintExpressionContents(currFunction, o).visit(curr);
2166     WASM_UNREACHABLE("TODO (gc): struct.set");
2167   }
visitArrayNewwasm::PrintSExpression2168   void visitArrayNew(ArrayNew* curr) {
2169     o << '(';
2170     PrintExpressionContents(currFunction, o).visit(curr);
2171     WASM_UNREACHABLE("TODO (gc): array.new");
2172   }
visitArrayGetwasm::PrintSExpression2173   void visitArrayGet(ArrayGet* curr) {
2174     o << '(';
2175     PrintExpressionContents(currFunction, o).visit(curr);
2176     WASM_UNREACHABLE("TODO (gc): array.get");
2177   }
visitArraySetwasm::PrintSExpression2178   void visitArraySet(ArraySet* curr) {
2179     o << '(';
2180     PrintExpressionContents(currFunction, o).visit(curr);
2181     WASM_UNREACHABLE("TODO (gc): array.set");
2182   }
visitArrayLenwasm::PrintSExpression2183   void visitArrayLen(ArrayLen* curr) {
2184     o << '(';
2185     PrintExpressionContents(currFunction, o).visit(curr);
2186     WASM_UNREACHABLE("TODO (gc): array.len");
2187   }
2188   // Module-level visitors
handleSignaturewasm::PrintSExpression2189   void handleSignature(Signature curr, Name* funcName = nullptr) {
2190     o << "(func";
2191     if (funcName) {
2192       o << " $" << *funcName;
2193     }
2194     if (curr.params.size() > 0) {
2195       o << maybeSpace;
2196       o << ParamType(curr.params);
2197     }
2198     if (curr.results.size() > 0) {
2199       o << maybeSpace;
2200       o << ResultType(curr.results);
2201     }
2202     o << ")";
2203   }
visitExportwasm::PrintSExpression2204   void visitExport(Export* curr) {
2205     o << '(';
2206     printMedium(o, "export ");
2207     printText(o, curr->name.str) << " (";
2208     switch (curr->kind) {
2209       case ExternalKind::Function:
2210         o << "func";
2211         break;
2212       case ExternalKind::Table:
2213         o << "table";
2214         break;
2215       case ExternalKind::Memory:
2216         o << "memory";
2217         break;
2218       case ExternalKind::Global:
2219         o << "global";
2220         break;
2221       case ExternalKind::Event:
2222         o << "event";
2223         break;
2224       case ExternalKind::Invalid:
2225         WASM_UNREACHABLE("invalid ExternalKind");
2226     }
2227     o << ' ';
2228     printName(curr->value, o) << "))";
2229   }
emitImportHeaderwasm::PrintSExpression2230   void emitImportHeader(Importable* curr) {
2231     printMedium(o, "import ");
2232     printText(o, curr->module.str) << ' ';
2233     printText(o, curr->base.str) << ' ';
2234   }
visitGlobalwasm::PrintSExpression2235   void visitGlobal(Global* curr) {
2236     if (curr->imported()) {
2237       visitImportedGlobal(curr);
2238     } else {
2239       visitDefinedGlobal(curr);
2240     }
2241   }
emitGlobalTypewasm::PrintSExpression2242   void emitGlobalType(Global* curr) {
2243     if (curr->mutable_) {
2244       o << "(mut " << SExprType(curr->type) << ')';
2245     } else {
2246       o << SExprType(curr->type);
2247     }
2248   }
visitImportedGlobalwasm::PrintSExpression2249   void visitImportedGlobal(Global* curr) {
2250     doIndent(o, indent);
2251     o << '(';
2252     emitImportHeader(curr);
2253     o << "(global ";
2254     printName(curr->name, o) << ' ';
2255     emitGlobalType(curr);
2256     o << "))" << maybeNewLine;
2257   }
visitDefinedGlobalwasm::PrintSExpression2258   void visitDefinedGlobal(Global* curr) {
2259     doIndent(o, indent);
2260     o << '(';
2261     printMedium(o, "global ");
2262     printName(curr->name, o) << ' ';
2263     emitGlobalType(curr);
2264     o << ' ';
2265     visit(curr->init);
2266     o << ')';
2267     o << maybeNewLine;
2268   }
visitFunctionwasm::PrintSExpression2269   void visitFunction(Function* curr) {
2270     if (curr->imported()) {
2271       visitImportedFunction(curr);
2272     } else {
2273       visitDefinedFunction(curr);
2274     }
2275   }
visitImportedFunctionwasm::PrintSExpression2276   void visitImportedFunction(Function* curr) {
2277     doIndent(o, indent);
2278     currFunction = curr;
2279     lastPrintedLocation = {0, 0, 0};
2280     o << '(';
2281     emitImportHeader(curr);
2282     handleSignature(curr->sig, &curr->name);
2283     o << ')';
2284     o << maybeNewLine;
2285   }
visitDefinedFunctionwasm::PrintSExpression2286   void visitDefinedFunction(Function* curr) {
2287     doIndent(o, indent);
2288     currFunction = curr;
2289     lastPrintedLocation = {0, 0, 0};
2290     if (currFunction->prologLocation.size()) {
2291       printDebugLocation(*currFunction->prologLocation.begin());
2292     }
2293     o << '(';
2294     printMajor(o, "func ");
2295     printName(curr->name, o);
2296     if (!printStackIR && curr->stackIR && !minify) {
2297       o << " (; has Stack IR ;)";
2298     }
2299     if (curr->sig.params.size() > 0) {
2300       Index i = 0;
2301       for (const auto& param : curr->sig.params) {
2302         o << maybeSpace;
2303         o << '(';
2304         printMinor(o, "param ");
2305         printLocal(i, currFunction, o);
2306         o << ' ' << param << ')';
2307         ++i;
2308       }
2309     }
2310     if (curr->sig.results != Type::none) {
2311       o << maybeSpace;
2312       o << ResultType(curr->sig.results);
2313     }
2314     incIndent();
2315     for (size_t i = curr->getVarIndexBase(); i < curr->getNumLocals(); i++) {
2316       doIndent(o, indent);
2317       o << '(';
2318       printMinor(o, "local ");
2319       printLocal(i, currFunction, o)
2320         << ' ' << SExprType(curr->getLocalType(i)) << ')';
2321       o << maybeNewLine;
2322     }
2323     // Print the body.
2324     if (!printStackIR || !curr->stackIR) {
2325       // It is ok to emit a block here, as a function can directly contain a
2326       // list, even if our ast avoids that for simplicity. We can just do that
2327       // optimization here..
2328       if (!full && curr->body->is<Block>() &&
2329           curr->body->cast<Block>()->name.isNull()) {
2330         Block* block = curr->body->cast<Block>();
2331         for (auto item : block->list) {
2332           printFullLine(item);
2333         }
2334       } else {
2335         printFullLine(curr->body);
2336       }
2337     } else {
2338       // Print the stack IR.
2339       WasmPrinter::printStackIR(curr->stackIR.get(), o, curr);
2340     }
2341     if (currFunction->epilogLocation.size() &&
2342         lastPrintedLocation != *currFunction->epilogLocation.begin()) {
2343       // Print last debug location: mix of decIndent and printDebugLocation
2344       // logic.
2345       doIndent(o, indent);
2346       if (!minify) {
2347         indent--;
2348       }
2349       printDebugLocation(*currFunction->epilogLocation.begin());
2350       o << ')';
2351     } else {
2352       decIndent();
2353     }
2354     o << maybeNewLine;
2355   }
visitEventwasm::PrintSExpression2356   void visitEvent(Event* curr) {
2357     if (curr->imported()) {
2358       visitImportedEvent(curr);
2359     } else {
2360       visitDefinedEvent(curr);
2361     }
2362   }
visitImportedEventwasm::PrintSExpression2363   void visitImportedEvent(Event* curr) {
2364     doIndent(o, indent);
2365     o << '(';
2366     emitImportHeader(curr);
2367     o << "(event ";
2368     printName(curr->name, o);
2369     o << maybeSpace << "(attr " << curr->attribute << ')' << maybeSpace;
2370     o << ParamType(curr->sig.params);
2371     o << "))";
2372     o << maybeNewLine;
2373   }
visitDefinedEventwasm::PrintSExpression2374   void visitDefinedEvent(Event* curr) {
2375     doIndent(o, indent);
2376     o << '(';
2377     printMedium(o, "event ");
2378     printName(curr->name, o);
2379     o << maybeSpace << "(attr " << curr->attribute << ')' << maybeSpace;
2380     o << ParamType(curr->sig.params);
2381     o << ")" << maybeNewLine;
2382   }
printTableHeaderwasm::PrintSExpression2383   void printTableHeader(Table* curr) {
2384     o << '(';
2385     printMedium(o, "table") << ' ';
2386     printName(curr->name, o) << ' ';
2387     o << curr->initial;
2388     if (curr->hasMax()) {
2389       o << ' ' << curr->max;
2390     }
2391     o << " funcref)";
2392   }
visitTablewasm::PrintSExpression2393   void visitTable(Table* curr) {
2394     if (!curr->exists) {
2395       return;
2396     }
2397     if (curr->imported()) {
2398       doIndent(o, indent);
2399       o << '(';
2400       emitImportHeader(curr);
2401       printTableHeader(&currModule->table);
2402       o << ')' << maybeNewLine;
2403     } else {
2404       doIndent(o, indent);
2405       printTableHeader(curr);
2406       o << maybeNewLine;
2407     }
2408     for (auto& segment : curr->segments) {
2409       // Don't print empty segments
2410       if (segment.data.empty()) {
2411         continue;
2412       }
2413       doIndent(o, indent);
2414       o << '(';
2415       printMajor(o, "elem ");
2416       visit(segment.offset);
2417       for (auto name : segment.data) {
2418         o << ' ';
2419         printName(name, o);
2420       }
2421       o << ')' << maybeNewLine;
2422     }
2423   }
printMemoryHeaderwasm::PrintSExpression2424   void printMemoryHeader(Memory* curr) {
2425     o << '(';
2426     printMedium(o, "memory") << ' ';
2427     printName(curr->name, o) << ' ';
2428     if (curr->shared) {
2429       o << '(';
2430       printMedium(o, "shared ");
2431     }
2432     if (curr->is64()) {
2433       o << "i64 ";
2434     }
2435     o << curr->initial;
2436     if (curr->hasMax()) {
2437       o << ' ' << curr->max;
2438     }
2439     if (curr->shared) {
2440       o << ")";
2441     }
2442     o << ")";
2443   }
visitMemorywasm::PrintSExpression2444   void visitMemory(Memory* curr) {
2445     if (!curr->exists) {
2446       return;
2447     }
2448     if (curr->imported()) {
2449       doIndent(o, indent);
2450       o << '(';
2451       emitImportHeader(curr);
2452       printMemoryHeader(&currModule->memory);
2453       o << ')' << maybeNewLine;
2454     } else {
2455       doIndent(o, indent);
2456       printMemoryHeader(curr);
2457       o << '\n';
2458     }
2459     for (auto segment : curr->segments) {
2460       doIndent(o, indent);
2461       o << '(';
2462       printMajor(o, "data ");
2463       if (segment.isPassive) {
2464         printMedium(o, "passive");
2465       } else {
2466         visit(segment.offset);
2467       }
2468       o << " \"";
2469       for (size_t i = 0; i < segment.data.size(); i++) {
2470         unsigned char c = segment.data[i];
2471         switch (c) {
2472           case '\n':
2473             o << "\\n";
2474             break;
2475           case '\r':
2476             o << "\\0d";
2477             break;
2478           case '\t':
2479             o << "\\t";
2480             break;
2481           case '\f':
2482             o << "\\0c";
2483             break;
2484           case '\b':
2485             o << "\\08";
2486             break;
2487           case '\\':
2488             o << "\\\\";
2489             break;
2490           case '"':
2491             o << "\\\"";
2492             break;
2493           case '\'':
2494             o << "\\'";
2495             break;
2496           default: {
2497             if (c >= 32 && c < 127) {
2498               o << c;
2499             } else {
2500               o << std::hex << '\\' << (c / 16) << (c % 16) << std::dec;
2501             }
2502           }
2503         }
2504       }
2505       o << "\")" << maybeNewLine;
2506     }
2507   }
printDylinkSectionwasm::PrintSExpression2508   void printDylinkSection(const std::unique_ptr<DylinkSection>& dylinkSection) {
2509     doIndent(o, indent) << ";; dylink section\n";
2510     doIndent(o, indent) << ";;   memorysize: " << dylinkSection->memorySize
2511                         << '\n';
2512     doIndent(o, indent) << ";;   memoryalignment: "
2513                         << dylinkSection->memoryAlignment << '\n';
2514     doIndent(o, indent) << ";;   tablesize: " << dylinkSection->tableSize
2515                         << '\n';
2516     doIndent(o, indent) << ";;   tablealignment: "
2517                         << dylinkSection->tableAlignment << '\n';
2518     for (auto& neededDynlib : dylinkSection->neededDynlibs) {
2519       doIndent(o, indent) << ";;   needed dynlib: " << neededDynlib << '\n';
2520     }
2521   }
visitModulewasm::PrintSExpression2522   void visitModule(Module* curr) {
2523     currModule = curr;
2524     o << '(';
2525     printMajor(o, "module");
2526     if (curr->name.is()) {
2527       o << ' ';
2528       printName(curr->name, o);
2529     }
2530     incIndent();
2531     std::vector<Signature> signatures;
2532     std::unordered_map<Signature, Index> indices;
2533     ModuleUtils::collectSignatures(*curr, signatures, indices);
2534     for (auto sig : signatures) {
2535       doIndent(o, indent);
2536       o << '(';
2537       printMedium(o, "type") << ' ';
2538       o << SigName(sig) << ' ';
2539       handleSignature(sig);
2540       o << ")" << maybeNewLine;
2541     }
2542     ModuleUtils::iterImportedMemories(
2543       *curr, [&](Memory* memory) { visitMemory(memory); });
2544     ModuleUtils::iterImportedTables(*curr,
2545                                     [&](Table* table) { visitTable(table); });
2546     ModuleUtils::iterImportedGlobals(
2547       *curr, [&](Global* global) { visitGlobal(global); });
2548     ModuleUtils::iterImportedFunctions(
2549       *curr, [&](Function* func) { visitFunction(func); });
2550     ModuleUtils::iterImportedEvents(*curr,
2551                                     [&](Event* event) { visitEvent(event); });
2552     ModuleUtils::iterDefinedMemories(
2553       *curr, [&](Memory* memory) { visitMemory(memory); });
2554     ModuleUtils::iterDefinedTables(*curr,
2555                                    [&](Table* table) { visitTable(table); });
2556     ModuleUtils::iterDefinedGlobals(
2557       *curr, [&](Global* global) { visitGlobal(global); });
2558     ModuleUtils::iterDefinedEvents(*curr,
2559                                    [&](Event* event) { visitEvent(event); });
2560     for (auto& child : curr->exports) {
2561       doIndent(o, indent);
2562       visitExport(child.get());
2563       o << maybeNewLine;
2564     }
2565     if (curr->start.is()) {
2566       doIndent(o, indent);
2567       o << '(';
2568       printMedium(o, "start") << ' ';
2569       printName(curr->start, o) << ')';
2570       o << maybeNewLine;
2571     }
2572     ModuleUtils::iterDefinedFunctions(
2573       *curr, [&](Function* func) { visitFunction(func); });
2574     if (curr->dylinkSection) {
2575       printDylinkSection(curr->dylinkSection);
2576     }
2577     for (auto& section : curr->userSections) {
2578       doIndent(o, indent);
2579       o << ";; custom section \"" << section.name << "\", size "
2580         << section.data.size();
2581       bool isPrintable = true;
2582       for (auto c : section.data) {
2583         if (!isprint(static_cast<unsigned char>(c))) {
2584           isPrintable = false;
2585           break;
2586         }
2587       }
2588       if (isPrintable) {
2589         o << ", contents: ";
2590         // std::quoted is not available in all the supported compilers yet.
2591         o << '"';
2592         for (auto c : section.data) {
2593           if (c == '\\' || c == '"') {
2594             o << '\\';
2595           }
2596           o << c;
2597         }
2598         o << '"';
2599       }
2600       o << maybeNewLine;
2601     }
2602     decIndent();
2603     o << maybeNewLine;
2604     currModule = nullptr;
2605   }
2606 };
2607 
2608 // Prints out a module
2609 class Printer : public Pass {
2610 protected:
2611   std::ostream& o;
2612 
2613 public:
Printer()2614   Printer() : o(std::cout) {}
Printer(std::ostream * o)2615   Printer(std::ostream* o) : o(*o) {}
2616 
modifiesBinaryenIR()2617   bool modifiesBinaryenIR() override { return false; }
2618 
run(PassRunner * runner,Module * module)2619   void run(PassRunner* runner, Module* module) override {
2620     PrintSExpression print(o);
2621     print.setDebugInfo(runner->options.debugInfo);
2622     print.visitModule(module);
2623   }
2624 };
2625 
createPrinterPass()2626 Pass* createPrinterPass() { return new Printer(); }
2627 
2628 // Prints out a minified module
2629 
2630 class MinifiedPrinter : public Printer {
2631 public:
2632   MinifiedPrinter() = default;
MinifiedPrinter(std::ostream * o)2633   MinifiedPrinter(std::ostream* o) : Printer(o) {}
2634 
run(PassRunner * runner,Module * module)2635   void run(PassRunner* runner, Module* module) override {
2636     PrintSExpression print(o);
2637     print.setMinify(true);
2638     print.setDebugInfo(runner->options.debugInfo);
2639     print.visitModule(module);
2640   }
2641 };
2642 
createMinifiedPrinterPass()2643 Pass* createMinifiedPrinterPass() { return new MinifiedPrinter(); }
2644 
2645 // Prints out a module withough elision, i.e., the full ast
2646 
2647 class FullPrinter : public Printer {
2648 public:
2649   FullPrinter() = default;
FullPrinter(std::ostream * o)2650   FullPrinter(std::ostream* o) : Printer(o) {}
2651 
run(PassRunner * runner,Module * module)2652   void run(PassRunner* runner, Module* module) override {
2653     PrintSExpression print(o);
2654     print.setFull(true);
2655     print.setDebugInfo(runner->options.debugInfo);
2656     print.visitModule(module);
2657   }
2658 };
2659 
createFullPrinterPass()2660 Pass* createFullPrinterPass() { return new FullPrinter(); }
2661 
2662 // Print Stack IR (if present)
2663 
2664 class PrintStackIR : public Printer {
2665 public:
2666   PrintStackIR() = default;
PrintStackIR(std::ostream * o)2667   PrintStackIR(std::ostream* o) : Printer(o) {}
2668 
run(PassRunner * runner,Module * module)2669   void run(PassRunner* runner, Module* module) override {
2670     PrintSExpression print(o);
2671     print.setDebugInfo(runner->options.debugInfo);
2672     print.setPrintStackIR(true);
2673     print.visitModule(module);
2674   }
2675 };
2676 
createPrintStackIRPass()2677 Pass* createPrintStackIRPass() { return new PrintStackIR(); }
2678 
2679 // Print individual expressions
2680 
printModule(Module * module,std::ostream & o)2681 std::ostream& WasmPrinter::printModule(Module* module, std::ostream& o) {
2682   PassRunner runner(module);
2683   Printer(&o).run(&runner, module);
2684   return o;
2685 }
2686 
printModule(Module * module)2687 std::ostream& WasmPrinter::printModule(Module* module) {
2688   return printModule(module, std::cout);
2689 }
2690 
printExpression(Expression * expression,std::ostream & o,bool minify,bool full)2691 std::ostream& WasmPrinter::printExpression(Expression* expression,
2692                                            std::ostream& o,
2693                                            bool minify,
2694                                            bool full) {
2695   if (!expression) {
2696     o << "(null expression)";
2697     return o;
2698   }
2699   PrintSExpression print(o);
2700   print.setMinify(minify);
2701   if (full || isFullForced()) {
2702     print.setFull(true);
2703     o << "[" << expression->type << "] ";
2704   }
2705   print.visit(expression);
2706   return o;
2707 }
2708 
2709 std::ostream&
printStackInst(StackInst * inst,std::ostream & o,Function * func)2710 WasmPrinter::printStackInst(StackInst* inst, std::ostream& o, Function* func) {
2711   switch (inst->op) {
2712     case StackInst::Basic: {
2713       PrintExpressionContents(func, o).visit(inst->origin);
2714       break;
2715     }
2716     case StackInst::BlockBegin:
2717     case StackInst::IfBegin:
2718     case StackInst::LoopBegin:
2719     case StackInst::TryBegin: {
2720       o << getExpressionName(inst->origin);
2721       break;
2722     }
2723     case StackInst::BlockEnd:
2724     case StackInst::IfEnd:
2725     case StackInst::LoopEnd:
2726     case StackInst::TryEnd: {
2727       o << "end (" << inst->type << ')';
2728       break;
2729     }
2730     case StackInst::IfElse: {
2731       o << "else";
2732       break;
2733     }
2734     case StackInst::Catch: {
2735       o << "catch";
2736       break;
2737     }
2738     default:
2739       WASM_UNREACHABLE("unexpeted op");
2740   }
2741   return o;
2742 }
2743 
2744 std::ostream&
printStackIR(StackIR * ir,std::ostream & o,Function * func)2745 WasmPrinter::printStackIR(StackIR* ir, std::ostream& o, Function* func) {
2746   size_t indent = func ? 2 : 0;
2747   auto doIndent = [&indent, &o]() {
2748     for (size_t j = 0; j < indent; j++) {
2749       o << ' ';
2750     }
2751   };
2752   for (Index i = 0; i < (*ir).size(); i++) {
2753     auto* inst = (*ir)[i];
2754     if (!inst) {
2755       continue;
2756     }
2757     switch (inst->op) {
2758       case StackInst::Basic: {
2759         doIndent();
2760         // Pop is a pseudo instruction and should not be printed in the stack IR
2761         // format to make it valid wat form.
2762         if (inst->origin->is<Pop>()) {
2763           break;
2764         }
2765         PrintExpressionContents(func, o).visit(inst->origin);
2766         break;
2767       }
2768       case StackInst::BlockBegin:
2769       case StackInst::IfBegin:
2770       case StackInst::LoopBegin:
2771       case StackInst::TryBegin: {
2772         doIndent();
2773         PrintExpressionContents(func, o).visit(inst->origin);
2774         indent++;
2775         break;
2776       }
2777       case StackInst::BlockEnd:
2778       case StackInst::IfEnd:
2779       case StackInst::LoopEnd:
2780       case StackInst::TryEnd: {
2781         indent--;
2782         doIndent();
2783         o << "end";
2784         break;
2785       }
2786       case StackInst::IfElse: {
2787         indent--;
2788         doIndent();
2789         o << "else";
2790         indent++;
2791         break;
2792       }
2793       case StackInst::Catch: {
2794         indent--;
2795         doIndent();
2796         o << "catch";
2797         indent++;
2798         break;
2799       }
2800       default:
2801         WASM_UNREACHABLE("unexpeted op");
2802     }
2803     std::cout << '\n';
2804   }
2805   return o;
2806 }
2807 
2808 } // namespace wasm
2809