From 521ca3709da252911b61cb785b29875bf1a24c48 Mon Sep 17 00:00:00 2001 From: Ajay Brahmakshatriya Date: Tue, 3 Oct 2023 10:16:46 -0400 Subject: [PATCH] Basic implementation for defering jumps from the loop that cut through the outer loop --- include/blocks/loop_finder.h | 11 ++ samples/outputs.var_names/sample51 | 125 +++++++++++++++++ samples/outputs/sample51 | 125 +++++++++++++++++ samples/sample51.cpp | 31 +++++ src/blocks/loop_finder.cpp | 214 ++++++++++++++++++++++------- 5 files changed, 453 insertions(+), 53 deletions(-) create mode 100644 samples/outputs.var_names/sample51 create mode 100644 samples/outputs/sample51 create mode 100644 samples/sample51.cpp diff --git a/include/blocks/loop_finder.h b/include/blocks/loop_finder.h index 317cca8..800de65 100644 --- a/include/blocks/loop_finder.h +++ b/include/blocks/loop_finder.h @@ -8,6 +8,8 @@ class loop_finder : public block_visitor { public: using block_visitor::visit; stmt::Ptr ast; + + int loop_hook_counter = 0; void visit_label(label_stmt::Ptr, stmt_block::Ptr); virtual void visit(stmt_block::Ptr); }; @@ -26,5 +28,14 @@ class continue_finder : public block_visitor { bool has_continue = false; virtual void visit(continue_stmt::Ptr); }; + +class outer_jump_finder : public block_visitor { +public: + using block_visitor::visit; + int &loop_hook_counter; + outer_jump_finder(int &lc) : loop_hook_counter(lc) {} + std::vector> created_vars; + virtual void visit(stmt_block::Ptr); +}; } // namespace block #endif diff --git a/samples/outputs.var_names/sample51 b/samples/outputs.var_names/sample51 new file mode 100644 index 0000000..c1a26c6 --- /dev/null +++ b/samples/outputs.var_names/sample51 @@ -0,0 +1,125 @@ +FUNC_DECL + SCALAR_TYPE (VOID) + STMT_BLOCK + DECL_STMT + SCALAR_TYPE (INT) + VAR (x_0) + INT_CONST (5) + WHILE_STMT + LT_EXPR + VAR_EXPR + VAR (x_0) + INT_CONST (10) + STMT_BLOCK + DECL_STMT + SCALAR_TYPE (INT) + VAR (control_guard0) + INT_CONST (0) + WHILE_STMT + GT_EXPR + VAR_EXPR + VAR (x_0) + INT_CONST (100) + STMT_BLOCK + EXPR_STMT + ASSIGN_EXPR + VAR_EXPR + VAR (control_guard0) + INT_CONST (0) + IF_STMT + EQUALS_EXPR + VAR_EXPR + VAR (x_0) + INT_CONST (0) + STMT_BLOCK + EXPR_STMT + ASSIGN_EXPR + VAR_EXPR + VAR (x_0) + PLUS_EXPR + VAR_EXPR + VAR (x_0) + INT_CONST (5) + WHILE_STMT + NE_EXPR + VAR_EXPR + VAR (x_0) + INT_CONST (-1) + STMT_BLOCK + EXPR_STMT + ASSIGN_EXPR + VAR_EXPR + VAR (x_0) + PLUS_EXPR + VAR_EXPR + VAR (x_0) + INT_CONST (6) + EXPR_STMT + ASSIGN_EXPR + VAR_EXPR + VAR (control_guard0) + INT_CONST (1) + BREAK_STMT + STMT_BLOCK + EXPR_STMT + ASSIGN_EXPR + VAR_EXPR + VAR (x_0) + PLUS_EXPR + VAR_EXPR + VAR (x_0) + INT_CONST (4) + IF_STMT + VAR_EXPR + VAR (control_guard0) + STMT_BLOCK + CONTINUE_STMT + STMT_BLOCK + EXPR_STMT + ASSIGN_EXPR + VAR_EXPR + VAR (x_0) + PLUS_EXPR + VAR_EXPR + VAR (x_0) + INT_CONST (5) + WHILE_STMT + NE_EXPR + VAR_EXPR + VAR (x_0) + INT_CONST (-1) + STMT_BLOCK + EXPR_STMT + ASSIGN_EXPR + VAR_EXPR + VAR (x_0) + PLUS_EXPR + VAR_EXPR + VAR (x_0) + INT_CONST (6) +void bar (void) { + int x_0 = 5; + while (x_0 < 10) { + int control_guard0 = 0; + while (x_0 > 100) { + control_guard0 = 0; + if (x_0 == 0) { + x_0 = x_0 + 5; + while (x_0 != -1) { + x_0 = x_0 + 6; + } + control_guard0 = 1; + break; + } + x_0 = x_0 + 4; + } + if (control_guard0) { + continue; + } + x_0 = x_0 + 5; + while (x_0 != -1) { + x_0 = x_0 + 6; + } + } +} + diff --git a/samples/outputs/sample51 b/samples/outputs/sample51 new file mode 100644 index 0000000..03a3d57 --- /dev/null +++ b/samples/outputs/sample51 @@ -0,0 +1,125 @@ +FUNC_DECL + SCALAR_TYPE (VOID) + STMT_BLOCK + DECL_STMT + SCALAR_TYPE (INT) + VAR (var0) + INT_CONST (5) + WHILE_STMT + LT_EXPR + VAR_EXPR + VAR (var0) + INT_CONST (10) + STMT_BLOCK + DECL_STMT + SCALAR_TYPE (INT) + VAR (control_guard0) + INT_CONST (0) + WHILE_STMT + GT_EXPR + VAR_EXPR + VAR (var0) + INT_CONST (100) + STMT_BLOCK + EXPR_STMT + ASSIGN_EXPR + VAR_EXPR + VAR (control_guard0) + INT_CONST (0) + IF_STMT + EQUALS_EXPR + VAR_EXPR + VAR (var0) + INT_CONST (0) + STMT_BLOCK + EXPR_STMT + ASSIGN_EXPR + VAR_EXPR + VAR (var0) + PLUS_EXPR + VAR_EXPR + VAR (var0) + INT_CONST (5) + WHILE_STMT + NE_EXPR + VAR_EXPR + VAR (var0) + INT_CONST (-1) + STMT_BLOCK + EXPR_STMT + ASSIGN_EXPR + VAR_EXPR + VAR (var0) + PLUS_EXPR + VAR_EXPR + VAR (var0) + INT_CONST (6) + EXPR_STMT + ASSIGN_EXPR + VAR_EXPR + VAR (control_guard0) + INT_CONST (1) + BREAK_STMT + STMT_BLOCK + EXPR_STMT + ASSIGN_EXPR + VAR_EXPR + VAR (var0) + PLUS_EXPR + VAR_EXPR + VAR (var0) + INT_CONST (4) + IF_STMT + VAR_EXPR + VAR (control_guard0) + STMT_BLOCK + CONTINUE_STMT + STMT_BLOCK + EXPR_STMT + ASSIGN_EXPR + VAR_EXPR + VAR (var0) + PLUS_EXPR + VAR_EXPR + VAR (var0) + INT_CONST (5) + WHILE_STMT + NE_EXPR + VAR_EXPR + VAR (var0) + INT_CONST (-1) + STMT_BLOCK + EXPR_STMT + ASSIGN_EXPR + VAR_EXPR + VAR (var0) + PLUS_EXPR + VAR_EXPR + VAR (var0) + INT_CONST (6) +void bar (void) { + int var0 = 5; + while (var0 < 10) { + int control_guard0 = 0; + while (var0 > 100) { + control_guard0 = 0; + if (var0 == 0) { + var0 = var0 + 5; + while (var0 != -1) { + var0 = var0 + 6; + } + control_guard0 = 1; + break; + } + var0 = var0 + 4; + } + if (control_guard0) { + continue; + } + var0 = var0 + 5; + while (var0 != -1) { + var0 = var0 + 6; + } + } +} + diff --git a/samples/sample51.cpp b/samples/sample51.cpp new file mode 100644 index 0000000..4861025 --- /dev/null +++ b/samples/sample51.cpp @@ -0,0 +1,31 @@ +#include "blocks/c_code_generator.h" +#include "builder/builder_context.h" +#include "builder/dyn_var.h" +#include "builder/static_var.h" +#include +using builder::dyn_var; +using builder::static_var; + +static void bar(void) { + // This example tests the case where a jump cuts + // through the parent loop + dyn_var x = 5; + while (x < 10) { + while (x > 100) { + if (x == 0) + break; + x = x + 4; + } + x = x + 5; + while (x != -1) + x = x + 6; + } +} + +int main(int argc, char *argv[]) { + builder::builder_context context; + auto ast = context.extract_function_ast(bar, "bar"); + ast->dump(std::cout, 0); + block::c_code_generator::generate_code(ast, std::cout, 0); + return 0; +} diff --git a/src/blocks/loop_finder.cpp b/src/blocks/loop_finder.cpp index 4b382ad..7fc1a81 100644 --- a/src/blocks/loop_finder.cpp +++ b/src/blocks/loop_finder.cpp @@ -129,6 +129,14 @@ static void trim_from_parents(std::vector &parents, std::vector } void loop_finder::visit(stmt_block::Ptr a) { + + // We do this inside out, first do the innermost loop + + // Visit the instructions normally + for (auto stmt : a->stmts) { + stmt->accept(this); + } + // Check if this block has a label while (1) { label_stmt::Ptr found_label = nullptr; @@ -141,11 +149,63 @@ void loop_finder::visit(stmt_block::Ptr a) { break; visit_label(found_label, a); } - // Once all labels are done, visit the instructions normally - for (auto stmt : a->stmts) { - stmt->accept(this); +} + +static void merge_condition_with_loop(while_stmt::Ptr new_while) { + // If the body of the while loop only has a single if condition and + // the else part of the condition is just a break, fuse the if with + // the loop + + if (to(new_while->body)->stmts.size() == 1 && + isa(to(new_while->body)->stmts[0])) { + if_stmt::Ptr if_body = to(to(new_while->body)->stmts[0]); + + stmt::Ptr then_stmt = if_body->then_stmt; + stmt::Ptr else_stmt = if_body->else_stmt; + + if (isa(else_stmt) && to(else_stmt)->stmts.size() == 1) { + if (isa(to(else_stmt)->stmts[0])) { + new_while->cond = if_body->cond; + // new_while->body = + // std::make_shared(); + new_while->body = then_stmt; + return; + } + } + if (isa(then_stmt) && to(then_stmt)->stmts.size() == 1) { + if (isa(to(then_stmt)->stmts[0])) { + not_expr::Ptr new_cond = std::make_shared(); + new_cond->static_offset = if_body->cond->static_offset; + new_cond->expr1 = if_body->cond; + new_while->cond = new_cond; + new_while->body = else_stmt; + return; + } + } + } + // Other pattern is if the loops first statement is a if condition that + // breaks + if (isa(to(new_while->body)->stmts[0])) { + if_stmt::Ptr if_body = to(to(new_while->body)->stmts[0]); + stmt::Ptr then_stmt = if_body->then_stmt; + + if (isa(then_stmt) && to(then_stmt)->stmts.size() == 1) { + if (isa(to(then_stmt)->stmts[0])) { + not_expr::Ptr new_cond = std::make_shared(); + new_cond->static_offset = if_body->cond->static_offset; + new_cond->expr1 = if_body->cond; + new_while->cond = new_cond; + auto new_body = std::make_shared(); + for (unsigned int i = 1; i < to(new_while->body)->stmts.size(); i++) { + new_body->stmts.push_back(to(new_while->body)->stmts[i]); + } + new_while->body = new_body; + return; + } + } } } + void loop_finder::visit_label(label_stmt::Ptr a, stmt_block::Ptr parent) { // First separate out the stmts before the loop begin @@ -218,68 +278,116 @@ void loop_finder::visit_label(label_stmt::Ptr a, stmt_block::Ptr parent) { } std::reverse(trimmed.begin(), trimmed.end()); + + merge_condition_with_loop(new_while); + + // Once we are happy with the loops, we have to make sure that this loop doesn't have any other jumps + // If it does, we should pull them out. So outer loops can handle them + outer_jump_finder outer_finder(loop_hook_counter); + new_while->accept(&outer_finder); + + // For every control guard variable insert a initialization before the loop and the beginning of the loop + + std::vector new_body_stmts; + std::vector guard_decl_stmts; + std::vector guarded_jumps; + for (auto guards : outer_finder.created_vars) { + var::Ptr var1 = guards.first; + + auto var_expr1 = std::make_shared(); + var_expr1->var1 = var1; + auto const_expr1 = std::make_shared(); + const_expr1->value = 0; + const_expr1->is_64bit = false; + auto assign_expr1 = std::make_shared(); + assign_expr1->var1 = var_expr1; + assign_expr1->expr1 = const_expr1; + + auto expr_stmt1 = std::make_shared(); + expr_stmt1->expr1 = assign_expr1; + + new_body_stmts.push_back(expr_stmt1); + + auto var_decl1 = std::make_shared(); + var_decl1->decl_var = var1; + var_decl1->init_expr = const_expr1; + guard_decl_stmts.push_back(var_decl1); + + auto if_stmt1 = std::make_shared(); + if_stmt1->else_stmt = std::make_shared(); + auto stmt_block1 = std::make_shared(); + if_stmt1->then_stmt = stmt_block1; + stmt_block1->stmts.push_back(guards.second); + + auto var_expr2 = std::make_shared(); + var_expr2->var1 = var1; + if_stmt1->cond = var_expr2; + + guarded_jumps.push_back(if_stmt1); + } + + // Insert all the original statements + for (auto stmt : to(new_while->body)->stmts) { + new_body_stmts.push_back(stmt); + } + to(new_while->body)->stmts = new_body_stmts; + + // New while is ready to be inserted parent->stmts = stmts_before; + // Insert the new guard decls we created + for (auto stmt : guard_decl_stmts) { + parent->stmts.push_back(stmt); + } parent->stmts.push_back(new_while); + // Insert the guaded jumps afer + for (auto stmt : guarded_jumps) { + parent->stmts.push_back(stmt); + } for (auto stmt : trimmed) { parent->stmts.push_back(stmt); } for (auto stmt : stmts_after_body) { parent->stmts.push_back(stmt); } +} - // If the body of the while loop only has a single if condition and - // the else part of the condition is just a break, fuse the if with - // the loop - - if (to(new_while->body)->stmts.size() == 1 && - isa(to(new_while->body)->stmts[0])) { - if_stmt::Ptr if_body = to(to(new_while->body)->stmts[0]); - - stmt::Ptr then_stmt = if_body->then_stmt; - stmt::Ptr else_stmt = if_body->else_stmt; - - if (isa(else_stmt) && to(else_stmt)->stmts.size() == 1) { - if (isa(to(else_stmt)->stmts[0])) { - new_while->cond = if_body->cond; - // new_while->body = - // std::make_shared(); - new_while->body = then_stmt; - return; - } - } - if (isa(then_stmt) && to(then_stmt)->stmts.size() == 1) { - if (isa(to(then_stmt)->stmts[0])) { - not_expr::Ptr new_cond = std::make_shared(); - new_cond->static_offset = if_body->cond->static_offset; - new_cond->expr1 = if_body->cond; - new_while->cond = new_cond; - new_while->body = else_stmt; - return; - } - } - } - // Other patter is if the loops first statement is a if condition that - // breaks - if (isa(to(new_while->body)->stmts[0])) { - if_stmt::Ptr if_body = to(to(new_while->body)->stmts[0]); - stmt::Ptr then_stmt = if_body->then_stmt; - - if (isa(then_stmt) && to(then_stmt)->stmts.size() == 1) { - if (isa(to(then_stmt)->stmts[0])) { - not_expr::Ptr new_cond = std::make_shared(); - new_cond->static_offset = if_body->cond->static_offset; - new_cond->expr1 = if_body->cond; - new_while->cond = new_cond; - auto new_body = std::make_shared(); - for (unsigned int i = 1; i < to(new_while->body)->stmts.size(); i++) { - new_body->stmts.push_back(to(new_while->body)->stmts[i]); - } - new_while->body = new_body; - return; - } +void outer_jump_finder::visit(stmt_block::Ptr block) { + // First visit all the statements normally + block_visitor::visit(block); + + std::vector new_stmts; + for (auto stmt : block->stmts) { + if (isa(stmt)) { + // We found a jump statement, this must escape this loop, otherwise it would have + // been replaced with a continue + // we should now create a new variable and assignment + auto var1 = std::make_shared(); + var1->var_name = "control_guard" + std::to_string(loop_hook_counter++); + auto scalar_type1 = std::make_shared(); + var1->var_type = scalar_type1; + scalar_type1->scalar_type_id = scalar_type::INT_TYPE; + auto var_expr1 = std::make_shared(); + var_expr1->var1 = var1; + auto const_expr1 = std::make_shared(); + const_expr1->value = 1; + const_expr1->is_64bit = false; + auto assign_expr1 = std::make_shared(); + assign_expr1->var1 = var_expr1; + assign_expr1->expr1 = const_expr1; + + auto expr_stmt1 = std::make_shared(); + expr_stmt1->expr1 = assign_expr1; + + new_stmts.push_back(expr_stmt1); + + created_vars.push_back(std::make_pair(var1, stmt)); + } else { + new_stmts.push_back(stmt); } } + block->stmts = new_stmts; } + void last_jump_finder::visit(goto_stmt::Ptr a) { if (a->label1 == jump_label) { has_jump_to = true;