Skip to content

Commit 1743ea5

Browse files
[blocks] Add basic blocks for CFG representation of the AST
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 ++++++
1 parent f9e418d commit 1743ea5

File tree

4 files changed

+273
-2
lines changed

4 files changed

+273
-2
lines changed

include/blocks/basic_blocks.h

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
#ifndef BASIC_BLOCKS_H
2+
#define BASIC_BLOCKS_H
3+
#include "blocks/stmt.h"
4+
#include <vector>
5+
#include <deque>
6+
#include <string>
7+
#include <map>
8+
9+
class basic_block {
10+
public:
11+
typedef std::vector<std::shared_ptr<basic_block>> cfg_block;
12+
basic_block(std::string label): name(label) {};
13+
14+
cfg_block predecessor;
15+
cfg_block successor;
16+
block::expr::Ptr branch_expr;
17+
std::shared_ptr<basic_block> then_branch;
18+
std::shared_ptr<basic_block> else_branch;
19+
std::shared_ptr<basic_block> exit_block;
20+
bool is_exit_block = false;
21+
block::stmt::Ptr parent;
22+
unsigned int ast_index;
23+
unsigned int ast_depth;
24+
unsigned int id;
25+
std::string name;
26+
static std::map<block::stmt::Ptr, std::shared_ptr<basic_block>> ast_to_basic_block_map;
27+
};
28+
29+
basic_block::cfg_block generate_basic_blocks(block::stmt_block::Ptr ast);
30+
31+
#endif

include/builder/builder_context.h

+1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#ifndef BUILDER_CONTEXT
22
#define BUILDER_CONTEXT
3+
#include "blocks/basic_blocks.h"
34
#include "blocks/expr.h"
45
#include "blocks/stmt.h"
56
#include "builder/forward_declarations.h"

src/blocks/basic_blocks.cpp

