Use backend interface for constant switch statements.
authorIan Lance Taylor <iant@google.com>
Wed, 6 Apr 2011 23:07:13 +0000 (23:07 +0000)
committerIan Lance Taylor <ian@gcc.gnu.org>
Wed, 6 Apr 2011 23:07:13 +0000 (23:07 +0000)
* go-gcc.cc (if_statement): Use build3_loc.
(Gcc_backend::switch_statement): New function.
(Gcc_backend::statement_list): New function.

From-SVN: r172066

gcc/go/ChangeLog
gcc/go/go-gcc.cc
gcc/go/gofrontend/backend.h
gcc/go/gofrontend/statements.cc
gcc/go/gofrontend/statements.h

index ff56bfe9883b3253020428406c0d6956c998a19c..3ee5fb10c0cda17e3b15763498e75021f923a068 100644 (file)
@@ -1,3 +1,9 @@
+2011-04-06  Ian Lance Taylor  <iant@google.com>
+
+       * go-gcc.cc (if_statement): Use build3_loc.
+       (Gcc_backend::switch_statement): New function.
+       (Gcc_backend::statement_list): New function.
+
 2011-04-06  Ian Lance Taylor  <iant@google.com>
 
        * go-gcc.cc (Gcc_backend::if_statement): New function.
index de689f8478e54747fa22f0c66af719bfdf73e5e8..f9efb3c9e9bf3c97212f831eada69ac87c892653 100644 (file)
@@ -180,6 +180,15 @@ class Gcc_backend : public Backend
   if_statement(Bexpression* condition, Bstatement* then_block,
               Bstatement* else_block, source_location);
 
+  Bstatement*
+  switch_statement(Bexpression* value,
+                  const std::vector<std::vector<Bexpression*> >& cases,
+                  const std::vector<Bstatement*>& statements,
+                  source_location);
+
+  Bstatement*
+  statement_list(const std::vector<Bstatement*>&);
+
   // Labels.
 
   Blabel*
@@ -310,12 +319,90 @@ Gcc_backend::if_statement(Bexpression* condition, Bstatement* then_block,
       || then_tree == error_mark_node
       || else_tree == error_mark_node)
     return this->make_statement(error_mark_node);
-  tree ret = build3(COND_EXPR, void_type_node, cond_tree, then_tree,
-                   else_tree);
-  SET_EXPR_LOCATION(ret, location);
+  tree ret = build3_loc(location, COND_EXPR, void_type_node, cond_tree,
+                       then_tree, else_tree);
   return this->make_statement(ret);
 }
 
+// Switch.
+
+Bstatement*
+Gcc_backend::switch_statement(
+    Bexpression* value,
+    const std::vector<std::vector<Bexpression*> >& cases,
+    const std::vector<Bstatement*>& statements,
+    source_location switch_location)
+{
+  gcc_assert(cases.size() == statements.size());
+
+  tree stmt_list = NULL_TREE;
+  std::vector<std::vector<Bexpression*> >::const_iterator pc = cases.begin();
+  for (std::vector<Bstatement*>::const_iterator ps = statements.begin();
+       ps != statements.end();
+       ++ps, ++pc)
+    {
+      if (pc->empty())
+       {
+         source_location loc = (*ps != NULL
+                                ? EXPR_LOCATION((*ps)->get_tree())
+                                : UNKNOWN_LOCATION);
+         tree label = create_artificial_label(loc);
+         tree c = build3_loc(loc, CASE_LABEL_EXPR, void_type_node, NULL_TREE,
+                             NULL_TREE, label);
+         append_to_statement_list(c, &stmt_list);
+       }
+      else
+       {
+         for (std::vector<Bexpression*>::const_iterator pcv = pc->begin();
+              pcv != pc->end();
+              ++pcv)
+           {
+             tree t = (*pcv)->get_tree();
+             if (t == error_mark_node)
+               return this->make_statement(error_mark_node);
+             source_location loc = EXPR_LOCATION(t);
+             tree label = create_artificial_label(loc);
+             tree c = build3_loc(loc, CASE_LABEL_EXPR, void_type_node,
+                                 (*pcv)->get_tree(), NULL_TREE, label);
+             append_to_statement_list(c, &stmt_list);
+           }
+       }
+
+      if (*ps != NULL)
+       {
+         tree t = (*ps)->get_tree();
+         if (t == error_mark_node)
+           return this->make_statement(error_mark_node);
+         append_to_statement_list(t, &stmt_list);
+       }
+    }
+
+  tree tv = value->get_tree();
+  if (tv == error_mark_node)
+    return this->make_statement(error_mark_node);
+  tree t = build3_loc(switch_location, SWITCH_EXPR, void_type_node,
+                     tv, stmt_list, NULL_TREE);
+  return this->make_statement(t);
+}
+
+// List of statements.
+
+Bstatement*
+Gcc_backend::statement_list(const std::vector<Bstatement*>& statements)
+{
+  tree stmt_list = NULL_TREE;
+  for (std::vector<Bstatement*>::const_iterator p = statements.begin();
+       p != statements.end();
+       ++p)
+    {
+      tree t = (*p)->get_tree();
+      if (t == error_mark_node)
+       return this->make_statement(error_mark_node);
+      append_to_statement_list(t, &stmt_list);
+    }
+  return this->make_statement(stmt_list);
+}
+
 // Make a label.
 
 Blabel*
