| // dataflow.cc -- Go frontend dataflow. |
| |
| // Copyright 2009 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| #include "go-system.h" |
| |
| #include "gogo.h" |
| #include "expressions.h" |
| #include "statements.h" |
| #include "dataflow.h" |
| |
| // This class is used to traverse the tree to look for uses of |
| // variables. |
| |
| class Dataflow_traverse_expressions : public Traverse |
| { |
| public: |
| Dataflow_traverse_expressions(Dataflow* dataflow, Statement* statement) |
| : Traverse(traverse_blocks | traverse_expressions), |
| dataflow_(dataflow), statement_(statement) |
| { } |
| |
| protected: |
| // Only look at top-level expressions: do not descend into blocks. |
| // They will be examined via Dataflow_traverse_statements. |
| int |
| block(Block*) |
| { return TRAVERSE_SKIP_COMPONENTS; } |
| |
| int |
| expression(Expression**); |
| |
| private: |
| // The dataflow information. |
| Dataflow* dataflow_; |
| // The Statement in which we are looking. |
| Statement* statement_; |
| }; |
| |
| // Given an expression, return the Named_object that it refers to, if |
| // it is a local variable. |
| |
| static Named_object* |
| get_var(Expression* expr) |
| { |
| Var_expression* ve = expr->var_expression(); |
| if (ve == NULL) |
| return NULL; |
| Named_object* no = ve->named_object(); |
| go_assert(no->is_variable() || no->is_result_variable()); |
| if (no->is_variable() && no->var_value()->is_global()) |
| return NULL; |
| return no; |
| } |
| |
| // Look for a reference to a variable in an expression. |
| |
| int |
| Dataflow_traverse_expressions::expression(Expression** expr) |
| { |
| Named_object* no = get_var(*expr); |
| if (no != NULL) |
| this->dataflow_->add_ref(no, this->statement_); |
| return TRAVERSE_CONTINUE; |
| } |
| |
| // This class is used to handle an assignment statement. |
| |
| class Dataflow_traverse_assignment : public Traverse_assignments |
| { |
| public: |
| Dataflow_traverse_assignment(Dataflow* dataflow, Statement* statement) |
| : dataflow_(dataflow), statement_(statement) |
| { } |
| |
| protected: |
| void |
| initialize_variable(Named_object*); |
| |
| void |
| assignment(Expression** lhs, Expression** rhs); |
| |
| void |
| value(Expression**, bool, bool); |
| |
| private: |
| // The dataflow information. |
| Dataflow* dataflow_; |
| // The Statement in which we are looking. |
| Statement* statement_; |
| }; |
| |
| // Handle a variable initialization. |
| |
| void |
| Dataflow_traverse_assignment::initialize_variable(Named_object* var) |
| { |
| Expression* init = var->var_value()->init(); |
| this->dataflow_->add_def(var, init, this->statement_, true); |
| if (init != NULL) |
| { |
| Expression* e = init; |
| this->value(&e, true, true); |
| go_assert(e == init); |
| } |
| } |
| |
| // Handle an assignment in a statement. |
| |
| void |
| Dataflow_traverse_assignment::assignment(Expression** plhs, Expression** prhs) |
| { |
| Named_object* no = get_var(*plhs); |
| if (no != NULL) |
| { |
| Expression* rhs = prhs == NULL ? NULL : *prhs; |
| this->dataflow_->add_def(no, rhs, this->statement_, false); |
| } |
| else |
| { |
| // If this is not a variable it may be some computed lvalue, and |
| // we want to look for references to variables in that lvalue. |
| this->value(plhs, false, false); |
| } |
| if (prhs != NULL) |
| this->value(prhs, true, false); |
| } |
| |
| // Handle a value in a statement. |
| |
| void |
| Dataflow_traverse_assignment::value(Expression** pexpr, bool, bool) |
| { |
| Named_object* no = get_var(*pexpr); |
| if (no != NULL) |
| this->dataflow_->add_ref(no, this->statement_); |
| else |
| { |
| Dataflow_traverse_expressions dte(this->dataflow_, this->statement_); |
| Expression::traverse(pexpr, &dte); |
| } |
| } |
| |
| // This class is used to traverse the tree to look for statements. |
| |
| class Dataflow_traverse_statements : public Traverse |
| { |
| public: |
| Dataflow_traverse_statements(Dataflow* dataflow) |
| : Traverse(traverse_statements), |
| dataflow_(dataflow) |
| { } |
| |
| protected: |
| int |
| statement(Block*, size_t* pindex, Statement*); |
| |
| private: |
| // The dataflow information. |
| Dataflow* dataflow_; |
| }; |
| |
| // For each Statement, we look for expressions. |
| |
| int |
| Dataflow_traverse_statements::statement(Block* block, size_t* pindex, |
| Statement *statement) |
| { |
| Dataflow_traverse_assignment dta(this->dataflow_, statement); |
| if (!statement->traverse_assignments(&dta)) |
| { |
| Dataflow_traverse_expressions dte(this->dataflow_, statement); |
| statement->traverse(block, pindex, &dte); |
| } |
| return TRAVERSE_CONTINUE; |
| } |
| |
| // Compare variables. |
| |
| bool |
| Dataflow::Compare_vars::operator()(const Named_object* no1, |
| const Named_object* no2) const |
| { |
| if (no1->name() < no2->name()) |
| return true; |
| if (no1->name() > no2->name()) |
| return false; |
| |
| // We can have two different variables with the same name. |
| Location loc1 = no1->location(); |
| Location loc2 = no2->location(); |
| if (loc1 < loc2) |
| return false; |
| if (loc1 > loc2) |
| return true; |
| |
| if (no1 == no2) |
| return false; |
| |
| // We can't have two variables with the same name in the same |
| // location. |
| go_unreachable(); |
| } |
| |
| // Class Dataflow. |
| |
| Dataflow::Dataflow() |
| : defs_(), refs_() |
| { |
| } |
| |
| // Build the dataflow information. |
| |
| void |
| Dataflow::initialize(Gogo* gogo) |
| { |
| Dataflow_traverse_statements dts(this); |
| gogo->traverse(&dts); |
| } |
| |
| // Add a definition of a variable. |
| |
| void |
| Dataflow::add_def(Named_object* var, Expression* val, Statement* statement, |
| bool is_init) |
| { |
| Defs* defnull = NULL; |
| std::pair<Defmap::iterator, bool> ins = |
| this->defs_.insert(std::make_pair(var, defnull)); |
| if (ins.second) |
| ins.first->second = new Defs; |
| Def def; |
| def.statement = statement; |
| def.val = val; |
| def.is_init = is_init; |
| ins.first->second->push_back(def); |
| } |
| |
| // Add a reference to a variable. |
| |
| void |
| Dataflow::add_ref(Named_object* var, Statement* statement) |
| { |
| Refs* refnull = NULL; |
| std::pair<Refmap::iterator, bool> ins = |
| this->refs_.insert(std::make_pair(var, refnull)); |
| if (ins.second) |
| ins.first->second = new Refs; |
| Ref ref; |
| ref.statement = statement; |
| ins.first->second->push_back(ref); |
| } |
| |
| // Return the definitions of a variable. |
| |
| const Dataflow::Defs* |
| Dataflow::find_defs(Named_object* var) const |
| { |
| Defmap::const_iterator p = this->defs_.find(var); |
| if (p == this->defs_.end()) |
| return NULL; |
| else |
| return p->second; |
| } |
| |
| // Return the references of a variable. |
| |
| const Dataflow::Refs* |
| Dataflow::find_refs(Named_object* var) const |
| { |
| Refmap::const_iterator p = this->refs_.find(var); |
| if (p == this->refs_.end()) |
| return NULL; |
| else |
| return p->second; |
| } |