Skip to content

Commit

Permalink
[blocks] Add basic blocks for CFG representation of the AST
Browse files Browse the repository at this point in the history
This patch adds a basic_block class and a function to translate the AST
to a CFG representation. It also dumps the basic block to std::cerr in
builder_context.cpp for debugging

The algorithm to convert the AST to CFG uses a worklist to do so, it
first creates basic blocks for all the top level AST elements, and then
using a worklist iteratively expands these top level AST elements,
adding more basic blocks between them.

bb1 ---> bb2 ==> bb1 ---> (bb-a1...bb-an) ---> bb2

It also pads if statement blocks with an exit blocks. This makes it
easier to handle loops, as we now have a single entry/exit into the if
block.

              |----<then_block>----|
<if_block>----|                    |----<exit_block>
	      |----<else_block>----|

* buildit input source code
dyn_var<int> a = 0;
for (dyn_var<int> c = 0; c < 100; c = c + 3) {
	for (dyn_var<int> b = 0; b < 10; b = b + 1) {
		a = a + b;
	}
}

* output of std::cerr, dump of the generated basic blocks
++++++ basic blocks ++++++
0:decl0:  ; 0
  br decl1,
1:decl1:  ; decl0, 0
  br label2,
2:label2:  ; decl1, goto13, 0
  br if3,
3:if3:  ; label2, 0
  LT_EXPR
  VAR_EXPR
    VAR (var1)
  INT_CONST (100)
  br stmt8, stmtexit7,
4:stmt8:  ; if3, 0
  br decl9,
5:decl9:  ; stmt8, 1
  br label10,
6:label10:  ; decl9, goto21, 1
  br if11,
7:if11:  ; label10, 1
  LT_EXPR
  VAR_EXPR
    VAR (var2)
  INT_CONST (10)
  br stmt18, stmtexit17,
8:stmt18:  ; if11, 1
  br expr19,
9:expr19:  ; stmt18, 2
  br expr20,
10:expr20:  ; expr19, 2
  br goto21,
11:goto21:  ; expr20, 2
  br label10,
12:stmtexit17:  ; if11, 1
  br expr12,
13:expr12:  ; stmtexit17, 1
  br goto13,
14:goto13:  ; expr12, 1
  br label2,
15:stmtexit7:  ; if3, 0
  br
++++++ basic blocks ++++++
  • Loading branch information
VedantParanjape committed Jan 30, 2024
1 parent f9e418d commit 1743ea5
Show file tree
Hide file tree
Showing 4 changed files with 273 additions and 2 deletions.
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
1 change: 1 addition & 0 deletions include/builder/builder_context.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#ifndef BUILDER_CONTEXT
#define BUILDER_CONTEXT
#include "blocks/basic_blocks.h"
#include "blocks/expr.h"
#include "blocks/stmt.h"
#include "builder/forward_declarations.h"
Expand Down
210 changes: 210 additions & 0 deletions src/blocks/basic_blocks.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
#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;
}
33 changes: 31 additions & 2 deletions src/builder/builder_context.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,28 @@ block::stmt::Ptr builder_context::extract_ast_from_function_impl(void) {
if (feature_unstructured)
return ast;

basic_block::cfg_block BBs = generate_basic_blocks(block::to<block::stmt_block>(ast));
std::cerr << "++++++ basic blocks ++++++ \n";
for (auto bb: BBs) {
std::cerr << bb->id << ":" << bb->name << ":" << " ; ";
for (auto pred: bb->predecessor) {
std::cerr << pred->name << ", ";
}
std::cerr << bb->ast_depth;
std::cerr << "\n";
if (bb->branch_expr) {
std::cerr << " ";
bb->branch_expr->dump(std::cerr, 0);
}
std::cerr << " ";
std::cerr << "br ";
for (auto branches: bb->successor) {
std::cerr << branches->name << ", ";
}
std::cerr << "\n";
}
std::cerr << "++++++ basic blocks ++++++ \n";

block::loop_finder finder;
finder.ast = ast;
ast->accept(&finder);
Expand Down Expand Up @@ -405,7 +427,6 @@ block::stmt::Ptr builder_context::extract_ast_from_function_internal(std::vector
ret_ast = ast;
} catch (LoopBackException &e) {
current_builder_context = nullptr;

block::goto_stmt::Ptr goto_stmt = std::make_shared<block::goto_stmt>();
goto_stmt->static_offset.clear();
goto_stmt->temporary_label_number = e.static_offset;
Expand All @@ -421,7 +442,15 @@ block::stmt::Ptr builder_context::extract_ast_from_function_internal(std::vector
add_stmt_to_current_block(goto_stmt, false);
} else {
for (unsigned int i = e.child_id; i < e.parent->stmts.size(); i++) {
add_stmt_to_current_block(e.parent->stmts[i], false);
if (isa<block::goto_stmt>(e.parent->stmts[i])) {
block::goto_stmt::Ptr goto_stmt = std::make_shared<block::goto_stmt>();
goto_stmt->static_offset.clear();
goto_stmt->temporary_label_number = to<block::goto_stmt>(e.parent->stmts[i])->temporary_label_number;
add_stmt_to_current_block(goto_stmt, false);
}
else {
add_stmt_to_current_block(e.parent->stmts[i], false);
}
}
}
ret_ast = ast;
Expand Down

0 comments on commit 1743ea5

Please sign in to comment.