From a13397ff13805e55f3872fdefbd82da6a6db598a Mon Sep 17 00:00:00 2001 From: Ajay Brahmakshatriya Date: Thu, 23 May 2024 16:57:06 -0400 Subject: [PATCH 1/5] Finished phase1 RCE rewrite --- samples/sample57.cpp | 9 ++ src/blocks/rce.cpp | 282 ++++++++++++++++++++----------------------- 2 files changed, 138 insertions(+), 153 deletions(-) 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..dcebcc4 100644 --- a/src/blocks/rce.cpp +++ b/src/blocks/rce.cpp @@ -3,209 +3,185 @@ #include "blocks/block_visitor.h" #include "blocks/expr.h" #include "blocks/stmt.h" -#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 { + +// 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) + 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 + check_side_effects checker; + ds->init_expr->accept(&checker); + if (checker.has_side_effects) + return; + + // All good, we are ready to substiture this variable + value_map[v] = ds->init_expr; + } - if (use_count == 1) { + virtual void visit(stmt_block::Ptr sb) override { + node = sb; + for (unsigned int i = 0; i < sb->stmts.size(); i++) { + // Before we rewrite statements check if it has side effects + // If it does, clear the value map 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; - } - } + sb->stmts[i]->accept(&checker); + if (checker.has_side_effects) + value_map.clear(); + sb->stmts[i] = rewrite(sb->stmts[i]); + } } -}; -class replace_rce_vars : public block_replacer { -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); - } - } - 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]); - } - 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; - } else { - node = ve; - } + 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 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; } }; -void eliminate_redundant_vars(block::Ptr ast) { - var_use_counter counter; +static void rce_phase1(block::Ptr ast) { + // 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; + + phase1_visitor p1v; + p1v.usage_count = counter.usage_count; + p1v.address_taken_vars = counter.address_taken_vars; + ast->accept(&p1v); + + // 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); + + // Phase 1 RCE done + +} + +static void rce_phase2(block::Ptr ast) { } -} // namespace block +void eliminate_redundant_vars(block::Ptr ast) { + rce_phase1(ast); + rce_phase2(ast); +} + +} From cc33ffad5c3d98130a8c98a14df6357cc26f913e Mon Sep 17 00:00:00 2001 From: Ajay Brahmakshatriya Date: Thu, 23 May 2024 18:35:22 -0400 Subject: [PATCH 2/5] Relaxed RCE Phase 1 to bo look for side effects in sequenced sub blocks --- src/blocks/rce.cpp | 71 +++++++++++++++++++++++++++++++++++----------- 1 file changed, 55 insertions(+), 16 deletions(-) diff --git a/src/blocks/rce.cpp b/src/blocks/rce.cpp index dcebcc4..867eebb 100644 --- a/src/blocks/rce.cpp +++ b/src/blocks/rce.cpp @@ -51,6 +51,12 @@ class check_side_effects: 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 @@ -72,9 +78,11 @@ class phase1_visitor: public block_replacer { // Before we do anything, visit the RHS // If there is no RHS, stop - if (ds->init_expr != nullptr) + if (ds->init_expr != nullptr) { + if (has_side_effects(ds->init_expr)) + value_map.clear(); ds->init_expr = rewrite(ds->init_expr); - else + } else return; // Check if this variable is eligible for phase 1 RCE @@ -89,28 +97,59 @@ class phase1_visitor: public block_replacer { 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 - check_side_effects checker; - ds->init_expr->accept(&checker); - if (checker.has_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; } - virtual void visit(stmt_block::Ptr sb) override { - node = sb; - for (unsigned int i = 0; i < sb->stmts.size(); i++) { - // Before we rewrite statements check if it has side effects - // If it does, clear the value map - check_side_effects checker; - sb->stmts[i]->accept(&checker); - if (checker.has_side_effects) - value_map.clear(); - sb->stmts[i] = rewrite(sb->stmts[i]); - } + 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()) From b09810e7fccaf4a4439135ea0655a53a99baffbd Mon Sep 17 00:00:00 2001 From: Ajay Brahmakshatriya Date: Thu, 23 May 2024 20:14:39 -0400 Subject: [PATCH 3/5] Added implememtation for RCE Phase 2 --- samples/outputs.var_names/sample57 | 6 ++ samples/outputs/sample57 | 6 ++ src/blocks/rce.cpp | 130 ++++++++++++++++++++++++++++- 3 files changed, 138 insertions(+), 4 deletions(-) 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/src/blocks/rce.cpp b/src/blocks/rce.cpp index 867eebb..7465fe6 100644 --- a/src/blocks/rce.cpp +++ b/src/blocks/rce.cpp @@ -4,6 +4,7 @@ #include "blocks/expr.h" #include "blocks/stmt.h" #include +#include #include namespace block { @@ -159,6 +160,116 @@ class phase1_visitor: public block_replacer { } }; + + +// Utility class to find modified variables from an expression +class side_effects_gather: public block_visitor { +public: + using block_visitor::visit; + std::set modify_set; + virtual void visit(assign_expr::Ptr ae) { + if (isa(ae->var1)) { + var::Ptr v = to(ae->var1)->var1; + modify_set.insert(v); + } + } +}; + + +// 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; + } + } + } + + 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 { + 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) { + node = es; + purge_side_effects(es->expr1); + es->expr1 = rewrite(es->expr1); + } + + virtual void visit(return_stmt::Ptr rs) { + node = rs; + purge_side_effects(rs->return_val); + rs->return_val = rewrite(rs->return_val); + } + + virtual void visit(while_stmt::Ptr ws) { + node = ws; + purge_side_effects(ws->cond); + ws->cond = rewrite(ws->cond); + ws->body = rewrite(ws->body); + } + + virtual void visit(for_stmt::Ptr fs) { + 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) { + 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) { + 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 decl_deleter: public block_visitor { public: using block_visitor::visit; @@ -197,11 +308,20 @@ static void rce_phase1(block::Ptr ast) { usage_counter counter; ast->accept(&counter); - + // Phase 1 RCE phase1_visitor p1v; p1v.usage_count = counter.usage_count; p1v.address_taken_vars = counter.address_taken_vars; ast->accept(&p1v); + + // 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); + // Perform a second usage count before cleanup usage_counter counter2; @@ -210,12 +330,14 @@ static void rce_phase1(block::Ptr ast) { decl_deleter deleter; deleter.usage_count = counter2.usage_count; ast->accept(&deleter); - - // Phase 1 RCE done - } static void rce_phase2(block::Ptr ast) { + // phase 2 also needs usage information + // for filtering out variables that have their address taken + + usage_counter counter; + ast->accept(&counter); } void eliminate_redundant_vars(block::Ptr ast) { From 15a2c33bea2e9cfc42075cd13280e3785d724857 Mon Sep 17 00:00:00 2001 From: Ajay Brahmakshatriya Date: Fri, 24 May 2024 16:53:47 -0400 Subject: [PATCH 4/5] Moved phase 2 RCE into a separate function --- src/blocks/rce.cpp | 31 ++++++++++++------------------- 1 file changed, 12 insertions(+), 19 deletions(-) diff --git a/src/blocks/rce.cpp b/src/blocks/rce.cpp index 7465fe6..45a1095 100644 --- a/src/blocks/rce.cpp +++ b/src/blocks/rce.cpp @@ -303,17 +303,15 @@ class decl_deleter: public block_visitor { } }; -static void rce_phase1(block::Ptr ast) { - // gather general statistics first - usage_counter counter; - ast->accept(&counter); - +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 @@ -321,8 +319,16 @@ static void rce_phase1(block::Ptr ast) { phase2_visitor p2v; p2v.address_taken_vars = counter.address_taken_vars; ast->accept(&p2v); +} + +void eliminate_redundant_vars(block::Ptr ast) { + // gather general statistics first + usage_counter counter; + ast->accept(&counter); + + rce_phase1(ast, counter); + rce_phase2(ast, counter); - // Perform a second usage count before cleanup usage_counter counter2; ast->accept(&counter2); @@ -332,17 +338,4 @@ static void rce_phase1(block::Ptr ast) { ast->accept(&deleter); } -static void rce_phase2(block::Ptr ast) { - // phase 2 also needs usage information - // for filtering out variables that have their address taken - - usage_counter counter; - ast->accept(&counter); -} - -void eliminate_redundant_vars(block::Ptr ast) { - rce_phase1(ast); - rce_phase2(ast); -} - } From 4476ad7a6c3546488f2552b4c7705727d52c168f Mon Sep 17 00:00:00 2001 From: Ajay Brahmakshatriya Date: Fri, 24 May 2024 17:30:54 -0400 Subject: [PATCH 5/5] Added missing override to RCE --- src/blocks/rce.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/blocks/rce.cpp b/src/blocks/rce.cpp index 45a1095..4b174f5 100644 --- a/src/blocks/rce.cpp +++ b/src/blocks/rce.cpp @@ -167,7 +167,7 @@ class side_effects_gather: public block_visitor { public: using block_visitor::visit; std::set modify_set; - virtual void visit(assign_expr::Ptr ae) { + virtual void visit(assign_expr::Ptr ae) override { if (isa(ae->var1)) { var::Ptr v = to(ae->var1)->var1; modify_set.insert(v); @@ -226,26 +226,26 @@ class phase2_visitor: public block_replacer { value_map[v1] = v2; } - virtual void visit(expr_stmt::Ptr es) { + 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) { + 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) { + 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) { + virtual void visit(for_stmt::Ptr fs) override { node = fs; fs->decl_stmt = rewrite(fs->decl_stmt); purge_side_effects(fs->cond); @@ -254,7 +254,7 @@ class phase2_visitor: public block_replacer { fs->update = rewrite(fs->update); fs->body = rewrite(fs->body); } - virtual void visit(if_stmt::Ptr is) { + virtual void visit(if_stmt::Ptr is) override { node = is; purge_side_effects(is->cond); is->cond = rewrite(is->cond); @@ -262,7 +262,7 @@ class phase2_visitor: public block_replacer { is->else_stmt = rewrite(is->else_stmt); } - virtual void visit(var_expr::Ptr ve) { + 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)