compiler: permit inlining temporary statements and references

This increases the number of inlinable functions from 439 to 455.
    An example is math/bits.Mul32, which uses temporaries to handle the
    tuple assignment.
    
    Reviewed-on: https://go-review.googlesource.com/c/gofrontend/+/180837

From-SVN: r272022
This commit is contained in:
Ian Lance Taylor 2019-06-06 23:34:00 +00:00
parent 5e664ed093
commit 93cbebde76
9 changed files with 255 additions and 6 deletions

View File

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

View File

@ -6,15 +6,14 @@
#include "go-system.h"
#include "go-sha1.h"
#include "go-c.h"
#include "go-diagnostics.h"
#include "go-sha1.h"
#include "gogo.h"
#include "types.h"
#include "expressions.h"
#include "statements.h"
#include "export.h"
#include "go-linemap.h"
#include "backend.h"
@ -1297,3 +1296,33 @@ Stream_to_section::do_write(const char* bytes, size_t length)
{
this->backend_->write_export_data (bytes, length);
}
// Class Export_function_body.
// Record a temporary statement.
unsigned int
Export_function_body::record_temporary(const Temporary_statement* temp)
{
unsigned int ret = this->next_temporary_index_;
if (ret > 0x7fffffff)
go_error_at(temp->location(),
"too many temporary statements in export data");
++this->next_temporary_index_;
std::pair<const Temporary_statement*, unsigned int> val(temp, ret);
std::pair<Unordered_map(const Temporary_statement*, unsigned int)::iterator,
bool> ins = this->temporary_indexes_.insert(val);
go_assert(ins.second);
return ret;
}
// Return the index of a temporary statement.
unsigned int
Export_function_body::temporary_index(const Temporary_statement* temp)
{
Unordered_map(const Temporary_statement*, unsigned int)::const_iterator p =
this->temporary_indexes_.find(temp);
go_assert(p != this->temporary_indexes_.end());
return p->second;
}

View File

@ -20,6 +20,7 @@ class Type;
class Package;
class Import_init_set;
class Backend;
class Temporary_statement;
// Codes used for the builtin types. These are all negative to make
// them easily distinct from the codes assigned by Export::write_type.
@ -307,7 +308,8 @@ class Export_function_body : public String_dump
{
public:
Export_function_body(Export* exp, int indent)
: exp_(exp), type_context_(NULL), indent_(indent)
: exp_(exp), body_(), type_context_(NULL), next_temporary_index_(0),
temporary_indexes_(), indent_(indent)
{ }
// Write a character to the body.
@ -363,6 +365,14 @@ class Export_function_body : public String_dump
package_index(const Package* p) const
{ return this->exp_->package_index(p); }
// Record a temporary statement and return its index.
unsigned int
record_temporary(const Temporary_statement*);
// Return the index of a temporary statement.
unsigned int
temporary_index(const Temporary_statement*);
// Return a reference to the completed body.
const std::string&
body() const
@ -375,6 +385,10 @@ class Export_function_body : public String_dump
std::string body_;
// Current type context. Used to avoid duplicate type conversions.
Type* type_context_;
// Index to give to next temporary statement.
unsigned int next_temporary_index_;
// Map temporary statements to indexes.
Unordered_map(const Temporary_statement*, unsigned int) temporary_indexes_;
// Current indentation level: the number of spaces before each statement.
int indent_;
};

View File

@ -1025,6 +1025,57 @@ Temporary_reference_expression::do_address_taken(bool)
this->statement_->set_is_address_taken();
}
// Export a reference to a temporary.
void
Temporary_reference_expression::do_export(Export_function_body* efb) const
{
unsigned int idx = efb->temporary_index(this->statement_);
char buf[50];
snprintf(buf, sizeof buf, "$t%u", idx);
efb->write_c_string(buf);
}
// Import a reference to a temporary.
Expression*
Temporary_reference_expression::do_import(Import_function_body* ifb,
Location loc)
{
std::string id = ifb->read_identifier();
go_assert(id[0] == '$' && id[1] == 't');
const char *p = id.c_str();
char *end;
long idx = strtol(p + 2, &end, 10);
if (*end != '\0' || idx > 0x7fffffff)
{
if (!ifb->saw_error())
go_error_at(loc,
("invalid export data for %qs: "
"invalid temporary reference index at %lu"),
ifb->name().c_str(),
static_cast<unsigned long>(ifb->off()));
ifb->set_saw_error();
return Expression::make_error(loc);
}
Temporary_statement* temp =
ifb->temporary_statement(static_cast<unsigned int>(idx));
if (temp == NULL)
{
if (!ifb->saw_error())
go_error_at(loc,
("invalid export data for %qs: "
"undefined temporary reference index at %lu"),
ifb->name().c_str(),
static_cast<unsigned long>(ifb->off()));
ifb->set_saw_error();
return Expression::make_error(loc);
}
return Expression::make_temporary_reference(temp, loc);
}
// Get a backend expression referring to the variable.
Bexpression*
@ -17819,6 +17870,10 @@ Expression::import_expression_without_suffix(Import_expression* imp,
}
if (ifb->saw_error())
return Expression::make_error(loc);
if (ifb->match_c_string("$t"))
return Temporary_reference_expression::do_import(ifb, loc);
return Expression::import_identifier(ifb, loc);
}

View File

