diff options
author | Alon Zakai <alonzakai@gmail.com> | 2016-02-17 12:44:14 -0800 |
---|---|---|
committer | Alon Zakai <alonzakai@gmail.com> | 2016-02-17 14:25:01 -0800 |
commit | dbb5f32bafcf3c8f51eefb95e6c298ce6b9ac8cc (patch) | |
tree | 88a9617fb2ad21c329043f1f4efd72a0abbb29dc | |
parent | 252962faba97b68684a470447c7202cf27ad8c05 (diff) | |
download | binaryen-dbb5f32bafcf3c8f51eefb95e6c298ce6b9ac8cc.tar.gz binaryen-dbb5f32bafcf3c8f51eefb95e6c298ce6b9ac8cc.tar.bz2 binaryen-dbb5f32bafcf3c8f51eefb95e6c298ce6b9ac8cc.zip |
move printing to a pass
-rw-r--r-- | CMakeLists.txt | 6 | ||||
-rw-r--r-- | src/asm2wasm-main.cpp | 2 | ||||
-rw-r--r-- | src/binaryen-shell.cpp | 6 | ||||
-rw-r--r-- | src/pass.cpp | 11 | ||||
-rw-r--r-- | src/pass.h | 34 | ||||
-rw-r--r-- | src/passes/Print.cpp | 482 | ||||
-rw-r--r-- | src/s2wasm-main.cpp | 3 | ||||
-rw-r--r-- | src/s2wasm.h | 3 | ||||
-rw-r--r-- | src/support/file.h | 4 | ||||
-rw-r--r-- | src/wasm-dis.cpp | 4 | ||||
-rw-r--r-- | src/wasm.h | 511 |
11 files changed, 536 insertions, 530 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index c7f4858c6..e3ec72c6e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -80,6 +80,7 @@ SET(binaryen-shell_SOURCES src/passes/MergeBlocks.cpp src/passes/NameManager.cpp src/passes/PostEmscripten.cpp + src/passes/Print.cpp src/passes/RemoveImports.cpp src/passes/RemoveUnusedBrs.cpp src/passes/RemoveUnusedNames.cpp @@ -98,6 +99,7 @@ SET(asm2wasm_SOURCES src/pass.cpp src/passes/MergeBlocks.cpp src/passes/PostEmscripten.cpp + src/passes/Print.cpp src/passes/RemoveUnusedBrs.cpp src/passes/RemoveUnusedNames.cpp src/passes/SimplifyLocals.cpp @@ -126,6 +128,8 @@ SET_PROPERTY(TARGET wasm2asm PROPERTY CXX_STANDARD_REQUIRED ON) INSTALL(TARGETS wasm2asm DESTINATION bin) SET(s2wasm_SOURCES + src/pass.cpp + src/passes/Print.cpp src/s2wasm-main.cpp ) ADD_EXECUTABLE(s2wasm @@ -146,6 +150,8 @@ SET_PROPERTY(TARGET wasm-as PROPERTY CXX_STANDARD_REQUIRED ON) INSTALL(TARGETS wasm-as DESTINATION bin) SET(wasm_dis_SOURCES + src/pass.cpp + src/passes/Print.cpp src/wasm-dis.cpp ) ADD_EXECUTABLE(wasm-dis diff --git a/src/asm2wasm-main.cpp b/src/asm2wasm-main.cpp index 1fd1273c0..3c1377c08 100644 --- a/src/asm2wasm-main.cpp +++ b/src/asm2wasm-main.cpp @@ -76,7 +76,7 @@ int main(int argc, const char *argv[]) { if (options.debug) std::cerr << "printing..." << std::endl; Output output(options.extra["output"], options.debug); - output << wasm; + printWasm(&wasm, output.getStream()); if (mappedGlobals) { if (options.debug) diff --git a/src/binaryen-shell.cpp b/src/binaryen-shell.cpp index d450e4db7..1b0bf8f78 100644 --- a/src/binaryen-shell.cpp +++ b/src/binaryen-shell.cpp @@ -281,7 +281,7 @@ static void run_asserts(size_t* i, bool* checked, AllocatingModule* wasm, Colors::bold(std::cout); std::cerr << "printing in module invalidity test:\n"; Colors::normal(std::cout); - std::cout << wasm; + printWasm(&wasm, std::cout); } bool invalid = false; std::unique_ptr<SExpressionWasmBuilder> builder; @@ -411,7 +411,7 @@ int main(int argc, const char* argv[]) { Colors::bold(std::cout); std::cerr << "printing before:\n"; Colors::normal(std::cout); - std::cout << wasm; + printWasm(&wasm, std::cout); } MixedArena moreModuleAllocations; @@ -429,7 +429,7 @@ int main(int argc, const char* argv[]) { Colors::bold(std::cout); std::cerr << "printing after:\n"; Colors::normal(std::cout); - std::cout << wasm; + printWasm(&wasm, std::cout); } run_asserts(&i, &checked, &wasm, &root, &builder, print_before, print_after, entry); diff --git a/src/pass.cpp b/src/pass.cpp index 1d3c2d67d..432979413 100644 --- a/src/pass.cpp +++ b/src/pass.cpp @@ -53,17 +53,6 @@ std::string PassRegistry::getPassDescription(std::string name) { // PassRunner -void PassRunner::add(std::string passName) { - auto pass = PassRegistry::get()->createPass(passName); - assert(pass); - passes.push_back(pass); -} - -template<class P> -void PassRunner::add() { - passes.push_back(new P()); -} - void PassRunner::run(Module* module) { for (auto pass : passes) { currPass = pass; diff --git a/src/pass.h b/src/pass.h index a95be9850..250ba59b2 100644 --- a/src/pass.h +++ b/src/pass.h @@ -71,10 +71,21 @@ struct PassRunner { PassRunner(MixedArena* allocator) : allocator(allocator) {} - void add(std::string passName); + void add(std::string passName) { + auto pass = PassRegistry::get()->createPass(passName); + assert(pass); + passes.push_back(pass); + } template<class P> - void add(); + void add() { + passes.push_back(new P()); + } + + template<class P, class Arg> + void add(Arg& arg){ + passes.push_back(new P(arg)); + } void run(Module* module); @@ -143,6 +154,25 @@ private: size_t counter = 0; }; +// Prints out a module +class Printer : public Pass { + std::ostream& o; + +public: + Printer() : o(std::cout) {} + Printer(std::ostream& o) : o(o) {} + + void run(PassRunner* runner, Module* module) override; +}; + +// Standard pass utilities + +inline void printWasm(Module* module, std::ostream& o) { + PassRunner passRunner(nullptr); + passRunner.add<Printer>(o); + passRunner.run(module); +} + } // namespace wasm #endif // wasm_pass_h diff --git a/src/passes/Print.cpp b/src/passes/Print.cpp new file mode 100644 index 000000000..14cb929c5 --- /dev/null +++ b/src/passes/Print.cpp @@ -0,0 +1,482 @@ +/* + * Copyright 2015 WebAssembly Community Group participants + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// +// Print out text in s-expression format +// + +#include <wasm.h> +#include <pass.h> + +namespace wasm { + +struct PrintSExpression : public WasmVisitor<PrintSExpression, void> { + std::ostream& o; + unsigned indent; + + PrintSExpression(std::ostream& o) : o(o), indent(0) {} + + void printFullLine(Expression *expression) { + doIndent(o, indent); + visit(expression); + o << '\n'; + } + + void visitBlock(Block *curr) { + printOpening(o, "block"); + if (curr->name.is()) { + o << ' ' << curr->name; + } + incIndent(o, indent); + for (auto expression : curr->list) { + printFullLine(expression); + } + decIndent(o, indent); + } + void visitIf(If *curr) { + printOpening(o, curr->ifFalse ? "if_else" : "if"); + incIndent(o, indent); + printFullLine(curr->condition); + printFullLine(curr->ifTrue); + if (curr->ifFalse) printFullLine(curr->ifFalse); + decIndent(o, indent); + } + void visitLoop(Loop *curr) { + printOpening(o, "loop"); + if (curr->out.is()) { + o << ' ' << curr->out; + assert(curr->in.is()); // if just one is printed, it must be the in + } + if (curr->in.is()) { + o << ' ' << curr->in; + } + incIndent(o, indent); + auto block = curr->body->dyn_cast<Block>(); + if (block && block->name.isNull()) { + // wasm spec has loops containing children directly, while our ast + // has a single child for simplicity. print out the optimal form. + for (auto expression : block->list) { + printFullLine(expression); + } + } else { + printFullLine(curr->body); + } + decIndent(o, indent); + } + void visitBreak(Break *curr) { + if (curr->condition) { + printOpening(o, "br_if ") << curr->name; + incIndent(o, indent); + } else { + printOpening(o, "br ") << curr->name; + if (!curr->value || curr->value->is<Nop>()) { + // avoid a new line just for the parens + o << ")"; + return; + } + incIndent(o, indent); + } + if (curr->value && !curr->value->is<Nop>()) printFullLine(curr->value); + if (curr->condition) { + printFullLine(curr->condition); + } + decIndent(o, indent); + } + void visitSwitch(Switch *curr) { + printOpening(o, "tableswitch "); + if (curr->name.is()) o << curr->name; + incIndent(o, indent); + printFullLine(curr->value); + doIndent(o, indent) << "(table"; + std::set<Name> caseNames; + for (auto& c : curr->cases) { + caseNames.insert(c.name); + } + for (auto& t : curr->targets) { + o << " (" << (caseNames.count(t) == 0 ? "br" : "case") << " " << (t.is() ? t : curr->default_) << ")"; + } + o << ")"; + if (curr->default_.is()) o << " (" << (caseNames.count(curr->default_) == 0 ? "br" : "case") << " " << curr->default_ << ")"; + o << "\n"; + for (auto& c : curr->cases) { + doIndent(o, indent); + printMinorOpening(o, "case ") << c.name; + incIndent(o, indent); + printFullLine(c.body); + decIndent(o, indent) << '\n'; + } + decIndent(o, indent); + } + + void printCallBody(Call* curr) { + o << curr->target; + if (curr->operands.size() > 0) { + incIndent(o, indent); + for (auto operand : curr->operands) { + printFullLine(operand); + } + decIndent(o, indent); + } else { + o << ')'; + } + } + + void visitCall(Call *curr) { + printOpening(o, "call "); + printCallBody(curr); + } + void visitCallImport(CallImport *curr) { + printOpening(o, "call_import "); + printCallBody(curr); + } + void visitCallIndirect(CallIndirect *curr) { + printOpening(o, "call_indirect ") << curr->fullType->name; + incIndent(o, indent); + printFullLine(curr->target); + for (auto operand : curr->operands) { + printFullLine(operand); + } + decIndent(o, indent); + } + void visitGetLocal(GetLocal *curr) { + printOpening(o, "get_local ") << curr->name << ')'; + } + void visitSetLocal(SetLocal *curr) { + printOpening(o, "set_local ") << curr->name; + incIndent(o, indent); + printFullLine(curr->value); + decIndent(o, indent); + } + void visitLoad(Load *curr) { + o << '('; + prepareColor(o) << printWasmType(curr->type) << ".load"; + if (curr->bytes < 4 || (curr->type == i64 && curr->bytes < 8)) { + if (curr->bytes == 1) { + o << '8'; + } else if (curr->bytes == 2) { + o << "16"; + } else if (curr->bytes == 4) { + o << "32"; + } else { + abort(); + } + o << (curr->signed_ ? "_s" : "_u"); + } + restoreNormalColor(o); + if (curr->offset) { + o << " offset=" << curr->offset; + } + if (curr->align) { + o << " align=" << curr->align; + } + incIndent(o, indent); + printFullLine(curr->ptr); + decIndent(o, indent); + } + void visitStore(Store *curr) { + o << '('; + prepareColor(o) << printWasmType(curr->type) << ".store"; + if (curr->bytes < 4 || (curr->type == i64 && curr->bytes < 8)) { + if (curr->bytes == 1) { + o << '8'; + } else if (curr->bytes == 2) { + o << "16"; + } else if (curr->bytes == 4) { + o << "32"; + } else { + abort(); + } + } + restoreNormalColor(o); + if (curr->offset) { + o << " offset=" << curr->offset; + } + if (curr->align) { + o << " align=" << curr->align; + } + incIndent(o, indent); + printFullLine(curr->ptr); + printFullLine(curr->value); + decIndent(o, indent); + } + void visitConst(Const *curr) { + o << curr->value; + } + void visitUnary(Unary *curr) { + o << '('; + prepareColor(o) << printWasmType(curr->type) << '.'; + switch (curr->op) { + case Clz: o << "clz"; break; + case Ctz: o << "ctz"; break; + case Popcnt: o << "popcnt"; break; + case Neg: o << "neg"; break; + case Abs: o << "abs"; break; + case Ceil: o << "ceil"; break; + case Floor: o << "floor"; break; + case Trunc: o << "trunc"; break; + case Nearest: o << "nearest"; break; + case Sqrt: o << "sqrt"; break; + case ExtendSInt32: o << "extend_s/i32"; break; + case ExtendUInt32: o << "extend_u/i32"; break; + case WrapInt64: o << "wrap/i64"; break; + case TruncSFloat32: o << "trunc_s/f32"; break; + case TruncUFloat32: o << "trunc_u/f32"; break; + case TruncSFloat64: o << "trunc_s/f64"; break; + case TruncUFloat64: o << "trunc_u/f64"; break; + case ReinterpretFloat: o << "reinterpret/" << (curr->type == i64 ? "f64" : "f32"); break; + case ConvertUInt32: o << "convert_u/i32"; break; + case ConvertSInt32: o << "convert_s/i32"; break; + case ConvertUInt64: o << "convert_u/i64"; break; + case ConvertSInt64: o << "convert_s/i64"; break; + case PromoteFloat32: o << "promote/f32"; break; + case DemoteFloat64: o << "demote/f64"; break; + case ReinterpretInt: o << "reinterpret/" << (curr->type == f64 ? "i64" : "i32"); break; + default: abort(); + } + incIndent(o, indent); + printFullLine(curr->value); + decIndent(o, indent); + } + void visitBinary(Binary *curr) { + o << '('; + prepareColor(o) << printWasmType(curr->isRelational() ? curr->left->type : curr->type) << '.'; + switch (curr->op) { + case Add: o << "add"; break; + case Sub: o << "sub"; break; + case Mul: o << "mul"; break; + case DivS: o << "div_s"; break; + case DivU: o << "div_u"; break; + case RemS: o << "rem_s"; break; + case RemU: o << "rem_u"; break; + case And: o << "and"; break; + case Or: o << "or"; break; + case Xor: o << "xor"; break; + case Shl: o << "shl"; break; + case ShrU: o << "shr_u"; break; + case ShrS: o << "shr_s"; break; + case Div: o << "div"; break; + case CopySign: o << "copysign"; break; + case Min: o << "min"; break; + case Max: o << "max"; break; + case Eq: o << "eq"; break; + case Ne: o << "ne"; break; + case LtS: o << "lt_s"; break; + case LtU: o << "lt_u"; break; + case LeS: o << "le_s"; break; + case LeU: o << "le_u"; break; + case GtS: o << "gt_s"; break; + case GtU: o << "gt_u"; break; + case GeS: o << "ge_s"; break; + case GeU: o << "ge_u"; break; + case Lt: o << "lt"; break; + case Le: o << "le"; break; + case Gt: o << "gt"; break; + case Ge: o << "ge"; break; + default: abort(); + } + restoreNormalColor(o); + incIndent(o, indent); + printFullLine(curr->left); + printFullLine(curr->right); + decIndent(o, indent); + } + void visitSelect(Select *curr) { + o << '('; + prepareColor(o) << printWasmType(curr->type) << ".select"; + incIndent(o, indent); + printFullLine(curr->ifTrue); + printFullLine(curr->ifFalse); + printFullLine(curr->condition); + decIndent(o, indent); + } + void visitReturn(Return *curr) { + printOpening(o, "return"); + if (!curr->value || curr->value->is<Nop>()) { + // avoid a new line just for the parens + o << ")"; + return; + } + incIndent(o, indent); + printFullLine(curr->value); + decIndent(o, indent); + } + void visitHost(Host *curr) { + switch (curr->op) { + case PageSize: printOpening(o, "pagesize") << ')'; break; + case MemorySize: printOpening(o, "memory_size") << ')'; break; + case GrowMemory: { + printOpening(o, "grow_memory"); + incIndent(o, indent); + printFullLine(curr->operands[0]); + decIndent(o, indent); + break; + } + case HasFeature: printOpening(o, "hasfeature ") << curr->nameOperand << ')'; break; + default: abort(); + } + } + void visitNop(Nop *curr) { + printMinorOpening(o, "nop") << ')'; + } + void visitUnreachable(Unreachable *curr) { + printMinorOpening(o, "unreachable") << ')'; + } + // Module-level visitors + void visitFunctionType(FunctionType *curr, bool full=false) { + if (full) { + printOpening(o, "type") << ' ' << curr->name << " (func"; + } + if (curr->params.size() > 0) { + o << ' '; + printMinorOpening(o, "param"); + for (auto& param : curr->params) { + o << ' ' << printWasmType(param); + } + o << ')'; + } + if (curr->result != none) { + o << ' '; + printMinorOpening(o, "result ") << printWasmType(curr->result) << ')'; + } + if (full) { + o << "))";; + } + } + void visitImport(Import *curr) { + printOpening(o, "import ") << curr->name << ' '; + printText(o, curr->module.str) << ' '; + printText(o, curr->base.str); + if (curr->type) visitFunctionType(curr->type); + o << ')'; + } + void visitExport(Export *curr) { + printOpening(o, "export "); + printText(o, curr->name.str) << ' ' << curr->value << ')'; + } + void visitFunction(Function *curr) { + printOpening(o, "func ", true) << curr->name; + if (curr->type.is()) { + o << " (type " << curr->type << ')'; + } + if (curr->params.size() > 0) { + for (auto& param : curr->params) { + o << ' '; + printMinorOpening(o, "param ") << param.name << ' ' << printWasmType(param.type) << ")"; + } + } + if (curr->result != none) { + o << ' '; + printMinorOpening(o, "result ") << printWasmType(curr->result) << ")"; + } + incIndent(o, indent); + for (auto& local : curr->locals) { + doIndent(o, indent); + printMinorOpening(o, "local ") << local.name << ' ' << printWasmType(local.type) << ")\n"; + } + // 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 (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); + } + decIndent(o, indent); + } + void visitTable(Table *curr) { + printOpening(o, "table"); + for (auto name : curr->names) { + o << ' ' << name; + } + o << ')'; + } + void visitModule(Module *curr) { + printOpening(o, "module", true); + incIndent(o, indent); + doIndent(o, indent); + printOpening(o, "memory") << " " << curr->memory.initial; + if (curr->memory.max && curr->memory.max != (uint32_t)-1) o << " " << curr->memory.max; + for (auto segment : curr->memory.segments) { + o << "\n (segment " << segment.offset << " \""; + for (size_t i = 0; i < segment.size; i++) { + unsigned char c = segment.data[i]; + switch (c) { + case '\n': o << "\\n"; break; + case '\r': o << "\\0d"; break; + case '\t': o << "\\t"; break; + case '\f': o << "\\0c"; break; + case '\b': o << "\\08"; break; + case '\\': o << "\\\\"; break; + case '"' : o << "\\\""; break; + case '\'' : o << "\\'"; break; + default: { + if (c >= 32 && c < 127) { + o << c; + } else { + o << std::hex << '\\' << (c/16) << (c%16) << std::dec; + } + } + } + } + o << "\")"; + } + o << (curr->memory.segments.size() > 0 ? "\n " : "") << ")\n"; + if (curr->start.is()) { + doIndent(o, indent); + printOpening(o, "start") << " " << curr->start << ")\n"; + } + for (auto& child : curr->functionTypes) { + doIndent(o, indent); + visitFunctionType(child, true); + o << '\n'; + } + for (auto& child : curr->imports) { + doIndent(o, indent); + visitImport(child); + o << '\n'; + } + for (auto& child : curr->exports) { + doIndent(o, indent); + visitExport(child); + o << '\n'; + } + if (curr->table.names.size() > 0) { + doIndent(o, indent); + visitTable(&curr->table); + o << '\n'; + } + for (auto& child : curr->functions) { + doIndent(o, indent); + visitFunction(child); + o << '\n'; + } + decIndent(o, indent); + o << '\n'; + } +}; + +// Main entry point. Eventually this will direct printing to one of various options. + +void Printer::run(PassRunner* runner, Module* module) { + PrintSExpression print(o); + print.visitModule(module); +} + +static RegisterPass<Printer> registerPass("print", "print in s-expression format"); + +} // namespace wasm diff --git a/src/s2wasm-main.cpp b/src/s2wasm-main.cpp index 03321a608..377d44c52 100644 --- a/src/s2wasm-main.cpp +++ b/src/s2wasm-main.cpp @@ -84,7 +84,8 @@ int main(int argc, const char *argv[]) { if (options.debug) std::cerr << "Printing..." << std::endl; Output output(options.extra["output"], options.debug); - output << wasm << meta.str() << std::endl; + printWasm(&wasm, output.getStream()); + output << meta.str() << std::endl; if (options.debug) std::cerr << "Done." << std::endl; } diff --git a/src/s2wasm.h b/src/s2wasm.h index 5557cc2ff..9c875ea9e 100644 --- a/src/s2wasm.h +++ b/src/s2wasm.h @@ -25,6 +25,7 @@ #include "wasm.h" #include "parsing.h" +#include "pass.h" #include "asm_v_wasm.h" namespace wasm { @@ -1262,7 +1263,7 @@ public: // extra emscripten processing void emscriptenGlue(std::ostream& o) { if (debug) { - std::cerr << wasm << '\n'; + printWasm(&wasm, std::cerr); } wasm.removeImport(EMSCRIPTEN_ASM_CONST); // we create _sig versions diff --git a/src/support/file.h b/src/support/file.h index 5e464d6c1..47f7ececb 100644 --- a/src/support/file.h +++ b/src/support/file.h @@ -44,6 +44,10 @@ class Output { return out << v; } + std::ostream& getStream() { + return out; + } + private: Output() = delete; Output(const Output &) = delete; diff --git a/src/wasm-dis.cpp b/src/wasm-dis.cpp index 7fb3ed2d5..c7ebf8861 100644 --- a/src/wasm-dis.cpp +++ b/src/wasm-dis.cpp @@ -18,6 +18,7 @@ // wasm2asm console tool // +#include "pass.h" #include "support/colors.h" #include "support/command-line.h" #include "support/file.h" @@ -49,7 +50,8 @@ int main(int argc, const char *argv[]) { if (options.debug) std::cerr << "Printing..." << std::endl; Output output(options.extra["output"], options.debug); - output << wasm << std::endl; + printWasm(&wasm, output.getStream()); + output << '\n'; if (options.debug) std::cerr << "Done." << std::endl; } diff --git a/src/wasm.h b/src/wasm.h index 516ea9630..387b39c02 100644 --- a/src/wasm.h +++ b/src/wasm.h @@ -22,12 +22,11 @@ // it easy to not just inspect but also to process. For example, some // things that this enables are: // -// * Pretty-printing: Implemented in this file (printing can help -// understand the data structures here). // * Interpreting: See wasm-interpreter.h. // * Optimizing: See asm2wasm.h, which performs some optimizations // after code generation. // * Validation: See wasm-validator.h. +// * Pretty-printing: See Print.cpp. // // @@ -756,18 +755,6 @@ public: assert(_id == T()._id); return (T*)this; } - - inline std::ostream& print(std::ostream &o, unsigned indent); // avoid virtual here, for performance - - friend std::ostream& operator<<(std::ostream &o, Expression* expression) { - return expression->print(o, 0); - } - - static std::ostream& printFullLine(std::ostream &o, unsigned indent, Expression *expression) { - doIndent(o, indent); - expression->print(o, indent); - return o << '\n'; - } }; inline const char *getExpressionName(Expression *curr) { @@ -801,10 +788,6 @@ typedef std::vector<Expression*> ExpressionList; // TODO: optimize? class Nop : public Expression { public: Nop() : Expression(NopId) {} - - std::ostream& doPrint(std::ostream &o, unsigned indent) { - return printMinorOpening(o, "nop") << ')'; - } }; class Block : public Expression { @@ -815,18 +798,6 @@ public: Name name; ExpressionList list; - - std::ostream& doPrint(std::ostream &o, unsigned indent) { - printOpening(o, "block"); - if (name.is()) { - o << ' ' << name; - } - incIndent(o, indent); - for (auto expression : list) { - printFullLine(o, indent, expression); - } - return decIndent(o, indent); - } }; class If : public Expression { @@ -837,15 +808,6 @@ public: Expression *condition, *ifTrue, *ifFalse; - std::ostream& doPrint(std::ostream &o, unsigned indent) { - printOpening(o, ifFalse ? "if_else" : "if"); - incIndent(o, indent); - printFullLine(o, indent, condition); - printFullLine(o, indent, ifTrue); - if (ifFalse) printFullLine(o, indent, ifFalse); - return decIndent(o, indent); - } - void finalize() { if (condition) { type = getReachableWasmType(ifTrue->type, ifFalse->type); @@ -860,29 +822,6 @@ public: Name out, in; Expression *body; - std::ostream& doPrint(std::ostream &o, unsigned indent) { - printOpening(o, "loop"); - if (out.is()) { - o << ' ' << out; - assert(in.is()); // if just one is printed, it must be the in - } - if (in.is()) { - o << ' ' << in; - } - incIndent(o, indent); - auto block = body->dyn_cast<Block>(); - if (block && block->name.isNull()) { - // wasm spec has loops containing children directly, while our ast - // has a single child for simplicity. print out the optimal form. - for (auto expression : block->list) { - printFullLine(o, indent, expression); - } - } else { - printFullLine(o, indent, body); - } - return decIndent(o, indent); - } - void finalize() { type = body->type; // loop might have a type, if the body ends in something that does not break } @@ -897,26 +836,6 @@ public: Name name; Expression *value; Expression *condition; - - std::ostream& doPrint(std::ostream &o, unsigned indent) { - if (condition) { - printOpening(o, "br_if ") << name; - incIndent(o, indent); - } else { - printOpening(o, "br ") << name; - if (!value || value->is<Nop>()) { - // avoid a new line just for the parens - o << ")"; - return o; - } - incIndent(o, indent); - } - if (value && !value->is<Nop>()) printFullLine(o, indent, value); - if (condition) { - printFullLine(o, indent, condition); - } - return decIndent(o, indent); - } }; class Switch : public Expression { @@ -935,33 +854,6 @@ public: std::vector<Name> targets; Name default_; std::vector<Case> cases; - - std::ostream& doPrint(std::ostream &o, unsigned indent) { - printOpening(o, "tableswitch "); - if (name.is()) o << name; - incIndent(o, indent); - printFullLine(o, indent, value); - doIndent(o, indent) << "(table"; - std::set<Name> caseNames; - for (auto& c : cases) { - caseNames.insert(c.name); - } - for (auto& t : targets) { - o << " (" << (caseNames.count(t) == 0 ? "br" : "case") << " " << (t.is() ? t : default_) << ")"; - } - o << ")"; - if (default_.is()) o << " (" << (caseNames.count(default_) == 0 ? "br" : "case") << " " << default_ << ")"; - o << "\n"; - for (auto& c : cases) { - doIndent(o, indent); - printMinorOpening(o, "case ") << c.name; - incIndent(o, indent); - printFullLine(o, indent, c.body); - decIndent(o, indent) << '\n'; - } - return decIndent(o, indent); - } - }; class CallBase : public Expression { @@ -976,25 +868,6 @@ public: Call() : CallBase(CallId) {} Name target; - - std::ostream& printBody(std::ostream &o, unsigned indent) { - o << target; - if (operands.size() > 0) { - incIndent(o, indent); - for (auto operand : operands) { - printFullLine(o, indent, operand); - } - decIndent(o, indent); - } else { - o << ')'; - } - return o; - } - - std::ostream& doPrint(std::ostream &o, unsigned indent) { - printOpening(o, "call "); - return printBody(o, indent); - } }; class CallImport : public Call { @@ -1002,11 +875,6 @@ public: CallImport() { _id = CallImportId; } - - std::ostream& doPrint(std::ostream &o, unsigned indent) { - printOpening(o, "call_import "); - return printBody(o, indent); - } }; class FunctionType { @@ -1017,28 +885,6 @@ public: FunctionType() : result(none) {} - std::ostream& print(std::ostream &o, unsigned indent, bool full=false) { - if (full) { - printOpening(o, "type") << ' ' << name << " (func"; - } - if (params.size() > 0) { - o << ' '; - printMinorOpening(o, "param"); - for (auto& param : params) { - o << ' ' << printWasmType(param); - } - o << ')'; - } - if (result != none) { - o << ' '; - printMinorOpening(o, "result ") << printWasmType(result) << ')'; - } - if (full) { - o << "))";; - } - return o; - } - bool operator==(FunctionType& b) { if (name != b.name) return false; // XXX if (result != b.result) return false; @@ -1059,16 +905,6 @@ public: FunctionType *fullType; Expression *target; - - std::ostream& doPrint(std::ostream &o, unsigned indent) { - printOpening(o, "call_indirect ") << fullType->name; - incIndent(o, indent); - printFullLine(o, indent, target); - for (auto operand : operands) { - printFullLine(o, indent, operand); - } - return decIndent(o, indent); - } }; class GetLocal : public Expression { @@ -1076,10 +912,6 @@ public: GetLocal() : Expression(GetLocalId) {} Name name; - - std::ostream& doPrint(std::ostream &o, unsigned indent) { - return printOpening(o, "get_local ") << name << ')'; - } }; class SetLocal : public Expression { @@ -1088,13 +920,6 @@ public: Name name; Expression *value; - - std::ostream& doPrint(std::ostream &o, unsigned indent) { - printOpening(o, "set_local ") << name; - incIndent(o, indent); - printFullLine(o, indent, value); - return decIndent(o, indent); - } }; class Load : public Expression { @@ -1106,33 +931,6 @@ public: uint32_t offset; uint32_t align; Expression *ptr; - - std::ostream& doPrint(std::ostream &o, unsigned indent) { - o << '('; - prepareColor(o) << printWasmType(type) << ".load"; - if (bytes < 4 || (type == i64 && bytes < 8)) { - if (bytes == 1) { - o << '8'; - } else if (bytes == 2) { - o << "16"; - } else if (bytes == 4) { - o << "32"; - } else { - abort(); - } - o << (signed_ ? "_s" : "_u"); - } - restoreNormalColor(o); - if (offset) { - o << " offset=" << offset; - } - if (align) { - o << " align=" << align; - } - incIndent(o, indent); - printFullLine(o, indent, ptr); - return decIndent(o, indent); - } }; class Store : public Expression { @@ -1143,33 +941,6 @@ public: uint32_t offset; unsigned align; Expression *ptr, *value; - - std::ostream& doPrint(std::ostream &o, unsigned indent) { - o << '('; - prepareColor(o) << printWasmType(type) << ".store"; - if (bytes < 4 || (type == i64 && bytes < 8)) { - if (bytes == 1) { - o << '8'; - } else if (bytes == 2) { - o << "16"; - } else if (bytes == 4) { - o << "32"; - } else { - abort(); - } - } - restoreNormalColor(o); - if (offset) { - o << " offset=" << offset; - } - if (align) { - o << " align=" << align; - } - incIndent(o, indent); - printFullLine(o, indent, ptr); - printFullLine(o, indent, value); - return decIndent(o, indent); - } }; class Const : public Expression { @@ -1183,10 +954,6 @@ public: type = value.type; return this; } - - std::ostream& doPrint(std::ostream &o, unsigned indent) { - return o << value; - } }; class Unary : public Expression { @@ -1195,42 +962,6 @@ public: UnaryOp op; Expression *value; - - std::ostream& doPrint(std::ostream &o, unsigned indent) { - o << '('; - prepareColor(o) << printWasmType(type) << '.'; - switch (op) { - case Clz: o << "clz"; break; - case Ctz: o << "ctz"; break; - case Popcnt: o << "popcnt"; break; - case Neg: o << "neg"; break; - case Abs: o << "abs"; break; - case Ceil: o << "ceil"; break; - case Floor: o << "floor"; break; - case Trunc: o << "trunc"; break; - case Nearest: o << "nearest"; break; - case Sqrt: o << "sqrt"; break; - case ExtendSInt32: o << "extend_s/i32"; break; - case ExtendUInt32: o << "extend_u/i32"; break; - case WrapInt64: o << "wrap/i64"; break; - case TruncSFloat32: o << "trunc_s/f32"; break; - case TruncUFloat32: o << "trunc_u/f32"; break; - case TruncSFloat64: o << "trunc_s/f64"; break; - case TruncUFloat64: o << "trunc_u/f64"; break; - case ReinterpretFloat: o << "reinterpret/" << (type == i64 ? "f64" : "f32"); break; - case ConvertUInt32: o << "convert_u/i32"; break; - case ConvertSInt32: o << "convert_s/i32"; break; - case ConvertUInt64: o << "convert_u/i64"; break; - case ConvertSInt64: o << "convert_s/i64"; break; - case PromoteFloat32: o << "promote/f32"; break; - case DemoteFloat64: o << "demote/f64"; break; - case ReinterpretInt: o << "reinterpret/" << (type == f64 ? "i64" : "i32"); break; - default: abort(); - } - incIndent(o, indent); - printFullLine(o, indent, value); - return decIndent(o, indent); - } }; class Binary : public Expression { @@ -1240,50 +971,6 @@ public: BinaryOp op; Expression *left, *right; - std::ostream& doPrint(std::ostream &o, unsigned indent) { - o << '('; - prepareColor(o) << printWasmType(isRelational() ? left->type : type) << '.'; - switch (op) { - case Add: o << "add"; break; - case Sub: o << "sub"; break; - case Mul: o << "mul"; break; - case DivS: o << "div_s"; break; - case DivU: o << "div_u"; break; - case RemS: o << "rem_s"; break; - case RemU: o << "rem_u"; break; - case And: o << "and"; break; - case Or: o << "or"; break; - case Xor: o << "xor"; break; - case Shl: o << "shl"; break; - case ShrU: o << "shr_u"; break; - case ShrS: o << "shr_s"; break; - case Div: o << "div"; break; - case CopySign: o << "copysign"; break; - case Min: o << "min"; break; - case Max: o << "max"; break; - case Eq: o << "eq"; break; - case Ne: o << "ne"; break; - case LtS: o << "lt_s"; break; - case LtU: o << "lt_u"; break; - case LeS: o << "le_s"; break; - case LeU: o << "le_u"; break; - case GtS: o << "gt_s"; break; - case GtU: o << "gt_u"; break; - case GeS: o << "ge_s"; break; - case GeU: o << "ge_u"; break; - case Lt: o << "lt"; break; - case Le: o << "le"; break; - case Gt: o << "gt"; break; - case Ge: o << "ge"; break; - default: abort(); - } - restoreNormalColor(o); - incIndent(o, indent); - printFullLine(o, indent, left); - printFullLine(o, indent, right); - return decIndent(o, indent); - } - // the type is always the type of the operands, // except for relationals @@ -1305,16 +992,6 @@ public: Expression *ifTrue, *ifFalse, *condition; - std::ostream& doPrint(std::ostream &o, unsigned indent) { - o << '('; - prepareColor(o) << printWasmType(type) << ".select"; - incIndent(o, indent); - printFullLine(o, indent, ifTrue); - printFullLine(o, indent, ifFalse); - printFullLine(o, indent, condition); - return decIndent(o, indent); - } - void finalize() { type = getReachableWasmType(ifTrue->type, ifFalse->type); } @@ -1327,18 +1004,6 @@ public: Return() : Expression(ReturnId), value(nullptr) { type = unreachable; } - - std::ostream& doPrint(std::ostream &o, unsigned indent) { - printOpening(o, "return"); - if (!value || value->is<Nop>()) { - // avoid a new line just for the parens - o << ")"; - return o; - } - incIndent(o, indent); - printFullLine(o, indent, value); - return decIndent(o, indent); - } }; class Host : public Expression { @@ -1349,23 +1014,6 @@ public: Name nameOperand; ExpressionList operands; - std::ostream& doPrint(std::ostream &o, unsigned indent) { - switch (op) { - case PageSize: printOpening(o, "pagesize") << ')'; break; - case MemorySize: printOpening(o, "memory_size") << ')'; break; - case GrowMemory: { - printOpening(o, "grow_memory"); - incIndent(o, indent); - printFullLine(o, indent, operands[0]); - decIndent(o, indent); - break; - } - case HasFeature: printOpening(o, "hasfeature ") << nameOperand << ')'; break; - default: abort(); - } - return o; - } - void finalize() { switch (op) { case PageSize: case MemorySize: case HasFeature: { @@ -1386,10 +1034,6 @@ public: Unreachable() : Expression(UnreachableId) { type = unreachable; } - - std::ostream& doPrint(std::ostream &o, unsigned indent) { - return printMinorOpening(o, "unreachable") << ')'; - } }; // Globals @@ -1411,39 +1055,6 @@ public: Expression *body; Function() : result(none) {} - - std::ostream& print(std::ostream &o, unsigned indent) { - printOpening(o, "func ", true) << name; - if (type.is()) { - o << " (type " << type << ')'; - } - if (params.size() > 0) { - for (auto& param : params) { - o << ' '; - printMinorOpening(o, "param ") << param.name << ' ' << printWasmType(param.type) << ")"; - } - } - if (result != none) { - o << ' '; - printMinorOpening(o, "result ") << printWasmType(result) << ")"; - } - incIndent(o, indent); - for (auto& local : locals) { - doIndent(o, indent); - printMinorOpening(o, "local ") << local.name << ' ' << printWasmType(local.type) << ")\n"; - } - // 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 (body->is<Block>() && body->cast<Block>()->name.isNull()) { - Block* block = body->cast<Block>(); - for (auto item : block->list) { - Expression::printFullLine(o, indent, item); - } - } else { - Expression::printFullLine(o, indent, body); - } - return decIndent(o, indent); - } }; class Import { @@ -1452,38 +1063,17 @@ public: FunctionType* type; Import() : type(nullptr) {} - - std::ostream& print(std::ostream &o, unsigned indent) { - printOpening(o, "import ") << name << ' '; - printText(o, module.str) << ' '; - printText(o, base.str); - if (type) type->print(o, indent); - return o << ')'; - } }; class Export { public: Name name; // exported name Name value; // internal name - - std::ostream& print(std::ostream &o, unsigned indent) { - printOpening(o, "export "); - return printText(o, name.str) << ' ' << value << ')'; - } }; class Table { public: std::vector<Name> names; - - std::ostream& print(std::ostream &o, unsigned indent) { - printOpening(o, "table"); - for (auto name : names) { - o << ' ' << name; - } - return o << ')'; - } }; class Memory { @@ -1575,71 +1165,6 @@ public: importsMap.erase(name); } - friend std::ostream& operator<<(std::ostream &o, Module module) { - unsigned indent = 0; - printOpening(o, "module", true); - incIndent(o, indent); - doIndent(o, indent); - printOpening(o, "memory") << " " << module.memory.initial; - if (module.memory.max && module.memory.max != (uint32_t)-1) o << " " << module.memory.max; - for (auto segment : module.memory.segments) { - o << "\n (segment " << segment.offset << " \""; - for (size_t i = 0; i < segment.size; i++) { - unsigned char c = segment.data[i]; - switch (c) { - case '\n': o << "\\n"; break; - case '\r': o << "\\0d"; break; - case '\t': o << "\\t"; break; - case '\f': o << "\\0c"; break; - case '\b': o << "\\08"; break; - case '\\': o << "\\\\"; break; - case '"' : o << "\\\""; break; - case '\'' : o << "\\'"; break; - default: { - if (c >= 32 && c < 127) { - o << c; - } else { - o << std::hex << '\\' << (c/16) << (c%16) << std::dec; - } - } - } - } - o << "\")"; - } - o << (module.memory.segments.size() > 0 ? "\n " : "") << ")\n"; - if (module.start.is()) { - doIndent(o, indent); - printOpening(o, "start") << " " << module.start << ")\n"; - } - for (auto& curr : module.functionTypes) { - doIndent(o, indent); - curr->print(o, indent, true); - o << '\n'; - } - for (auto& curr : module.imports) { - doIndent(o, indent); - curr->print(o, indent); - o << '\n'; - } - for (auto& curr : module.exports) { - doIndent(o, indent); - curr->print(o, indent); - o << '\n'; - } - if (module.table.names.size() > 0) { - doIndent(o, indent); - module.table.print(o, indent); - o << '\n'; - } - for (auto& curr : module.functions) { - doIndent(o, indent); - curr->print(o, indent); - o << '\n'; - } - decIndent(o, indent); - return o << '\n'; - } - private: size_t functionTypeIndex, importIndex, exportIndex, functionIndex; }; @@ -1726,40 +1251,6 @@ struct WasmVisitor { } }; -std::ostream& Expression::print(std::ostream &o, unsigned indent) { - struct ExpressionPrinter : public WasmVisitor<ExpressionPrinter, void> { - std::ostream &o; - unsigned indent; - - ExpressionPrinter(std::ostream &o, unsigned indent) : o(o), indent(indent) {} - - void visitBlock(Block *curr) { curr->doPrint(o, indent); } - void visitIf(If *curr) { curr->doPrint(o, indent); } - void visitLoop(Loop *curr) { curr->doPrint(o, indent); } - void visitBreak(Break *curr) { curr->doPrint(o, indent); } - void visitSwitch(Switch *curr) { curr->doPrint(o, indent); } - void visitCall(Call *curr) { curr->doPrint(o, indent); } - void visitCallImport(CallImport *curr) { curr->doPrint(o, indent); } - void visitCallIndirect(CallIndirect *curr) { curr->doPrint(o, indent); } - void visitGetLocal(GetLocal *curr) { curr->doPrint(o, indent); } - void visitSetLocal(SetLocal *curr) { curr->doPrint(o, indent); } - void visitLoad(Load *curr) { curr->doPrint(o, indent); } - void visitStore(Store *curr) { curr->doPrint(o, indent); } - void visitConst(Const *curr) { curr->doPrint(o, indent); } - void visitUnary(Unary *curr) { curr->doPrint(o, indent); } - void visitBinary(Binary *curr) { curr->doPrint(o, indent); } - void visitSelect(Select *curr) { curr->doPrint(o, indent); } - void visitReturn(Return *curr) { curr->doPrint(o, indent); } - void visitHost(Host *curr) { curr->doPrint(o, indent); } - void visitNop(Nop *curr) { curr->doPrint(o, indent); } - void visitUnreachable(Unreachable *curr) { curr->doPrint(o, indent); } - }; - - ExpressionPrinter(o, indent).visit(this); - - return o; -} - // // Base class for all WasmWalkers // |