diff mbox

Go patch committed: Use backend interface for switch statements

Message ID mcrlizn7zdo.fsf@google.com
State New
Headers show

Commit Message

Ian Lance Taylor April 6, 2011, 11:07 p.m. UTC
This patch to the Go frontend uses the backend interface to produce
switch statements for which all the cases are integer constants.
Bootstrapped and ran Go testsuite on x86_64-unknown-linux-gnu.
Committed to mainline.

Ian


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.
diff mbox

Patch

Index: gcc/go/go-gcc.cc
===================================================================
--- gcc/go/go-gcc.cc	(revision 172052)
+++ gcc/go/go-gcc.cc	(working copy)
@@ -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* c
       || 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: gcc/go/gofrontend/statements.cc
===================================================================
--- gcc/go/gofrontend/statements.cc	(revision 172052)
+++ gcc/go/gofrontend/statements.cc	(working copy)
@@ -2934,6 +2934,55 @@  Statement::make_if_statement(Expression*
   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_thro
   return this->statements_->may_fall_through();
 }
 
-// Build up the body of a SWITCH_EXPR.
-
-void
-Case_clauses::Case_clause::get_constant_tree(Translate_context* context,
-					     Unnamed_label* break_label,
-					     Case_constants* case_constants,
-					     tree* stmt_list) const
+// 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.
+
+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))
-	    {
-	      // 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)
+	  Expression* e = *p;
+	  if (e->classification() != Expression::EXPRESSION_INTEGER)
 	    {
-	      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: gcc/go/gofrontend/statements.h
===================================================================
--- gcc/go/gofrontend/statements.h	(revision 171959)
+++ gcc/go/gofrontend/statements.h	(working copy)
@@ -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.
Index: gcc/go/gofrontend/backend.h
===================================================================
--- gcc/go/gofrontend/backend.h	(revision 172052)
+++ gcc/go/gofrontend/backend.h	(working copy)
@@ -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