summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorThomas Lively <tlively@google.com>2023-08-24 12:11:55 -0500
committerGitHub <noreply@github.com>2023-08-24 10:11:55 -0700
commit73bbbab144c4543edb70d35160cd620d3fe94539 (patch)
tree47c40a9722f73c1be24ef5a963281aca371074ca /src
parentda8937a71e908fecfc5722594dadd3c7ec6e80be (diff)
downloadbinaryen-73bbbab144c4543edb70d35160cd620d3fe94539.tar.gz
binaryen-73bbbab144c4543edb70d35160cd620d3fe94539.tar.bz2
binaryen-73bbbab144c4543edb70d35160cd620d3fe94539.zip
Simplify and consolidate type printing (#5816)
When printing Binaryen IR, we previously generated names for unnamed heap types based on their structure. This was useful for seeing the structure of simple types at a glance without having to separately go look up their definitions, but it also had two problems: 1. The same name could be generated for multiple types. The generated names did not take into account rec group structure or finality, so types that differed only in these properties would have the same name. Also, generated type names were limited in length, so very large types that shared only some structure could also end up with the same names. Using the same name for multiple types produces incorrect and unparsable output. 2. The generated names were not useful beyond the most trivial examples. Even with length limits, names for nontrivial types were extremely long and visually noisy, which made reading disassembled real-world code more challenging. Fix these problems by emitting simple indexed names for unnamed heap types instead. This regresses readability for very simple examples, but the trade off is worth it. This change also reduces the number of type printing systems we have by one. Previously we had the system in Print.cpp, but we had another, more general and extensible system in wasm-type-printing.h and wasm-type.cpp as well. Remove the old type printing system from Print.cpp and replace it with a much smaller use of the new system. This requires significant refactoring of Print.cpp so that PrintExpressionContents object now holds a reference to a parent PrintSExpression object that holds the type name state. This diff is very large because almost every test output changed slightly. To minimize the diff and ease review, change the type printer in wasm-type.cpp to behave the same as the old type printer in Print.cpp except for the differences in name generation. These changes will be reverted in much smaller PRs in the future to generally improve how types are printed.
Diffstat (limited to 'src')
-rw-r--r--src/passes/Print.cpp2186
-rw-r--r--src/wasm/wasm-type.cpp34
2 files changed, 997 insertions, 1223 deletions
diff --git a/src/passes/Print.cpp b/src/passes/Print.cpp
index 5ab386ceb..7f06f8c17 100644
--- a/src/passes/Print.cpp
+++ b/src/passes/Print.cpp
@@ -24,10 +24,13 @@
#include <pass.h>
#include <pretty_printing.h>
#include <wasm-stack.h>
+#include <wasm-type-printing.h>
#include <wasm.h>
namespace wasm {
+struct PrintSExpression;
+
static std::ostream& printExpression(Expression* expression,
std::ostream& o,
bool minify = false,
@@ -37,8 +40,7 @@ static std::ostream& printExpression(Expression* expression,
static std::ostream&
printStackInst(StackInst* inst, std::ostream& o, Function* func = nullptr);
-static std::ostream&
-printStackIR(StackIR* ir, std::ostream& o, Function* func = nullptr);
+static std::ostream& printStackIR(StackIR* ir, PrintSExpression&);
namespace {
@@ -83,314 +85,6 @@ std::ostream& printLocal(Index index, Function* func, std::ostream& o) {
return printName(name, o);
}
-bool maybePrintRefShorthand(std::ostream& o, Type type) {
- if (!type.isRef()) {
- return false;
- }
- auto heapType = type.getHeapType();
- if (heapType.isBasic() && type.isNullable()) {
- switch (heapType.getBasic()) {
- case HeapType::ext:
- o << "externref";
- return true;
- case HeapType::func:
- o << "funcref";
- return true;
- case HeapType::any:
- o << "anyref";
- return true;
- case HeapType::eq:
- o << "eqref";
- return true;
- case HeapType::i31:
- o << "i31ref";
- return true;
- case HeapType::struct_:
- o << "structref";
- return true;
- case HeapType::array:
- o << "arrayref";
- return true;
- case HeapType::string:
- o << "stringref";
- return true;
- case HeapType::stringview_wtf8:
- o << "stringview_wtf8";
- return true;
- case HeapType::stringview_wtf16:
- o << "stringview_wtf16";
- return true;
- case HeapType::stringview_iter:
- o << "stringview_iter";
- return true;
- case HeapType::none:
- o << "nullref";
- return true;
- case HeapType::noext:
- o << "nullexternref";
- return true;
- case HeapType::nofunc:
- o << "nullfuncref";
- return true;
- }
- }
- return false;
-}
-
-// Helper for printing the name of a type. This output is guaranteed to not
-// contain spaces.
-struct TypeNamePrinter {
- // Optional. If present, the module's HeapType names will be used.
- Module* wasm;
-
- // Keep track of the first depth at which we see each HeapType so if we see it
- // again, we can unambiguously refer to it without infinitely recursing.
- size_t currHeapTypeDepth = 0;
- std::unordered_map<HeapType, size_t> heapTypeDepths;
-
- // The stream we are printing to.
- std::ostream& os;
-
- TypeNamePrinter(std::ostream& os, Module* wasm = nullptr)
- : wasm(wasm), os(os) {}
-
- void print(Type type);
- void print(HeapType heapType);
- void print(const Tuple& tuple);
- void print(const Field& field);
- void print(const Signature& sig);
- void print(const Struct& struct_);
- void print(const Array& array);
-
- // FIXME: This hard limit on how many times we call print() avoids extremely
- // large outputs, which can be inconveniently large in some cases, but
- // we should have a better mechanism for this.
- static const size_t MaxPrints = 100;
-
- size_t prints = 0;
-
- bool exceededLimit() {
- if (prints >= MaxPrints) {
- os << "?";
- return true;
- }
- prints++;
- return false;
- }
-};
-
-void TypeNamePrinter::print(Type type) {
- if (exceededLimit()) {
- return;
- }
- if (type.isBasic()) {
- os << type;
- } else if (type.isTuple()) {
- print(type.getTuple());
- } else if (type.isRef()) {
- if (!maybePrintRefShorthand(os, type)) {
- os << "ref";
- if (type.isNullable()) {
- os << "?";
- }
- os << '|';
- print(type.getHeapType());
- os << '|';
- }
- } else {
- WASM_UNREACHABLE("unexpected type");
- }
-}
-
-void TypeNamePrinter::print(HeapType type) {
- if (exceededLimit()) {
- return;
- }
- if (type.isBasic()) {
- os << type;
- return;
- }
- // If there is a name for this type in this module, use it.
- // FIXME: in theory there could be two types, one with a name, and one
- // without, and the one without gets an automatic name that matches the
- // other's. To check for that, if (first) we could assert at the very end of
- // this function that the automatic name is not present in the given names.
- if (wasm && wasm->typeNames.count(type)) {
- os << '$' << wasm->typeNames[type].name;
- return;
- }
- // If we have seen this HeapType before, just print its relative depth instead
- // of infinitely recursing.
- auto it = heapTypeDepths.find(type);
- if (it != heapTypeDepths.end()) {
- assert(it->second <= currHeapTypeDepth);
- size_t relativeDepth = currHeapTypeDepth - it->second;
- os << "..." << relativeDepth;
- return;
- }
-
- // If this is the top-level heap type, add a $
- if (currHeapTypeDepth == 0) {
- os << "$";
- }
-
- // Update the context for the current HeapType before recursing.
- heapTypeDepths[type] = ++currHeapTypeDepth;
-
- if (type.isSignature()) {
- print(type.getSignature());
- } else if (type.isStruct()) {
- print(type.getStruct());
- } else if (type.isArray()) {
- print(type.getArray());
- } else {
- WASM_UNREACHABLE("unexpected type");
- }
-
- // Restore the previous context after the recursion.
- heapTypeDepths.erase(type);
- --currHeapTypeDepth;
-}
-
-void TypeNamePrinter::print(const Tuple& tuple) {
- auto sep = "";
- for (auto type : tuple) {
- os << sep;
- sep = "_";
- print(type);
- }
-}
-
-void TypeNamePrinter::print(const Field& field) {
- if (field.mutable_) {
- os << "mut:";
- }
- if (field.type == Type::i32 && field.packedType != Field::not_packed) {
- if (field.packedType == Field::i8) {
- os << "i8";
- } else if (field.packedType == Field::i16) {
- os << "i16";
- } else {
- WASM_UNREACHABLE("invalid packed type");
- }
- } else {
- print(field.type);
- }
-}
-
-void TypeNamePrinter::print(const Signature& sig) {
- // TODO: Switch to using an unambiguous delimiter rather than differentiating
- // only the top level with a different arrow.
- print(sig.params);
- if (currHeapTypeDepth == 1) {
- os << "_=>_";
- } else {
- os << "_->_";
- }
- print(sig.results);
-}
-
-void TypeNamePrinter::print(const Struct& struct_) {
- os << '{';
- auto sep = "";
- for (const auto& field : struct_.fields) {
- os << sep;
- sep = "_";
- print(field);
- }
- os << '}';
-}
-
-void TypeNamePrinter::print(const Array& array) {
- os << '[';
- print(array.element);
- os << ']';
-}
-
-std::ostream& printType(std::ostream& o, Type type, Module* wasm) {
- if (type.isBasic()) {
- o << type;
- } else if (type.isTuple()) {
- o << '(';
- auto sep = "";
- for (const auto& t : type) {
- o << sep;
- printType(o, t, wasm);
- sep = " ";
- }
- o << ')';
- } else if (type.isRef()) {
- if (!maybePrintRefShorthand(o, type)) {
- o << "(ref ";
- if (type.isNullable()) {
- o << "null ";
- }
- TypeNamePrinter(o, wasm).print(type.getHeapType());
- o << ')';
- }
- } else {
- WASM_UNREACHABLE("unexpected type");
- }
- return o;
-}
-
-std::ostream& printHeapType(std::ostream& o, HeapType type, Module* wasm) {
- TypeNamePrinter(o, wasm).print(type);
- return o;
-}
-
-std::ostream& printPrefixedTypes(std::ostream& o,
- const char* prefix,
- Type type,
- Module* wasm) {
- o << '(' << prefix;
- if (type == Type::none) {
- return o << ')';
- }
- if (type.isTuple()) {
- // Tuple types are not printed in parens, we can just emit them one after
- // the other in the same list as the "result".
- for (auto t : type) {
- o << ' ';
- printType(o, t, wasm);
- }
- } else {
- o << ' ';
- printType(o, type, wasm);
- }
- o << ')';
- return o;
-}
-
-std::ostream& printResultType(std::ostream& o, Type type, Module* wasm) {
- return printPrefixedTypes(o, "result", type, wasm);
-}
-
-std::ostream& printParamType(std::ostream& o, Type type, Module* wasm) {
- return printPrefixedTypes(o, "param", type, wasm);
-}
-
-// Generic processing of a struct's field, given an optional module. Calls func
-// with the field name, if it is present, or with a null Name if not.
-template<typename T>
-void processFieldName(Module* wasm, HeapType type, Index index, T func) {
- if (wasm) {
- auto it = wasm->typeNames.find(type);
- if (it != wasm->typeNames.end()) {
- auto& fieldNames = it->second.fieldNames;
- auto it = fieldNames.find(index);
- if (it != fieldNames.end()) {
- auto name = it->second;
- if (name.is()) {
- func(it->second);
- return;
- }
- }
- }
- }
- func(Name());
-}
-
std::ostream& printEscapedString(std::ostream& os, std::string_view str) {
os << '"';
for (unsigned char c : str) {
@@ -452,20 +146,208 @@ static Type forceConcrete(Type type) {
return type.isConcrete() ? type : Type::i32;
}
+struct PrintSExpression : public UnifiedExpressionVisitor<PrintSExpression> {
+ std::ostream& o;
+ unsigned indent = 0;
+
+ bool minify;
+ const char* maybeSpace;
+ const char* maybeNewLine;
+
+ bool full = false; // whether to not elide nodes in output when possible
+ // (like implicit blocks) and to emit types
+ bool stackIR = false; // whether to print stack IR if it is present
+ // (if false, and Stack IR is there, we just
+ // note it exists)
+
+ Module* currModule = nullptr;
+ Function* currFunction = nullptr;
+ Function::DebugLocation lastPrintedLocation;
+ bool debugInfo;
+
+ // Used to print delegate's depth argument when it throws to the caller
+ int controlFlowDepth = 0;
+
+ std::vector<HeapType> heapTypes;
+
+ // Print type names by saved name or index if we have a module, or otherwise
+ // by generating minimalist names. TODO: Handle conflicts between
+ // user-provided names and the fallback indexed names.
+ struct TypePrinter : TypeNameGeneratorBase<TypePrinter> {
+ PrintSExpression& parent;
+ IndexedTypeNameGenerator<> fallback;
+
+ TypePrinter(PrintSExpression& parent, const std::vector<HeapType>& types)
+ : parent(parent), fallback(types) {}
+
+ TypeNames getNames(HeapType type) {
+ if (parent.currModule) {
+ if (auto it = parent.currModule->typeNames.find(type);
+ it != parent.currModule->typeNames.end()) {
+ return it->second;
+ }
+ }
+ return fallback.getNames(type);
+ }
+
+ Name getName(HeapType type) { return getNames(type).name; }
+ } typePrinter;
+
+ PrintSExpression(std::ostream& o) : o(o), typePrinter(*this, heapTypes) {
+ setMinify(false);
+ if (!full) {
+ full = isFullForced();
+ }
+ }
+
+ void setModule(Module* module);
+
+ std::ostream& printType(Type type) { return o << typePrinter(type); }
+
+ std::ostream& printHeapType(HeapType type) {
+ if (type.isBasic()) {
+ return o << type;
+ }
+ return o << '$' << typePrinter.getNames(type).name;
+ }
+
+ std::ostream& printPrefixedTypes(const char* prefix, Type type);
+
+ std::ostream& printResultType(Type type) {
+ return printPrefixedTypes("result", type);
+ }
+
+ std::ostream& printParamType(Type type) {
+ return printPrefixedTypes("param", type);
+ }
+
+ void printDebugLocation(const Function::DebugLocation& location);
+ void printDebugLocation(Expression* curr);
+
+ // Prints debug info for a delimiter in an expression.
+ void printDebugDelimiterLocation(Expression* curr, Index i);
+
+ void printExpressionContents(Expression* curr);
+
+ void visit(Expression* curr) {
+ printDebugLocation(curr);
+ UnifiedExpressionVisitor<PrintSExpression>::visit(curr);
+ }
+
+ void setMinify(bool minify_) {
+ minify = minify_;
+ maybeSpace = minify ? "" : " ";
+ maybeNewLine = minify ? "" : "\n";
+ }
+
+ void setFull(bool full_) { full = full_; }
+
+ void setStackIR(bool stackIR_) { stackIR = stackIR_; }
+
+ void setDebugInfo(bool debugInfo_) { debugInfo = debugInfo_; }
+
+ void incIndent();
+ void decIndent();
+ void printFullLine(Expression* expression);
+
+ // loop, if, and try can contain implicit blocks. But they are not needed to
+ // be printed in some cases.
+ void maybePrintImplicitBlock(Expression* curr, bool allowMultipleInsts);
+
+ // Generic visitor, overridden only when necessary.
+ void visitExpression(Expression* curr);
+
+ void visitBlock(Block* curr);
+ void visitIf(If* curr);
+ void visitLoop(Loop* curr);
+ void visitTry(Try* curr);
+ void maybePrintUnreachableReplacement(Expression* curr, Type type);
+ void maybePrintUnreachableOrNullReplacement(Expression* curr, Type type);
+ void visitCallRef(CallRef* curr) {
+ maybePrintUnreachableOrNullReplacement(curr, curr->target->type);
+ }
+ void visitRefCast(RefCast* curr) {
+ maybePrintUnreachableReplacement(curr, curr->type);
+ }
+ void visitStructNew(StructNew* curr) {
+ maybePrintUnreachableReplacement(curr, curr->type);
+ }
+ void visitStructSet(StructSet* curr) {
+ maybePrintUnreachableOrNullReplacement(curr, curr->ref->type);
+ }
+ void visitStructGet(StructGet* curr) {
+ maybePrintUnreachableOrNullReplacement(curr, curr->ref->type);
+ }
+ void visitArrayNew(ArrayNew* curr) {
+ maybePrintUnreachableReplacement(curr, curr->type);
+ }
+ void visitArrayNewData(ArrayNewData* curr) {
+ maybePrintUnreachableReplacement(curr, curr->type);
+ }
+ void visitArrayNewElem(ArrayNewElem* curr) {
+ maybePrintUnreachableReplacement(curr, curr->type);
+ }
+ void visitArrayNewFixed(ArrayNewFixed* curr) {
+ maybePrintUnreachableReplacement(curr, curr->type);
+ }
+ void visitArraySet(ArraySet* curr) {
+ maybePrintUnreachableOrNullReplacement(curr, curr->ref->type);
+ }
+ void visitArrayGet(ArrayGet* curr) {
+ maybePrintUnreachableOrNullReplacement(curr, curr->ref->type);
+ }
+ // Module-level visitors
+ void handleSignature(HeapType curr, Name name = Name());
+ void visitExport(Export* curr);
+ void emitImportHeader(Importable* curr);
+ void visitGlobal(Global* curr);
+ void emitGlobalType(Global* curr);
+ void visitImportedGlobal(Global* curr);
+ void visitDefinedGlobal(Global* curr);
+ void visitFunction(Function* curr);
+ void visitImportedFunction(Function* curr);
+ void visitDefinedFunction(Function* curr);
+ void visitTag(Tag* curr);
+ void visitImportedTag(Tag* curr);
+ void visitDefinedTag(Tag* curr);
+ void printTableHeader(Table* curr);
+ void visitTable(Table* curr);
+ void visitElementSegment(ElementSegment* curr);
+ void printMemoryHeader(Memory* curr);
+ void visitMemory(Memory* curr);
+ void visitDataSegment(DataSegment* curr);
+ void printDylinkSection(const std::unique_ptr<DylinkSection>& dylinkSection);
+ void visitModule(Module* curr);
+};
+
// Prints the internal contents of an expression: everything but
// the children.
struct PrintExpressionContents
: public OverriddenVisitor<PrintExpressionContents> {
+ PrintSExpression& parent;
Module* wasm = nullptr;
Function* currFunction = nullptr;
std::ostream& o;
FeatureSet features;
- PrintExpressionContents(Module* wasm, Function* currFunction, std::ostream& o)
- : wasm(wasm), currFunction(currFunction), o(o), features(wasm->features) {}
+ PrintExpressionContents(PrintSExpression& parent)
+ : parent(parent), wasm(parent.currModule),
+ currFunction(parent.currFunction), o(parent.o),
+ features(wasm ? wasm->features : FeatureSet::All) {}
- PrintExpressionContents(Function* currFunction, std::ostream& o)
- : currFunction(currFunction), o(o), features(FeatureSet::All) {}
+ std::ostream& printType(Type type) { return parent.printType(type); }
+
+ std::ostream& printHeapType(HeapType type) {
+ return parent.printHeapType(type);
+ }
+
+ std::ostream& printResultType(Type type) {
+ return parent.printResultType(type);
+ }
+
+ std::ostream& printParamType(Type type) {
+ return parent.printParamType(type);
+ }
void visitBlock(Block* curr) {
printMedium(o, "block");
@@ -475,14 +357,14 @@ struct PrintExpressionContents
}
if (curr->type.isConcrete()) {
o << ' ';
- printResultType(o, curr->type, wasm);
+ printResultType(curr->type);
}
}
void visitIf(If* curr) {
printMedium(o, "if");
if (curr->type.isConcrete()) {
o << ' ';
- printResultType(o, curr->type, wasm);
+ printResultType(curr->type);
}
}
void visitLoop(Loop* curr) {
@@ -493,7 +375,7 @@ struct PrintExpressionContents
}
if (curr->type.isConcrete()) {
o << ' ';
- printResultType(o, curr->type, wasm);
+ printResultType(curr->type);
}
}
void visitBreak(Break* curr) {
@@ -536,7 +418,7 @@ struct PrintExpressionContents
o << '(';
printMinor(o, "type ");
- TypeNamePrinter(o, wasm).print(curr->heapType);
+ printHeapType(curr->heapType);
o << ')';
}
@@ -1983,7 +1865,7 @@ struct PrintExpressionContents
restoreNormalColor(o);
if (curr->type.isRef()) {
o << ' ';
- printResultType(o, curr->type, wasm);
+ printResultType(curr->type);
}
}
void visitDrop(Drop* curr) { printMedium(o, "drop"); }
@@ -1998,7 +1880,7 @@ struct PrintExpressionContents
}
void visitRefNull(RefNull* curr) {
printMedium(o, "ref.null ");
- printHeapType(o, curr->type.getHeapType(), wasm);
+ printHeapType(curr->type.getHeapType());
}
void visitRefIsNull(RefIsNull* curr) { printMedium(o, "ref.is_null"); }
void visitRefFunc(RefFunc* curr) {
@@ -2030,7 +1912,7 @@ struct PrintExpressionContents
}
if (curr->type.isConcrete()) {
o << ' ';
- printResultType(o, curr->type, wasm);
+ printResultType(curr->type);
}
}
void visitThrow(Throw* curr) {
@@ -2047,7 +1929,7 @@ struct PrintExpressionContents
prepareColor(o) << "pop";
for (auto type : curr->type) {
o << ' ';
- printType(o, type, wasm);
+ printType(type);
}
restoreNormalColor(o);
}
@@ -2089,18 +1971,18 @@ struct PrintExpressionContents
return;
}
printMedium(o, curr->isReturn ? "return_call_ref " : "call_ref ");
- printHeapType(o, curr->target->type.getHeapType(), wasm);
+ printHeapType(curr->target->type.getHeapType());
}
void visitRefTest(RefTest* curr) {
printMedium(o, "ref.test ");
- printType(o, curr->castType, wasm);
+ printType(curr->castType);
}
void visitRefCast(RefCast* curr) {
if (printUnreachableReplacement(curr)) {
return;
}
printMedium(o, "ref.cast ");
- printType(o, curr->type, wasm);
+ printType(curr->type);
}
void visitBrOn(BrOn* curr) {
@@ -2117,17 +1999,17 @@ struct PrintExpressionContents
printMedium(o, "br_on_cast ");
printName(curr->name, o);
o << ' ';
- printType(o, curr->ref->type, wasm);
+ printType(curr->ref->type);
o << ' ';
- printType(o, curr->castType, wasm);
+ printType(curr->castType);
return;
case BrOnCastFail:
printMedium(o, "br_on_cast_fail ");
printName(curr->name, o);
o << ' ';
- printType(o, curr->ref->type, wasm);
+ printType(curr->ref->type);
o << ' ';
- printType(o, curr->castType, wasm);
+ printType(curr->castType);
return;
}
WASM_UNREACHABLE("Unexpected br_on* op");
@@ -2141,16 +2023,15 @@ struct PrintExpressionContents
printMedium(o, "_default");
}
o << ' ';
- TypeNamePrinter(o, wasm).print(curr->type.getHeapType());
+ printHeapType(curr->type.getHeapType());
}
void printFieldName(HeapType type, Index index) {
- processFieldName(wasm, type, index, [&](Name name) {
- if (name.is()) {
- o << '$' << name;
- } else {
- o << index;
- }
- });
+ auto names = parent.typePrinter.getNames(type).fieldNames;
+ if (auto it = names.find(index); it != names.end()) {
+ o << '$' << it->second;
+ } else {
+ o << index;
+ }
}
void visitStructGet(StructGet* curr) {
if (printUnreachableOrNullReplacement(curr->ref)) {
@@ -2167,7 +2048,7 @@ struct PrintExpressionContents
} else {
printMedium(o, "struct.get ");
}
- TypeNamePrinter(o, wasm).print(heapType);
+ printHeapType(heapType);
o << ' ';
printFieldName(heapType, curr->index);
}
@@ -2177,7 +2058,7 @@ struct PrintExpressionContents
}
printMedium(o, "struct.set ");
auto heapType = curr->ref->type.getHeapType();
- TypeNamePrinter(o, wasm).print(heapType);
+ printHeapType(heapType);
o << ' ';
printFieldName(heapType, curr->index);
}
@@ -2190,7 +2071,7 @@ struct PrintExpressionContents
printMedium(o, "_default");
}
o << ' ';
- TypeNamePrinter(o, wasm).print(curr->type.getHeapType());
+ printHeapType(curr->type.getHeapType());
}
void visitArrayNewData(ArrayNewData* curr) {
if (printUnreachableReplacement(curr)) {
@@ -2198,7 +2079,7 @@ struct PrintExpressionContents
}
printMedium(o, "array.new_data");
o << ' ';
- TypeNamePrinter(o, wasm).print(curr->type.getHeapType());
+ printHeapType(curr->type.getHeapType());
o << " $" << curr->segment;
}
void visitArrayNewElem(ArrayNewElem* curr) {
@@ -2207,7 +2088,7 @@ struct PrintExpressionContents
}
printMedium(o, "array.new_elem");
o << ' ';
- TypeNamePrinter(o, wasm).print(curr->type.getHeapType());
+ printHeapType(curr->type.getHeapType());
o << " $" << curr->segment;
}
void visitArrayNewFixed(ArrayNewFixed* curr) {
@@ -2216,7 +2097,7 @@ struct PrintExpressionContents
}
printMedium(o, "array.new_fixed");
o << ' ';
- TypeNamePrinter(o, wasm).print(curr->type.getHeapType());
+ printHeapType(curr->type.getHeapType());
o << ' ';
o << curr->values.size();
}
@@ -2234,14 +2115,14 @@ struct PrintExpressionContents
} else {
printMedium(o, "array.get ");
}
- TypeNamePrinter(o, wasm).print(curr->ref->type.getHeapType());
+ printHeapType(curr->ref->type.getHeapType());
}
void visitArraySet(ArraySet* curr) {
if (printUnreachableOrNullReplacement(curr->ref)) {
return;
}
printMedium(o, "array.set ");
- TypeNamePrinter(o, wasm).print(curr->ref->type.getHeapType());
+ printHeapType(curr->ref->type.getHeapType());
}
void visitArrayLen(ArrayLen* curr) { printMedium(o, "array.len"); }
void visitArrayCopy(ArrayCopy* curr) {
@@ -2250,23 +2131,23 @@ struct PrintExpressionContents
return;
}
printMedium(o, "array.copy ");
- TypeNamePrinter(o, wasm).print(curr->destRef->type.getHeapType());
+ printHeapType(curr->destRef->type.getHeapType());
o << ' ';
- TypeNamePrinter(o, wasm).print(curr->srcRef->type.getHeapType());
+ printHeapType(curr->srcRef->type.getHeapType());
}
void visitArrayFill(ArrayFill* curr) {
if (printUnreachableOrNullReplacement(curr->ref)) {
return;
}
printMedium(o, "array.fill ");
- TypeNamePrinter(o, wasm).print(curr->ref->type.getHeapType());
+ printHeapType(curr->ref->type.getHeapType());
}
void visitArrayInitData(ArrayInitData* curr) {
if (printUnreachableOrNullReplacement(curr->ref)) {
return;
}
printMedium(o, "array.init_data ");
- TypeNamePrinter(o, wasm).print(curr->ref->type.getHeapType());
+ printHeapType(curr->ref->type.getHeapType());
o << " $" << curr->segment;
}
void visitArrayInitElem(ArrayInitElem* curr) {
@@ -2274,7 +2155,7 @@ struct PrintExpressionContents
return;
}
printMedium(o, "array.init_elem ");
- TypeNamePrinter(o, wasm).print(curr->ref->type.getHeapType());
+ printHeapType(curr->ref->type.getHeapType());
o << " $" << curr->segment;
}
void visitRefAs(RefAs* curr) {
@@ -2459,980 +2340,869 @@ struct PrintExpressionContents
}
};
-// Prints an expression in s-expr format, including both the
-// internal contents and the nested children.
-struct PrintSExpression : public UnifiedExpressionVisitor<PrintSExpression> {
- std::ostream& o;
- unsigned indent = 0;
-
- bool minify;
- const char* maybeSpace;
- const char* maybeNewLine;
-
- bool full = false; // whether to not elide nodes in output when possible
- // (like implicit blocks) and to emit types
- bool stackIR = false; // whether to print stack IR if it is present
- // (if false, and Stack IR is there, we just
- // note it exists)
-
- Module* currModule = nullptr;
- Function* currFunction = nullptr;
- Function::DebugLocation lastPrintedLocation;
- bool debugInfo;
-
- // Used to print delegate's depth argument when it throws to the caller
- int controlFlowDepth = 0;
-
- PrintSExpression(std::ostream& o) : o(o) {
- setMinify(false);
- if (!full) {
- full = isFullForced();
- }
+void PrintSExpression::setModule(Module* module) {
+ currModule = module;
+ if (module) {
+ heapTypes = ModuleUtils::getOptimizedIndexedHeapTypes(*module).types;
+ } else {
+ heapTypes = {};
}
+ // Reset the type printer for this module's types (or absence thereof).
+ typePrinter.~TypePrinter();
+ new (&typePrinter) TypePrinter(*this, heapTypes);
+}
- void printDebugLocation(const Function::DebugLocation& location) {
- if (lastPrintedLocation == location) {
- return;
+std::ostream& PrintSExpression::printPrefixedTypes(const char* prefix,
+ Type type) {
+ o << '(' << prefix;
+ if (type == Type::none) {
+ return o << ')';
+ }
+ if (type.isTuple()) {
+ // Tuple types are not printed in parens, we can just emit them one after
+ // the other in the same list as the "result".
+ for (auto t : type) {
+ o << ' ';
+ printType(t);
}
- lastPrintedLocation = location;
- auto fileName = currModule->debugInfoFileNames[location.fileIndex];
- o << ";;@ " << fileName << ":" << location.lineNumber << ":"
- << location.columnNumber << '\n';
- doIndent(o, indent);
+ } else {
+ o << ' ';
+ printType(type);
}
+ o << ')';
+ return o;
+}
- void printDebugLocation(Expression* curr) {
- if (currFunction) {
- // show an annotation, if there is one
- auto& debugLocations = currFunction->debugLocations;
- auto iter = debugLocations.find(curr);
- if (iter != debugLocations.end()) {
- printDebugLocation(iter->second);
- }
- // show a binary position, if there is one
- if (debugInfo) {
- auto iter = currFunction->expressionLocations.find(curr);
- if (iter != currFunction->expressionLocations.end()) {
- Colors::grey(o);
- o << ";; code offset: 0x" << std::hex << iter->second.start
- << std::dec << '\n';
- restoreNormalColor(o);
- doIndent(o, indent);
- }
- }
- }
+void PrintSExpression::printDebugLocation(
+ const Function::DebugLocation& location) {
+ if (lastPrintedLocation == location) {
+ return;
}
+ lastPrintedLocation = location;
+ auto fileName = currModule->debugInfoFileNames[location.fileIndex];
+ o << ";;@ " << fileName << ":" << location.lineNumber << ":"
+ << location.columnNumber << '\n';
+ doIndent(o, indent);
+}
- // Prints debug info for a delimiter in an expression.
- void printDebugDelimiterLocation(Expression* curr, Index i) {
- if (currFunction && debugInfo) {
- auto iter = currFunction->delimiterLocations.find(curr);
- if (iter != currFunction->delimiterLocations.end()) {
- auto& locations = iter->second;
+void PrintSExpression::printDebugLocation(Expression* curr) {
+ if (currFunction) {
+ // show an annotation, if there is one
+ auto& debugLocations = currFunction->debugLocations;
+ auto iter = debugLocations.find(curr);
+ if (iter != debugLocations.end()) {
+ printDebugLocation(iter->second);
+ }
+ // show a binary position, if there is one
+ if (debugInfo) {
+ auto iter = currFunction->expressionLocations.find(curr);
+ if (iter != currFunction->expressionLocations.end()) {
Colors::grey(o);
- o << ";; code offset: 0x" << std::hex << locations[i] << std::dec
+ o << ";; code offset: 0x" << std::hex << iter->second.start << std::dec
<< '\n';
restoreNormalColor(o);
doIndent(o, indent);
}
}
}
+}
- void printExpressionContents(Expression* curr) {
- if (currModule) {
- PrintExpressionContents(currModule, currFunction, o).visit(curr);
- } else {
- PrintExpressionContents(currFunction, o).visit(curr);
+void PrintSExpression::printDebugDelimiterLocation(Expression* curr, Index i) {
+ if (currFunction && debugInfo) {
+ auto iter = currFunction->delimiterLocations.find(curr);
+ if (iter != currFunction->delimiterLocations.end()) {
+ auto& locations = iter->second;
+ Colors::grey(o);
+ o << ";; code offset: 0x" << std::hex << locations[i] << std::dec << '\n';
+ restoreNormalColor(o);
+ doIndent(o, indent);
}
}
+}
- void visit(Expression* curr) {
- printDebugLocation(curr);
- UnifiedExpressionVisitor<PrintSExpression>::visit(curr);
- }
+void PrintSExpression::printExpressionContents(Expression* curr) {
+ PrintExpressionContents(*this).visit(curr);
+}
- void setMinify(bool minify_) {
- minify = minify_;
- maybeSpace = minify ? "" : " ";
- maybeNewLine = minify ? "" : "\n";
+void PrintSExpression::incIndent() {
+ if (minify) {
+ return;
}
+ o << '\n';
+ indent++;
+}
- void setFull(bool full_) { full = full_; }
-
- void setStackIR(bool stackIR_) { stackIR = stackIR_; }
+void PrintSExpression::decIndent() {
+ if (!minify) {
+ assert(indent > 0);
+ indent--;
+ doIndent(o, indent);
+ }
+ o << ')';
+}
- void setDebugInfo(bool debugInfo_) { debugInfo = debugInfo_; }
+void PrintSExpression::printFullLine(Expression* expression) {
+ if (!minify) {
+ doIndent(o, indent);
+ }
+ if (full) {
+ o << "[";
+ printTypeOrName(expression->type, o, currModule);
+ o << "] ";
+ }
+ visit(expression);
+ o << maybeNewLine;
+}
- void incIndent() {
- if (minify) {
- return;
+void PrintSExpression::maybePrintImplicitBlock(Expression* curr,
+ bool allowMultipleInsts) {
+ auto block = curr->dynCast<Block>();
+ if (!full && block && block->name.isNull() &&
+ (allowMultipleInsts || block->list.size() == 1)) {
+ for (auto expression : block->list) {
+ printFullLine(expression);
}
- o << '\n';
- indent++;
+ } else {
+ printFullLine(curr);
}
- void decIndent() {
- if (!minify) {
- assert(indent > 0);
- indent--;
- doIndent(o, indent);
+}
+
+void PrintSExpression::visitExpression(Expression* curr) {
+ o << '(';
+ printExpressionContents(curr);
+ auto it = ChildIterator(curr);
+ if (!it.children.empty()) {
+ incIndent();
+ for (auto* child : it) {
+ printFullLine(child);
}
+ decIndent();
+ } else {
o << ')';
}
- void printFullLine(Expression* expression) {
- if (!minify) {
+}
+
+void PrintSExpression::visitBlock(Block* curr) {
+ // special-case Block, because Block nesting (in their first element) can be
+ // incredibly deep
+ std::vector<Block*> stack;
+ while (1) {
+ if (stack.size() > 0) {
doIndent(o, indent);
+ printDebugLocation(curr);
}
+ stack.push_back(curr);
if (full) {
o << "[";
- printTypeOrName(expression->type, o, currModule);
- o << "] ";
+ printTypeOrName(curr->type, o, currModule);
+ o << "]";
}
- visit(expression);
- o << maybeNewLine;
- }
-
- // loop, if, and try can contain implicit blocks. But they are not needed to
- // be printed in some cases.
- void maybePrintImplicitBlock(Expression* curr, bool allowMultipleInsts) {
- auto block = curr->dynCast<Block>();
- if (!full && block && block->name.isNull() &&
- (allowMultipleInsts || block->list.size() == 1)) {
- for (auto expression : block->list) {
- printFullLine(expression);
- }
- } else {
- printFullLine(curr);
- }
- }
-
- // Generic visitor, overridden only when necessary.
- void visitExpression(Expression* curr) {
o << '(';
printExpressionContents(curr);
- auto it = ChildIterator(curr);
- if (!it.children.empty()) {
- incIndent();
- for (auto* child : it) {
- printFullLine(child);
- }
- decIndent();
+ incIndent();
+ if (curr->list.size() > 0 && curr->list[0]->is<Block>()) {
+ // recurse into the first element
+ curr = curr->list[0]->cast<Block>();
+ continue;
} else {
- o << ')';
+ break; // that's all we can recurse, start to unwind
}
}
- void visitBlock(Block* curr) {
- // special-case Block, because Block nesting (in their first element) can be
- // incredibly deep
- std::vector<Block*> stack;
- while (1) {
- if (stack.size() > 0) {
- doIndent(o, indent);
- printDebugLocation(curr);
- }
- stack.push_back(curr);
- if (full) {
- o << "[";
- printTypeOrName(curr->type, o, currModule);
- o << "]";
- }
- o << '(';
- printExpressionContents(curr);
- incIndent();
- if (curr->list.size() > 0 && curr->list[0]->is<Block>()) {
- // recurse into the first element
- curr = curr->list[0]->cast<Block>();
- continue;
- } else {
- break; // that's all we can recurse, start to unwind
- }
- }
-
- controlFlowDepth += stack.size();
- auto* top = stack.back();
- while (stack.size() > 0) {
- curr = stack.back();
- stack.pop_back();
- auto& list = curr->list;
- for (size_t i = 0; i < list.size(); i++) {
- if (curr != top && i == 0) {
- // one of the block recursions we already handled
- decIndent();
- if (full) {
- o << " ;; end block";
- auto* child = list[0]->cast<Block>();
- if (child->name.is()) {
- o << ' ' << child->name;
- }
+ controlFlowDepth += stack.size();
+ auto* top = stack.back();
+ while (stack.size() > 0) {
+ curr = stack.back();
+ stack.pop_back();
+ auto& list = curr->list;
+ for (size_t i = 0; i < list.size(); i++) {
+ if (curr != top && i == 0) {
+ // one of the block recursions we already handled
+ decIndent();
+ if (full) {
+ o << " ;; end block";
+ auto* child = list[0]->cast<Block>();
+ if (child->name.is()) {
+ o << ' ' << child->name;
}
- o << '\n';
- continue;
}
- printFullLine(list[i]);
- }
- controlFlowDepth--;
- }
- decIndent();
- if (full) {
- o << " ;; end block";
- if (curr->name.is()) {
- o << ' ' << curr->name;
+ o << '\n';
+ continue;
}
+ printFullLine(list[i]);
}
+ controlFlowDepth--;
}
- void visitIf(If* curr) {
- controlFlowDepth++;
- o << '(';
- printExpressionContents(curr);
- incIndent();
- printFullLine(curr->condition);
- maybePrintImplicitBlock(curr->ifTrue, false);
- if (curr->ifFalse) {
- // Note: debug info here is not used as LLVM does not emit ifs, and since
- // LLVM is the main source of DWARF, effectively we never encounter ifs
- // with DWARF.
- printDebugDelimiterLocation(curr, BinaryLocations::Else);
- maybePrintImplicitBlock(curr->ifFalse, false);
+ decIndent();
+ if (full) {
+ o << " ;; end block";
+ if (curr->name.is()) {
+ o << ' ' << curr->name;
}
- decIndent();
- if (full) {
- o << " ;; end if";
+ }
+}
+
+void PrintSExpression::visitIf(If* curr) {
+ controlFlowDepth++;
+ o << '(';
+ printExpressionContents(curr);
+ incIndent();
+ printFullLine(curr->condition);
+ maybePrintImplicitBlock(curr->ifTrue, false);
+ if (curr->ifFalse) {
+ // Note: debug info here is not used as LLVM does not emit ifs, and since
+ // LLVM is the main source of DWARF, effectively we never encounter ifs
+ // with DWARF.
+ printDebugDelimiterLocation(curr, BinaryLocations::Else);
+ maybePrintImplicitBlock(curr->ifFalse, false);
+ }
+ decIndent();
+ if (full) {
+ o << " ;; end if";
+ }
+ controlFlowDepth--;
+}
+
+void PrintSExpression::visitLoop(Loop* curr) {
+ controlFlowDepth++;
+ o << '(';
+ printExpressionContents(curr);
+ incIndent();
+ maybePrintImplicitBlock(curr->body, true);
+ decIndent();
+ if (full) {
+ o << " ;; end loop";
+ if (curr->name.is()) {
+ o << ' ' << curr->name;
}
- controlFlowDepth--;
}
- void visitLoop(Loop* curr) {
- controlFlowDepth++;
+ controlFlowDepth--;
+}
+
+// try-catch-end is written in the folded wat format as
+// (try
+// (do
+// ...
+// )
+// (catch $e
+// ...
+// )
+// ...
+// (catch_all
+// ...
+// )
+// )
+// The parenthesis wrapping do/catch/catch_all is just a syntax and does not
+// affect nested depths of instructions within.
+//
+// try-delegate is written in the forded format as
+// (try
+// (do
+// ...
+// )
+// (delegate $label)
+// )
+// When the 'delegate' delegates to the caller, we write the argument as an
+// immediate.
+void PrintSExpression::visitTry(Try* curr) {
+ controlFlowDepth++;
+ o << '(';
+ printExpressionContents(curr);
+ incIndent();
+ doIndent(o, indent);
+ o << '(';
+ printMedium(o, "do");
+ incIndent();
+ maybePrintImplicitBlock(curr->body, true);
+ decIndent();
+ o << "\n";
+ for (size_t i = 0; i < curr->catchTags.size(); i++) {
+ doIndent(o, indent);
+ printDebugDelimiterLocation(curr, i);
o << '(';
- printExpressionContents(curr);
+ printMedium(o, "catch ");
+ printName(curr->catchTags[i], o);
incIndent();
- maybePrintImplicitBlock(curr->body, true);
+ maybePrintImplicitBlock(curr->catchBodies[i], true);
decIndent();
- if (full) {
- o << " ;; end loop";
- if (curr->name.is()) {
- o << ' ' << curr->name;
- }
- }
- controlFlowDepth--;
+ o << "\n";
}
- // try-catch-end is written in the folded wat format as
- // (try
- // (do
- // ...
- // )
- // (catch $e
- // ...
- // )
- // ...
- // (catch_all
- // ...
- // )
- // )
- // The parenthesis wrapping do/catch/catch_all is just a syntax and does not
- // affect nested depths of instructions within.
- //
- // try-delegate is written in the forded format as
- // (try
- // (do
- // ...
- // )
- // (delegate $label)
- // )
- // When the 'delegate' delegates to the caller, we write the argument as an
- // immediate.
- void visitTry(Try* curr) {
- controlFlowDepth++;
- o << '(';
- printExpressionContents(curr);
- incIndent();
+ if (curr->hasCatchAll()) {
doIndent(o, indent);
+ printDebugDelimiterLocation(curr, curr->catchTags.size());
o << '(';
- printMedium(o, "do");
+ printMedium(o, "catch_all");
incIndent();
- maybePrintImplicitBlock(curr->body, true);
+ maybePrintImplicitBlock(curr->catchBodies.back(), true);
decIndent();
o << "\n";
- for (size_t i = 0; i < curr->catchTags.size(); i++) {
- doIndent(o, indent);
- printDebugDelimiterLocation(curr, i);
- o << '(';
- printMedium(o, "catch ");
- printName(curr->catchTags[i], o);
- incIndent();
- maybePrintImplicitBlock(curr->catchBodies[i], true);
- decIndent();
- o << "\n";
- }
- if (curr->hasCatchAll()) {
- doIndent(o, indent);
- printDebugDelimiterLocation(curr, curr->catchTags.size());
- o << '(';
- printMedium(o, "catch_all");
- incIndent();
- maybePrintImplicitBlock(curr->catchBodies.back(), true);
- decIndent();
- o << "\n";
- }
- controlFlowDepth--;
-
- if (curr->isDelegate()) {
- doIndent(o, indent);
- o << '(';
- printMedium(o, "delegate ");
- if (curr->delegateTarget == DELEGATE_CALLER_TARGET) {
- o << controlFlowDepth;
- } else {
- printName(curr->delegateTarget, o);
- }
- o << ")\n";
- }
- decIndent();
- if (full) {
- o << " ;; end try";
- }
- }
- void maybePrintUnreachableReplacement(Expression* curr, Type type) {
- // See the parallel function
- // PrintExpressionContents::printUnreachableReplacement for background. That
- // one handles the header, and this one the body. For convenience, this one
- // also gets a parameter of the type to check for unreachability, to avoid
- // boilerplate in the callers; if the type is not unreachable, it does the
- // normal behavior.
- //
- // Note that the list of instructions using that function must match those
- // using this one, so we print the header and body properly together.
-
- if (type != Type::unreachable) {
- visitExpression(curr);
- return;
- }
-
- // Emit a block with drops of the children.
- o << "(block";
- if (!minify) {
- o << " ;; (replaces something unreachable we can't emit)";
- }
- incIndent();
- for (auto* child : ChildIterator(curr)) {
- Drop drop;
- drop.value = child;
- printFullLine(&drop);
- }
- Unreachable unreachable;
- printFullLine(&unreachable);
- decIndent();
}
- // This must be used for the same Expressions that use
- // PrintExpressionContents::printUnreachableOrNullReplacement.
- void maybePrintUnreachableOrNullReplacement(Expression* curr, Type type) {
- if (type.isNull()) {
- type = Type::unreachable;
+ controlFlowDepth--;
+
+ if (curr->isDelegate()) {
+ doIndent(o, indent);
+ o << '(';
+ printMedium(o, "delegate ");
+ if (curr->delegateTarget == DELEGATE_CALLER_TARGET) {
+ o << controlFlowDepth;
+ } else {
+ printName(curr->delegateTarget, o);
}
- maybePrintUnreachableReplacement(curr, type);
+ o << ")\n";
}
- void visitCallRef(CallRef* curr) {
- maybePrintUnreachableOrNullReplacement(curr, curr->target->type);
+ decIndent();
+ if (full) {
+ o << " ;; end try";
}
- void visitRefCast(RefCast* curr) {
- maybePrintUnreachableReplacement(curr, curr->type);
- }
- void visitStructNew(StructNew* curr) {
- maybePrintUnreachableReplacement(curr, curr->type);
- }
- void visitStructSet(StructSet* curr) {
- maybePrintUnreachableOrNullReplacement(curr, curr->ref->type);
- }
- void visitStructGet(StructGet* curr) {
- maybePrintUnreachableOrNullReplacement(curr, curr->ref->type);
- }
- void visitArrayNew(ArrayNew* curr) {
- maybePrintUnreachableReplacement(curr, curr->type);
- }
- void visitArrayNewData(ArrayNewData* curr) {
- maybePrintUnreachableReplacement(curr, curr->type);
- }
- void visitArrayNewElem(ArrayNewElem* curr) {
- maybePrintUnreachableReplacement(curr, curr->type);
- }
- void visitArrayNewFixed(ArrayNewFixed* curr) {
- maybePrintUnreachableReplacement(curr, curr->type);
+}
+
+void PrintSExpression::maybePrintUnreachableReplacement(Expression* curr,
+ Type type) {
+ // See the parallel function
+ // PrintExpressionContents::printUnreachableReplacement for background. That
+ // one handles the header, and this one the body. For convenience, this one
+ // also gets a parameter of the type to check for unreachability, to avoid
+ // boilerplate in the callers; if the type is not unreachable, it does the
+ // normal behavior.
+ //
+ // Note that the list of instructions using that function must match those
+ // using this one, so we print the header and body properly together.
+
+ if (type != Type::unreachable) {
+ visitExpression(curr);
+ return;
}
- void visitArraySet(ArraySet* curr) {
- maybePrintUnreachableOrNullReplacement(curr, curr->ref->type);
+
+ // Emit a block with drops of the children.
+ o << "(block";
+ if (!minify) {
+ o << " ;; (replaces something unreachable we can't emit)";
}
- void visitArrayGet(ArrayGet* curr) {
- maybePrintUnreachableOrNullReplacement(curr, curr->ref->type);
+ incIndent();
+ for (auto* child : ChildIterator(curr)) {
+ Drop drop;
+ drop.value = child;
+ printFullLine(&drop);
}
- // Module-level visitors
- void printSupertypeOr(HeapType curr, std::string noSuper) {
- if (auto super = curr.getSuperType()) {
- TypeNamePrinter(o, currModule).print(*super);
- } else {
- o << noSuper;
- }
+ Unreachable unreachable;
+ printFullLine(&unreachable);
+ decIndent();
+}
+
+// This must be used for the same Expressions that use
+// PrintExpressionContents::printUnreachableOrNullReplacement.
+void PrintSExpression::maybePrintUnreachableOrNullReplacement(Expression* curr,
+ Type type) {
+ if (type.isNull()) {
+ type = Type::unreachable;
}
+ maybePrintUnreachableReplacement(curr, type);
+}
- void handleSignature(HeapType curr, Name name = Name()) {
- Signature sig = curr.getSignature();
- o << "(func";
- if (name.is()) {
- o << " $" << name;
- if (currModule && currModule->features.hasGC()) {
- o << " (type ";
- printHeapType(o, curr, currModule) << ')';
- }
- }
- if (sig.params.size() > 0) {
- o << maybeSpace;
- o << "(param ";
- auto sep = "";
- for (auto type : sig.params) {
- o << sep;
- printType(o, type, currModule);
- sep = " ";
- }
- o << ')';
- }
- if (sig.results.size() > 0) {
- o << maybeSpace;
- o << "(result ";
- auto sep = "";
- for (auto type : sig.results) {
- o << sep;
- printType(o, type, currModule);
- sep = " ";
- }
- o << ')';
+void PrintSExpression::handleSignature(HeapType curr, Name name) {
+ Signature sig = curr.getSignature();
+ o << "(func";
+ if (name.is()) {
+ o << " $" << name;
+ if (currModule && currModule->features.hasGC()) {
+ o << " (type ";
+ printHeapType(curr) << ')';
}
- o << ")";
}
- void handleFieldBody(const Field& field) {
- if (field.mutable_) {
- o << "(mut ";
- }
- if (field.type == Type::i32 && field.packedType != Field::not_packed) {
- if (field.packedType == Field::i8) {
- o << "i8";
- } else if (field.packedType == Field::i16) {
- o << "i16";
- } else {
- WASM_UNREACHABLE("invalid packed type");
- }
- } else {
- printType(o, field.type, currModule);
- }
- if (field.mutable_) {
- o << ')';
+ if (sig.params.size() > 0) {
+ o << maybeSpace;
+ o << "(param ";
+ auto sep = "";
+ for (auto type : sig.params) {
+ o << sep;
+ printType(type);
+ sep = " ";
}
- }
- void handleArray(HeapType curr) {
- o << "(array ";
- handleFieldBody(curr.getArray().element);
o << ')';
}
- void handleStruct(HeapType curr) {
- const auto& fields = curr.getStruct().fields;
- o << "(struct ";
+ if (sig.results.size() > 0) {
+ o << maybeSpace;
+ o << "(result ";
auto sep = "";
- for (Index i = 0; i < fields.size(); i++) {
- o << sep << "(field ";
- processFieldName(currModule, curr, i, [&](Name name) {
- if (name.is()) {
- o << '$' << name << ' ';
- }
- });
- handleFieldBody(fields[i]);
- o << ')';
+ for (auto type : sig.results) {
+ o << sep;
+ printType(type);
sep = " ";
}
o << ')';
}
- void handleHeapType(HeapType type) {
- auto super = type.getSuperType();
- bool useSub = false;
- // TODO: Once we parse MVP signature types as final, use the MVP shorthand
- // for final types without supertypes.
- if (super || type.isFinal()) {
- useSub = true;
- o << "(sub ";
- if (type.isFinal()) {
- o << "final ";
- }
- if (super) {
- TypeNamePrinter(o, currModule).print(*super);
- o << ' ';
- }
- }
- if (type.isSignature()) {
- handleSignature(type);
- } else if (type.isArray()) {
- handleArray(type);
- } else if (type.isStruct()) {
- handleStruct(type);
- } else {
- o << type;
- }
- if (useSub) {
- o << ')';
- }
+ o << ")";
+}
+
+void PrintSExpression::visitExport(Export* curr) {
+ o << '(';
+ printMedium(o, "export ");
+ // TODO: Escape the string properly.
+ printText(o, curr->name.str.data()) << " (";
+ switch (curr->kind) {
+ case ExternalKind::Function:
+ o << "func";
+ break;
+ case ExternalKind::Table:
+ o << "table";
+ break;
+ case ExternalKind::Memory:
+ o << "memory";
+ break;
+ case ExternalKind::Global:
+ o << "global";
+ break;
+ case ExternalKind::Tag:
+ o << "tag";
+ break;
+ case ExternalKind::Invalid:
+ WASM_UNREACHABLE("invalid ExternalKind");
}
- void visitExport(Export* curr) {
- o << '(';
- printMedium(o, "export ");
- // TODO: Escape the string properly.
- printText(o, curr->name.str.data()) << " (";
- switch (curr->kind) {
- case ExternalKind::Function:
- o << "func";
- break;
- case ExternalKind::Table:
- o << "table";
- break;
- case ExternalKind::Memory:
- o << "memory";
- break;
- case ExternalKind::Global:
- o << "global";
- break;
- case ExternalKind::Tag:
- o << "tag";
- break;
- case ExternalKind::Invalid:
- WASM_UNREACHABLE("invalid ExternalKind");
- }
- o << ' ';
- printName(curr->value, o) << "))";
+ o << ' ';
+ printName(curr->value, o) << "))";
+}
+
+void PrintSExpression::emitImportHeader(Importable* curr) {
+ printMedium(o, "import ");
+ // TODO: Escape the strings properly and use std::string_view.
+ printText(o, curr->module.str.data()) << ' ';
+ printText(o, curr->base.str.data()) << ' ';
+}
+
+void PrintSExpression::visitGlobal(Global* curr) {
+ if (curr->imported()) {
+ visitImportedGlobal(curr);
+ } else {
+ visitDefinedGlobal(curr);
}
- void emitImportHeader(Importable* curr) {
- printMedium(o, "import ");
- // TODO: Escape the strings properly and use std::string_view.
- printText(o, curr->module.str.data()) << ' ';
- printText(o, curr->base.str.data()) << ' ';
+}
+
+void PrintSExpression::emitGlobalType(Global* curr) {
+ if (curr->mutable_) {
+ o << "(mut ";
+ printType(curr->type) << ')';
+ } else {
+ printType(curr->type);
}
- void visitGlobal(Global* curr) {
- if (curr->imported()) {
- visitImportedGlobal(curr);
- } else {
- visitDefinedGlobal(curr);
- }
+}
+
+void PrintSExpression::visitImportedGlobal(Global* curr) {
+ doIndent(o, indent);
+ o << '(';
+ emitImportHeader(curr);
+ o << "(global ";
+ printName(curr->name, o) << ' ';
+ emitGlobalType(curr);
+ o << "))" << maybeNewLine;
+}
+
+void PrintSExpression::visitDefinedGlobal(Global* curr) {
+ doIndent(o, indent);
+ o << '(';
+ printMedium(o, "global ");
+ printName(curr->name, o) << ' ';
+ emitGlobalType(curr);
+ o << ' ';
+ visit(curr->init);
+ o << ')';
+ o << maybeNewLine;
+}
+
+void PrintSExpression::visitFunction(Function* curr) {
+ if (curr->imported()) {
+ visitImportedFunction(curr);
+ } else {
+ visitDefinedFunction(curr);
}
- void emitGlobalType(Global* curr) {
- if (curr->mutable_) {
- o << "(mut ";
- printType(o, curr->type, currModule) << ')';
- } else {
- printType(o, curr->type, currModule);
+}
+
+void PrintSExpression::visitImportedFunction(Function* curr) {
+ doIndent(o, indent);
+ currFunction = curr;
+ lastPrintedLocation = {0, 0, 0};
+ o << '(';
+ emitImportHeader(curr);
+ handleSignature(curr->getSig(), curr->name);
+ o << ')';
+ o << maybeNewLine;
+}
+
+void PrintSExpression::visitDefinedFunction(Function* curr) {
+ doIndent(o, indent);
+ currFunction = curr;
+ lastPrintedLocation = {0, 0, 0};
+ if (currFunction->prologLocation.size()) {
+ printDebugLocation(*currFunction->prologLocation.begin());
+ }
+ o << '(';
+ printMajor(o, "func ");
+ printName(curr->name, o);
+ if (currModule && currModule->features.hasGC()) {
+ o << " (type ";
+ printHeapType(curr->type) << ')';
+ }
+ if (!stackIR && curr->stackIR && !minify) {
+ o << " (; has Stack IR ;)";
+ }
+ if (curr->getParams().size() > 0) {
+ Index i = 0;
+ for (const auto& param : curr->getParams()) {
+ o << maybeSpace;
+ o << '(';
+ printMinor(o, "param ");
+ printLocal(i, currFunction, o);
+ o << ' ';
+ printType(param) << ')';
+ ++i;
}
}
- void visitImportedGlobal(Global* curr) {
- doIndent(o, indent);
- o << '(';
- emitImportHeader(curr);
- o << "(global ";
- printName(curr->name, o) << ' ';
- emitGlobalType(curr);
- o << "))" << maybeNewLine;
+ if (curr->getResults() != Type::none) {
+ o << maybeSpace;
+ printResultType(curr->getResults());
}
- void visitDefinedGlobal(Global* curr) {
+ incIndent();
+ for (size_t i = curr->getVarIndexBase(); i < curr->getNumLocals(); i++) {
doIndent(o, indent);
o << '(';
- printMedium(o, "global ");
- printName(curr->name, o) << ' ';
- emitGlobalType(curr);
- o << ' ';
- visit(curr->init);
- o << ')';
+ printMinor(o, "local ");
+ printLocal(i, currFunction, o) << ' ';
+ printType(curr->getLocalType(i)) << ')';
o << maybeNewLine;
}
- void visitFunction(Function* curr) {
- if (curr->imported()) {
- visitImportedFunction(curr);
+ // Print the body.
+ if (!stackIR || !curr->stackIR) {
+ // It is ok to emit a block here, as a function can directly contain a
+ // list, even if our ast avoids that for simplicity. We can just do that
+ // optimization here..
+ if (!full && curr->body->is<Block>() &&
+ curr->body->cast<Block>()->name.isNull()) {
+ Block* block = curr->body->cast<Block>();
+ for (auto item : block->list) {
+ printFullLine(item);
+ }
} else {
- visitDefinedFunction(curr);
+ printFullLine(curr->body);
}
+ assert(controlFlowDepth == 0);
+ } else {
+ // Print the stack IR.
+ printStackIR(curr->stackIR.get(), *this);
}
- void visitImportedFunction(Function* curr) {
+ if (currFunction->epilogLocation.size() &&
+ lastPrintedLocation != *currFunction->epilogLocation.begin()) {
+ // Print last debug location: mix of decIndent and printDebugLocation
+ // logic.
doIndent(o, indent);
- currFunction = curr;
- lastPrintedLocation = {0, 0, 0};
- o << '(';
- emitImportHeader(curr);
- handleSignature(curr->getSig(), curr->name);
+ if (!minify) {
+ indent--;
+ }
+ printDebugLocation(*currFunction->epilogLocation.begin());
o << ')';
- o << maybeNewLine;
+ } else {
+ decIndent();
}
- void visitDefinedFunction(Function* curr) {
- doIndent(o, indent);
- currFunction = curr;
- lastPrintedLocation = {0, 0, 0};
- if (currFunction->prologLocation.size()) {
- printDebugLocation(*currFunction->prologLocation.begin());
- }
- o << '(';
- printMajor(o, "func ");
- printName(curr->name, o);
- if (currModule && currModule->features.hasGC()) {
- o << " (type ";
- printHeapType(o, curr->type, currModule) << ')';
- }
- if (!stackIR && curr->stackIR && !minify) {
- o << " (; has Stack IR ;)";
- }
- if (curr->getParams().size() > 0) {
- Index i = 0;
- for (const auto& param : curr->getParams()) {
- o << maybeSpace;
- o << '(';
- printMinor(o, "param ");
- printLocal(i, currFunction, o);
- o << ' ';
- printType(o, param, currModule) << ')';
- ++i;
- }
- }
- if (curr->getResults() != Type::none) {
- o << maybeSpace;
- printResultType(o, curr->getResults(), currModule);
- }
- incIndent();
- for (size_t i = curr->getVarIndexBase(); i < curr->getNumLocals(); i++) {
- doIndent(o, indent);
- o << '(';
- printMinor(o, "local ");
- printLocal(i, currFunction, o) << ' ';
- printType(o, curr->getLocalType(i), currModule) << ')';
- o << maybeNewLine;
- }
- // Print the body.
- if (!stackIR || !curr->stackIR) {
- // It is ok to emit a block here, as a function can directly contain a
- // list, even if our ast avoids that for simplicity. We can just do that
- // optimization here..
- if (!full && curr->body->is<Block>() &&
- curr->body->cast<Block>()->name.isNull()) {
- Block* block = curr->body->cast<Block>();
- for (auto item : block->list) {
- printFullLine(item);
- }
- } else {
- printFullLine(curr->body);
- }
- assert(controlFlowDepth == 0);
- } else {
- // Print the stack IR.
- printStackIR(curr->stackIR.get(), o, curr);
- }
- if (currFunction->epilogLocation.size() &&
- lastPrintedLocation != *currFunction->epilogLocation.begin()) {
- // Print last debug location: mix of decIndent and printDebugLocation
- // logic.
- doIndent(o, indent);
- if (!minify) {
- indent--;
- }
- printDebugLocation(*currFunction->epilogLocation.begin());
- o << ')';
- } else {
- decIndent();
- }
- o << maybeNewLine;
+ o << maybeNewLine;
+}
+
+void PrintSExpression::visitTag(Tag* curr) {
+ if (curr->imported()) {
+ visitImportedTag(curr);
+ } else {
+ visitDefinedTag(curr);
}
- void visitTag(Tag* curr) {
- if (curr->imported()) {
- visitImportedTag(curr);
- } else {
- visitDefinedTag(curr);
- }
+}
+
+void PrintSExpression::visitImportedTag(Tag* curr) {
+ doIndent(o, indent);
+ o << '(';
+ emitImportHeader(curr);
+ o << "(tag ";
+ printName(curr->name, o);
+ o << maybeSpace;
+ printParamType(curr->sig.params);
+ o << "))";
+ o << maybeNewLine;
+}
+
+void PrintSExpression::visitDefinedTag(Tag* curr) {
+ doIndent(o, indent);
+ o << '(';
+ printMedium(o, "tag ");
+ printName(curr->name, o);
+ o << maybeSpace;
+ printParamType(curr->sig.params);
+ o << ")" << maybeNewLine;
+}
+
+void PrintSExpression::printTableHeader(Table* curr) {
+ o << '(';
+ printMedium(o, "table") << ' ';
+ printName(curr->name, o) << ' ';
+ o << curr->initial;
+ if (curr->hasMax()) {
+ o << ' ' << curr->max;
}
- void visitImportedTag(Tag* curr) {
+ o << ' ';
+ printType(curr->type) << ')';
+}
+
+void PrintSExpression::visitTable(Table* curr) {
+ if (curr->imported()) {
doIndent(o, indent);
o << '(';
emitImportHeader(curr);
- o << "(tag ";
- printName(curr->name, o);
- o << maybeSpace;
- printParamType(o, curr->sig.params, currModule);
- o << "))";
- o << maybeNewLine;
- }
- void visitDefinedTag(Tag* curr) {
+ printTableHeader(curr);
+ o << ')' << maybeNewLine;
+ } else {
doIndent(o, indent);
- o << '(';
- printMedium(o, "tag ");
- printName(curr->name, o);
- o << maybeSpace;
- printParamType(o, curr->sig.params, currModule);
- o << ")" << maybeNewLine;
- }
- void printTableHeader(Table* curr) {
- o << '(';
- printMedium(o, "table") << ' ';
- printName(curr->name, o) << ' ';
- o << curr->initial;
- if (curr->hasMax()) {
- o << ' ' << curr->max;
- }
- o << ' ';
- printType(o, curr->type, currModule) << ')';
+ printTableHeader(curr);
+ o << maybeNewLine;
}
- void visitTable(Table* curr) {
- if (curr->imported()) {
- doIndent(o, indent);
- o << '(';
- emitImportHeader(curr);
- printTableHeader(curr);
- o << ')' << maybeNewLine;
+}
+
+void PrintSExpression::visitElementSegment(ElementSegment* curr) {
+ bool usesExpressions = TableUtils::usesExpressions(curr, currModule);
+ auto printElemType = [&]() {
+ if (!usesExpressions) {
+ o << "func";
} else {
- doIndent(o, indent);
- printTableHeader(curr);
- o << maybeNewLine;
+ printType(curr->type);
}
- }
- void visitElementSegment(ElementSegment* curr) {
- bool usesExpressions = TableUtils::usesExpressions(curr, currModule);
- auto printElemType = [&]() {
- if (!usesExpressions) {
- o << "func";
- } else {
- printType(o, curr->type, currModule);
- }
- };
+ };
- doIndent(o, indent);
- o << '(';
- printMedium(o, "elem ");
- printName(curr->name, o);
+ doIndent(o, indent);
+ o << '(';
+ printMedium(o, "elem ");
+ printName(curr->name, o);
- if (curr->table.is()) {
- if (usesExpressions || currModule->tables.size() > 1) {
- // tableuse
- o << " (table ";
- printName(curr->table, o);
- o << ")";
- }
+ if (curr->table.is()) {
+ if (usesExpressions || currModule->tables.size() > 1) {
+ // tableuse
+ o << " (table ";
+ printName(curr->table, o);
+ o << ")";
+ }
- o << ' ';
- visit(curr->offset);
+ o << ' ';
+ visit(curr->offset);
- if (usesExpressions || currModule->tables.size() > 1) {
- o << ' ';
- printElemType();
- }
- } else {
+ if (usesExpressions || currModule->tables.size() > 1) {
o << ' ';
printElemType();
}
+ } else {
+ o << ' ';
+ printElemType();
+ }
- if (!usesExpressions) {
- for (auto* entry : curr->data) {
- auto* refFunc = entry->cast<RefFunc>();
- o << ' ';
- printName(refFunc->func, o);
- }
- } else {
- for (auto* entry : curr->data) {
- o << ' ';
- visit(entry);
- }
+ if (!usesExpressions) {
+ for (auto* entry : curr->data) {
+ auto* refFunc = entry->cast<RefFunc>();
+ o << ' ';
+ printName(refFunc->func, o);
+ }
+ } else {
+ for (auto* entry : curr->data) {
+ o << ' ';
+ visit(entry);
}
- o << ')' << maybeNewLine;
}
- void printMemoryHeader(Memory* curr) {
+ o << ')' << maybeNewLine;
+}
+
+void PrintSExpression::printMemoryHeader(Memory* curr) {
+ o << '(';
+ printMedium(o, "memory") << ' ';
+ printName(curr->name, o) << ' ';
+ if (curr->shared) {
o << '(';
- printMedium(o, "memory") << ' ';
- printName(curr->name, o) << ' ';
- if (curr->shared) {
- o << '(';
- printMedium(o, "shared ");
- }
- if (curr->is64()) {
- o << "i64 ";
- }
- o << curr->initial;
- if (curr->hasMax()) {
- o << ' ' << curr->max;
- }
- if (curr->shared) {
- o << ")";
- }
- o << ")";
+ printMedium(o, "shared ");
}
- void visitMemory(Memory* curr) {
- if (curr->imported()) {
- doIndent(o, indent);
- o << '(';
- emitImportHeader(curr);
- printMemoryHeader(curr);
- o << ')' << maybeNewLine;
- } else {
- doIndent(o, indent);
- printMemoryHeader(curr);
- o << '\n';
- }
+ if (curr->is64()) {
+ o << "i64 ";
+ }
+ o << curr->initial;
+ if (curr->hasMax()) {
+ o << ' ' << curr->max;
+ }
+ if (curr->shared) {
+ o << ")";
}
- void visitDataSegment(DataSegment* curr) {
+ o << ")";
+}
+
+void PrintSExpression::visitMemory(Memory* curr) {
+ if (curr->imported()) {
doIndent(o, indent);
o << '(';
- printMajor(o, "data ");
- printName(curr->name, o);
- o << ' ';
- if (!curr->isPassive) {
- assert(!currModule || currModule->memories.size() > 0);
- if (!currModule || curr->memory != currModule->memories[0]->name) {
- o << "(memory $" << curr->memory << ") ";
- }
- visit(curr->offset);
- o << ' ';
- }
- printEscapedString(o, {curr->data.data(), curr->data.size()});
+ emitImportHeader(curr);
+ printMemoryHeader(curr);
o << ')' << maybeNewLine;
+ } else {
+ doIndent(o, indent);
+ printMemoryHeader(curr);
+ o << '\n';
}
- void printDylinkSection(const std::unique_ptr<DylinkSection>& dylinkSection) {
- doIndent(o, indent) << ";; dylink section\n";
- doIndent(o, indent) << ";; memorysize: " << dylinkSection->memorySize
- << '\n';
- doIndent(o, indent) << ";; memoryalignment: "
- << dylinkSection->memoryAlignment << '\n';
- doIndent(o, indent) << ";; tablesize: " << dylinkSection->tableSize
- << '\n';
- doIndent(o, indent) << ";; tablealignment: "
- << dylinkSection->tableAlignment << '\n';
- for (auto& neededDynlib : dylinkSection->neededDynlibs) {
- doIndent(o, indent) << ";; needed dynlib: " << neededDynlib << '\n';
- }
- if (dylinkSection->tail.size()) {
- doIndent(o, indent) << ";; extra dylink data, size "
- << dylinkSection->tail.size() << "\n";
- }
- }
- void visitModule(Module* curr) {
- currModule = curr;
- o << '(';
- printMajor(o, "module");
- if (curr->name.is()) {
- o << ' ';
- printName(curr->name, o);
- }
- incIndent();
+}
- // Use the same type order as the binary output would even though there is
- // no code size benefit in the text format.
- auto indexedTypes = ModuleUtils::getOptimizedIndexedHeapTypes(*curr);
- std::optional<RecGroup> currGroup;
- bool nontrivialGroup = false;
- auto finishGroup = [&]() {
- if (nontrivialGroup) {
- decIndent();
- o << maybeNewLine;
- }
- };
- for (auto type : indexedTypes.types) {
- RecGroup newGroup = type.getRecGroup();
- if (!currGroup || *currGroup != newGroup) {
- if (currGroup) {
- finishGroup();
- }
- currGroup = newGroup;
- nontrivialGroup = currGroup->size() > 1;
- if (nontrivialGroup) {
- doIndent(o, indent);
- o << "(rec";
- incIndent();
- }
+void PrintSExpression::visitDataSegment(DataSegment* curr) {
+ doIndent(o, indent);
+ o << '(';
+ printMajor(o, "data ");
+ printName(curr->name, o);
+ o << ' ';
+ if (!curr->isPassive) {
+ assert(!currModule || currModule->memories.size() > 0);
+ if (!currModule || curr->memory != currModule->memories[0]->name) {
+ o << "(memory $" << curr->memory << ") ";
+ }
+ visit(curr->offset);
+ o << ' ';
+ }
+ printEscapedString(o, {curr->data.data(), curr->data.size()});
+ o << ')' << maybeNewLine;
+}
+
+void PrintSExpression::printDylinkSection(
+ const std::unique_ptr<DylinkSection>& dylinkSection) {
+ doIndent(o, indent) << ";; dylink section\n";
+ doIndent(o, indent) << ";; memorysize: " << dylinkSection->memorySize
+ << '\n';
+ doIndent(o, indent) << ";; memoryalignment: "
+ << dylinkSection->memoryAlignment << '\n';
+ doIndent(o, indent) << ";; tablesize: " << dylinkSection->tableSize << '\n';
+ doIndent(o, indent) << ";; tablealignment: "
+ << dylinkSection->tableAlignment << '\n';
+ for (auto& neededDynlib : dylinkSection->neededDynlibs) {
+ doIndent(o, indent) << ";; needed dynlib: " << neededDynlib << '\n';
+ }
+ if (dylinkSection->tail.size()) {
+ doIndent(o, indent) << ";; extra dylink data, size "
+ << dylinkSection->tail.size() << "\n";
+ }
+}
+
+void PrintSExpression::visitModule(Module* curr) {
+ setModule(curr);
+ o << '(';
+ printMajor(o, "module");
+ if (curr->name.is()) {
+ o << ' ';
+ printName(curr->name, o);
+ }
+ incIndent();
+
+ // Use the same type order as the binary output would even though there is
+ // no code size benefit in the text format.
+ std::optional<RecGroup> currGroup;
+ bool nontrivialGroup = false;
+ auto finishGroup = [&]() {
+ if (nontrivialGroup) {
+ decIndent();
+ o << maybeNewLine;
+ }
+ };
+ for (auto type : heapTypes) {
+ RecGroup newGroup = type.getRecGroup();
+ if (!currGroup || *currGroup != newGroup) {
+ if (currGroup) {
+ finishGroup();
}
- doIndent(o, indent);
- o << '(';
- printMedium(o, "type") << ' ';
- TypeNamePrinter(o, curr).print(type);
- o << ' ';
- handleHeapType(type);
- o << ")" << maybeNewLine;
- }
- finishGroup();
-
- ModuleUtils::iterImportedMemories(
- *curr, [&](Memory* memory) { visitMemory(memory); });
- ModuleUtils::iterImportedTables(*curr,
- [&](Table* table) { visitTable(table); });
- ModuleUtils::iterImportedGlobals(
- *curr, [&](Global* global) { visitGlobal(global); });
- ModuleUtils::iterImportedFunctions(
- *curr, [&](Function* func) { visitFunction(func); });
- ModuleUtils::iterImportedTags(*curr, [&](Tag* tag) { visitTag(tag); });
- ModuleUtils::iterDefinedGlobals(
- *curr, [&](Global* global) { visitGlobal(global); });
- ModuleUtils::iterDefinedMemories(
- *curr, [&](Memory* memory) { visitMemory(memory); });
- for (auto& segment : curr->dataSegments) {
- visitDataSegment(segment.get());
- }
- ModuleUtils::iterDefinedTables(*curr,
- [&](Table* table) { visitTable(table); });
- for (auto& segment : curr->elementSegments) {
- visitElementSegment(segment.get());
- }
- auto elemDeclareNames = TableUtils::getFunctionsNeedingElemDeclare(*curr);
- if (!elemDeclareNames.empty()) {
- doIndent(o, indent);
- printMedium(o, "(elem");
- o << " declare func";
- for (auto name : elemDeclareNames) {
- o << " $" << name;
+ currGroup = newGroup;
+ nontrivialGroup = currGroup->size() > 1;
+ if (nontrivialGroup) {
+ doIndent(o, indent);
+ o << "(rec";
+ incIndent();
}
- o << ')' << maybeNewLine;
- }
- ModuleUtils::iterDefinedTags(*curr, [&](Tag* tag) { visitTag(tag); });
- for (auto& child : curr->exports) {
- doIndent(o, indent);
- visitExport(child.get());
- o << maybeNewLine;
}
- if (curr->start.is()) {
- doIndent(o, indent);
- o << '(';
- printMedium(o, "start") << ' ';
- printName(curr->start, o) << ')';
- o << maybeNewLine;
+ doIndent(o, indent);
+ o << typePrinter(type) << maybeNewLine;
+ }
+ finishGroup();
+
+ ModuleUtils::iterImportedMemories(
+ *curr, [&](Memory* memory) { visitMemory(memory); });
+ ModuleUtils::iterImportedTables(*curr,
+ [&](Table* table) { visitTable(table); });
+ ModuleUtils::iterImportedGlobals(
+ *curr, [&](Global* global) { visitGlobal(global); });
+ ModuleUtils::iterImportedFunctions(
+ *curr, [&](Function* func) { visitFunction(func); });
+ ModuleUtils::iterImportedTags(*curr, [&](Tag* tag) { visitTag(tag); });
+ ModuleUtils::iterDefinedGlobals(*curr,
+ [&](Global* global) { visitGlobal(global); });
+ ModuleUtils::iterDefinedMemories(
+ *curr, [&](Memory* memory) { visitMemory(memory); });
+ for (auto& segment : curr->dataSegments) {
+ visitDataSegment(segment.get());
+ }
+ ModuleUtils::iterDefinedTables(*curr,
+ [&](Table* table) { visitTable(table); });
+ for (auto& segment : curr->elementSegments) {
+ visitElementSegment(segment.get());
+ }
+ auto elemDeclareNames = TableUtils::getFunctionsNeedingElemDeclare(*curr);
+ if (!elemDeclareNames.empty()) {
+ doIndent(o, indent);
+ printMedium(o, "(elem");
+ o << " declare func";
+ for (auto name : elemDeclareNames) {
+ o << " $" << name;
}
- ModuleUtils::iterDefinedFunctions(
- *curr, [&](Function* func) { visitFunction(func); });
- if (curr->dylinkSection) {
- printDylinkSection(curr->dylinkSection);
+ o << ')' << maybeNewLine;
+ }
+ ModuleUtils::iterDefinedTags(*curr, [&](Tag* tag) { visitTag(tag); });
+ for (auto& child : curr->exports) {
+ doIndent(o, indent);
+ visitExport(child.get());
+ o << maybeNewLine;
+ }
+ if (curr->start.is()) {
+ doIndent(o, indent);
+ o << '(';
+ printMedium(o, "start") << ' ';
+ printName(curr->start, o) << ')';
+ o << maybeNewLine;
+ }
+ ModuleUtils::iterDefinedFunctions(
+ *curr, [&](Function* func) { visitFunction(func); });
+ if (curr->dylinkSection) {
+ printDylinkSection(curr->dylinkSection);
+ }
+ for (auto& section : curr->customSections) {
+ doIndent(o, indent);
+ o << ";; custom section \"" << section.name << "\", size "
+ << section.data.size();
+ bool isPrintable = true;
+ for (auto c : section.data) {
+ if (!isprint(static_cast<unsigned char>(c))) {
+ isPrintable = false;
+ break;
+ }
}
- for (auto& section : curr->customSections) {
- doIndent(o, indent);
- o << ";; custom section \"" << section.name << "\", size "
- << section.data.size();
- bool isPrintable = true;
+ if (isPrintable) {
+ o << ", contents: ";
+ // std::quoted is not available in all the supported compilers yet.
+ o << '"';
for (auto c : section.data) {
- if (!isprint(static_cast<unsigned char>(c))) {
- isPrintable = false;
- break;
- }
- }
- if (isPrintable) {
- o << ", contents: ";
- // std::quoted is not available in all the supported compilers yet.
- o << '"';
- for (auto c : section.data) {
- if (c == '\\' || c == '"') {
- o << '\\';
- }
- o << c;
+ if (c == '\\' || c == '"') {
+ o << '\\';
}
- o << '"';
+ o << c;
}
- o << maybeNewLine;
- }
- if (curr->hasFeaturesSection) {
- doIndent(o, indent);
- o << ";; features section: " << curr->features.toString() << '\n';
+ o << '"';
}
- decIndent();
o << maybeNewLine;
- currModule = nullptr;
}
-};
+ if (curr->hasFeaturesSection) {
+ doIndent(o, indent);
+ o << ";; features section: " << curr->features.toString() << '\n';
+ }
+ decIndent();
+ o << maybeNewLine;
+ setModule(nullptr);
+}
// Prints out a module
class Printer : public Pass {
@@ -3531,13 +3301,14 @@ static std::ostream& printExpression(Expression* expression,
static std::ostream&
printStackInst(StackInst* inst, std::ostream& o, Function* func) {
+ PrintSExpression printer(o);
switch (inst->op) {
case StackInst::Basic:
case StackInst::BlockBegin:
case StackInst::IfBegin:
case StackInst::LoopBegin:
case StackInst::TryBegin: {
- PrintExpressionContents(func, o).visit(inst->origin);
+ PrintExpressionContents(printer).visit(inst->origin);
break;
}
case StackInst::BlockEnd:
@@ -3546,7 +3317,7 @@ printStackInst(StackInst* inst, std::ostream& o, Function* func) {
case StackInst::TryEnd: {
printMedium(o, "end");
o << " ;; type: ";
- TypeNamePrinter(o).print(inst->type);
+ printer.printType(inst->type);
break;
}
case StackInst::IfElse: {
@@ -3574,9 +3345,9 @@ printStackInst(StackInst* inst, std::ostream& o, Function* func) {
return o;
}
-static std::ostream&
-printStackIR(StackIR* ir, std::ostream& o, Function* func) {
- size_t indent = func ? 2 : 0;
+static std::ostream& printStackIR(StackIR* ir, PrintSExpression& printer) {
+ std::ostream& o = printer.o;
+ size_t indent = printer.currFunction ? 2 : 0;
auto doIndent = [&]() { o << std::string(indent, ' '); };
int controlFlowDepth = 0;
@@ -3596,7 +3367,7 @@ printStackIR(StackIR* ir, std::ostream& o, Function* func) {
break;
}
- PrintExpressionContents(func, o).visit(inst->origin);
+ PrintExpressionContents(printer).visit(inst->origin);
break;
}
case StackInst::TryBegin:
@@ -3607,7 +3378,7 @@ printStackIR(StackIR* ir, std::ostream& o, Function* func) {
case StackInst::LoopBegin: {
controlFlowDepth++;
doIndent();
- PrintExpressionContents(func, o).visit(inst->origin);
+ PrintExpressionContents(printer).visit(inst->origin);
indent++;
break;
}
@@ -3715,13 +3486,9 @@ std::ostream& operator<<(std::ostream& o, wasm::ModuleExpression pair) {
}
std::ostream& operator<<(std::ostream& o, wasm::ShallowExpression expression) {
- if (expression.module) {
- wasm::PrintExpressionContents printer(expression.module, nullptr, o);
- printer.visit(expression.expr);
- } else {
- wasm::PrintExpressionContents printer(nullptr, o);
- printer.visit(expression.expr);
- }
+ wasm::PrintSExpression printer(o);
+ printer.setModule(expression.module);
+ wasm::PrintExpressionContents(printer).visit(expression.expr);
return o;
}
@@ -3730,7 +3497,8 @@ std::ostream& operator<<(std::ostream& o, wasm::StackInst& inst) {
}
std::ostream& operator<<(std::ostream& o, wasm::StackIR& ir) {
- return wasm::printStackIR(&ir, o);
+ wasm::PrintSExpression printer(o);
+ return wasm::printStackIR(&ir, printer);
}
} // namespace std
diff --git a/src/wasm/wasm-type.cpp b/src/wasm/wasm-type.cpp
index fd9838b31..0c4e7b049 100644
--- a/src/wasm/wasm-type.cpp
+++ b/src/wasm/wasm-type.cpp
@@ -155,7 +155,8 @@ struct TypePrinter {
std::ostream& print(const Tuple& tuple);
std::ostream& print(const Field& field);
std::ostream& print(const Signature& sig);
- std::ostream& print(const Struct& struct_);
+ std::ostream& print(const Struct& struct_,
+ const std::unordered_map<Index, Name>& fieldNames);
std::ostream& print(const Array& array);
};
@@ -1762,9 +1763,9 @@ std::ostream& TypePrinter::print(HeapType type) {
}
}
- os << "(type ";
- printHeapTypeName(type);
- os << " ";
+ auto names = generator(type);
+
+ os << "(type $" << names.name << ' ';
if (isTemp(type)) {
os << "(; temp ;) ";
@@ -1787,7 +1788,7 @@ std::ostream& TypePrinter::print(HeapType type) {
if (type.isSignature()) {
print(type.getSignature());
} else if (type.isStruct()) {
- print(type.getStruct());
+ print(type.getStruct(), names.fieldNames);
} else if (type.isArray()) {
print(type.getArray());
} else {
@@ -1854,19 +1855,24 @@ std::ostream& TypePrinter::print(const Signature& sig) {
return os << ')';
}
-std::ostream& TypePrinter::print(const Struct& struct_) {
+std::ostream&
+TypePrinter::print(const Struct& struct_,
+ const std::unordered_map<Index, Name>& fieldNames) {
os << "(struct";
- if (struct_.fields.size()) {
- os << " (field";
+ for (Index i = 0; i < struct_.fields.size(); ++i) {
+ // TODO: move this to the function for printing fields.
+ os << " (field ";
+ if (auto it = fieldNames.find(i); it != fieldNames.end()) {
+ os << '$' << it->second << ' ';
+ }
+ print(struct_.fields[i]);
+ os << ')';
}
- for (const Field& field : struct_.fields) {
+ // TODO: Remove this extra space kept to minimize test diffs.
+ if (struct_.fields.size() == 0) {
os << ' ';
- print(field);
}
- if (struct_.fields.size()) {
- os << ')';
- }
- return os << ')';
+ return os << ")";
}
std::ostream& TypePrinter::print(const Array& array) {