mirror of
git://gcc.gnu.org/git/gcc.git
synced 2025-02-25 05:45:39 +08:00
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:
parent
d17b0ae1ca
commit
8d0b03a22d
@ -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.
|
||||
|
@ -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*
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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.
|
||||
|
Loading…
Reference in New Issue
Block a user