Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for basic blocks and CFGs #49

Open
wants to merge 24 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
d73be04
Add support for basic blocks and CFGs
VedantParanjape May 15, 2023
c624e5b
Add code to populate the basic block predecessors
VedantParanjape May 16, 2023
851917a
Move the CFG generator driver to builder_context.cpp
VedantParanjape Jun 20, 2023
f0c30ad
Implement dominator_tree analysis pass
VedantParanjape Jul 3, 2023
cb06e58
Generate idom_map and add loop info pass skeleton
VedantParanjape Jul 11, 2023
a772a60
Implement loop_info analysis pass
VedantParanjape Jul 22, 2023
1cb4f46
Add a index to track the location of the AST block out of which a bas…
VedantParanjape Jul 29, 2023
e88e82e
Added code to populate loop latch blocks in a loop
VedantParanjape Jul 29, 2023
25a8425
WIP: Add infra to construct a while loop using the loop_info analysis
VedantParanjape Aug 26, 2023
79fd09e
WIP v2: Add infra to construct a while loop using the loop_info analy…
VedantParanjape Sep 3, 2023
02cb778
V3: Added postdom, and other stuff
VedantParanjape Sep 10, 2023
3a97de6
WIP v3: got majority test cases working
VedantParanjape Sep 12, 2023
ecd353a
WIP v4: got the insert loop latches/exits correctly and added while l…
VedantParanjape Sep 13, 2023
4a1613b
WIP v1: Implement a recursive convert_to_ast implementation (works be…
VedantParanjape Sep 23, 2023
7818fef
WIP v2: Handle the non-loop blocks using a iterative algorithm
VedantParanjape Sep 25, 2023
9bb93bc
WIP v3: Handle the non-loop blocks using a iterative algorithm
VedantParanjape Sep 26, 2023
489d511
WIP v4: able to generate loop exit breaks as well
VedantParanjape Sep 28, 2023
90b4aa4
WIP v5: generates clean and correct code for all testcases
VedantParanjape Sep 28, 2023
a6fee6c
WIP v6: reduce number of redundant continue stmts, few still remain
VedantParanjape Sep 28, 2023
98359b4
WIP
VedantParanjape Sep 29, 2023
7eeed44
Now postdom can also handle infinite loops
VedantParanjape Sep 30, 2023
390a79b
Improved handling of unconditional loops
VedantParanjape Sep 30, 2023
7f69c81
Fix bugs and emit guards for latches that cut through and go to loop …
VedantParanjape Nov 1, 2023
863b433
Added support for emitting multiple guard blocks for goto that cut th…
VedantParanjape Jan 28, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions include/blocks/basic_blocks.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#ifndef BASIC_BLOCKS_H
#define BASIC_BLOCKS_H
#include "blocks/stmt.h"
#include <vector>
#include <deque>
#include <string>
#include <map>

class basic_block {
public:
typedef std::vector<std::shared_ptr<basic_block>> cfg_block;
basic_block(std::string label): name(label) {};

cfg_block predecessor;
cfg_block successor;
block::expr::Ptr branch_expr;
std::shared_ptr<basic_block> then_branch;
std::shared_ptr<basic_block> else_branch;
std::shared_ptr<basic_block> exit_block;
bool is_exit_block = false;
block::stmt::Ptr parent;
unsigned int ast_index;
unsigned int ast_depth;
unsigned int id;
std::string name;
static std::map<block::stmt::Ptr, std::shared_ptr<basic_block>> ast_to_basic_block_map;
};

basic_block::cfg_block generate_basic_blocks(block::stmt_block::Ptr ast);

#endif
57 changes: 57 additions & 0 deletions include/blocks/dominance.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
#ifndef DOMINANCE_H
#define DOMINANCE_H
#include "blocks/block_visitor.h"
#include "blocks/basic_blocks.h"
#include "blocks/stmt.h"
#include <map>
#include <vector>
#include <stack>
#include <string>
#include <bitset>
#include <algorithm>

class dominator_analysis {
public:
// struct dominator_tree_ {
// dominator_tree_(int id): bb_id(id);
// int bb_id;
// std::vector<std::shared_ptr<dominator_tree_>> child_nodes;
// } dominator_tree;
dominator_analysis(basic_block::cfg_block cfg, bool is_postdom = false);
basic_block::cfg_block cfg_;
bool is_postdom_;
int max_depth;
unsigned int max_depth_bb_id;
std::vector<int> &get_postorder_bb_map();
std::vector<int> &get_postorder();
std::vector<int> &get_preorder_bb_map();
std::vector<int> &get_preorder();
std::vector<int> &get_idom();
std::map<int, std::vector<int>> &get_idom_map();
std::vector<int> &get_postorder_idom_map();
int get_idom(int bb_id);
std::vector<int> get_idom_map(int bb_id);
int get_postorder_idom_map(int idom_id);
bool dominates(int bb1_id, int bb2_id);
bool is_reachable_from_entry(int bb_id);
void analyze();

private:
std::vector<int> idom;
std::map<int, std::vector<int>> idom_map;
std::vector<int> postorder_idom;
std::vector<int> postorder;
std::vector<int> postorder_bb_map;
std::vector<int> preorder;
std::vector<int> preorder_bb_map;
void reverse_cfg();
void postorder_idom_helper(std::vector<bool> &visited, int id);
void postorder_dfs_helper(std::vector<bool> &visited_bbs, int id, int depth);
void postorder_dfs(bool reverse_cfg);
void preorder_dfs_helper(std::vector<bool> &visited_bbs, int id);
void preorder_dfs(bool reverse_cfg);
int intersect(int bb1_id, int bb2_id);
};


#endif
63 changes: 63 additions & 0 deletions include/blocks/loops.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
#ifndef LOOP_H
#define LOOP_H
#include "blocks/block_visitor.h"
#include "blocks/basic_blocks.h"
#include "blocks/dominance.h"
#include "blocks/stmt.h"
#include <unordered_set>

using namespace block;
class loop_info;
class loop {
public:
loop(std::shared_ptr<basic_block> header): header_block(header) {}
stmt::Ptr convert_to_ast_impl(loop_info &li, dominator_analysis &dta_, std::vector<std::pair<std::shared_ptr<basic_block>, stmt_block::Ptr>> &return_blocks);

struct loop_bounds_ {
stmt::Ptr ind_var;
// MISS: intial value of ind var
stmt::Ptr steps_ind_var;
// MISS: value of the step
// MISS: final value of the step
stmt::Ptr cond_ind_var;
// MISS: direction of step
stmt::Ptr entry_stmt;
} loop_bounds;

unsigned int loop_id;
basic_block::cfg_block blocks;
std::unordered_set<int> blocks_id_map;
std::shared_ptr<loop> parent_loop;
std::shared_ptr<basic_block> header_block;
std::shared_ptr<basic_block> condition_block;
std::shared_ptr<basic_block> unique_exit_block;
basic_block::cfg_block loop_latch_blocks;
basic_block::cfg_block loop_exit_blocks;
std::vector<std::shared_ptr<loop>> subloops;
while_stmt::Ptr structured_ast_loop;
};

class loop_info {
public:
loop_info(basic_block::cfg_block ast, dominator_analysis &dt, dominator_analysis &post_dt): parent_ast(ast), dta(dt), post_dta(post_dt) {
analyze();
}
std::shared_ptr<loop> allocate_loop(std::shared_ptr<basic_block> header);
block::stmt_block::Ptr convert_to_ast(block::stmt_block::Ptr ast);
std::map<unsigned int, std::vector<int>> postorder_loops_map;
std::map<unsigned int, std::vector<int>> preorder_loops_map;
std::vector<std::shared_ptr<loop>> loops;
std::vector<std::shared_ptr<loop>> top_level_loops;

private:
basic_block::cfg_block parent_ast;
dominator_analysis dta;
dominator_analysis post_dta;
std::map<int, std::shared_ptr<loop>> bb_loop_map;
void postorder_dfs_helper(std::vector<int> &postorder_loops_map, std::vector<bool> &visited_loops, int id);
void preorder_dfs_helper(std::vector<int> &preorder_loops_map, std::vector<bool> &visited_loops, int id);
// discover loops during traversal of the abstract syntax tree
void analyze();
};

#endif
3 changes: 3 additions & 0 deletions include/builder/builder_context.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
#ifndef BUILDER_CONTEXT
#define BUILDER_CONTEXT
#include "blocks/basic_blocks.h"
#include "blocks/dominance.h"
#include "blocks/loops.h"
#include "blocks/expr.h"
#include "blocks/stmt.h"
#include "builder/forward_declarations.h"
Expand Down
211 changes: 211 additions & 0 deletions src/blocks/basic_blocks.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,211 @@
#include "blocks/basic_blocks.h"
#include <algorithm>

using namespace block;

std::map<block::stmt::Ptr, std::shared_ptr<basic_block>> basic_block::ast_to_basic_block_map = {};

basic_block::cfg_block generate_basic_blocks(block::stmt_block::Ptr ast) {
std::deque<std::shared_ptr<basic_block>> work_list;
basic_block::cfg_block return_list;
int basic_block_count = 0;

// step 1: fill the work_list
unsigned int ast_index_counter = 0;
for (auto st: ast->stmts) {
auto bb = std::make_shared<basic_block>(std::to_string(basic_block_count));
bb->parent = st;
bb->ast_index = ast_index_counter++;
bb->ast_depth = 0;
work_list.push_back(bb);
basic_block_count++;
}

// step 2: add successors
for (unsigned i = 0; work_list.size() != 0 && i < work_list.size() - 1; i++) {
work_list[i]->successor.push_back(work_list[i+1]);
}

// step 3: process blocks: every xx_stmt type statement is made out into a basic block
while (work_list.size()) {
auto bb = work_list.front();

if (isa<block::stmt_block>(bb->parent)) {
ast_index_counter = 0;
stmt_block::Ptr stmt_block_ = to<stmt_block>(bb->parent);
bb->name = "stmt" + bb->name;

if (stmt_block_->stmts.size() > 0) {
basic_block::cfg_block stmt_block_list;

// convert all statements of this stmt_block into a basic block
for (auto st: stmt_block_->stmts) {
stmt_block_list.push_back(std::make_shared<basic_block>(std::to_string(basic_block_count++)));
stmt_block_list.back()->parent = st;
stmt_block_list.back()->ast_index = ast_index_counter++;
stmt_block_list.back()->ast_depth = bb->ast_depth + 1;
}

// set the basic block successors
for (unsigned i = 0; stmt_block_list.size() != 0 && i < stmt_block_list.size() - 1; i++) {
stmt_block_list[i]->successor.push_back(stmt_block_list[i+1]);
}

// since we insert these stmts between bb1 ---> bb2 ==> bb1 ---> (bb-a1...bb-an) ---> bb2
// point the successor of the stmt_block_list to the basic block that bb1's successor
// pointed to. After this, clear the bb1's successor and push the front of stmt_block_list
// to bb1's successor list.
stmt_block_list.back()->successor.push_back(bb->successor.front());
bb->successor.clear();
bb->successor.push_back(stmt_block_list.front());

// push a rather empty-ish basic block, which will branch to the next basic block, or the next statement.
return_list.push_back(bb);
work_list.pop_front();
// now insert the pending blocks to be processed at the front of the work_list
work_list.insert(work_list.begin(), stmt_block_list.begin(), stmt_block_list.end());
}
else {
return_list.push_back(bb);
work_list.pop_front();
}
}
else if (isa<if_stmt>(bb->parent)) {
bb->name = "if" + bb->name;

if_stmt::Ptr if_stmt_ = to<if_stmt>(bb->parent);
// assign the if condition to the basic block
bb->branch_expr = if_stmt_->cond;

// create a exit block
auto exit_bb = std::make_shared<basic_block>("exit" + std::to_string(basic_block_count));
// assign it a empty stmt_block as parent
exit_bb->parent = std::make_shared<stmt_block>();
// mark the basic block as exit block
exit_bb->is_exit_block = true;
// set the ast depth of the basic block
exit_bb->ast_depth = bb->ast_depth;
// check if this is the last block, if yes the successor will be empty
if (bb->successor.size()) {
// set the successor to the block that if_stmt successor pointer to earlier
exit_bb->successor.push_back(bb->successor.front());
// clear the successor block from the if_stmt
bb->successor.clear();
}
// remove the if from the work_list
work_list.pop_front();
// push the exit block to the work_list
work_list.push_front(exit_bb);
std::cerr << "inside if handler: " << bb->name << "\n";
// if there is a then_stmt, create a basic block for it
if (to<stmt_block>(if_stmt_->then_stmt)->stmts.size() != 0) {
auto then_bb = std::make_shared<basic_block>(std::to_string(++basic_block_count));
// set the parent of this block as the then stmts
then_bb->parent = if_stmt_->then_stmt;
// set the ast depth of the basic block
then_bb->ast_depth = bb->ast_depth;
// set the successor of this block to be the exit block
then_bb->successor.push_back(exit_bb);
// set the successor of the original if_stmt block to be this then block
bb->successor.push_back(then_bb);
// set the then branch ptr
bb->then_branch = then_bb;
// push the block to the work_list, to expand it further
work_list.push_front(then_bb);
std::cerr << "inside then" << "\n";
}
// if there is a else_stmt, create a basic block for it
if (to<stmt_block>(if_stmt_->else_stmt)->stmts.size() != 0) {
auto else_bb = std::make_shared<basic_block>(std::to_string(++basic_block_count));
// set the parent of this block as the else stmts
else_bb->parent = if_stmt_->else_stmt;
// set the ast depth of the basic block
else_bb->ast_depth = bb->ast_depth;
// set the successor of this block to be the exit block
else_bb->successor.push_back(exit_bb);
// set the successor of the orignal if_stmt block to be this else block
bb->successor.push_back(else_bb);
// set the else branch ptr
bb->else_branch = else_bb;
// push the block to the work_list, to expand it further
work_list.insert(work_list.begin() + 1, else_bb);
std::cerr << "inside else" << "\n";
}

// if there is no then/else block, then have the exit block as successor as well.
if (bb->successor.size() <= 1) bb->successor.push_back(exit_bb);

// set the missing block as the exit block
if (!bb->then_branch) bb->then_branch = exit_bb;
else if (!bb->else_branch) bb->else_branch = exit_bb;

// set the exit block of this if stmt
bb->exit_block = exit_bb;

return_list.push_back(bb);
}
else if (isa<block::expr_stmt>(bb->parent)) {
bb->name = "expr" + bb->name;
return_list.push_back(bb);
work_list.pop_front();
}
else if (isa<block::decl_stmt>(bb->parent)) {
bb->name = "decl" + bb->name;
return_list.push_back(bb);
work_list.pop_front();
}
else if (isa<block::label_stmt>(bb->parent)) {
bb->name = "label" + bb->name;
return_list.push_back(bb);
work_list.pop_front();
}
else if (isa<block::goto_stmt>(bb->parent)) {
bb->name = "goto" + bb->name;
return_list.push_back(bb);
work_list.pop_front();
}
else if (isa<block::return_stmt>(bb->parent)) {
bb->name = "return" + bb->name;
return_list.push_back(bb);
work_list.pop_front();
}

basic_block_count++;
}

// step 4: resolve goto calls to successors of labels
for (auto bb: return_list) {
if (isa<block::goto_stmt>(bb->parent)) {
auto goto_source = std::find_if(return_list.begin(), return_list.end(),
[bb](std::shared_ptr<basic_block> bb_l) {
if (isa<label_stmt>(bb_l->parent)) {
return to<label_stmt>(bb_l->parent)->label1 == to<goto_stmt>(bb->parent)->label1;
}
return false;
});
if (goto_source != return_list.end()) {
bb->successor.clear();
bb->successor.push_back(*goto_source);
}
}
}

// step 5: populate the predecessors
for (auto bb: return_list) {
for (auto succ: bb->successor) {
succ->predecessor.push_back(bb);
}
}

// step 6: assign each basic_block an id
for (unsigned int i = 0; i < return_list.size(); i++) {
return_list[i]->id = i;
}

// step 7: populate the ast -> bb map
for (auto bb: return_list) {
bb->ast_to_basic_block_map[bb->parent] = bb;
}

return return_list;
}
Loading