@ -1531,6 +1531,9 @@ class Temporary_reference_expression : public Expression
set_is_lvalue()
{ this->is_lvalue_ = true; }
static Expression*
do_import(Import_function_body*, Location);
protected:
Type*
do_type();
@ -1543,6 +1546,13 @@ class Temporary_reference_expression : public Expression
do_copy()
{ return make_temporary_reference(this->statement_, this->location()); }
int
do_inlining_cost() const
{ return 1; }
void
do_export(Export_function_body*) const;
bool
do_is_addressable() const
{ return true; }

View File

@ -1611,3 +1611,34 @@ Import_function_body::read_type()
return type;
}
// Record the index of a temporary statement.
void
Import_function_body::record_temporary(Temporary_statement* temp,
unsigned int idx)
{
size_t have = this->temporaries_.size();
if (static_cast<size_t>(idx) >= have)
{
size_t want;
if (have == 0)
want = 8;
else if (have < 256)
want = have * 2;
else
want = have + 64;
this->temporaries_.resize(want, NULL);
}
this->temporaries_[idx] = temp;
}
// Return a temporary statement given an index.
Temporary_statement*
Import_function_body::temporary_statement(unsigned int idx)
{
if (static_cast<size_t>(idx) >= this->temporaries_.size())
return NULL;
return this->temporaries_[idx];
}

View File

@ -587,7 +587,8 @@ class Import_function_body : public Import_expression
const std::string& body, size_t off, Block* block,
int indent)
: gogo_(gogo), imp_(imp), named_object_(named_object), body_(body),
off_(off), block_(block), indent_(indent), saw_error_(false)
off_(off), block_(block), indent_(indent), temporaries_(),
saw_error_(false)
{ }
// The IR.
@ -695,6 +696,14 @@ class Import_function_body : public Import_expression
version() const
{ return this->imp_->version(); }
// Record the index of a temporary statement.
void
record_temporary(Temporary_statement*, unsigned int);
// Return a temporary statement given an index.
Temporary_statement*
temporary_statement(unsigned int);
// Implement Import_expression.
Import_function_body*
ifb()
@ -736,6 +745,8 @@ class Import_function_body : public Import_expression
Block* block_;
// Current expected indentation level.
int indent_;
// Temporary statements by index.
std::vector<Temporary_statement*> temporaries_;
// Whether we've seen an error. Used to avoid reporting excess
// errors.
bool saw_error_;

View File

@ -155,6 +155,8 @@ Statement::import_statement(Import_function_body* ifb, Location loc)
ifb->advance(6);
return Statement::make_return_statement(NULL, loc);
}
else if (ifb->match_c_string("var $t"))
return Temporary_statement::do_import(ifb, loc);
else if (ifb->match_c_string("var "))
return Variable_declaration_statement::do_import(ifb, loc);
@ -693,6 +695,92 @@ Statement::make_temporary(Type* type, Expression* init,
return new Temporary_statement(type, init, location);
}
// Export a temporary statement.
void
Temporary_statement::do_export_statement(Export_function_body* efb)
{
unsigned int idx = efb->record_temporary(this);
char buf[100];
snprintf(buf, sizeof buf, "var $t%u", idx);
efb->write_c_string(buf);
if (this->type_ != NULL)
{
efb->write_c_string(" ");
efb->write_type(this->type_);
}
if (this->init_ != NULL)
{
efb->write_c_string(" = ");
go_assert(efb->type_context() == NULL);
efb->set_type_context(this->type_);
this->init_->export_expression(efb);
efb->set_type_context(NULL);
}
}
// Import a temporary statement.
Statement*
Temporary_statement::do_import(Import_function_body* ifb, Location loc)
{
ifb->require_c_string("var ");
std::string id = ifb->read_identifier();
go_assert(id[0] == '$' && id[1] == 't');
const char *p = id.c_str();
char *end;
long idx = strtol(p + 2, &end, 10);
if (*end != '\0' || idx > 0x7fffffff)
{
if (!ifb->saw_error())
go_error_at(loc,
("invalid export data for %qs: "
"bad temporary statement index at %lu"),
ifb->name().c_str(),
static_cast<unsigned long>(ifb->off()));
ifb->set_saw_error();
return Statement::make_error_statement(loc);
}
Type* type = NULL;
if (!ifb->match_c_string(" = "))
{
ifb->require_c_string(" ");
type = ifb->read_type();
}
Expression* init = NULL;
if (ifb->match_c_string(" = "))
{
ifb->advance(3);
init = Expression::import_expression(ifb, loc);
if (type != NULL)
{
Type_context context(type, false);
init->determine_type(&context);
}
}
if (type == NULL && init == NULL)
{
if (!ifb->saw_error())
go_error_at(loc,
("invalid export data for %qs: "
"temporary statement has neither type nor init at %lu"),
ifb->name().c_str(),
static_cast<unsigned long>(ifb->off()));
ifb->set_saw_error();
return Statement::make_error_statement(loc);
}
Temporary_statement* temp = Statement::make_temporary(type, init, loc);
ifb->record_temporary(temp, static_cast<unsigned int>(idx));
return temp;
}
// The Move_subexpressions class is used to move all top-level
// subexpressions of an expression. This is used for things like
// index expressions in which we must evaluate the index value before

View File

@ -739,6 +739,10 @@ class Temporary_statement : public Statement
Bvariable*
get_backend_variable(Translate_context*) const;
// Import the declaration of a temporary.
static Statement*
do_import(Import_function_body*, Location);
protected:
int
do_traverse(Traverse*);
@ -752,6 +756,13 @@ class Temporary_statement : public Statement
void
do_check_types(Gogo*);
int
do_inlining_cost()
{ return 1; }
void
do_export_statement(Export_function_body*);
Statement*
do_flatten(Gogo*, Named_object*, Block*, Statement_inserter*);