index a6c242680ff3459dc2ecbc0a1bb9126460086422..01f3cfa4c2bec586b81a3f58dec9484aabc2c9b7 100644 (file)
@@ -127,6 +127,23 @@ class Backend
   if_statement(Bexpression* condition, Bstatement* then_block,
               Bstatement* else_block, source_location) = 0;
 
+  // Create a switch statement where the case values are constants.
+  // CASES and STATEMENTS must have the same number of entries.  If
+  // VALUE matches any of the list in CASES[i], which will all be
+  // integers, then STATEMENTS[i] is executed.  STATEMENTS[i] will
+  // either end with a goto statement or will fall through into
+  // STATEMENTS[i + 1].  CASES[i] is empty for the default clause,
+  // which need not be last.
+  virtual Bstatement*
+  switch_statement(Bexpression* value,
+                  const std::vector<std::vector<Bexpression*> >& cases,
+                  const std::vector<Bstatement*>& statements,
+                  source_location) = 0;
+
+  // Create a single statement from a list of statements.
+  virtual Bstatement*
+  statement_list(const std::vector<Bstatement*>&) = 0;
+
   // Labels.
   
   // Create a new label.  NAME will be empty if this is a label
index 131001a9ce1f54c7c49bba33f8655ec1b8b64d1e..f84b2d4ae92311d067cd22f1700b026e1c3bd452 100644 (file)
@@ -2934,6 +2934,55 @@ Statement::make_if_statement(Expression* cond, Block* then_block,
   return new If_statement(cond, then_block, else_block, location);
 }
 
+// Class Case_clauses::Hash_integer_value.
+
+class Case_clauses::Hash_integer_value
+{
+ public:
+  size_t
+  operator()(Expression*) const;
+};
+
+size_t
+Case_clauses::Hash_integer_value::operator()(Expression* pe) const
+{
+  Type* itype;
+  mpz_t ival;
+  mpz_init(ival);
+  if (!pe->integer_constant_value(true, ival, &itype))
+    gcc_unreachable();
+  size_t ret = mpz_get_ui(ival);
+  mpz_clear(ival);
+  return ret;
+}
+
+// Class Case_clauses::Eq_integer_value.
+
+class Case_clauses::Eq_integer_value
+{
+ public:
+  bool
+  operator()(Expression*, Expression*) const;
+};
+
+bool
+Case_clauses::Eq_integer_value::operator()(Expression* a, Expression* b) const
+{
+  Type* atype;
+  Type* btype;
+  mpz_t aval;
+  mpz_t bval;
+  mpz_init(aval);
+  mpz_init(bval);
+  if (!a->integer_constant_value(true, aval, &atype)
+      || !b->integer_constant_value(true, bval, &btype))
+    gcc_unreachable();
+  bool ret = mpz_cmp(aval, bval) == 0;
+  mpz_clear(aval);
+  mpz_clear(bval);
+  return ret;
+}
+
 // Class Case_clauses::Case_clause.
 
 // Traversal.
@@ -3090,76 +3139,82 @@ Case_clauses::Case_clause::may_fall_through() const
   return this->statements_->may_fall_through();
 }
 
-// Build up the body of a SWITCH_EXPR.
+// Convert the case values and statements to the backend
+// representation.  BREAK_LABEL is the label which break statements
+// should branch to.  CASE_CONSTANTS is used to detect duplicate
+// constants.  *CASES should be passed as an empty vector; the values
+// for this case will be added to it.  If this is the default case,
+// *CASES will remain empty.  This returns the statement to execute if
+// one of these cases is selected.
 
