From: Ian Lance Taylor Date: Wed, 6 Apr 2011 23:07:13 +0000 (+0000) Subject: Use backend interface for constant switch statements. X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=8d0b03a22da97caa72c0e6e6c3020b87c06ef03a;p=gcc.git 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 --- diff --git a/gcc/go/ChangeLog b/gcc/go/ChangeLog index ff56bfe9883..3ee5fb10c0c 100644 --- a/gcc/go/ChangeLog +++ b/gcc/go/ChangeLog @@ -1,3 +1,9 @@ +2011-04-06 Ian Lance Taylor + + * 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 * go-gcc.cc (Gcc_backend::if_statement): New function. diff --git a/gcc/go/go-gcc.cc b/gcc/go/go-gcc.cc index de689f8478e..f9efb3c9e9b 100644 --- a/gcc/go/go-gcc.cc +++ b/gcc/go/go-gcc.cc @@ -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 >& cases, + const std::vector& statements, + source_location); + + Bstatement* + statement_list(const std::vector&); + // 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 >& cases, + const std::vector& statements, + source_location switch_location) +{ + gcc_assert(cases.size() == statements.size()); + + tree stmt_list = NULL_TREE; + std::vector >::const_iterator pc = cases.begin(); + for (std::vector::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::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& statements) +{ + tree stmt_list = NULL_TREE; + for (std::vector::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* diff --git a/gcc/go/gofrontend/backend.h b/gcc/go/gofrontend/backend.h index a6c242680ff..01f3cfa4c2b 100644 --- a/gcc/go/gofrontend/backend.h +++ b/gcc/go/gofrontend/backend.h @@ -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 >& cases, + const std::vector& statements, + source_location) = 0; + + // Create a single statement from a list of statements. + virtual Bstatement* + statement_list(const std::vector&) = 0; + // Labels. // Create a new label. NAME will be empty if this is a label diff --git a/gcc/go/gofrontend/statements.cc b/gcc/go/gofrontend/statements.cc index 131001a9ce1..f84b2d4ae92 100644 --- a/gcc/go/gofrontend/statements.cc +++ b/gcc/go/gofrontend/statements.cc @@ -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* 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 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 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 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 >* all_cases, + std::vector* 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 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 > all_cases; + std::vector 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 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. diff --git a/gcc/go/gofrontend/statements.h b/gcc/go/gofrontend/statements.h index 986d72b8780..826cd0cc05f 100644 --- a/gcc/go/gofrontend/statements.h +++ b/gcc/go/gofrontend/statements.h @@ -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 >* all_cases, + std::vector* 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* cases) const; private: // The list of case expressions.