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