aboutsummaryrefslogtreecommitdiff
path: root/gcc
diff options
context:
space:
mode:
authorIan Lance Taylor <ian@gcc.gnu.org>2019-07-04 02:20:37 +0000
committerIan Lance Taylor <ian@gcc.gnu.org>2019-07-04 02:20:37 +0000
commit0e68d70b7fbf4533d7b5ccd84c439026062b1a0e (patch)
treeb01088e287d6565278c17bc004de616ba1a0891f /gcc
parent9c5784fa7585c274b188f4df26bb80244ef97cb0 (diff)
downloadgcc-0e68d70b7fbf4533d7b5ccd84c439026062b1a0e.zip
gcc-0e68d70b7fbf4533d7b5ccd84c439026062b1a0e.tar.gz
gcc-0e68d70b7fbf4533d7b5ccd84c439026062b1a0e.tar.bz2
compiler: optimize 0,1,2-case select statement
For a select statement with zero-, one-, or two-case with a default case, we can generate simpler code instead of calling the generic selectgo. A zero-case select is just blocking the execution. A one-case select is mostly just executing the case. A two-case select with a default case is a non-blocking send or receive. We add these special cases for lowering a select statement. Reviewed-on: https://go-review.googlesource.com/c/gofrontend/+/184998 From-SVN: r273034
Diffstat (limited to 'gcc')
-rw-r--r--gcc/go/gofrontend/MERGE2
-rw-r--r--gcc/go/gofrontend/gogo.cc3
-rw-r--r--gcc/go/gofrontend/runtime.def16
-rw-r--r--gcc/go/gofrontend/statements.cc229
-rw-r--r--gcc/go/gofrontend/statements.h40
5 files changed, 285 insertions, 5 deletions
diff --git a/gcc/go/gofrontend/MERGE b/gcc/go/gofrontend/MERGE
index 9c5c13b..410afb0 100644
--- a/gcc/go/gofrontend/MERGE
+++ b/gcc/go/gofrontend/MERGE
@@ -1,4 +1,4 @@
-197b6fdfb861f07bab7365e350b5b855cfccc290
+7a8e10be0ddb8909ce25a264d03b24cee4df60cc
The first line of this file holds the git revision number of the last
merge done from the gofrontend repository.
diff --git a/gcc/go/gofrontend/gogo.cc b/gcc/go/gofrontend/gogo.cc
index fcf9a93..3abf7ea 100644
--- a/gcc/go/gofrontend/gogo.cc
+++ b/gcc/go/gofrontend/gogo.cc
@@ -6262,7 +6262,8 @@ Function_declaration::get_or_make_decl(Gogo* gogo, Named_object* no)
if (this->asm_name_ == "runtime.gopanic"
|| this->asm_name_ == "__go_runtime_error"
- || this->asm_name_ == "runtime.panicdottype")
+ || this->asm_name_ == "runtime.panicdottype"
+ || this->asm_name_ == "runtime.block")
flags |= Backend::function_does_not_return;
}
diff --git a/gcc/go/gofrontend/runtime.def b/gcc/go/gofrontend/runtime.def
index f510a65..c754739 100644
--- a/gcc/go/gofrontend/runtime.def
+++ b/gcc/go/gofrontend/runtime.def
@@ -204,6 +204,22 @@ DEF_GO_RUNTIME(CHANRECV2, "runtime.chanrecv2", P2(CHAN, POINTER), R1(BOOL))
DEF_GO_RUNTIME(SELECTGO, "runtime.selectgo", P3(POINTER, POINTER, INT),
R2(INT, BOOL))
+// Non-blocking send a value on a channel, used for two-case select
+// statement with a default case.
+DEF_GO_RUNTIME(SELECTNBSEND, "runtime.selectnbsend", P2(CHAN, POINTER), R1(BOOL))
+
+// Non-blocking receive a value from a channel, used for two-case select
+// statement with a default case.
+DEF_GO_RUNTIME(SELECTNBRECV, "runtime.selectnbrecv", P2(POINTER, CHAN), R1(BOOL))
+
+// Non-blocking tuple receive from a channel, used for two-case select
+// statement with a default case.
+DEF_GO_RUNTIME(SELECTNBRECV2, "runtime.selectnbrecv2", P3(POINTER, POINTER, CHAN),
+ R1(BOOL))
+
+// Block execution. Used for zero-case select.
+DEF_GO_RUNTIME(BLOCK, "runtime.block", P0(), R0())
+
// Panic.
DEF_GO_RUNTIME(GOPANIC, "runtime.gopanic", P1(EFACE), R0())
diff --git a/gcc/go/gofrontend/statements.cc b/gcc/go/gofrontend/statements.cc
index 332d63f..1e88fab 100644
--- a/gcc/go/gofrontend/statements.cc
+++ b/gcc/go/gofrontend/statements.cc
@@ -5665,6 +5665,28 @@ Select_statement::do_lower(Gogo* gogo, Named_object* function,
Block* b = new Block(enclosing, loc);
int ncases = this->clauses_->size();
+
+ // Zero-case select. Just block the execution.
+ if (ncases == 0)
+ {
+ Expression* call = Runtime::make_call(Runtime::BLOCK, loc, 0);
+ Statement *s = Statement::make_statement(call, false);
+ b->add_statement(s);
+ this->is_lowered_ = true;
+ return Statement::make_block_statement(b, loc);
+ }
+
+ // One-case select. It is mostly just to run the case.
+ if (ncases == 1)
+ return this->lower_one_case(b);
+
+ // Two-case select with one default case. It is a non-blocking
+ // send/receive.
+ if (ncases == 2
+ && (this->clauses_->at(0).is_default()
+ || this->clauses_->at(1).is_default()))
+ return this->lower_two_case(b);
+
Type* scase_type = Channel_type::select_case_type();
Expression* ncases_expr =
Expression::make_integer_ul(ncases, NULL,
@@ -5733,6 +5755,213 @@ Select_statement::do_lower(Gogo* gogo, Named_object* function,
return Statement::make_block_statement(b, loc);
}
+// Lower a one-case select statement.
+
+Statement*
+Select_statement::lower_one_case(Block* b)
+{
+ Select_clauses::Select_clause& scase = this->clauses_->at(0);
+ Location loc = this->location();
+ Expression* chan = scase.channel();
+ if (chan != NULL)
+ {
+ // Lower this to
+ // if chan == nil { block() }; send/recv; body
+ Temporary_statement* chantmp = Statement::make_temporary(NULL, chan, loc);
+ b->add_statement(chantmp);
+ Expression* chanref = Expression::make_temporary_reference(chantmp, loc);
+
+ Expression* nil = Expression::make_nil(loc);
+ Expression* cond = Expression::make_binary(OPERATOR_EQEQ, chanref, nil, loc);
+ Block* bnil = new Block(b, loc);
+ Expression* call = Runtime::make_call(Runtime::BLOCK, loc, 0);
+ Statement* s = Statement::make_statement(call, false);
+ bnil->add_statement(s);
+ Statement* ifs = Statement::make_if_statement(cond, bnil, NULL, loc);
+ b->add_statement(ifs);
+
+ chanref = chanref->copy();
+ Location cloc = scase.location();
+ if (scase.is_send())
+ {
+ s = Statement::make_send_statement(chanref, scase.val(), cloc);
+ b->add_statement(s);
+ }
+ else
+ {
+ if (scase.closed() == NULL && scase.closedvar() == NULL)
+ {
+ // Simple receive.
+ Expression* recv = Expression::make_receive(chanref, cloc);
+ if (scase.val() != NULL)
+ s = Statement::make_assignment(scase.val(), recv, cloc);
+ else if (scase.var() != NULL)
+ {
+ Temporary_statement *ts =
+ Statement::make_temporary(NULL, recv, cloc);
+ Expression* ref =
+ Expression::make_temporary_reference(ts, cloc);
+ s = ts;
+ scase.var()->var_value()->set_init(ref);
+ scase.var()->var_value()->clear_type_from_chan_element();
+ }
+ else
+ s = Statement::make_statement(recv, false);
+ b->add_statement(s);
+ }
+ else
+ {
+ // Tuple receive.
+ Expression* lhs;
+ if (scase.val() != NULL)
+ lhs = scase.val();
+ else
+ {
+ Type* valtype = chan->type()->channel_type()->element_type();
+ Temporary_statement *ts =
+ Statement::make_temporary(valtype, NULL, cloc);
+ lhs = Expression::make_temporary_reference(ts, cloc);
+ b->add_statement(ts);
+ }
+
+ Expression* lhs2;
+ if (scase.closed() != NULL)
+ lhs2 = scase.closed();
+ else
+ {
+ Type* booltype = Type::make_boolean_type();
+ Temporary_statement *ts =
+ Statement::make_temporary(booltype, NULL, cloc);
+ lhs2 = Expression::make_temporary_reference(ts, cloc);
+ b->add_statement(ts);
+ }
+
+ s = Statement::make_tuple_receive_assignment(lhs, lhs2, chanref, cloc);
+ b->add_statement(s);
+
+ if (scase.var() != NULL)
+ {
+ scase.var()->var_value()->set_init(lhs->copy());
+ scase.var()->var_value()->clear_type_from_chan_element();
+ }
+
+ if (scase.closedvar() != NULL)
+ scase.closedvar()->var_value()->set_init(lhs2->copy());
+ }
+ }
+ }
+
+ Statement* bs =
+ Statement::make_block_statement(scase.statements(), scase.location());
+ b->add_statement(bs);
+
+ this->is_lowered_ = true;
+ return Statement::make_block_statement(b, loc);
+}
+
+// Lower a two-case select statement with one default case.
+
+Statement*
+Select_statement::lower_two_case(Block* b)
+{
+ Select_clauses::Select_clause& chancase =
+ (this->clauses_->at(0).is_default()
+ ? this->clauses_->at(1)
+ : this->clauses_->at(0));
+ Select_clauses::Select_clause& defcase =
+ (this->clauses_->at(0).is_default()
+ ? this->clauses_->at(0)
+ : this->clauses_->at(1));
+ Location loc = this->location();
+ Expression* chan = chancase.channel();
+
+ Temporary_statement* chantmp = Statement::make_temporary(NULL, chan, loc);
+ b->add_statement(chantmp);
+ Expression* chanref = Expression::make_temporary_reference(chantmp, loc);
+
+ Block* bchan;
+ Expression* call;
+ if (chancase.is_send())
+ {
+ // if selectnbsend(chan, &val) { body } else { default body }
+
+ Temporary_statement* ts = Statement::make_temporary(NULL, chancase.val(), loc);
+ // Tell the escape analysis that the value escapes, as it may be sent
+ // to a channel.
+ ts->set_value_escapes();
+ b->add_statement(ts);
+
+ Expression* ref = Expression::make_temporary_reference(ts, loc);
+ Expression* addr = Expression::make_unary(OPERATOR_AND, ref, loc);
+ call = Runtime::make_call(Runtime::SELECTNBSEND, loc, 2, chanref, addr);
+ bchan = chancase.statements();
+ }
+ else
+ {
+ Type* valtype = chan->type()->channel_type()->element_type();
+ Temporary_statement* ts = Statement::make_temporary(valtype, NULL, loc);
+ b->add_statement(ts);
+
+ Expression* ref = Expression::make_temporary_reference(ts, loc);
+ Expression* addr = Expression::make_unary(OPERATOR_AND, ref, loc);
+ Expression* okref = NULL;
+ if (chancase.closed() == NULL && chancase.closedvar() == NULL)
+ {
+ // Simple receive.
+ // if selectnbrecv(&lhs, chan) { body } else { default body }
+ call = Runtime::make_call(Runtime::SELECTNBRECV, loc, 2, addr, chanref);
+ }
+ else
+ {
+ // Tuple receive.
+ // if selectnbrecv2(&lhs, &ok, chan) { body } else { default body }
+
+ Type* booltype = Type::make_boolean_type();
+ Temporary_statement* ts = Statement::make_temporary(booltype, NULL, loc);
+ b->add_statement(ts);
+
+ okref = Expression::make_temporary_reference(ts, loc);
+ Expression* okaddr = Expression::make_unary(OPERATOR_AND, okref, loc);
+ call = Runtime::make_call(Runtime::SELECTNBRECV2, loc, 3, addr, okaddr,
+ chanref);
+ }
+
+ Location cloc = chancase.location();
+ bchan = new Block(b, loc);
+ if (chancase.val() != NULL && !chancase.val()->is_sink_expression())
+ {
+ Statement* as = Statement::make_assignment(chancase.val(), ref->copy(),
+ cloc);
+ bchan->add_statement(as);
+ }
+ else if (chancase.var() != NULL)
+ {
+ chancase.var()->var_value()->set_init(ref->copy());
+ chancase.var()->var_value()->clear_type_from_chan_element();
+ }
+
+ if (chancase.closed() != NULL && !chancase.closed()->is_sink_expression())
+ {
+ Statement* as = Statement::make_assignment(chancase.closed(),
+ okref->copy(), cloc);
+ bchan->add_statement(as);
+ }
+ else if (chancase.closedvar() != NULL)
+ chancase.closedvar()->var_value()->set_init(okref->copy());
+
+ Statement* bs = Statement::make_block_statement(chancase.statements(),
+ cloc);
+ bchan->add_statement(bs);
+ }
+
+ Statement* ifs =
+ Statement::make_if_statement(call, bchan, defcase.statements(), loc);
+ b->add_statement(ifs);
+
+ this->is_lowered_ = true;
+ return Statement::make_block_statement(b, loc);
+}
+
// Whether the select statement itself may fall through to the following
// statement.
diff --git a/gcc/go/gofrontend/statements.h b/gcc/go/gofrontend/statements.h
index 432da30..7c254d0 100644
--- a/gcc/go/gofrontend/statements.h
+++ b/gcc/go/gofrontend/statements.h
@@ -1061,7 +1061,7 @@ class Select_clauses
// for the variable to set, and CLOSED is either NULL or a
// Var_expression to set to whether the channel is closed. If VAL
// is NULL, VAR may be a variable to be initialized with the
- // received value, and CLOSEDVAR ma be a variable to be initialized
+ // received value, and CLOSEDVAR may be a variable to be initialized
// with whether the channel is closed. IS_DEFAULT is true if this
// is the default clause. STATEMENTS is the list of statements to
// execute.
@@ -1110,7 +1110,6 @@ class Select_clauses
void
dump_clauses(Ast_dump_context*) const;
- private:
// A single clause.
class Select_clause
{
@@ -1166,8 +1165,30 @@ class Select_clauses
return this->is_send_;
}
+ // Return the value to send or the lvalue to receive into.
+ Expression*
+ val() const
+ { return this->val_; }
+
+ // Return the lvalue to set to whether the channel is closed
+ // on a receive.
+ Expression*
+ closed() const
+ { return this->closed_; }
+
+ // Return the variable to initialize, for "case a := <-ch".
+ Named_object*
+ var() const
+ { return this->var_; }
+
+ // Return the variable to initialize to whether the channel
+ // is closed, for "case a, c := <-ch".
+ Named_object*
+ closedvar() const
+ { return this->closedvar_; }
+
// Return the statements.
- const Block*
+ Block*
statements() const
{ return this->statements_; }
@@ -1235,6 +1256,11 @@ class Select_clauses
bool is_lowered_;
};
+ Select_clause&
+ at(size_t i)
+ { return this->clauses_.at(i); }
+
+ private:
typedef std::vector<Select_clause> Clauses;
Clauses clauses_;
@@ -1288,6 +1314,14 @@ class Select_statement : public Statement
do_dump_statement(Ast_dump_context*) const;
private:
+ // Lower a one-case select statement.
+ Statement*
+ lower_one_case(Block*);
+
+ // Lower a two-case select statement with one defualt case.
+ Statement*
+ lower_two_case(Block*);
+
// The select clauses.
Select_clauses* clauses_;
// A temporary that holds the index value returned by selectgo.