compiler: use a single temporary for calls with multiple results

For calls that return multiple results we used to create a temporary
    of struct type to hold the results, and also create a separate
    temporary for each result.  Then the call expression would copy each
    result out of the struct to the temporary, and Call_result_expression
    would refer to the desired temporary.
    
    Simplify this to just use a single temporary of struct type, and
    change Call_result_expression to fetch a field of the struct.
    
    This may reduce some incorrect tree sharing in the backend code.
    
    Reviewed-on: https://go-review.googlesource.com/51770

From-SVN: r250682
This commit is contained in:
Ian Lance Taylor 2017-07-28 17:42:05 +00:00
parent 9de009354e
commit 97a0928c52
3 changed files with 60 additions and 135 deletions

View file

@ -1,4 +1,4 @@
feb26fbb5065eadfe1f8610e9b74b3749a87c52d
27804ec53590e3644e030c9860822139a0cfb03f
The first line of this file holds the git revision number of the last
merge done from the gofrontend repository.

View file

@ -9463,24 +9463,28 @@ Call_expression::do_lower(Gogo* gogo, Named_object* function,
this->is_varargs_, loc);
// If this call returns multiple results, create a temporary
// variable for each result.
size_t rc = this->result_count();
if (rc > 1 && this->results_ == NULL)
// variable to hold them.
if (this->result_count() > 1 && this->call_temp_ == NULL)
{
std::vector<Temporary_statement*>* temps =
new std::vector<Temporary_statement*>;
temps->reserve(rc);
Struct_field_list* sfl = new Struct_field_list();
Function_type* fntype = this->get_function_type();
const Typed_identifier_list* results = fntype->results();
Location loc = this->location();
int i = 0;
char buf[20];
for (Typed_identifier_list::const_iterator p = results->begin();
p != results->end();
++p)
{
Temporary_statement* temp = Statement::make_temporary(p->type(),
NULL, loc);
inserter->insert(temp);
temps->push_back(temp);
}
this->results_ = temps;
p != results->end();
++p, ++i)
{
snprintf(buf, sizeof buf, "res%d", i);
sfl->push_back(Struct_field(Typed_identifier(buf, p->type(), loc)));
}
Struct_type* st = Type::make_struct_type(sfl, loc);
st->set_is_struct_incomparable();
this->call_temp_ = Statement::make_temporary(st, NULL, loc);
inserter->insert(this->call_temp_);
}
// Handle a call to a varargs function by packaging up the extra
@ -9779,30 +9783,6 @@ Call_expression::do_flatten(Gogo* gogo, Named_object*,
this->args_ = args;
}
size_t rc = this->result_count();
if (rc > 1 && this->call_temp_ == NULL)
{
Struct_field_list* sfl = new Struct_field_list();
Function_type* fntype = this->get_function_type();
const Typed_identifier_list* results = fntype->results();
Location loc = this->location();
int i = 0;
char buf[20];
for (Typed_identifier_list::const_iterator p = results->begin();
p != results->end();
++p, ++i)
{
snprintf(buf, sizeof buf, "res%d", i);
sfl->push_back(Struct_field(Typed_identifier(buf, p->type(), loc)));
}
Struct_type* st = Type::make_struct_type(sfl, loc);
st->set_is_struct_incomparable();
this->call_temp_ = Statement::make_temporary(st, NULL, loc);
inserter->insert(this->call_temp_);
}
return this;
}
@ -9827,17 +9807,18 @@ Call_expression::result_count() const
return fntype->results()->size();
}
// Return the temporary which holds a result.
// Return the temporary that holds the result for a call with multiple
// results.
Temporary_statement*
Call_expression::result(size_t i) const
Call_expression::results() const
{
if (this->results_ == NULL || this->results_->size() <= i)
if (this->call_temp_ == NULL)
{
go_assert(saw_errors());
return NULL;
}
return (*this->results_)[i];
return this->call_temp_;
}
// Set the number of results expected from a call expression.
@ -10191,8 +10172,21 @@ Call_expression::interface_method_function(
Bexpression*
Call_expression::do_get_backend(Translate_context* context)
{
Location location = this->location();
if (this->call_ != NULL)
return this->call_;
{
// If the call returns multiple results, make a new reference to
// the temporary.
if (this->call_temp_ != NULL)
{
Expression* ref =
Expression::make_temporary_reference(this->call_temp_, location);
return ref->get_backend(context);
}
return this->call_;
}
Function_type* fntype = this->get_function_type();
if (fntype == NULL)
@ -10202,7 +10196,6 @@ Call_expression::do_get_backend(Translate_context* context)
return context->backend()->error_expression();
Gogo* gogo = context->gogo();
Location location = this->location();
Func_expression* func = this->fn_->func_expression();
Interface_field_reference_expression* interface_method =
@ -10323,91 +10316,28 @@ Call_expression::do_get_backend(Translate_context* context)
fn_args, bclosure,
location);
if (this->results_ != NULL)
if (this->call_temp_ != NULL)
{
Bexpression* bcall_ref = this->call_result_ref(context);
Bstatement* assn_stmt =
gogo->backend()->assignment_statement(bfunction,
bcall_ref, call, location);
// This case occurs when the call returns multiple results.
this->call_ = this->set_results(context);
Expression* ref = Expression::make_temporary_reference(this->call_temp_,
location);
Bexpression* bref = ref->get_backend(context);
Bstatement* bassn = gogo->backend()->assignment_statement(bfunction,
bref, call,
location);
Bexpression* set_and_call =
gogo->backend()->compound_expression(assn_stmt, this->call_,
location);
return set_and_call;
ref = Expression::make_temporary_reference(this->call_temp_, location);
this->call_ = ref->get_backend(context);
return gogo->backend()->compound_expression(bassn, this->call_,
location);
}
this->call_ = call;
return this->call_;
}
// Return the backend representation of a reference to the struct used
// to capture the result of a multiple-output call.
Bexpression*
Call_expression::call_result_ref(Translate_context* context)
{
go_assert(this->call_temp_ != NULL);
Location location = this->location();
Expression* call_ref =
Expression::make_temporary_reference(this->call_temp_, location);
Bexpression* bcall_ref = call_ref->get_backend(context);
return bcall_ref;
}
// Set the result variables if this call returns multiple results.
Bexpression*
Call_expression::set_results(Translate_context* context)
{
Gogo* gogo = context->gogo();
Bexpression* results = NULL;
Location loc = this->location();
go_assert(this->call_temp_ != NULL);
size_t rc = this->result_count();
for (size_t i = 0; i < rc; ++i)
{
Temporary_statement* temp = this->result(i);
if (temp == NULL)
{
go_assert(saw_errors());
return gogo->backend()->error_expression();
}
Temporary_reference_expression* ref =
Expression::make_temporary_reference(temp, loc);
ref->set_is_lvalue();
Bfunction* bfunction = context->function()->func_value()->get_decl();
Bexpression* result_ref = ref->get_backend(context);
Bexpression* bcall_ref = this->call_result_ref(context);
Bexpression* call_result =
gogo->backend()->struct_field_expression(bcall_ref, i, loc);
Bstatement* assn_stmt =
gogo->backend()->assignment_statement(bfunction,
result_ref, call_result, loc);
bcall_ref = this->call_result_ref(context);
call_result = gogo->backend()->struct_field_expression(bcall_ref, i, loc);
Bexpression* result =
gogo->backend()->compound_expression(assn_stmt, call_result, loc);
if (results == NULL)
results = result;
else
{
Bstatement* expr_stmt =
gogo->backend()->expression_statement(bfunction, result);
results =
gogo->backend()->compound_expression(expr_stmt, results, loc);
}
}
return results;
}
// Dump ast representation for a call expressin.
void
@ -10528,13 +10458,14 @@ Call_result_expression::do_get_backend(Translate_context* context)
go_assert(this->call_->is_error_expression());
return context->backend()->error_expression();
}
Temporary_statement* ts = ce->result(this->index_);
Temporary_statement* ts = ce->results();
if (ts == NULL)
{
go_assert(saw_errors());
return context->backend()->error_expression();
}
Expression* ref = Expression::make_temporary_reference(ts, this->location());
ref = Expression::make_field_reference(ref, this->index_, this->location());
return ref->get_backend(context);
}

View file

@ -2115,8 +2115,8 @@ class Call_expression : public Expression
Call_expression(Expression* fn, Expression_list* args, bool is_varargs,
Location location)
: Expression(EXPRESSION_CALL, location),
fn_(fn), args_(args), type_(NULL), results_(NULL), call_(NULL),
call_temp_(NULL), expected_result_count_(0), is_varargs_(is_varargs),
fn_(fn), args_(args), type_(NULL), call_(NULL), call_temp_(NULL)
, expected_result_count_(0), is_varargs_(is_varargs),
varargs_are_lowered_(false), types_are_determined_(false),
is_deferred_(false), is_concurrent_(false), issued_error_(false),
is_multi_value_arg_(false), is_flattened_(false)
@ -2144,11 +2144,11 @@ class Call_expression : public Expression
size_t
result_count() const;
// Return the temporary variable which holds result I. This is only
// valid after the expression has been lowered, and is only valid
// for calls which return multiple results.
// Return the temporary variable that holds the results. This is
// only valid after the expression has been lowered, and is only
// valid for calls which return multiple results.
Temporary_statement*
result(size_t i) const;
results() const;
// Set the number of results expected from this call. This is used
// when the call appears in a context that expects multiple results,
@ -2292,9 +2292,6 @@ class Call_expression : public Expression
Bexpression*
set_results(Translate_context*);
Bexpression*
call_result_ref(Translate_context* context);
// The function to call.
Expression* fn_;
// The arguments to pass. This may be NULL if there are no
@ -2302,9 +2299,6 @@ class Call_expression : public Expression
Expression_list* args_;
// The type of the expression, to avoid recomputing it.
Type* type_;
// The list of temporaries which will hold the results if the
// function returns a tuple.
std::vector<Temporary_statement*>* results_;
// The backend expression for the call, used for a call which returns a tuple.
Bexpression* call_;
// A temporary variable to store this call if the function returns a tuple.