+210
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
1+
#include "blocks/basic_blocks.h"
2+
#include <algorithm>
3+
4+
using namespace block;
5+
std::map<block::stmt::Ptr, std::shared_ptr<basic_block>> basic_block::ast_to_basic_block_map = {};
6+
7+
basic_block::cfg_block generate_basic_blocks(block::stmt_block::Ptr ast) {
8+
std::deque<std::shared_ptr<basic_block>> work_list;
9+
basic_block::cfg_block return_list;
10+
int basic_block_count = 0;
11+
12+
// step 1: fill the work_list
13+
unsigned int ast_index_counter = 0;
14+
for (auto st: ast->stmts) {
15+
auto bb = std::make_shared<basic_block>(std::to_string(basic_block_count));
16+
bb->parent = st;
17+
bb->ast_index = ast_index_counter++;
18+
bb->ast_depth = 0;
19+
work_list.push_back(bb);
20+
basic_block_count++;
21+
}
22+
23+
// step 2: add successors
24+
for (unsigned i = 0; work_list.size() != 0 && i < work_list.size() - 1; i++) {
25+
work_list[i]->successor.push_back(work_list[i+1]);
26+
}
27+
28+
// step 3: process blocks: every xx_stmt type statement is made out into a basic block
29+
while (work_list.size()) {
30+
auto bb = work_list.front();
31+
32+
if (isa<block::stmt_block>(bb->parent)) {
33+
ast_index_counter = 0;
34+
stmt_block::Ptr stmt_block_ = to<stmt_block>(bb->parent);
35+
bb->name = "stmt" + bb->name;
36+
37+
if (stmt_block_->stmts.size() > 0) {
38+
basic_block::cfg_block stmt_block_list;
39+
40+
// convert all statements of this stmt_block into a basic block
41+
for (auto st: stmt_block_->stmts) {
42+
stmt_block_list.push_back(std::make_shared<basic_block>(std::to_string(basic_block_count++)));
43+
stmt_block_list.back()->parent = st;
44+
stmt_block_list.back()->ast_index = ast_index_counter++;
45+
stmt_block_list.back()->ast_depth = bb->ast_depth + 1;
46+
}
47+
48+
// set the basic block successors
49+
for (unsigned i = 0; stmt_block_list.size() != 0 && i < stmt_block_list.size() - 1; i++) {
50+
stmt_block_list[i]->successor.push_back(stmt_block_list[i+1]);
51+
}
52+
53+
// since we insert these stmts between bb1 ---> bb2 ==> bb1 ---> (bb-a1...bb-an) ---> bb2
54+
// point the successor of the stmt_block_list to the basic block that bb1's successor
55+
// pointed to. After this, clear the bb1's successor and push the front of stmt_block_list
56+
// to bb1's successor list.
57+
stmt_block_list.back()->successor.push_back(bb->successor.front());
58+
bb->successor.clear();
59+
bb->successor.push_back(stmt_block_list.front());
60+
61+
// push a rather empty-ish basic block, which will branch to the next basic block, or the next statement.
62+
return_list.push_back(bb);
63+
work_list.pop_front();
64+
// now insert the pending blocks to be processed at the front of the work_list
65+
work_list.insert(work_list.begin(), stmt_block_list.begin(), stmt_block_list.end());
66+
}
67+
else {
68+
return_list.push_back(bb);
69+
work_list.pop_front();
70+
}
71+
}
72+
else if (isa<if_stmt>(bb->parent)) {
73+
bb->name = "if" + bb->name;
74+
75+
if_stmt::Ptr if_stmt_ = to<if_stmt>(bb->parent);
76+
// assign the if condition to the basic block
77+
bb->branch_expr = if_stmt_->cond;
78+
79+
// create a exit block
80+
auto exit_bb = std::make_shared<basic_block>("exit" + std::to_string(basic_block_count));
81+
// assign it a empty stmt_block as parent
82+
exit_bb->parent = std::make_shared<stmt_block>();
83+
// mark the basic block as exit block
84+
exit_bb->is_exit_block = true;
85+
// set the ast depth of the basic block
86+
exit_bb->ast_depth = bb->ast_depth;
87+
// check if this is the last block, if yes the successor will be empty
88+
if (bb->successor.size()) {
89+
// set the successor to the block that if_stmt successor pointer to earlier
90+
exit_bb->successor.push_back(bb->successor.front());
91+
// clear the successor block from the if_stmt
92+
bb->successor.clear();
93+
}
94+
// remove the if from the work_list
95+
work_list.pop_front();
96+
// push the exit block to the work_list
97+
work_list.push_front(exit_bb);
98+
std::cerr << "inside if handler: " << bb->name << "\n";
99+
// if there is a then_stmt, create a basic block for it
100+
if (to<stmt_block>(if_stmt_->then_stmt)->stmts.size() != 0) {
101+
auto then_bb = std::make_shared<basic_block>(std::to_string(++basic_block_count));
102+
// set the parent of this block as the then stmts
103+
then_bb->parent = if_stmt_->then_stmt;
104+
// set the ast depth of the basic block
105+
then_bb->ast_depth = bb->ast_depth;
106+
// set the successor of this block to be the exit block
107+
then_bb->successor.push_back(exit_bb);
108+
// set the successor of the original if_stmt block to be this then block
109+
bb->successor.push_back(then_bb);
110+
// set the then branch ptr
111+
bb->then_branch = then_bb;
112+
// push the block to the work_list, to expand it further
113+
work_list.push_front(then_bb);
114+
std::cerr << "inside then" << "\n";
115+
}
116+
// if there is a else_stmt, create a basic block for it
117+
if (to<stmt_block>(if_stmt_->else_stmt)->stmts.size() != 0) {
118+
auto else_bb = std::make_shared<basic_block>(std::to_string(++basic_block_count));
119+
// set the parent of this block as the else stmts
120+
else_bb->parent = if_stmt_->else_stmt;
121+
// set the ast depth of the basic block
122+
else_bb->ast_depth = bb->ast_depth;
123+
// set the successor of this block to be the exit block
124+
else_bb->successor.push_back(exit_bb);
125+
// set the successor of the orignal if_stmt block to be this else block
126+
bb->successor.push_back(else_bb);
127+
// set the else branch ptr
128+
bb->else_branch = else_bb;
129+
// push the block to the work_list, to expand it further
130+
work_list.insert(work_list.begin() + 1, else_bb);
131+
std::cerr << "inside else" << "\n";
132+
}
133+
134+
// if there is no then/else block, then have the exit block as successor as well.
135+
if (bb->successor.size() <= 1) bb->successor.push_back(exit_bb);
136+
137+
// set the missing block as the exit block
138+
if (!bb->then_branch) bb->then_branch = exit_bb;
139+
else if (!bb->else_branch) bb->else_branch = exit_bb;
140+
141+
// set the exit block of this if stmt
142+
bb->exit_block = exit_bb;
143+
144+
return_list.push_back(bb);
145+
}
146+
else if (isa<block::expr_stmt>(bb->parent)) {
147+
bb->name = "expr" + bb->name;
148+
return_list.push_back(bb);
149+
work_list.pop_front();
150+
}
151+
else if (isa<block::decl_stmt>(bb->parent)) {
152+
bb->name = "decl" + bb->name;
153+
return_list.push_back(bb);
154+
work_list.pop_front();
155+
}
156+
else if (isa<block::label_stmt>(bb->parent)) {
157+
bb->name = "label" + bb->name;
158+
return_list.push_back(bb);
159+
work_list.pop_front();
160+
}
161+
else if (isa<block::goto_stmt>(bb->parent)) {
162+
bb->name = "goto" + bb->name;
163+
return_list.push_back(bb);
164+
work_list.pop_front();
165+
}
166+
else if (isa<block::return_stmt>(bb->parent)) {
167+
bb->name = "return" + bb->name;
168+
return_list.push_back(bb);
169+
work_list.pop_front();
170+
}
171+
172+
basic_block_count++;
173+
}
174+
175+
// step 4: resolve goto calls to successors of labels
176+
for (auto bb: return_list) {
177+
if (isa<block::goto_stmt>(bb->parent)) {
178+
auto goto_source = std::find_if(return_list.begin(), return_list.end(),
179+
[bb](std::shared_ptr<basic_block> bb_l) {
180+
if (isa<label_stmt>(bb_l->parent)) {
181+
return to<label_stmt>(bb_l->parent)->label1 == to<goto_stmt>(bb->parent)->label1;
182+
}
183+
return false;
184+
});
185+
if (goto_source != return_list.end()) {
186+
bb->successor.clear();
187+
bb->successor.push_back(*goto_source);
188+
}
189+
}
190+
}
191+
192+
// step 5: populate the predecessors
193+
for (auto bb: return_list) {
194+
for (auto succ: bb->successor) {
195+
succ->predecessor.push_back(bb);
196+
}
197+
}
198+
199+
// step 6: assign each basic_block an id
200+
for (unsigned int i = 0; i < return_list.size(); i++) {
201+
return_list[i]->id = i;
202+
}
203+
204+
// step 7: populate the ast -> bb map
205+
for (auto bb: return_list) {
206+
bb->ast_to_basic_block_map[bb->parent] = bb;
207+
}
208+
209+
return return_list;
210+
}

