summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
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) {