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
-bc7374913367fba9b10dc284af87eb539fb6c5b2
+015785baa74629baafe520367b9c71707366c6eb
The first line of this file holds the git revision number of the last
merge done from the gofrontend repository.
#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"
{
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;
+}
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.
{
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.
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
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_;
};
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*
}
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);
}
set_is_lvalue()
{ this->is_lvalue_ = true; }
+ static Expression*
+ do_import(Import_function_body*, Location);
+
protected:
Type*
do_type();
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; }
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];
+}
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.
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()
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_;
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);
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
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*);
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*);