Use backend interface for constant switch statements.

* go-gcc.cc (if_statement): Use build3_loc.
	(Gcc_backend::switch_statement): New function.
	(Gcc_backend::statement_list): New function.

From-SVN: r172066
This commit is contained in:
Ian Lance Taylor 2011-04-06 23:07:13 +00:00 committed by Ian Lance Taylor
parent d17b0ae1ca
commit 8d0b03a22d
5 changed files with 270 additions and 80 deletions

View 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.

View 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*

View 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

View 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;
}
tree label = create_artificial_label(this->location_);
append_to_statement_list(build3(CASE_LABEL_EXPR, void_type_node,
val, NULL_TREE, label),
stmt_list);
gcc_assert(itype != NULL);
e = Expression::make_integer(&ival, itype, e->location());
mpz_clear(ival);
}
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);
std::vector<std::vector<Bexpression*> > all_cases;
std::vector<Bstatement*> all_statements;
this->clauses_->get_backend(context, break_label, &all_cases,
&all_statements);
Bstatement* ldef = break_label->get_definition(context);
append_to_statement_list(stat_to_tree(ldef), &stmt_list);
Bstatement* switch_statement;
switch_statement = context->backend()->switch_statement(switch_val_expr,
all_cases,
all_statements,
this->location());
return stmt_list;
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.

View 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.