diff options
-rw-r--r-- | src/decompiler-ast.h | 36 | ||||
-rw-r--r-- | src/decompiler-naming.h | 2 | ||||
-rw-r--r-- | src/decompiler.cc | 25 | ||||
-rw-r--r-- | test/decompile/basic.txt | 22 |
4 files changed, 70 insertions, 15 deletions
diff --git a/src/decompiler-ast.h b/src/decompiler-ast.h index 5d04ddfc..9c269e3c 100644 --- a/src/decompiler-ast.h +++ b/src/decompiler-ast.h @@ -22,7 +22,7 @@ #include "src/ir.h" #include "src/ir-util.h" -#include <set> +#include <map> namespace wabt { @@ -81,7 +81,7 @@ struct AST { mc.BeginFunc(*f); for (Index i = 0; i < f->GetNumParams(); i++) { auto name = "$" + IndexToAlphaName(i); - vars_defined.insert(name); + vars_defined.insert({ name, { 0, false }}); } } } @@ -107,16 +107,25 @@ struct AST { } 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); + if (local) { + auto ret = vars_defined.insert({ ve.var.name(), { cur_block_id, false }}); + if (ret.second) { + // Use before def, may happen since locals are guaranteed 0. + PreDecl(ve); + } else if (blocks_closed[ret.first->second.block_id]) { + // This is a use of a variable that was defined in a block that has + // already ended. This happens rarely, but we should cater for this + // case by lifting it to the top scope. + PreDecl(ve); + } } InsertNode(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 (local && + vars_defined.insert({ ve.var.name(), { cur_block_id, false }}).second) { if (value_stack_depth == 1) { // Top level, declare it here. InsertNode(NodeType::DeclInit, ExprType::Nop, nullptr, 1).u.var = @@ -206,6 +215,9 @@ struct AST { } void Construct(const ExprList& es, Index nresults, bool is_function_body) { + block_stack.push_back(cur_block_id); + cur_block_id = blocks_closed.size(); + blocks_closed.push_back(false); auto start = exp_stack.size(); auto value_stack_depth_start = value_stack_depth; auto value_stack_in_variables = value_stack_depth; @@ -328,6 +340,9 @@ struct AST { InsertNode(NodeType::EndReturn, ExprType::Nop, nullptr, nresults); } } + // TODO: these predecls are always at top level, but in the case of + // use inside an exp, it be nice to do it in the current block. Can't + // do that for predecls that are "out if scope" however. std::move(predecls.begin(), predecls.end(), std::back_inserter(exp_stack)); std::rotate(exp_stack.begin(), exp_stack.end() - predecls.size(), @@ -340,6 +355,9 @@ struct AST { if (size != 1) { InsertNode(NodeType::Statements, ExprType::Nop, nullptr, size); } + blocks_closed[cur_block_id] = true; + cur_block_id = block_stack.back(); + block_stack.pop_back(); } ModuleContext& mc; @@ -347,8 +365,12 @@ struct AST { std::vector<Node> predecls; const Func *f; int value_stack_depth = 0; - std::set<std::string> vars_defined; + struct Variable { size_t block_id; bool defined; }; + std::map<std::string, Variable> vars_defined; Index flushed_vars = 0; + size_t cur_block_id = 0; + std::vector<size_t> block_stack; + std::vector<bool> blocks_closed; }; } // namespace wabt diff --git a/src/decompiler-naming.h b/src/decompiler-naming.h index 8aefcfd6..fcf982ac 100644 --- a/src/decompiler-naming.h +++ b/src/decompiler-naming.h @@ -19,6 +19,8 @@ #include "src/decompiler-ast.h" +#include <set> + namespace wabt { inline void RenameToIdentifier(std::string& name, Index i, diff --git a/src/decompiler.cc b/src/decompiler.cc index 92a33780..8d4d6df3 100644 --- a/src/decompiler.cc +++ b/src/decompiler.cc @@ -365,19 +365,26 @@ struct Decompiler { return WrapNAry(args, "return ", "", Precedence::None); } case NodeType::Decl: { + cur_ast->vars_defined[n.u.var->name()].defined = true; return Value{ {"var " + LocalDecl(std::string(n.u.var->name()), cur_func->GetLocalType(*n.u.var))}, Precedence::None}; } case NodeType::DeclInit: { - return WrapChild( - args[0], - cat("var ", - LocalDecl(std::string(n.u.var->name()), - cur_func->GetLocalType(*n.u.var)), - " = "), - "", Precedence::None); + if (cur_ast->vars_defined[n.u.var->name()].defined) { + // This has already been pre-declared, output as assign. + return WrapChild(args[0], cat(VarName(n.u.var->name()), " = "), "", + Precedence::None); + } else { + return WrapChild( + args[0], + cat("var ", + LocalDecl(std::string(n.u.var->name()), + cur_func->GetLocalType(*n.u.var)), + " = "), + "", Precedence::None); + } } case NodeType::Expr: // We're going to fall thru to the second switch to deal with ExprType. @@ -756,6 +763,7 @@ struct Decompiler { auto is_import = CheckImportExport(s, ExternalKind::Func, func_index, f->name); AST ast(mc, f); + cur_ast = * if (!is_import) { ast.Construct(f->exprs, f->GetNumResults(), true); lst.Track(ast.exp_stack[0]); @@ -799,6 +807,8 @@ struct Decompiler { mc.EndFunc(); lst.Clear(); func_index++; + cur_ast = nullptr; + cur_func = nullptr; } return s; } @@ -808,6 +818,7 @@ struct Decompiler { size_t indent_amount = 2; size_t target_exp_width = 70; const Func* cur_func = nullptr; + AST* cur_ast = nullptr; LoadStoreTracking lst; }; diff --git a/test/decompile/basic.txt b/test/decompile/basic.txt index 66c05323..31be88b9 100644 --- a/test/decompile/basic.txt +++ b/test/decompile/basic.txt @@ -102,6 +102,17 @@ call_indirect i32.const 0 ) + (func $g (param) (result) (local i32) + ;; This is rare, but requires special handling: uses of local outside scope + ;; of first definition. + loop + i32.const 1 + tee_local 0 + br_if 0 + end + get_local 0 + drop + ) ;; LLD outputs a name section with de-mangled C++ function signatures as names, ;; so have to make sure special chars get removed. (func (export "void signature-&<>(int a)") (param) (result)) @@ -169,13 +180,22 @@ export function f(a:int, b:int):int { return 0; } +function f_c() { + var a:int; + loop L_a { + a = 1; + if (a) continue L_a; + } + a; +} + function signature() { } function signature_1() { } -function f_e() { +function f_f() { } ;;; STDOUT ;;) |