summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/decompiler-ast.inl258
-rw-r--r--src/decompiler.cc433
-rw-r--r--src/ir-util.cc16
-rw-r--r--src/ir-util.h12
4 files changed, 436 insertions, 283 deletions
diff --git a/src/decompiler-ast.inl b/src/decompiler-ast.inl
new file mode 100644
index 00000000..146d6315
--- /dev/null
+++ b/src/decompiler-ast.inl
@@ -0,0 +1,258 @@
+/*
+ * Copyright 2016 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.
+ */
+
+#include <algorithm>
+#include <array>
+#include <cassert>
+#include <cinttypes>
+#include <cstdarg>
+#include <cstdio>
+#include <iterator>
+#include <map>
+#include <numeric>
+#include <set>
+#include <string>
+#include <vector>
+#include <sstream>
+
+#include "src/cast.h"
+#include "src/common.h"
+#include "src/expr-visitor.h"
+#include "src/ir.h"
+#include "src/ir-util.h"
+#include "src/literal.h"
+#include "src/generate-names.h"
+#include "src/stream.h"
+
+namespace wabt {
+
+enum class NodeType {
+ PushAll,
+ Pop,
+ Statements,
+ EndReturn,
+ Decl,
+ DeclInit,
+ Expr
+};
+
+// The AST we're going to convert the standard IR into.
+struct Node {
+ NodeType ntype;
+ ExprType etype; // Only if ntype == Expr.
+ const Expr* e;
+ std::vector<Node> children;
+ // Node specific annotations.
+ union {
+ LabelType lt; // br/br_if target.
+ };
+
+ Node()
+ : ntype(NodeType::Expr), etype(ExprType::Nop), e(nullptr),
+ lt(LabelType::First) {}
+ Node(NodeType ntype, ExprType etype, const Expr* e)
+ : ntype(ntype), etype(etype), e(e), lt(LabelType::First) {}
+
+ // This value should really never be copied, only moved.
+ Node(Node&& rhs) = default;
+ Node(const Node& rhs) = delete;
+ Node& operator=(Node&& rhs) = default;
+ Node& operator=(const Node& rhs) = delete;
+};
+
+struct AST {
+ AST(ModuleContext& mc, const Func *f) : mc(mc), f(f) {
+ if (f) {
+ mc.BeginFunc(*f);
+ for (Index i = 0; i < f->GetNumParams(); i++) {
+ auto name = IndexToAlphaName(i);
+ vars_defined.insert(name);
+ }
+ }
+ }
+
+ ~AST() {
+ if (f) mc.EndFunc();
+ }
+
+ void NewNode(NodeType ntype, ExprType etype, const Expr* e, Index nargs) {
+ assert(stack.size() >= nargs);
+ Node n { ntype, etype, e };
+ n.children.reserve(nargs);
+ std::move(stack.end() - nargs, stack.end(),
+ std::back_inserter(n.children));
+ stack.resize(stack.size() - nargs);
+ stack.push_back(std::move(n));
+ }
+
+ template<ExprType T> void PreDecl(const VarExpr<T>& ve) {
+ stack.emplace(stack.begin() + pre_decl_insertion_point++,
+ NodeType::Decl, ExprType::Nop, &ve);
+ }
+
+ template<ExprType T> void Get(const VarExpr<T>& ve, bool local) {
+ if (local && vars_defined.insert(ve.var.name()).second) {
+ // Use before def, may happen since locals are guaranteed 0.
+ PreDecl(ve);
+ }
+ NewNode(NodeType::Expr, T, &ve, 0);
+ }
+
+ template<ExprType T> void Set(const VarExpr<T>& ve, bool local) {
+ // Seen this var before?
+ if (local && vars_defined.insert(ve.var.name()).second) {
+ if (stack_depth == 1) {
+ // Top level, declare it here.
+ NewNode(NodeType::DeclInit, ExprType::Nop, &ve, 1);
+ return;
+ } else {
+ // Inside exp, better leave it as assignment exp and lift the decl out.
+ PreDecl(ve);
+ }
+ }
+ NewNode(NodeType::Expr, T, &ve, 1);
+ }
+
+ template<ExprType T> void Block(const BlockExprBase<T>& be, LabelType label) {
+ mc.BeginBlock(label, be.block);
+ Construct(be.block.exprs, be.block.decl.GetNumResults(), false);
+ mc.EndBlock();
+ NewNode(NodeType::Expr, T, &be, 1);
+ }
+
+ void Construct(const Expr& e) {
+ auto arity = mc.GetExprArity(e);
+ switch (e.type()) {
+ case ExprType::LocalGet: {
+ Get(*cast<LocalGetExpr>(&e), true);
+ return;
+ }
+ case ExprType::GlobalGet: {
+ Get(*cast<GlobalGetExpr>(&e), false);
+ return;
+ }
+ case ExprType::LocalSet: {
+ Set(*cast<LocalSetExpr>(&e), true);
+ return;
+ }
+ case ExprType::GlobalSet: {
+ Set(*cast<GlobalSetExpr>(&e), false);
+ return;
+ }
+ case ExprType::LocalTee: {
+ auto& lt = *cast<LocalTeeExpr>(&e);
+ Set(lt, true);
+ if (stack_depth == 1) { // Tee is the only thing on there.
+ Get(lt, true); // Now Set + Get instead.
+ } else {
+ // Things are above us on the stack so the Tee can't be eliminated.
+ // The Set makes this work as a Tee when consumed by a parent.
+ }
+ return;
+ }
+ case ExprType::If: {
+ auto ife = cast<IfExpr>(&e);
+ stack_depth--; // Condition.
+ mc.BeginBlock(LabelType::Block, ife->true_);
+ Construct(ife->true_.exprs, ife->true_.decl.GetNumResults(), false);
+ if (!ife->false_.empty()) {
+ Construct(ife->false_, ife->true_.decl.GetNumResults(), false);
+ }
+ mc.EndBlock();
+ stack_depth++; // Put Condition back.
+ NewNode(NodeType::Expr, ExprType::If, &e, ife->false_.empty() ? 2 : 3);
+ return;
+ }
+ case ExprType::Block: {
+ Block(*cast<BlockExpr>(&e), LabelType::Block);
+ return;
+ }
+ case ExprType::Loop: {
+ Block(*cast<LoopExpr>(&e), LabelType::Loop);
+ return;
+ }
+ case ExprType::Br: {
+ NewNode(NodeType::Expr, ExprType::Br, &e, 0);
+ stack.back().lt = mc.GetLabel(cast<BrExpr>(&e)->var)->label_type;
+ return;
+ }
+ case ExprType::BrIf: {
+ NewNode(NodeType::Expr, ExprType::BrIf, &e, 1);
+ stack.back().lt = mc.GetLabel(cast<BrIfExpr>(&e)->var)->label_type;
+ return;
+ }
+ default: {
+ NewNode(NodeType::Expr, e.type(), &e, mc.GetExprArity(e).nargs);
+ return;
+ }
+ }
+ NewNode(NodeType::Expr, e.type(), &e, arity.nargs);
+ }
+
+ void Construct(const ExprList& es, Index nresults, bool return_results) {
+ auto start = stack.size();
+ auto stack_depth_start = stack_depth;
+ bool unreachable = false;
+ for (auto& e : es) {
+ Construct(e);
+ auto arity = mc.GetExprArity(e);
+ stack_depth -= arity.nargs;
+ stack_depth += arity.nreturns;
+ unreachable = unreachable || arity.unreachable;
+ assert(unreachable || stack_depth >= 0);
+ if (arity.nreturns > 1) {
+ // Multivalue: we "push" everything on to the stack.
+ NewNode(NodeType::PushAll, ExprType::Nop, nullptr, 1);
+ // All values become pops.
+ for (Index i = 0; i < arity.nreturns; i++)
+ NewNode(NodeType::Pop, ExprType::Nop, nullptr, 0);
+ // TODO: can also implement a push_all_but_one that returns the top,
+ // then insert N-1 pops below it. Or have a function that returns N
+ // values direcly to N arguments for when structs are passed on,
+ // etc.
+ }
+ }
+ assert(unreachable ||
+ stack_depth - stack_depth_start == static_cast<int>(nresults));
+ // Undo any changes to stack_depth, since parent takes care of arity
+ // changes.
+ stack_depth = stack_depth_start;
+ auto end = stack.size();
+ assert(end >= start);
+ if (return_results && nresults) {
+ // Combine nresults into a return statement, for when this is used as
+ // a function body.
+ // TODO: if this is some other kind of block and >1 value is being
+ // returned, probably need some kind of syntax to make that clearer.
+ NewNode(NodeType::EndReturn, ExprType::Nop, nullptr, nresults);
+ }
+ end = stack.size();
+ assert(end >= start);
+ auto size = end - start;
+ if (size != 1) {
+ NewNode(NodeType::Statements, ExprType::Nop, nullptr, size);
+ }
+ }
+
+ ModuleContext& mc;
+ std::vector<Node> stack;
+ const Func *f;
+ size_t pre_decl_insertion_point = 0;
+ int stack_depth = 0;
+ std::set<std::string> vars_defined;
+};
+
+} // namespace wabt
diff --git a/src/decompiler.cc b/src/decompiler.cc
index 15d744b3..936e0553 100644
--- a/src/decompiler.cc
+++ b/src/decompiler.cc
@@ -16,40 +16,17 @@
#include "src/decompiler.h"
-#include <algorithm>
-#include <array>
-#include <cassert>
-#include <cinttypes>
-#include <cstdarg>
-#include <cstdio>
-#include <iterator>
-#include <map>
-#include <numeric>
-#include <set>
-#include <string>
-#include <vector>
-#include <sstream>
-
-#include "src/cast.h"
-#include "src/common.h"
-#include "src/expr-visitor.h"
-#include "src/ir.h"
-#include "src/ir-util.h"
-#include "src/literal.h"
-#include "src/generate-names.h"
-#include "src/stream.h"
+#include "src/decompiler-ast.inl"
#define WABT_TRACING 0
#include "src/tracing.h"
namespace wabt {
-namespace {
-
-struct Decompiler : ModuleContext {
+struct Decompiler {
Decompiler(Stream& stream, const Module& module,
const DecompileOptions& options)
- : ModuleContext(module), stream(stream), options(options) {}
+ : mc(module), stream(stream), options(options) {}
struct Value {
std::vector<std::string> v;
@@ -57,6 +34,9 @@ struct Decompiler : ModuleContext {
// TODO: replace with a system based on precedence?
bool needs_bracketing;
+ Value(std::vector<std::string>&& v, bool nb)
+ : v(v), needs_bracketing(nb) {}
+
size_t width() {
size_t w = 0;
for (auto &s : v) {
@@ -64,6 +44,12 @@ struct Decompiler : ModuleContext {
}
return w;
}
+
+ // This value should really never be copied, only moved.
+ Value(Value&& rhs) = default;
+ Value(const Value& rhs) = delete;
+ Value& operator=(Value&& rhs) = default;
+ Value& operator=(const Value& rhs) = delete;
};
std::string Indent(size_t amount) {
@@ -86,7 +72,7 @@ struct Decompiler : ModuleContext {
}
}
- void WrapChild(Value &child, string_view prefix, string_view postfix) {
+ Value WrapChild(Value &child, string_view prefix, string_view postfix) {
WABT_TRACE_ARGS(WrapChild, "\"" PRIstringview "\" - \"" PRIstringview "\"",
WABT_PRINTF_STRING_VIEW_ARG(prefix),
WABT_PRINTF_STRING_VIEW_ARG(postfix));
@@ -109,80 +95,74 @@ struct Decompiler : ModuleContext {
v.insert(v.begin(), std::string(prefix));
v.back().append(postfix.data(), postfix.size());
}
+ return std::move(child);
}
void BracketIfNeeded(Value &val) {
if (!val.needs_bracketing) return;
- WrapChild(val, "(", ")");
+ val = WrapChild(val, "(", ")");
val.needs_bracketing = false;
}
- void WrapBinary(string_view infix, bool indent_right) {
- auto right = std::move(stack.back());
- stack.pop_back();
- BracketIfNeeded(right);
- auto left = std::move(stack.back());
- stack.pop_back();
+ Value WrapBinary(std::vector<Value>& args, string_view infix, bool indent_right) {
+ assert(args.size() == 2);
+ auto& left = args[0];
+ auto& right = args[1];
BracketIfNeeded(left);
+ BracketIfNeeded(right);
auto width = infix.size() + left.width() + right.width();
if (width < target_exp_width && left.v.size() == 1 && right.v.size() == 1) {
- stack.push_back({
+ return Value {
{ left.v[0] + std::string(infix) + right.v[0] },
true
- });
+ };
} else {
Value bin { {}, true };
std::move(left.v.begin(), left.v.end(), std::back_inserter(bin.v));
bin.v.back().append(infix.data(), infix.size());
if (indent_right) IndentValue(right, indent_amount, {});
std::move(right.v.begin(), right.v.end(), std::back_inserter(bin.v));
- stack.push_back(std::move(bin));
+ return bin;
}
}
- void WrapNAry(Index nargs, string_view prefix, string_view postfix) {
+ Value WrapNAry(std::vector<Value>& args, string_view prefix, string_view postfix) {
size_t total_width = 0;
size_t max_width = 0;
bool multiline = false;
- for (Index i = 0; i < nargs; i++) {
- auto& child = stack[stack.size() - i - 1];
+ for (auto& child : args) {
auto w = child.width();
max_width = std::max(max_width, w);
total_width += w;
multiline = multiline || child.v.size() > 1;
}
if (!multiline &&
- total_width + prefix.size() + postfix.size() < target_exp_width) {
+ (total_width + prefix.size() + postfix.size() < target_exp_width ||
+ args.empty())) {
// Single line.
ss << prefix;
- if (nargs) {
- for (Index i = 0; i < nargs; i++) {
- auto& child = stack[stack.size() - nargs + i];
- if (i) ss << ", ";
- ss << child.v[0];
- }
- stack.resize(stack.size() - nargs);
+ for (auto& child : args) {
+ if (&child != &args[0]) ss << ", ";
+ ss << child.v[0];
}
ss << postfix;
- PushSStream();
- return;
+ return PushSStream();
} else {
// Multi-line.
+ Value ml { {}, false };
auto ident_with_name = max_width + prefix.size() < target_exp_width;
- for (Index i = 0; i < nargs; i++) {
- auto& child = stack[stack.size() - nargs + i];
+ size_t i = 0;
+ for (auto& child : args) {
IndentValue(child, ident_with_name ? prefix.size() : indent_amount,
!i && ident_with_name ? prefix : string_view {});
- child.v.back() += i == nargs - 1 ? std::string(postfix) : ",";
- if (i) {
- std::move(child.v.begin(), child.v.end(),
- std::back_inserter(stack[stack.size() - nargs].v));
- } else if (!ident_with_name) {
- child.v.insert(child.v.begin(), std::string(prefix));
- }
+ if (i < args.size() - 1) child.v.back() += ",";
+ std::move(child.v.begin(), child.v.end(),
+ std::back_inserter(ml.v));
+ i++;
}
- stack.erase(stack.end() - nargs + 1, stack.end());
- return;
+ if (!ident_with_name) ml.v.insert(ml.v.begin(), std::string(prefix));
+ ml.v.back() += std::string(postfix);
+ return ml;
}
}
@@ -223,56 +203,75 @@ struct Decompiler : ModuleContext {
return op;
}
- void PushSStream() {
- stack.push_back({ { ss.str() }, false });
+ Value PushSStream() {
+ auto v = Value { { ss.str() }, false };
ss.str({});
+ return v;
}
- void PreDecl(const Var& var) {
- predecl << Indent(indent_amount) << "var " << var.name() << ":"
- << GetDecompTypeName(cur_func->GetLocalType(var)) << ";\n";
-
- }
-
- template<ExprType T> void Get(const VarExpr<T>& ve, bool local) {
- if (local && vars_defined.insert(ve.var.name()).second) {
- // Use before def, may happen since locals are guaranteed 0.
- PreDecl(ve.var);
- }
+ template<ExprType T> Value Get(const VarExpr<T>& ve) {
ss << ve.var.name();
- PushSStream();
+ return PushSStream();
}
- template<ExprType T> void Set(const VarExpr<T>& ve, bool local) {
- auto decl = "";
- // Seen this var before?
- if (local && vars_defined.insert(ve.var.name()).second) {
- if (stack_depth == 1) {
- // Top level, declare it here.
- decl = "var ";
- } else {
- // Inside exp, better leave it as assignment exp and lift the decl out.
- PreDecl(ve.var);
- }
- }
- WrapChild(stack.back(), decl + ve.var.name() + " = ", "");
- stack.back().needs_bracketing = true;
+ template<ExprType T> Value Set(Value& child, const VarExpr<T>& ve) {
+ child.needs_bracketing = true;
+ return WrapChild(child, ve.var.name() + " = ", "");
}
- void Block(const Block& block, LabelType label, const char *name) {
- BeginBlock(label, block);
- DecompileExprs(block.exprs, block.decl.GetNumResults(), false);
- EndBlock();
- auto &val = stack.back();
+ Value Block(Value &val, const Block& block, LabelType label, const char *name) {
IndentValue(val, indent_amount, {});
val.v.insert(val.v.begin(), std::string(name) + " " + block.label + " {");
val.v.push_back("}");
+ return std::move(val);
}
- void DecompileExpr(const Expr& e) {
- switch (e.type()) {
+ Value DecompileExpr(const Node &n) {
+ std::vector<Value> args;
+ for (auto &c : n.children) {
+ args.push_back(DecompileExpr(c));
+ }
+ // First deal with the specialized node types.
+ switch (n.ntype) {
+ case NodeType::PushAll: {
+ return WrapChild(args[0], "push_all(", ")");
+ }
+ case NodeType::Pop: {
+ return Value { { "pop()" }, false };
+ }
+ case NodeType::Statements: {
+ Value stats { {}, false };
+ for (size_t i = 0; i < n.children.size(); i++) {
+ auto& s = args[i].v.back();
+ if (s.back() != '}') s += ';';
+ std::move(args[i].v.begin(), args[i].v.end(),
+ std::back_inserter(stats.v));
+ }
+ return stats;
+ }
+ case NodeType::EndReturn: {
+ return WrapNAry(args, "return ", "");
+ }
+ case NodeType::Decl: {
+ // FIXME: this is icky.
+ auto lg = reinterpret_cast<const VarExpr<ExprType::LocalGet> *>(n.e);
+ ss << "var " << lg->var.name() << ":"
+ << GetDecompTypeName(cur_func->GetLocalType(lg->var));
+ return PushSStream();
+ }
+ case NodeType::DeclInit: {
+ // FIXME: this is icky.
+ auto lg = reinterpret_cast<const VarExpr<ExprType::LocalGet> *>(n.e);
+ return WrapChild(args[0], "var " + lg->var.name() + ":"
+ + GetDecompTypeName(cur_func->GetLocalType(lg->var)) + " = ", "");
+ }
+ case NodeType::Expr:
+ // We're going to fall thru to the second switch to deal with ExprType.
+ break;
+ }
+ switch (n.etype) {
case ExprType::Const: {
- auto& c = cast<ConstExpr>(&e)->const_;
+ auto& c = cast<ConstExpr>(n.e)->const_;
switch (c.type) {
case Type::I32:
ss << static_cast<int32_t>(c.u32);
@@ -292,98 +291,75 @@ struct Decompiler : ModuleContext {
default:
WABT_UNREACHABLE;
}
- PushSStream();
- return;
+ return PushSStream();
}
case ExprType::LocalGet: {
- Get(*cast<LocalGetExpr>(&e), true);
- return;
+ return Get(*cast<LocalGetExpr>(n.e));
}
case ExprType::GlobalGet: {
- Get(*cast<GlobalGetExpr>(&e), false);
- return;
+ return Get(*cast<GlobalGetExpr>(n.e));
}
case ExprType::LocalSet: {
- Set(*cast<LocalSetExpr>(&e), true);
- return;
+ return Set(args[0], *cast<LocalSetExpr>(n.e));
}
case ExprType::GlobalSet: {
- Set(*cast<GlobalSetExpr>(&e), false);
- return;
+ return Set(args[0], *cast<GlobalSetExpr>(n.e));
}
case ExprType::LocalTee: {
- auto& lt = *cast<LocalTeeExpr>(&e);
- Set(lt, true);
- if (stack_depth == 1) { // Tee is the only thing on there.
- Get(lt, true); // Now Set + Get instead.
- } else {
- // Things are above us on the stack so the Tee can't be eliminated.
- // The Set makes this work as a Tee when consumed by a parent.
- }
- return;
+ auto& te = *cast<LocalTeeExpr>(n.e);
+ return args.empty() ? Get(te) : Set(args[0], te);
}
case ExprType::Binary: {
- auto& be = *cast<BinaryExpr>(&e);
- WrapBinary(" " + std::string(OpcodeToToken(be.opcode)) + " ", false);
- return;
+ auto& be = *cast<BinaryExpr>(n.e);
+ return WrapBinary(args, " " + std::string(OpcodeToToken(be.opcode)) + " ", false);
}
case ExprType::Compare: {
- auto& ce = *cast<CompareExpr>(&e);
- WrapBinary(" " + std::string(OpcodeToToken(ce.opcode)) + " ", false);
- return;
+ auto& ce = *cast<CompareExpr>(n.e);
+ return WrapBinary(args, " " + std::string(OpcodeToToken(ce.opcode)) + " ", false);
}
case ExprType::Unary: {
- auto& ue = *cast<UnaryExpr>(&e);
+ auto& ue = *cast<UnaryExpr>(n.e);
//BracketIfNeeded(stack.back());
// TODO: also version without () depending on precedence.
- WrapChild(stack.back(),
+ return WrapChild(args[0],
std::string(OpcodeToToken(ue.opcode)) + "(", ")");
- return;
}
case ExprType::Load: {
- auto& le = *cast<LoadExpr>(&e);
- BracketIfNeeded(stack.back());
+ auto& le = *cast<LoadExpr>(n.e);
+ BracketIfNeeded(args[0]);
std::string suffix = "[";
suffix += std::to_string(le.offset);
suffix += "]:";
suffix += TypeFromLoadStore(le.opcode, string_view(".load"));
- stack.back().v.back() += suffix;
+ args[0].v.back() += suffix;
// FIXME: align
- return;
+ return std::move(args[0]);
}
case ExprType::Store: {
- auto& se = *cast<StoreExpr>(&e);
- BracketIfNeeded(stack[stack.size() - 2]);
+ auto& se = *cast<StoreExpr>(n.e);
+ BracketIfNeeded(args[0]);
std::string suffix = "[";
suffix += std::to_string(se.offset);
suffix += "]:";
suffix += TypeFromLoadStore(se.opcode, string_view(".store"));
- stack[stack.size() - 2].v.back() += suffix;
- WrapBinary(" = ", true);
+ args[0].v.back() += suffix;
// FIXME: align
- return;
+ return WrapBinary(args, " = ", true);
}
case ExprType::If: {
- auto ife = cast<IfExpr>(&e);
- auto ifs = std::move(stack.back());
- stack.pop_back();
- stack_depth--; // Condition.
- BeginBlock(LabelType::Block, ife->true_);
- DecompileExprs(ife->true_.exprs, ife->true_.decl.GetNumResults(), false);
- auto thenp = std::move(stack.back());
- stack.pop_back();
+ auto ife = cast<IfExpr>(n.e);
+ Value *elsep = nullptr;
+ if (!ife->false_.empty()) {
+ elsep = &args[2];
+ }
+ auto& thenp = args[1];
+ auto& ifs = args[0];
bool multiline = ifs.v.size() > 1 || thenp.v.size() > 1;
size_t width = ifs.width() + thenp.width();
- Value elsep { {}, false };
- if (!ife->false_.empty()) {
- DecompileExprs(ife->false_, ife->true_.decl.GetNumResults(), false);
- elsep = std::move(stack.back());
- width += elsep.width();
- stack.pop_back();
- multiline = multiline || elsep.v.size() > 1;
+ if (elsep) {
+ width += elsep->width();
+ multiline = multiline || elsep->v.size() > 1;
}
- EndBlock();
- stack_depth++; // Put Condition back.
multiline = multiline || width > target_exp_width;
if (multiline) {
auto if_start = string_view("if (");
@@ -391,145 +367,76 @@ struct Decompiler : ModuleContext {
ifs.v.back() += ") {";
IndentValue(thenp, indent_amount, {});
std::move(thenp.v.begin(), thenp.v.end(), std::back_inserter(ifs.v));
- if (!elsep.v.empty()) {
+ if (elsep) {
ifs.v.push_back("} else {");
- IndentValue(elsep, indent_amount, {});
- std::move(elsep.v.begin(), elsep.v.end(), std::back_inserter(ifs.v));
+ IndentValue(*elsep, indent_amount, {});
+ std::move(elsep->v.begin(), elsep->v.end(), std::back_inserter(ifs.v));
}
ifs.v.push_back("}");
- stack.push_back(std::move(ifs));
- return;
+ return std::move(ifs);
} else {
ss << "if (" << ifs.v[0] << ") { " << thenp.v[0] << " }";
- if (!elsep.v.empty()) ss << " else { " << elsep.v[0] << " }";
- PushSStream();
- return;
+ if (elsep) ss << " else { " << elsep->v[0] << " }";
+ return PushSStream();
}
}
case ExprType::Block: {
- Block(cast<BlockExpr>(&e)->block, LabelType::Block, "block");
- return;
+ return Block(args[0], cast<BlockExpr>(n.e)->block, LabelType::Block, "block");
}
case ExprType::Loop: {
- Block(cast<LoopExpr>(&e)->block, LabelType::Loop, "loop");
- return;
+ return Block(args[0], cast<LoopExpr>(n.e)->block, LabelType::Loop, "loop");
}
case ExprType::Br: {
- auto be = cast<BrExpr>(&e);
- auto label = GetLabel(be->var);
- auto jmp = label->label_type == LabelType::Loop ? "continue" : "break";
+ auto be = cast<BrExpr>(n.e);
+ auto jmp = n.lt == LabelType::Loop ? "continue" : "break";
ss << jmp << " " << be->var.name();
- PushSStream();
- return;
+ return PushSStream();
}
case ExprType::BrIf: {
- auto bie = cast<BrIfExpr>(&e);
- auto label = GetLabel(bie->var);
- auto jmp = label->label_type == LabelType::Loop ? "continue" : "break";
- WrapChild(stack.back(), "if (",
+ auto bie = cast<BrIfExpr>(n.e);
+ auto jmp = n.lt == LabelType::Loop ? "continue" : "break";
+ return WrapChild(args[0], "if (",
") " + std::string(jmp) + " " + bie->var.name());
- return;
}
default: {
std::string name;
- switch (e.type()) {
+ switch (n.etype) {
case ExprType::Call:
- name = cast<CallExpr>(&e)->var.name();
+ name = cast<CallExpr>(n.e)->var.name();
break;
case ExprType::Convert:
- name = std::string(OpcodeToToken(cast<ConvertExpr>(&e)->opcode));
+ name = std::string(OpcodeToToken(cast<ConvertExpr>(n.e)->opcode));
break;
default:
- name = GetExprTypeName(e.type());
+ name = GetExprTypeName(n.etype);
break;
}
- WrapNAry(GetExprArity(e).nargs, name + "(", ")");
- return;
+ return WrapNAry(args, name + "(", ")");
}
}
}
- void DecompileExprs(const ExprList &es, Index nresults, bool return_results) {
- auto start = stack.size();
- auto stack_depth_start = stack_depth;
- bool unreachable = false;
- for (auto& e : es) {
- DecompileExpr(e);
- auto arity = GetExprArity(e);
- stack_depth -= arity.nargs;
- stack_depth += arity.nreturns;
- unreachable = unreachable || arity.unreachable;
- Assert(unreachable || stack_depth >= 0);
- if (arity.nreturns > 1) {
- // Multivalue: we "push" everything on to the stack.
- WrapChild(stack.back(), "push_all(", ")");
- // All values become pops.
- for (Index i = 0; i < arity.nreturns; i++)
- stack.insert(stack.end(), Value { { "pop()" }, false });
- // TODO: can also implement a push_all_but_one that returns the top,
- // then insert N-1 pops below it. Or have a function that returns N
- // values direcly to N arguments for when structs are passed on,
- // etc.
- }
- }
- Assert(unreachable ||
- stack_depth - stack_depth_start == static_cast<int>(nresults));
- // Undo any changes to stack_depth, since parent takes care of arity
- // changes.
- stack_depth = stack_depth_start;
- auto end = stack.size();
- Assert(end >= start);
- if (return_results) {
- // Combine nresults into a return statement, for when this is used as
- // a function body.
- // TODO: if this is some other kind of block and >1 value is being
- // returned, probably need some kind of syntax to make that clearer.
- WrapNAry(nresults, "return ", "");
- }
- // Now we only have "statements" left, so add semicolons.
- for (size_t i = start; i < end; i++) {
- auto& s = stack[i].v.back();
- if (s.back() != '}') s += ';';
- }
- if (end - start == 0) {
- stack.push_back({ { "{}" }, false });
- } else if (end - start > 1) {
- Value stats { {}, false };
- // FIXME: insert ; ?
- for (size_t i = start; i < end; i++) {
- auto &e = stack[i];
- std::move(e.v.begin(), e.v.end(), std::back_inserter(stats.v));
- }
- stack.resize(start);
- stack.push_back(std::move(stats));
- }
- }
-
- void Reset() {
- stack.clear();
- stack_depth = 0;
- vars_defined.clear();
- predecl.str({});
- }
-
void Decompile() {
- for (auto g : module.globals) {
- DecompileExprs(g->init_expr, 1, false);
+ for (auto g : mc.module.globals) {
+ AST ast(mc, nullptr);
+ ast.Construct(g->init_expr, 1, false);
+ auto val = DecompileExpr(ast.stack[0]);
+ assert(ast.stack.size() == 1 && val.v.size() == 1);
stream.Writef("global %s:%s = %s\n", g->name.c_str(),
- GetDecompTypeName(g->type), stack[0].v[0].c_str());
- Reset();
+ GetDecompTypeName(g->type), val.v[0].c_str());
}
- if (!module.globals.empty()) stream.Writef("\n");
+ if (!mc.module.globals.empty()) stream.Writef("\n");
Index func_index = 0;
- for(auto f : module.funcs) {
+ for(auto f : mc.module.funcs) {
cur_func = f;
- BeginFunc(*f);
+ auto is_import = mc.module.IsImport(ExternalKind::Func, Var(func_index));
+ AST ast(mc, f);
+ if (!is_import) ast.Construct(f->exprs, f->GetNumResults(), true);
stream.Writef("function %s(", f->name.c_str());
for (Index i = 0; i < f->GetNumParams(); i++) {
if (i) stream.Writef(", ");
auto t = f->GetParamType(i);
auto name = IndexToAlphaName(i);
- vars_defined.insert(name);
stream.Writef("%s:%s", name.c_str(), GetDecompTypeName(t));
}
stream.Writef(")");
@@ -545,31 +452,24 @@ struct Decompiler : ModuleContext {
stream.Writef(")");
}
}
- if (module.IsImport(ExternalKind::Func, Var(func_index))) {
+ if (is_import) {
stream.Writef(" = import;\n\n");
} else {
stream.Writef(" {\n");
- DecompileExprs(f->exprs, f->GetNumResults(), true);
- stream.Writef("%s", predecl.str().c_str());
- IndentValue(stack.back(), indent_amount, {});
- for (auto& s : stack[0].v) {
+ auto val = DecompileExpr(ast.stack[0]);
+ IndentValue(val, indent_amount, {});
+ for (auto& s : val.v) {
stream.Writef("%s\n", s.c_str());
}
stream.Writef("}\n\n");
}
- EndFunc();
- Reset();
+ mc.EndFunc();
func_index++;
}
}
void DumpInternalState() {
- // Dump the remainder of the stack.
- for (auto &se : stack) {
- for (auto& s : se.v) {
- stream.Writef("%s\n", s.c_str());
- }
- }
+ // TODO: print ast?
stream.Flush();
}
@@ -583,20 +483,15 @@ struct Decompiler : ModuleContext {
assert(false);
}
+ ModuleContext mc;
Stream& stream;
const DecompileOptions& options;
- std::vector<Value> stack;
std::ostringstream ss;
- std::ostringstream predecl;
size_t indent_amount = 2;
size_t target_exp_width = 70;
- int stack_depth = 0;
- std::set<std::string> vars_defined;
const Func* cur_func = nullptr;
};
-} // end anonymous namespace
-
Result Decompile(Stream& stream,
const Module& module,
const DecompileOptions& options) {
diff --git a/src/ir-util.cc b/src/ir-util.cc
index 9ee31b7b..c2cfd6d5 100644
--- a/src/ir-util.cc
+++ b/src/ir-util.cc
@@ -40,23 +40,23 @@
using namespace wabt;
-Label* ModuleContext::GetLabel(const Var& var) {
+const Label* ModuleContext::GetLabel(const Var& var) const {
if (var.is_name()) {
for (Index i = GetLabelStackSize(); i > 0; --i) {
- Label* label = &label_stack_[i - 1];
+ auto label = &label_stack_[i - 1];
if (label->name == var.name()) {
return label;
}
}
} else if (var.index() < GetLabelStackSize()) {
- Label* label = &label_stack_[GetLabelStackSize() - var.index() - 1];
+ auto label = &label_stack_[GetLabelStackSize() - var.index() - 1];
return label;
}
return nullptr;
}
-Index ModuleContext::GetLabelArity(const Var& var) {
- Label* label = GetLabel(var);
+Index ModuleContext::GetLabelArity(const Var& var) const {
+ auto label = GetLabel(var);
if (!label) {
return 0;
}
@@ -65,12 +65,12 @@ Index ModuleContext::GetLabelArity(const Var& var) {
: label->result_types.size();
}
-Index ModuleContext::GetFuncParamCount(const Var& var) {
+Index ModuleContext::GetFuncParamCount(const Var& var) const {
const Func* func = module.GetFunc(var);
return func ? func->GetNumParams() : 0;
}
-Index ModuleContext::GetFuncResultCount(const Var& var) {
+Index ModuleContext::GetFuncResultCount(const Var& var) const {
const Func* func = module.GetFunc(var);
return func ? func->GetNumResults() : 0;
}
@@ -95,7 +95,7 @@ void ModuleContext::EndFunc() {
current_func_ = nullptr;
}
-ModuleContext::Arities ModuleContext::GetExprArity(const Expr& expr) {
+ModuleContext::Arities ModuleContext::GetExprArity(const Expr& expr) const {
switch (expr.type()) {
case ExprType::AtomicNotify:
case ExprType::AtomicRmw:
diff --git a/src/ir-util.h b/src/ir-util.h
index c859432d..226e81aa 100644
--- a/src/ir-util.h
+++ b/src/ir-util.h
@@ -41,15 +41,15 @@ struct Label {
struct ModuleContext {
ModuleContext(const Module &module) : module(module) {}
- Index GetLabelStackSize() { return label_stack_.size(); }
- Label* GetLabel(const Var& var);
- Index GetLabelArity(const Var& var);
+ Index GetLabelStackSize() const { return label_stack_.size(); }
+ const Label* GetLabel(const Var& var) const;
+ Index GetLabelArity(const Var& var) const;
void SetTopLabelType(LabelType label_type) {
label_stack_.back().label_type = label_type;
}
- Index GetFuncParamCount(const Var& var);
- Index GetFuncResultCount(const Var& var);
+ Index GetFuncParamCount(const Var& var) const;
+ Index GetFuncResultCount(const Var& var) const;
void BeginBlock(LabelType label_type, const Block& block);
void EndBlock();
@@ -63,7 +63,7 @@ struct ModuleContext {
Arities(Index na, Index nr, bool ur = false)
: nargs(na), nreturns(nr), unreachable(ur) {}
};
- Arities GetExprArity(const Expr& expr);
+ Arities GetExprArity(const Expr& expr) const;
const Module &module;
private: