Commit 97a0928c by Ian Lance Taylor

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
parent 9de00935
feb26fbb5065eadfe1f8610e9b74b3749a87c52d 27804ec53590e3644e030c9860822139a0cfb03f
The first line of this file holds the git revision number of the last The first line of this file holds the git revision number of the last
merge done from the gofrontend repository. merge done from the gofrontend repository.
...@@ -9463,24 +9463,28 @@ Call_expression::do_lower(Gogo* gogo, Named_object* function, ...@@ -9463,24 +9463,28 @@ Call_expression::do_lower(Gogo* gogo, Named_object* function,
this->is_varargs_, loc); this->is_varargs_, loc);
// If this call returns multiple results, create a temporary // If this call returns multiple results, create a temporary
// variable for each result. // variable to hold them.
size_t rc = this->result_count(); if (this->result_count() > 1 && this->call_temp_ == NULL)
if (rc > 1 && this->results_ == NULL)
{ {
std::vector<Temporary_statement*>* temps = Struct_field_list* sfl = new Struct_field_list();
new std::vector<Temporary_statement*>; Function_type* fntype = this->get_function_type();
temps->reserve(rc);
const Typed_identifier_list* results = fntype->results(); 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(); for (Typed_identifier_list::const_iterator p = results->begin();
p != results->end(); p != results->end();
++p) ++p, ++i)
{ {
Temporary_statement* temp = Statement::make_temporary(p->type(), snprintf(buf, sizeof buf, "res%d", i);
NULL, loc); sfl->push_back(Struct_field(Typed_identifier(buf, p->type(), loc)));
inserter->insert(temp); }
temps->push_back(temp);
} Struct_type* st = Type::make_struct_type(sfl, loc);
this->results_ = temps; 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 // Handle a call to a varargs function by packaging up the extra
...@@ -9779,30 +9783,6 @@ Call_expression::do_flatten(Gogo* gogo, Named_object*, ...@@ -9779,30 +9783,6 @@ Call_expression::do_flatten(Gogo* gogo, Named_object*,
this->args_ = args; 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; return this;
} }
...@@ -9827,17 +9807,18 @@ Call_expression::result_count() const ...@@ -9827,17 +9807,18 @@ Call_expression::result_count() const
return fntype->results()->size(); 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* 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()); go_assert(saw_errors());
return NULL; return NULL;
} }
return (*this->results_)[i]; return this->call_temp_;
} }
// Set the number of results expected from a call expression. // Set the number of results expected from a call expression.
...@@ -10191,8 +10172,21 @@ Call_expression::interface_method_function( ...@@ -10191,8 +10172,21 @@ Call_expression::interface_method_function(
Bexpression* Bexpression*
Call_expression::do_get_backend(Translate_context* context) Call_expression::do_get_backend(Translate_context* context)
{ {
Location location = this->location();
if (this->call_ != NULL) 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(); Function_type* fntype = this->get_function_type();
if (fntype == NULL) if (fntype == NULL)
...@@ -10202,7 +10196,6 @@ Call_expression::do_get_backend(Translate_context* context) ...@@ -10202,7 +10196,6 @@ Call_expression::do_get_backend(Translate_context* context)
return context->backend()->error_expression(); return context->backend()->error_expression();
Gogo* gogo = context->gogo(); Gogo* gogo = context->gogo();
Location location = this->location();
Func_expression* func = this->fn_->func_expression(); Func_expression* func = this->fn_->func_expression();
Interface_field_reference_expression* interface_method = Interface_field_reference_expression* interface_method =
...@@ -10323,91 +10316,28 @@ Call_expression::do_get_backend(Translate_context* context) ...@@ -10323,91 +10316,28 @@ Call_expression::do_get_backend(Translate_context* context)
fn_args, bclosure, fn_args, bclosure,
location); location);
if (this->results_ != NULL) if (this->call_temp_ != NULL)
{ {
Bexpression* bcall_ref = this->call_result_ref(context); // This case occurs when the call returns multiple results.
Bstatement* assn_stmt =
gogo->backend()->assignment_statement(bfunction,
bcall_ref, call, location);
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 = ref = Expression::make_temporary_reference(this->call_temp_, location);
gogo->backend()->compound_expression(assn_stmt, this->call_, this->call_ = ref->get_backend(context);
location);
return set_and_call; return gogo->backend()->compound_expression(bassn, this->call_,
location);
} }
this->call_ = call; this->call_ = call;
return this->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. // Dump ast representation for a call expressin.
void void
...@@ -10528,13 +10458,14 @@ Call_result_expression::do_get_backend(Translate_context* context) ...@@ -10528,13 +10458,14 @@ Call_result_expression::do_get_backend(Translate_context* context)
go_assert(this->call_->is_error_expression()); go_assert(this->call_->is_error_expression());
return context->backend()->error_expression(); return context->backend()->error_expression();
} }
Temporary_statement* ts = ce->result(this->index_); Temporary_statement* ts = ce->results();
if (ts == NULL) if (ts == NULL)
{ {
go_assert(saw_errors()); go_assert(saw_errors());
return context->backend()->error_expression(); return context->backend()->error_expression();
} }
Expression* ref = Expression::make_temporary_reference(ts, this->location()); Expression* ref = Expression::make_temporary_reference(ts, this->location());
ref = Expression::make_field_reference(ref, this->index_, this->location());
return ref->get_backend(context); return ref->get_backend(context);
} }
......
...@@ -2115,8 +2115,8 @@ class Call_expression : public Expression ...@@ -2115,8 +2115,8 @@ class Call_expression : public Expression
Call_expression(Expression* fn, Expression_list* args, bool is_varargs, Call_expression(Expression* fn, Expression_list* args, bool is_varargs,
Location location) Location location)
: Expression(EXPRESSION_CALL, location), : Expression(EXPRESSION_CALL, location),
fn_(fn), args_(args), type_(NULL), results_(NULL), call_(NULL), fn_(fn), args_(args), type_(NULL), call_(NULL), call_temp_(NULL)
call_temp_(NULL), expected_result_count_(0), is_varargs_(is_varargs), , expected_result_count_(0), is_varargs_(is_varargs),
varargs_are_lowered_(false), types_are_determined_(false), varargs_are_lowered_(false), types_are_determined_(false),
is_deferred_(false), is_concurrent_(false), issued_error_(false), is_deferred_(false), is_concurrent_(false), issued_error_(false),
is_multi_value_arg_(false), is_flattened_(false) is_multi_value_arg_(false), is_flattened_(false)
...@@ -2144,11 +2144,11 @@ class Call_expression : public Expression ...@@ -2144,11 +2144,11 @@ class Call_expression : public Expression
size_t size_t
result_count() const; result_count() const;
// Return the temporary variable which holds result I. This is only // Return the temporary variable that holds the results. This is
// valid after the expression has been lowered, and is only valid // only valid after the expression has been lowered, and is only
// for calls which return multiple results. // valid for calls which return multiple results.
Temporary_statement* Temporary_statement*
result(size_t i) const; results() const;
// Set the number of results expected from this call. This is used // Set the number of results expected from this call. This is used
// when the call appears in a context that expects multiple results, // when the call appears in a context that expects multiple results,
...@@ -2292,9 +2292,6 @@ class Call_expression : public Expression ...@@ -2292,9 +2292,6 @@ class Call_expression : public Expression
Bexpression* Bexpression*
set_results(Translate_context*); set_results(Translate_context*);
Bexpression*
call_result_ref(Translate_context* context);
// The function to call. // The function to call.
Expression* fn_; Expression* fn_;
// The arguments to pass. This may be NULL if there are no // The arguments to pass. This may be NULL if there are no
...@@ -2302,9 +2299,6 @@ class Call_expression : public Expression ...@@ -2302,9 +2299,6 @@ class Call_expression : public Expression
Expression_list* args_; Expression_list* args_;
// The type of the expression, to avoid recomputing it. // The type of the expression, to avoid recomputing it.
Type* type_; 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. // The backend expression for the call, used for a call which returns a tuple.
Bexpression* call_; Bexpression* call_;
// A temporary variable to store this call if the function returns a tuple. // A temporary variable to store this call if the function returns a tuple.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment