diff --git a/samples/outputs.var_names/sample57 b/samples/outputs.var_names/sample57 index 182253d..cd9bcd2 100644 --- a/samples/outputs.var_names/sample57 +++ b/samples/outputs.var_names/sample57 @@ -2,5 +2,11 @@ void bar (void) { int x_0 = 0; int* y_1 = (&(x_0)); (y_1[0] = y_1[0] + 1) - 1; + int z_2 = 0; + (z_2 = z_2 + 1) - 1; + int a_3 = z_2; + (z_2 = z_2 + 1) - 1; + int b_4 = a_3; + (b_4 = b_4 + 1) - 1; } diff --git a/samples/outputs/sample57 b/samples/outputs/sample57 index 62b48df..78d541b 100644 --- a/samples/outputs/sample57 +++ b/samples/outputs/sample57 @@ -2,5 +2,11 @@ void bar (void) { int var0 = 0; int* var1 = (&(var0)); (var1[0] = var1[0] + 1) - 1; + int var2 = 0; + (var2 = var2 + 1) - 1; + int var3 = var2; + (var2 = var2 + 1) - 1; + int var4 = var3; + (var4 = var4 + 1) - 1; } diff --git a/samples/sample57.cpp b/samples/sample57.cpp index 05f11eb..6ee5732 100644 --- a/samples/sample57.cpp +++ b/samples/sample57.cpp @@ -16,6 +16,15 @@ static void bar(void) { dyn_var y = &x; (*y)++; + + dyn_var z = 0; + z++; + + dyn_var a = z; + z++; + + dyn_var b = a; + b++; } diff --git a/src/blocks/rce.cpp b/src/blocks/rce.cpp index ab3a0e4..4b174f5 100644 --- a/src/blocks/rce.cpp +++ b/src/blocks/rce.cpp @@ -3,209 +3,339 @@ #include "blocks/block_visitor.h" #include "blocks/expr.h" #include "blocks/stmt.h" -#include #include -#include +#include +#include + namespace block { -class var_use_counter : public block_visitor { +// General utility visitor to find usage statistics +class usage_counter: public block_visitor { public: using block_visitor::visit; - std::map usage_count; - std::vector assigned_vars; - virtual void visit(var_expr::Ptr e) override { + + std::map usage_count; + std::vector address_taken_vars; + + virtual void visit(var_expr::Ptr e) override { var::Ptr v = e->var1; if (usage_count.find(v) != usage_count.end()) usage_count[v]++; else usage_count[v] = 1; - } - virtual void visit(assign_expr::Ptr e) override { - e->var1->accept(this); - e->expr1->accept(this); - if (!isa(e->var1)) - return; - var_expr::Ptr ve = to(e->var1); - var::Ptr v = ve->var1; - if (std::find(assigned_vars.begin(), assigned_vars.end(), v) == assigned_vars.end()) - assigned_vars.push_back(v); - } + } + + // We don't care if variables are assigned to, but only ones that + // have their address taken. + // TODO: also discard variables that have references bound to them virtual void visit(addr_of_expr::Ptr e) override { e->expr1->accept(this); if (!isa(e->expr1)) return; - var_expr::Ptr ve = to(e->expr1); - var::Ptr v = ve->var1; - // Variables that have their addresses taken can potentially be assigned and hence should not be copy eliminated - if (std::find(assigned_vars.begin(), assigned_vars.end(), v) == assigned_vars.end()) - assigned_vars.push_back(v); + var::Ptr v = to(e->expr1)->var1; + if (std::find(address_taken_vars.begin(), address_taken_vars.end(), v) == address_taken_vars.end()) + address_taken_vars.push_back(v); } }; - -class check_side_effects : public block_visitor { +// General utility to check if an expression has side effects +class check_side_effects: public block_visitor { public: using block_visitor::visit; + bool has_side_effects = false; virtual void visit(assign_expr::Ptr) override { has_side_effects = true; } + virtual void visit(function_call_expr::Ptr) override { - has_side_effects = true; - } - virtual void visit(addr_of_expr::Ptr) override { - has_side_effects = true; + has_side_effects = true; } }; -class gather_rce_decls : public block_visitor { +static bool has_side_effects(block::Ptr b) { + check_side_effects checker; + b->accept(&checker); + return checker.has_side_effects; +} + + +// Both RCE phases leave variables that have their addresses taken, untouched + +// Phase 1 finds vars of the form int x = where x only has +// one use and has no side effects. +// With this, the substitution is stopped as soon as any side effect occurs, like assignment or function calls + +class phase1_visitor: public block_replacer { public: - using block_visitor::visit; - std::vector gathered_decls; - // This are vars (y) that may have one use but the use is of the form int x = y (use of y) - // and x has multiple uses. When x is replaced with y it will have multiple uses - // so we want to black list such x's - std::vector duplicated_vars; - std::map usage_count; - std::vector assigned_vars; - - virtual void visit(decl_stmt::Ptr decl) { - if (decl->init_expr == nullptr) + using block_replacer::visit; + + std::map value_map; + std::map usage_count; + std::vector address_taken_vars; + + virtual void visit(decl_stmt::Ptr ds) override { + // We are not changing decls, so this is okay to be written first + node = ds; + + // Before we do anything, visit the RHS + // If there is no RHS, stop + if (ds->init_expr != nullptr) { + if (has_side_effects(ds->init_expr)) + value_map.clear(); + ds->init_expr = rewrite(ds->init_expr); + } else return; - var::Ptr v = decl->decl_var; - if (std::find(assigned_vars.begin(), assigned_vars.end(), v) != assigned_vars.end()) + + // Check if this variable is eligible for phase 1 RCE + var::Ptr v = ds->decl_var; + // If the variable has no usage info, stop + if (usage_count.find(v) == usage_count.end()) return; - int use_count = 0; - if (usage_count.find(v) != usage_count.end()) - use_count = usage_count[v]; - if (isa(decl->init_expr)) { - // This is time to blacklist y - if (use_count > 1) { - var_expr::Ptr ve = to(decl->init_expr); - var::Ptr v = ve->var1; - if (std::find(duplicated_vars.begin(), duplicated_vars.end(), v) == - duplicated_vars.end()) { - duplicated_vars.push_back(v); - } - } - gathered_decls.push_back(decl); + // If usage count > 1 stop + if (usage_count[v] > 1) return; - } + // If variable has its address taken stop + if (std::find(address_taken_vars.begin(), address_taken_vars.end(), v) != address_taken_vars.end()) + return; + // Finally check if the init expr as side effects + if (has_side_effects(ds->init_expr)) + return; + + // All good, we are ready to substiture this variable + value_map[v] = ds->init_expr; + } - if (use_count == 1) { - check_side_effects checker; - decl->init_expr->accept(&checker); - // We will also allow variables that are used exactly once - // and don't have side effects - if (checker.has_side_effects == false) { - gathered_decls.push_back(decl); - return; - } - } + virtual void visit(expr_stmt::Ptr es) override { + node = es; + + // TODO: Special case for top-level assign exprs to defer clearing if each + // subexpression has no side effects + + if (has_side_effects(es->expr1)) + value_map.clear(); + es->expr1 = rewrite(es->expr1); + } + + virtual void visit(return_stmt::Ptr rs) override { + node = rs; + if (has_side_effects(rs->return_val)) + value_map.clear(); + rs->return_val = rewrite(rs->return_val); + } + + virtual void visit(while_stmt::Ptr ws) override { + node = ws; + if (has_side_effects(ws->cond)) + value_map.clear(); + ws->cond = rewrite(ws->cond); + ws->body = rewrite(ws->body); + } + virtual void visit(for_stmt::Ptr fs) override { + node = fs; + fs->decl_stmt = rewrite(fs->decl_stmt); + if (has_side_effects(fs->cond)) + value_map.clear(); + if (has_side_effects(fs->update)) + value_map.clear(); + fs->cond = rewrite(fs->cond); + fs->update = rewrite(fs->update); + fs->body = rewrite(fs->body); + } + virtual void visit(if_stmt::Ptr is) override { + node = is; + if (has_side_effects(is->cond)) + value_map.clear(); + is->cond = rewrite(is->cond); + is->then_stmt = rewrite(is->then_stmt); + is->else_stmt = rewrite(is->else_stmt); + } + + + virtual void visit(var_expr::Ptr ve) override { + node = ve; + if (value_map.find(ve->var1) == value_map.end()) + return; + // If we have a substitution make it now + node = value_map[ve->var1]; } }; -class replace_rce_vars : public block_replacer { + + +// Utility class to find modified variables from an expression +class side_effects_gather: public block_visitor { public: - using block_replacer::visit; - std::vector gathered_decls; - std::map var_decl_map; - std::vector perma_enabled_decls; - std::vector enabled_decls; - - virtual void visit(decl_stmt::Ptr decl) override { - if (decl->init_expr) - decl->init_expr = rewrite(decl->init_expr); - if (std::find(gathered_decls.begin(), gathered_decls.end(), decl) != gathered_decls.end()) { - if (isa(decl->init_expr)) - perma_enabled_decls.push_back(decl->decl_var); - else { - // Store decls that have a complex expression on the RHS - // separately, if there is any statement that has side-effects, - // we can immediately clear this - enabled_decls.push_back(decl->decl_var); - } + using block_visitor::visit; + std::set modify_set; + virtual void visit(assign_expr::Ptr ae) override { + if (isa(ae->var1)) { + var::Ptr v = to(ae->var1)->var1; + modify_set.insert(v); } - node = decl; - } - virtual void visit(assign_expr::Ptr assign) override { - assign->expr1 = rewrite(assign->expr1); - assign->var1 = rewrite(assign->var1); - enabled_decls.clear(); - node = assign; - } - virtual void visit(addr_of_expr::Ptr addr) override { - addr->expr1 = rewrite(addr->expr1); - enabled_decls.clear(); - node = addr; - } - virtual void visit(function_call_expr::Ptr f) override { - for (unsigned int i = 0; i < f->args.size(); i++) { - f->args[i] = rewrite(f->args[i]); + } +}; + + +// Phase 2 finds vars of the form int x = y where x can have more than one use. +// With this, the substitution is stopped as soon as either of x or y are modified +// Variables that have their addresses taken are discarded from both the x and y + +class phase2_visitor: public block_replacer { +public: + using block_replacer::visit; + + std::map value_map; + std::vector address_taken_vars; + + + void purge_side_effects(expr::Ptr e) { + side_effects_gather gatherer; + e->accept(&gatherer); + for (auto pair: value_map) { + var::Ptr v1 = pair.first; + var::Ptr v2 = pair.second; + if (gatherer.modify_set.find(v1) != gatherer.modify_set.end() || gatherer.modify_set.find(v2) + != gatherer.modify_set.end()) { + value_map[v1] = nullptr; + } } - enabled_decls.clear(); - node = f; } - virtual void visit(var_expr::Ptr ve) override { - var::Ptr v = ve->var1; - if (std::find(perma_enabled_decls.begin(), perma_enabled_decls.end(), v) != perma_enabled_decls.end() || - std::find(enabled_decls.begin(), enabled_decls.end(), v) != enabled_decls.end()) { - decl_stmt::Ptr de = var_decl_map[v]; - node = de->init_expr; + + virtual void visit(decl_stmt::Ptr ds) override { + node = ds; + // Before handling this decl, process the RHS + if (ds->init_expr) { + purge_side_effects(ds->init_expr); + ds->init_expr = rewrite(ds->init_expr); } else { - node = ve; + return; } + + // Only keep decls of the form int x = y; + if (!isa(ds->init_expr)) + return; + var::Ptr v1 = ds->decl_var; + var::Ptr v2 = to(ds->init_expr)->var1; + + // If either of the two have address taken, discard + if (std::find(address_taken_vars.begin(), address_taken_vars.end(), v1) != address_taken_vars.end()) + return; + if (std::find(address_taken_vars.begin(), address_taken_vars.end(), v2) != address_taken_vars.end()) + return; + + value_map[v1] = v2; + } + + virtual void visit(expr_stmt::Ptr es) override { + node = es; + purge_side_effects(es->expr1); + es->expr1 = rewrite(es->expr1); + } + + virtual void visit(return_stmt::Ptr rs) override { + node = rs; + purge_side_effects(rs->return_val); + rs->return_val = rewrite(rs->return_val); + } + + virtual void visit(while_stmt::Ptr ws) override { + node = ws; + purge_side_effects(ws->cond); + ws->cond = rewrite(ws->cond); + ws->body = rewrite(ws->body); + } + + virtual void visit(for_stmt::Ptr fs) override { + node = fs; + fs->decl_stmt = rewrite(fs->decl_stmt); + purge_side_effects(fs->cond); + purge_side_effects(fs->update); + fs->cond = rewrite(fs->cond); + fs->update = rewrite(fs->update); + fs->body = rewrite(fs->body); + } + virtual void visit(if_stmt::Ptr is) override { + node = is; + purge_side_effects(is->cond); + is->cond = rewrite(is->cond); + is->then_stmt = rewrite(is->then_stmt); + is->else_stmt = rewrite(is->else_stmt); + } + + virtual void visit(var_expr::Ptr ve) override { + node = ve; + var::Ptr v1 = ve->var1; + if (value_map.find(v1) != value_map.end() && value_map[v1] != nullptr) + ve->var1 = value_map[v1]; } }; -class rce_decl_deleter : public block_visitor { +class decl_deleter: public block_visitor { public: using block_visitor::visit; - std::map usage_count; - virtual void visit(stmt_block::Ptr b) { + std::map usage_count; + virtual void visit(stmt_block::Ptr sb) override { std::vector new_stmts; - for (auto stmt : b->stmts) { - if (isa(stmt)) { - var::Ptr v = to(stmt)->decl_var; - if (usage_count.find(v) != usage_count.end()) { - // TODO: ensure that we are not deleting decls who's RHS have side-effects + for (auto stmt : sb->stmts) { + if (!isa(stmt)) { + stmt->accept(this); + new_stmts.push_back(stmt); + continue; + } + decl_stmt::Ptr ds = to(stmt); + var::Ptr dv = ds->decl_var; + if (usage_count.find(dv) != usage_count.end() && usage_count[dv] > 0) { + // No need to visit decl stmts, they cannot have decl stmts inside (right?) + new_stmts.push_back(stmt); + continue; + } + if (ds->init_expr != nullptr) { + check_side_effects checker; + ds->init_expr->accept(&checker); + if (checker.has_side_effects) { new_stmts.push_back(stmt); + continue; } - } else { - new_stmts.push_back(stmt); } - stmt->accept(this); + // All good, we are ready to drop } - b->stmts = new_stmts; + sb->stmts = new_stmts; } }; +static void rce_phase1(block::Ptr ast, const usage_counter& counter) { + // Phase 1 RCE + phase1_visitor p1v; + p1v.usage_count = counter.usage_count; + p1v.address_taken_vars = counter.address_taken_vars; + ast->accept(&p1v); +} + +static void rce_phase2(block::Ptr ast, const usage_counter& counter) { + // Phase 2 RCE + // Since Phase 2 RCE only uses address_taken + // we don't need to run usage_counter again + + phase2_visitor p2v; + p2v.address_taken_vars = counter.address_taken_vars; + ast->accept(&p2v); +} + void eliminate_redundant_vars(block::Ptr ast) { - var_use_counter counter; + // gather general statistics first + usage_counter counter; ast->accept(&counter); - gather_rce_decls gatherer; - gatherer.usage_count = counter.usage_count; - gatherer.assigned_vars = counter.assigned_vars; - ast->accept(&gatherer); - - replace_rce_vars replacer; - for (auto decl : gatherer.gathered_decls) { - var::Ptr v = decl->decl_var; - - if (!isa(decl->init_expr) && - std::find(gatherer.duplicated_vars.begin(), gatherer.duplicated_vars.end(), v) != - gatherer.duplicated_vars.end()) - continue; - replacer.gathered_decls.push_back(decl); - replacer.var_decl_map[v] = decl; - } - ast->accept(&replacer); - // Now that all th replacements have been done, we will decls that are not used - var_use_counter post_counter; - ast->accept(&post_counter); - - rce_decl_deleter deleter; - deleter.usage_count = post_counter.usage_count; + + rce_phase1(ast, counter); + rce_phase2(ast, counter); + + // Perform a second usage count before cleanup + usage_counter counter2; + ast->accept(&counter2); + + decl_deleter deleter; + deleter.usage_count = counter2.usage_count; ast->accept(&deleter); } -} // namespace block +}