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:
parent
9de009354e
commit
97a0928c52
3 changed files with 60 additions and 135 deletions
|
@ -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.
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
Loading…
Add table
Reference in a new issue