-void
-Case_clauses::Case_clause::get_constant_tree(Translate_context* context,
-                                            Unnamed_label* break_label,
-                                            Case_constants* case_constants,
-                                            tree* stmt_list) const
+Bstatement*
+Case_clauses::Case_clause::get_backend(Translate_context* context,
+                                      Unnamed_label* break_label,
+                                      Case_constants* case_constants,
+                                      std::vector<Bexpression*>* cases) const
 {
   if (this->cases_ != NULL)
     {
+      gcc_assert(!this->is_default_);
       for (Expression_list::const_iterator p = this->cases_->begin();
           p != this->cases_->end();
           ++p)
        {
-         Type* itype;
-         mpz_t ival;
-         mpz_init(ival);
-         if (!(*p)->integer_constant_value(true, ival, &itype))
+         Expression* e = *p;
+         if (e->classification() != Expression::EXPRESSION_INTEGER)
            {
-             // Something went wrong.  This can happen with a
-             // negative constant and an unsigned switch value.
-             gcc_assert(saw_errors());
-             continue;
-           }
-         gcc_assert(itype != NULL);
-         tree type_tree = itype->get_tree(context->gogo());
-         tree val = Expression::integer_constant_tree(ival, type_tree);
-         mpz_clear(ival);
-
-         if (val != error_mark_node)
-           {
-             gcc_assert(TREE_CODE(val) == INTEGER_CST);
-
-             std::pair<Case_constants::iterator, bool> ins =
-               case_constants->insert(val);
-             if (!ins.second)
+             Type* itype;
+             mpz_t ival;
+             mpz_init(ival);
+             if (!(*p)->integer_constant_value(true, ival, &itype))
                {
-                 // Value was already present.
-                 warning_at(this->location_, 0,
-                            "duplicate case value will never match");
+                 // Something went wrong.  This can happen with a
+                 // negative constant and an unsigned switch value.
+                 gcc_assert(saw_errors());
                  continue;
                }
+             gcc_assert(itype != NULL);
+             e = Expression::make_integer(&ival, itype, e->location());
+             mpz_clear(ival);
+           }
 
-             tree label = create_artificial_label(this->location_);
-             append_to_statement_list(build3(CASE_LABEL_EXPR, void_type_node,
-                                             val, NULL_TREE, label),
-                                      stmt_list);
+         std::pair<Case_constants::iterator, bool> ins =
+           case_constants->insert(e);
+         if (!ins.second)
+           {
+             // Value was already present.
+             error_at(this->location_, "duplicate case in switch");
+             continue;
            }
+
+         tree case_tree = e->get_tree(context);
+         Bexpression* case_expr = tree_to_expr(case_tree);
+         cases->push_back(case_expr);
        }
     }
 
-  if (this->is_default_)
-    {
-      tree label = create_artificial_label(this->location_);
-      append_to_statement_list(build3(CASE_LABEL_EXPR, void_type_node,
-                                     NULL_TREE, NULL_TREE, label),
-                              stmt_list);
-    }
+  Bstatement* statements;
+  if (this->statements_ == NULL)
+    statements = NULL;
+  else
+    statements = tree_to_stat(this->statements_->get_tree(context));
 
-  if (this->statements_ != NULL)
-    {
-      tree block_tree = this->statements_->get_tree(context);
-      if (block_tree != error_mark_node)
-       append_to_statement_list(block_tree, stmt_list);
-    }
+  Bstatement* break_stat;
+  if (this->is_fallthrough_)
+    break_stat = NULL;
+  else
+    break_stat = break_label->get_goto(context, this->location_);
 
-  if (!this->is_fallthrough_)
+  if (statements == NULL)
+    return break_stat;
+  else if (break_stat == NULL)
+    return statements;
+  else
     {
-      Bstatement* g = break_label->get_goto(context, this->location_);
-      append_to_statement_list(stat_to_tree(g), stmt_list);
+      std::vector<Bstatement*> list(2);
+      list[0] = statements;
+      list[1] = break_stat;
+      return context->backend()->statement_list(list);
     }
 }
 
@@ -3297,20 +3352,32 @@ Case_clauses::may_fall_through() const
   return !found_default;
 }
 
-// Return a tree when all case expressions are constants.
+// Convert the cases to the backend representation.  This sets
+// *ALL_CASES and *ALL_STATEMENTS.
 
-tree
-Case_clauses::get_constant_tree(Translate_context* context,
-                               Unnamed_label* break_label) const
+void
+Case_clauses::get_backend(Translate_context* context,
+                         Unnamed_label* break_label,
+                         std::vector<std::vector<Bexpression*> >* all_cases,
+                         std::vector<Bstatement*>* all_statements) const
 {
   Case_constants case_constants;
-  tree stmt_list = NULL_TREE;
+
+  size_t c = this->clauses_.size();
+  all_cases->resize(c);
+  all_statements->resize(c);
+
+  size_t i = 0;
   for (Clauses::const_iterator p = this->clauses_.begin();
        p != this->clauses_.end();
-       ++p)
-    p->get_constant_tree(context, break_label, &case_constants,
-                        &stmt_list);
-  return stmt_list;
+       ++p, ++i)
+    {
+      std::vector<Bexpression*> cases;
+      Bstatement* stat = p->get_backend(context, break_label, &case_constants,
+                                       &cases);
+      (*all_cases)[i].swap(cases);
+      (*all_statements)[i] = stat;
+    }
 }
 
 // A constant switch statement.  A Switch_statement is lowered to this
@@ -3401,22 +3468,28 @@ tree
 Constant_switch_statement::do_get_tree(Translate_context* context)
 {
   tree switch_val_tree = this->val_->get_tree(context);
+  Bexpression* switch_val_expr = tree_to_expr(switch_val_tree);
 
   Unnamed_label* break_label = this->break_label_;
   if (break_label == NULL)
     break_label = new Unnamed_label(this->location());
 
-  tree stmt_list = NULL_TREE;
-  tree s = build3(SWITCH_EXPR, void_type_node, switch_val_tree,
-                 this->clauses_->get_constant_tree(context, break_label),
-                 NULL_TREE);
-  SET_EXPR_LOCATION(s, this->location());
-  append_to_statement_list(s, &stmt_list);
-
-  Bstatement* ldef = break_label->get_definition(context);
-  append_to_statement_list(stat_to_tree(ldef), &stmt_list);
-
-  return stmt_list;
+  std::vector<std::vector<Bexpression*> > all_cases;
+  std::vector<Bstatement*> all_statements;
+  this->clauses_->get_backend(context, break_label, &all_cases,
+                             &all_statements);
+
+  Bstatement* switch_statement;
+  switch_statement = context->backend()->switch_statement(switch_val_expr,
+                                                         all_cases,
+                                                         all_statements,
+                                                         this->location());
+
+  std::vector<Bstatement*> stats(2);
+  stats[0] = switch_statement;
+  stats[1] = break_label->get_definition(context);
+  Bstatement* ret = context->backend()->statement_list(stats);
+  return stat_to_tree(ret);
 }
 
 // Class Switch_statement.
index 986d72b8780e0c0d34d900111d5e07c3aba587a1..826cd0cc05f14e50080316a64b69d2dc3820ed1e 100644 (file)
@@ -39,6 +39,8 @@ class Case_clauses;
 class Type_case_clauses;
 class Select_clauses;
 class Typed_identifier_list;
+class Bexpression;
+class Bstatement;
 
 // This class is used to traverse assignments made by a statement
 // which makes assignments.
@@ -1162,13 +1164,18 @@ class Case_clauses
 
   // Return the body of a SWITCH_EXPR when all the clauses are
   // constants.
-  tree
-  get_constant_tree(Translate_context*, Unnamed_label* break_label) const;
+  void
+  get_backend(Translate_context*, Unnamed_label* break_label,
+             std::vector<std::vector<Bexpression*> >* all_cases,
+             std::vector<Bstatement*>* all_statements) const;
 
  private:
   // For a constant tree we need to keep a record of constants we have
   // already seen.  Note that INTEGER_CST trees are interned.
-  typedef Unordered_set(tree) Case_constants;
+  class Hash_integer_value;
+  class Eq_integer_value;
+  typedef Unordered_set_hash(Expression*, Hash_integer_value,
+                            Eq_integer_value) Case_constants;
 
   // One case clause.
   class Case_clause
@@ -1226,11 +1233,11 @@ class Case_clauses
     bool
     may_fall_through() const;
 
-    // Build up the body of a SWITCH_EXPR when the case expressions
-    // are constant.
-    void
-    get_constant_tree(Translate_context*, Unnamed_label* break_label,
-                     Case_constants* case_constants, tree* stmt_list) const;
+    // Convert the case values and statements to the backend
+    // representation.
+    Bstatement*
+    get_backend(Translate_context*, Unnamed_label* break_label,
+               Case_constants*, std::vector<Bexpression*>* cases) const;
 
    private:
     // The list of case expressions.