Joos1W Compiler Framework
All Classes Functions Typedefs Pages
DataflowAnalysis.cc
1 #include "semantic/DataflowAnalysis.h"
2 
3 #include <algorithm>
4 #include <iostream>
5 #include <unordered_map>
6 #include <variant>
7 
8 #include "ast/Decl.h"
9 #include "diagnostics/Location.h"
10 
11 namespace semantic {
12 
13 using DFA = DataflowAnalysis;
14 void DFA::Check() const {
15  for(auto cu : lu->compliationUnits()) {
16  if(auto classDecl = dyn_cast_or_null<ast::ClassDecl>(cu->body())) {
17  for(auto method : classDecl->methods()) {
18  if(method->body()) {
19  auto cfg = cfgBuilder->build(method->body());
20  // cfg->printDot(std::cout);
21  ReachabilityCheck(cfg);
22  LiveVariableAnalysis(cfg);
23 
24  if(method->returnTy().type) {
25  FiniteLengthReturn(cfg);
26  }
27  }
28  }
29  for(auto method : classDecl->constructors()) {
30  if(method->body()) {
31  auto cfg = cfgBuilder->build(method->body());
32  // cfg->printDot(std::cout);
33  ReachabilityCheck(cfg);
34  LiveVariableAnalysis(cfg);
35  }
36  }
37  }
38  }
39 }
40 
41 void DFA::FiniteLengthReturn(const CFGNode* node) const {
42  if(node->hasBeenVisited()) {
43  return;
44  }
45 
46  node->setVisited(true);
47 
48  if(node->isReturnNode()) {
49  return;
50  }
51 
52  if(node->getChildren().begin() == node->getChildren().end()) {
53  if(std::holds_alternative<CFGNode::EmptyExpr>(node->getData())) {
54  diag.ReportError(SourceRange{}) << " Missing return statement here.";
55  } else {
56  diag.ReportError(node->location().value())
57  << " Missing return statement here.";
58  }
59 
60  return;
61  }
62 
63  for(auto child : node->getChildren()) {
64  FiniteLengthReturn(child);
65  }
66 }
67 
68 void DFA::ReachabilityCheck(const CFGNode* node) const {
69  std::unordered_map<const CFGNode*, bool> out;
70  std::pmr::set<const CFGNode*> worklist{alloc};
71  getAllNodes(node, worklist);
72  for(auto n : worklist) {
73  out[n] = true;
74  }
75  std::pmr::set<const CFGNode*> next{alloc};
76  ReachabilityCheckHelper(out, worklist, next);
77  for(auto n : worklist) {
78  bool reachable = n == node;
79  for(auto parent : n->getParents()) {
80  if(out[parent]) {
81  reachable = true;
82  break;
83  }
84  }
85  if(!reachable) {
86  if(n->location()) {
87  diag.ReportError(n->location().value()) << "Unreachable statement";
88  } else {
89  diag.ReportError(node->location().value()) << "Unreachable statement";
90  }
91  }
92  }
93 }
94 
95 void DFA::LiveVariableAnalysis(const CFGNode* node) const {
96  std::unordered_map<const CFGNode*, std::pmr::set<const ast::VarDecl*>> in;
97  std::pmr::set<const CFGNode*> worklist{alloc};
98  getAllNodes(node, worklist);
99  for(auto n : worklist) {
100  in[n] = std::pmr::set<const ast::VarDecl*>{alloc};
101  }
102  std::pmr::set<const CFGNode*> next{alloc};
103  LiveVariableAnalysisHelper(in, worklist, next);
104  for(auto n : worklist) {
105  const ast::VarDecl* varDecl = nullptr;
106  if(std::holds_alternative<const ast::Expr*>(n->getData())) {
107  auto expr = std::get<const ast::Expr*>(n->getData());
108  if(auto assignOp =
109  dyn_cast_or_null<const ast::exprnode::BinaryOp>(expr->tail())) {
110  if(assignOp->opType() == ast::exprnode::BinaryOp::OpType::Assignment) {
111  varDecl = assignOp->getVarAssigned();
112  }
113  }
114  }
115  if(varDecl) {
116  bool isLive = false;
117  for(auto child : n->getChildren()) {
118  if(in[child].count(varDecl) > 0) {
119  isLive = true;
120  break;
121  }
122  }
123  if(!isLive) {
124  if(n->location()) {
125  diag.ReportWarning(n->location().value())
126  << "Dead assignment: " << varDecl->name();
127  } else {
128  diag.ReportWarning(n->location().value())
129  << "Dead assignment: " << varDecl->name();
130  }
131  }
132  }
133  }
134 }
135 
136 void DFA::LiveVariableAnalysisHelper(
137  std::unordered_map<const CFGNode*, std::pmr::set<const ast::VarDecl*>>& in,
138  std::pmr::set<const CFGNode*>& cur,
139  std::pmr::set<const CFGNode*>& next) const {
140  if(cur.empty()) return;
141  for(auto n : cur) {
142  std::pmr::set<const ast::VarDecl*> newIn{alloc};
143  for(auto child : n->getChildren()) {
144  for(auto v : in[child]) {
145  newIn.insert(v);
146  }
147  }
148  if(std::holds_alternative<const ast::Expr*>(n->getData())) {
149  auto expr = std::get<const ast::Expr*>(n->getData());
150  if(auto assignOp =
151  dyn_cast_or_null<const ast::exprnode::BinaryOp>(expr->tail())) {
152  if(assignOp->opType() == ast::exprnode::BinaryOp::OpType::Assignment) {
153  int times = getLiveVariables(expr, newIn, assignOp->getVarAssigned());
154  if (times == 1) {
155  newIn.erase(assignOp->getVarAssigned());
156  }
157  } else {
158  getLiveVariables(expr, newIn);
159  }
160  } else {
161  getLiveVariables(expr, newIn);
162  }
163  } else if(std::holds_alternative<const ast::VarDecl*>(n->getData())) {
164  auto varDecl = std::get<const ast::VarDecl*>(n->getData());
165  int times = getLiveVariables(varDecl->init(), newIn, varDecl);
166  if(times > 0) {
167  diag.ReportError(varDecl->location()) << "Variable " << varDecl->name()
168  << " is used in its initializor";
169  }
170  newIn.erase(varDecl);
171  }
172  if(newIn != in[n]) {
173  in[n] = newIn;
174  for(auto parent : n->getParents()) {
175  next.insert(parent);
176  }
177  }
178  }
179  std::pmr::set<const CFGNode*> next_next{alloc};
180  LiveVariableAnalysisHelper(in, next, next_next);
181 }
182 
183 int DFA::getLiveVariables(const ast::Expr* expr,
184  std::pmr::set<const ast::VarDecl*>& live_vars,
185  const ast::VarDecl* decl) const {
186  int times = 0;
187  for(auto node : expr->nodes()) {
188  if(auto exprValue = dyn_cast_or_null<const ast::exprnode::ExprValue>(node)) {
189  if(auto varDecl =
190  dyn_cast_or_null<const ast::VarDecl>(exprValue->decl())) {
191  if(decl == varDecl) {
192  times++;
193  }
194  live_vars.insert(varDecl);
195  }
196  }
197  }
198  return times;
199 }
200 
201 void DFA::ReachabilityCheckHelper(std::unordered_map<const CFGNode*, bool>& out,
202  std::pmr::set<const CFGNode*>& cur,
203  std::pmr::set<const CFGNode*>& next) const {
204  if(cur.empty()) return;
205  for(auto n : cur) {
206  bool newOut = false;
207  if(n->isStart() && !n->isReturnNode()) {
208  newOut = true;
209  } else if(!n->isReturnNode()) {
210  for(auto child : n->getParents()) {
211  if(out[child]) { // if it is reachable from a parent, it is reachable
212  newOut = true;
213  break;
214  }
215  }
216  }
217  if(newOut != out[n]) {
218  out[n] = newOut;
219  for(auto child : n->getChildren()) {
220  next.insert(child);
221  }
222  }
223  }
224  std::pmr::set<const CFGNode*> next_next{alloc};
225  ReachabilityCheckHelper(out, next, next_next);
226 }
227 
228 void DFA::getAllNodes(const CFGNode* node,
229  std::pmr::set<const CFGNode*>& list) const {
230  if(list.count(node) > 0) {
231  return;
232  }
233  list.insert(node);
234  for(auto child : node->getChildren()) {
235  getAllNodes(child, list);
236  }
237 }
238 
239 } // namespace semantic