1 #include "semantic/DataflowAnalysis.h"
5 #include <unordered_map>
9 #include "diagnostics/Location.h"
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()) {
19 auto cfg = cfgBuilder->build(method->body());
21 ReachabilityCheck(cfg);
22 LiveVariableAnalysis(cfg);
24 if(method->returnTy().type) {
25 FiniteLengthReturn(cfg);
29 for(
auto method : classDecl->constructors()) {
31 auto cfg = cfgBuilder->build(method->body());
33 ReachabilityCheck(cfg);
34 LiveVariableAnalysis(cfg);
41 void DFA::FiniteLengthReturn(
const CFGNode* node)
const {
42 if(node->hasBeenVisited()) {
46 node->setVisited(
true);
48 if(node->isReturnNode()) {
52 if(node->getChildren().begin() == node->getChildren().end()) {
54 diag.ReportError(
SourceRange{}) <<
" Missing return statement here.";
56 diag.ReportError(node->location().value())
57 <<
" Missing return statement here.";
63 for(
auto child : node->getChildren()) {
64 FiniteLengthReturn(child);
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) {
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()) {
87 diag.ReportError(n->location().value()) <<
"Unreachable statement";
89 diag.ReportError(node->location().value()) <<
"Unreachable statement";
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};
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());
109 dyn_cast_or_null<
const ast::exprnode::BinaryOp>(expr->tail())) {
110 if(assignOp->opType() == ast::exprnode::BinaryOp::OpType::Assignment) {
111 varDecl = assignOp->getVarAssigned();
117 for(
auto child : n->getChildren()) {
118 if(in[child].count(varDecl) > 0) {
125 diag.ReportWarning(n->location().value())
126 <<
"Dead assignment: " << varDecl->name();
128 diag.ReportWarning(n->location().value())
129 <<
"Dead assignment: " << varDecl->name();
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;
142 std::pmr::set<
const ast::VarDecl*> newIn{alloc};
143 for(
auto child : n->getChildren()) {
144 for(
auto v : in[child]) {
148 if(std::holds_alternative<
const ast::Expr*>(n->getData())) {
149 auto expr = std::get<
const ast::Expr*>(n->getData());
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());
155 newIn.erase(assignOp->getVarAssigned());
158 getLiveVariables(expr, newIn);
161 getLiveVariables(expr, newIn);
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);
167 diag.ReportError(varDecl->location()) <<
"Variable " << varDecl->name()
168 <<
" is used in its initializor";
170 newIn.erase(varDecl);
174 for(
auto parent : n->getParents()) {
179 std::pmr::set<
const CFGNode*> next_next{alloc};
180 LiveVariableAnalysisHelper(in, next, next_next);
183 int DFA::getLiveVariables(
const ast::
Expr* expr,
184 std::pmr::set<
const ast::VarDecl*>& live_vars,
185 const ast::
VarDecl* decl)
const {
187 for(
auto node : expr->nodes()) {
188 if(
auto exprValue = dyn_cast_or_null<
const ast::exprnode::ExprValue>(node)) {
190 dyn_cast_or_null<
const ast::VarDecl>(exprValue->decl())) {
191 if(decl == varDecl) {
194 live_vars.insert(varDecl);
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;
207 if(n->isStart() && !n->isReturnNode()) {
209 }
else if(!n->isReturnNode()) {
210 for(
auto child : n->getParents()) {
217 if(newOut != out[n]) {
219 for(
auto child : n->getChildren()) {
224 std::pmr::set<
const CFGNode*> next_next{alloc};
225 ReachabilityCheckHelper(out, next, next_next);
228 void DFA::getAllNodes(
const CFGNode* node,
229 std::pmr::set<
const CFGNode*>& list)
const {
230 if(list.count(node) > 0) {
234 for(
auto child : node->getChildren()) {
235 getAllNodes(child, list);