src/builder/builder_context.cpp

+31-2
Original file line numberDiff line numberDiff line change
@@ -306,6 +306,28 @@ block::stmt::Ptr builder_context::extract_ast_from_function_impl(void) {
306306
if (feature_unstructured)
307307
return ast;
308308

309+
basic_block::cfg_block BBs = generate_basic_blocks(block::to<block::stmt_block>(ast));
310+
std::cerr << "++++++ basic blocks ++++++ \n";
311+
for (auto bb: BBs) {
312+
std::cerr << bb->id << ":" << bb->name << ":" << " ; ";
313+
for (auto pred: bb->predecessor) {
314+
std::cerr << pred->name << ", ";
315+
}
316+
std::cerr << bb->ast_depth;
317+
std::cerr << "\n";
318+
if (bb->branch_expr) {
319+
std::cerr << " ";
320+
bb->branch_expr->dump(std::cerr, 0);
321+
}
322+
std::cerr << " ";
323+
std::cerr << "br ";
324+
for (auto branches: bb->successor) {
325+
std::cerr << branches->name << ", ";
326+
}
327+
std::cerr << "\n";
328+
}
329+
std::cerr << "++++++ basic blocks ++++++ \n";
330+
309331
block::loop_finder finder;
310332
finder.ast = ast;
311333
ast->accept(&finder);
@@ -405,7 +427,6 @@ block::stmt::Ptr builder_context::extract_ast_from_function_internal(std::vector
405427
ret_ast = ast;
406428
} catch (LoopBackException &e) {
407429
current_builder_context = nullptr;
408-
409430
block::goto_stmt::Ptr goto_stmt = std::make_shared<block::goto_stmt>();
410431
goto_stmt->static_offset.clear();
411432
goto_stmt->temporary_label_number = e.static_offset;
@@ -421,7 +442,15 @@ block::stmt::Ptr builder_context::extract_ast_from_function_internal(std::vector
421442
add_stmt_to_current_block(goto_stmt, false);
422443
} else {
423444
for (unsigned int i = e.child_id; i < e.parent->stmts.size(); i++) {
424-
add_stmt_to_current_block(e.parent->stmts[i], false);
445+
if (isa<block::goto_stmt>(e.parent->stmts[i])) {
446+
block::goto_stmt::Ptr goto_stmt = std::make_shared<block::goto_stmt>();
447+
goto_stmt->static_offset.clear();
448+
goto_stmt->temporary_label_number = to<block::goto_stmt>(e.parent->stmts[i])->temporary_label_number;
449+
add_stmt_to_current_block(goto_stmt, false);
450+
}
451+
else {
452+
add_stmt_to_current_block(e.parent->stmts[i], false);
453+
}
425454
}
426455
}
427456
ret_ast = ast;

0 commit comments

Comments
 (0)