* Go patch committed: Make calls and functions inlinable
@ 2019-06-05 21:06 Ian Lance Taylor
0 siblings, 0 replies; only message in thread
From: Ian Lance Taylor @ 2019-06-05 21:06 UTC (permalink / raw)
To: gcc-patches, gofrontend-dev
[-- Attachment #1: Type: text/plain, Size: 629 bytes --]
This patch to the Go frontend makes call expressions and function
reference expressions inlinable.
We now scan inlinable methods for references to global variables and
functions (I forgot to do that earlier).
We now track all packages mentioned by exports (that should have been
done earlier too).
We record assembler names in the export data, so that we can inline
calls to non-Go functions. We modify gccgoimporter code to skip
assembler name.
This increases the number of inlinable functions in the standard
library from 215 to 439.
Bootstrapped and ran Go testsuite on x86_64-pc-linux-gnu. Committed
to mainline.
Ian
[-- Attachment #2: patch.txt --]
[-- Type: text/plain, Size: 20275 bytes --]
Index: gcc/go/gofrontend/MERGE
===================================================================
--- gcc/go/gofrontend/MERGE (revision 271945)
+++ gcc/go/gofrontend/MERGE (working copy)
@@ -1,4 +1,4 @@
-949c3b7aa603bc09e650d62e82c600b3463802f0
+2609f9b8420e2341fbbe40d7cf6af42b0fba7293
The first line of this file holds the git revision number of the last
merge done from the gofrontend repository.
Index: gcc/go/gofrontend/export.cc
===================================================================
--- gcc/go/gofrontend/export.cc (revision 271891)
+++ gcc/go/gofrontend/export.cc (working copy)
@@ -133,6 +133,11 @@ Collect_references_from_inline::expressi
if (fe != NULL)
{
Named_object* no = fe->named_object();
+
+ if (no->is_function_declaration()
+ && no->func_declaration_value()->type()->is_builtin())
+ return TRAVERSE_CONTINUE;
+
std::pair<Unordered_set(Named_object*)::iterator, bool> ins =
this->exports_->insert(no);
@@ -247,6 +252,22 @@ Export::export_globals(const std::string
if ((*p)->is_function()
&& (*p)->func_value()->export_for_inlining())
check_inline_refs.push_back(*p);
+ else if ((*p)->is_type())
+ {
+ const Bindings* methods = (*p)->type_value()->local_methods();
+ if (methods != NULL)
+ {
+ for (Bindings::const_definitions_iterator pm =
+ methods->begin_definitions();
+ pm != methods->end_definitions();
+ ++pm)
+ {
+ Function* fn = (*pm)->func_value();
+ if (fn->export_for_inlining())
+ check_inline_refs.push_back(*pm);
+ }
+ }
+ }
}
}
@@ -282,6 +303,9 @@ Export::export_globals(const std::string
}
}
+ // Track all imported packages mentioned in export data.
+ Unordered_set(const Package*) all_imports;
+
// Export the symbols in sorted order. That will reduce cases where
// irrelevant changes to the source code affect the exported
// interface.
@@ -291,15 +315,20 @@ Export::export_globals(const std::string
for (Unordered_set(Named_object*)::const_iterator p = exports.begin();
p != exports.end();
++p)
- sorted_exports.push_back(*p);
+ {
+ sorted_exports.push_back(*p);
+
+ const Package* pkg = (*p)->package();
+ if (pkg != NULL)
+ all_imports.insert(pkg);
+ }
std::sort(sorted_exports.begin(), sorted_exports.end(), Sort_bindings());
// Assign indexes to all exported types and types referenced by
// exported types, and collect all packages mentioned.
- Unordered_set(const Package*) type_imports;
int unexported_type_index = this->prepare_types(&sorted_exports,
- &type_imports);
+ &all_imports);
// Although the export data is readable, at least this version is,
// it is conceptually a binary format. Start with a four byte
@@ -327,7 +356,7 @@ Export::export_globals(const std::string
this->write_packages(packages);
- this->write_imports(imports, type_imports);
+ this->write_imports(imports, all_imports);
this->write_imported_init_fns(package_name, import_init_fn,
imported_init_fns);
@@ -693,7 +722,7 @@ import_compare(const std::pair<std::stri
void
Export::write_imports(const std::map<std::string, Package*>& imports,
- const Unordered_set(const Package*)& type_imports)
+ const Unordered_set(const Package*)& all_imports)
{
// Sort the imports for more consistent output.
Unordered_set(const Package*) seen;
@@ -729,8 +758,8 @@ Export::write_imports(const std::map<std
// Write out a separate list of indirectly imported packages.
std::vector<const Package*> indirect_imports;
for (Unordered_set(const Package*)::const_iterator p =
- type_imports.begin();
- p != type_imports.end();
+ all_imports.begin();
+ p != all_imports.end();
++p)
{
if (seen.find(*p) == seen.end())
Index: gcc/go/gofrontend/expressions.cc
===================================================================
--- gcc/go/gofrontend/expressions.cc (revision 271945)
+++ gcc/go/gofrontend/expressions.cc (working copy)
@@ -1356,6 +1356,29 @@ Func_expression::do_get_backend(Translat
return gogo->backend()->convert_expression(btype, bexpr, this->location());
}
+// The cost of inlining a function reference.
+
+int
+Func_expression::do_inlining_cost() const
+{
+ // FIXME: We don't inline references to nested functions.
+ if (this->closure_ != NULL)
+ return 0x100000;
+ if (this->function_->is_function()
+ && this->function_->func_value()->enclosing() != NULL)
+ return 0x100000;
+
+ return 1;
+}
+
+// Export a reference to a function.
+
+void
+Func_expression::do_export(Export_function_body* efb) const
+{
+ Expression::export_name(efb, this->function_);
+}
+
// Ast dump for function.
void
@@ -10088,38 +10111,82 @@ void
Builtin_call_expression::do_export(Export_function_body* efb) const
{
Numeric_constant nc;
- if (!this->numeric_constant_value(&nc))
+ if (this->numeric_constant_value(&nc))
{
- go_error_at(this->location(), "value is not constant");
- return;
- }
+ if (nc.is_int())
+ {
+ mpz_t val;
+ nc.get_int(&val);
+ Integer_expression::export_integer(efb, val);
+ mpz_clear(val);
+ }
+ else if (nc.is_float())
+ {
+ mpfr_t fval;
+ nc.get_float(&fval);
+ Float_expression::export_float(efb, fval);
+ mpfr_clear(fval);
+ }
+ else if (nc.is_complex())
+ {
+ mpc_t cval;
+ nc.get_complex(&cval);
+ Complex_expression::export_complex(efb, cval);
+ mpc_clear(cval);
+ }
+ else
+ go_unreachable();
- if (nc.is_int())
- {
- mpz_t val;
- nc.get_int(&val);
- Integer_expression::export_integer(efb, val);
- mpz_clear(val);
- }
- else if (nc.is_float())
- {
- mpfr_t fval;
- nc.get_float(&fval);
- Float_expression::export_float(efb, fval);
- mpfr_clear(fval);
+ // A trailing space lets us reliably identify the end of the number.
+ efb->write_c_string(" ");
}
- else if (nc.is_complex())
+ else
{
- mpc_t cval;
- nc.get_complex(&cval);
- Complex_expression::export_complex(efb, cval);
- mpc_clear(cval);
+ const char *s = NULL;
+ switch (this->code_)
+ {
+ default:
+ go_unreachable();
+ case BUILTIN_APPEND:
+ s = "append";
+ break;
+ case BUILTIN_COPY:
+ s = "copy";
+ break;
+ case BUILTIN_LEN:
+ s = "len";
+ break;
+ case BUILTIN_CAP:
+ s = "cap";
+ break;
+ case BUILTIN_PRINT:
+ s = "print";
+ break;
+ case BUILTIN_PRINTLN:
+ s = "println";
+ break;
+ case BUILTIN_PANIC:
+ s = "panic";
+ break;
+ case BUILTIN_RECOVER:
+ s = "recover";
+ break;
+ case BUILTIN_CLOSE:
+ s = "close";
+ break;
+ case BUILTIN_REAL:
+ s = "real";
+ break;
+ case BUILTIN_IMAG:
+ s = "imag";
+ break;
+ case BUILTIN_COMPLEX:
+ s = "complex";
+ break;
+ }
+ efb->write_c_string(s);
+ this->export_arguments(efb);
}
- else
- go_unreachable();
-
- // A trailing space lets us reliably identify the end of the number.
- efb->write_c_string(" ");
}
// Class Call_expression.
@@ -11637,7 +11704,55 @@ Call_expression::do_get_backend(Translat
return this->call_;
}
-// Dump ast representation for a call expressin.
+// The cost of inlining a call expression.
+
+int
+Call_expression::do_inlining_cost() const
+{
+ Func_expression* fn = this->fn_->func_expression();
+
+ // FIXME: We don't yet support all kinds of calls.
+ if (fn != NULL && fn->closure() != NULL)
+ return 0x100000;
+ if (this->fn_->interface_field_reference_expression())
+ return 0x100000;
+ if (this->get_function_type()->is_method())
+ return 0x100000;
+
+ return 5;
+}
+
+// Export a call expression.
+
+void
+Call_expression::do_export(Export_function_body* efb) const
+{
+ this->fn_->export_expression(efb);
+ this->export_arguments(efb);
+}
+
+// Export call expression arguments.
+
+void
+Call_expression::export_arguments(Export_function_body* efb) const
+{
+ efb->write_c_string("(");
+ if (this->args_ != NULL && !this->args_->empty())
+ {
+ Expression_list::const_iterator pa = this->args_->begin();
+ (*pa)->export_expression(efb);
+ for (pa++; pa != this->args_->end(); pa++)
+ {
+ efb->write_c_string(", ");
+ (*pa)->export_expression(efb);
+ }
+ if (this->is_varargs_)
+ efb->write_c_string("...");
+ }
+ efb->write_c_string(")");
+}
+
+// Dump ast representation for a call expression.
void
Call_expression::do_dump_expression(Ast_dump_context* ast_dump_context) const
@@ -17523,6 +17638,47 @@ Expression::make_backend(Bexpression* be
Expression*
Expression::import_expression(Import_expression* imp, Location loc)
{
+ Expression* expr = Expression::import_expression_without_suffix(imp, loc);
+ while (true)
+ {
+ if (imp->match_c_string("("))
+ {
+ imp->advance(1);
+ Expression_list* args = new Expression_list();
+ bool is_varargs = false;
+ while (!imp->match_c_string(")"))
+ {
+ Expression* arg = Expression::import_expression(imp, loc);
+ if (arg->is_error_expression())
+ return arg;
+ args->push_back(arg);
+ if (imp->match_c_string(")"))
+ break;
+ else if (imp->match_c_string("...)"))
+ {
+ imp->advance(3);
+ is_varargs = true;
+ break;
+ }
+ imp->require_c_string(", ");
+ }
+ imp->require_c_string(")");
+ expr = Expression::make_call(expr, args, is_varargs, loc);
+ }
+ else
+ break;
+ }
+
+ return expr;
+}
+
+// Import an expression without considering a suffix (function
+// arguments, index operations, etc.).
+
+Expression*
+Expression::import_expression_without_suffix(Import_expression* imp,
+ Location loc)
+{
int c = imp->peek_char();
if (c == '+' || c == '-' || c == '!' || c == '^' || c == '&' || c == '*')
return Unary_expression::do_import(imp, loc);
@@ -17608,7 +17764,21 @@ Expression::import_identifier(Import_fun
return Expression::make_error(loc);
}
- return Expression::make_var_reference(no, loc);
+ if (no->is_variable() || no->is_result_variable())
+ return Expression::make_var_reference(no, loc);
+ else if (no->is_function() || no->is_function_declaration())
+ return Expression::make_func_reference(no, NULL, loc);
+ else
+ {
+ if (!ifb->saw_error())
+ go_error_at(ifb->location(),
+ ("import error for %qs: "
+ "unexpected type of identifier %qs (%d)"),
+ ifb->name().c_str(),
+ id.c_str(), no->classification());
+ ifb->set_saw_error();
+ return Expression::make_error(loc);
+ }
}
// Class Expression_list.
Index: gcc/go/gofrontend/expressions.h
===================================================================
--- gcc/go/gofrontend/expressions.h (revision 271891)
+++ gcc/go/gofrontend/expressions.h (working copy)
@@ -1253,6 +1253,9 @@ class Expression
static Expression*
import_identifier(Import_function_body*, Location);
+ static Expression*
+ import_expression_without_suffix(Import_expression*, Location);
+
// The expression classification.
Expression_classification classification_;
// The location in the input file.
@@ -2409,6 +2412,12 @@ class Call_expression : public Expressio
virtual Bexpression*
do_get_backend(Translate_context*);
+ int
+ do_inlining_cost() const;
+
+ void
+ do_export(Export_function_body*) const;
+
virtual bool
do_is_recover_call() const;
@@ -2432,6 +2441,9 @@ class Call_expression : public Expressio
determining_types();
void
+ export_arguments(Export_function_body*) const;
+
+ void
do_dump_expression(Ast_dump_context*) const;
void
@@ -2571,6 +2583,10 @@ class Builtin_call_expression : public C
Bexpression*
do_get_backend(Translate_context*);
+ int
+ do_inlining_cost() const
+ { return 1; }
+
void
do_export(Export_function_body*) const;
@@ -2745,6 +2761,12 @@ class Func_expression : public Expressio
Bexpression*
do_get_backend(Translate_context*);
+ int
+ do_inlining_cost() const;
+
+ void
+ do_export(Export_function_body*) const;
+
void
do_dump_expression(Ast_dump_context*) const;
Index: gcc/go/gofrontend/gogo.cc
===================================================================
--- gcc/go/gofrontend/gogo.cc (revision 271894)
+++ gcc/go/gofrontend/gogo.cc (working copy)
@@ -5059,6 +5059,9 @@ Mark_inline_candidates::type(Type* t)
void
Gogo::do_exports()
{
+ if (saw_errors())
+ return;
+
// Mark any functions whose body should be exported for inlining by
// other packages.
Mark_inline_candidates mic;
@@ -5690,7 +5693,7 @@ Function::export_func(Export* exp, const
block = this->block_;
Function::export_func_with_type(exp, no, this->type_, this->results_,
this->is_method() && this->nointerface(),
- block, this->location_);
+ this->asm_name(), block, this->location_);
}
// Export a function with a type.
@@ -5699,7 +5702,8 @@ void
Function::export_func_with_type(Export* exp, const Named_object* no,
const Function_type* fntype,
Function::Results* result_vars,
- bool nointerface, Block* block, Location loc)
+ bool nointerface, const std::string& asm_name,
+ Block* block, Location loc)
{
exp->write_c_string("func ");
@@ -5709,6 +5713,13 @@ Function::export_func_with_type(Export*
exp->write_c_string("/*nointerface*/ ");
}
+ if (!asm_name.empty())
+ {
+ exp->write_c_string("/*asm ");
+ exp->write_string(asm_name);
+ exp->write_c_string(" */ ");
+ }
+
if (fntype->is_method())
{
exp->write_c_string("(");
@@ -5848,16 +5859,37 @@ Function::import_func(Import* imp, std::
Typed_identifier_list** presults,
bool* is_varargs,
bool* nointerface,
+ std::string* asm_name,
std::string* body)
{
imp->require_c_string("func ");
*nointerface = false;
- if (imp->match_c_string("/*"))
+ while (imp->match_c_string("/*"))
{
- imp->require_c_string("/*nointerface*/ ");
- *nointerface = true;
+ imp->advance(2);
+ if (imp->match_c_string("nointerface"))
+ {
+ imp->require_c_string("nointerface*/ ");
+ *nointerface = true;
+ }
+ else if (imp->match_c_string("asm"))
+ {
+ imp->require_c_string("asm ");
+ *asm_name = imp->read_identifier();
+ imp->require_c_string(" */ ");
+ }
+ else
+ {
+ go_error_at(imp->location(),
+ "import error at %d: unrecognized function comment",
+ imp->pos());
+ return false;
+ }
+ }
+ if (*nointerface)
+ {
// Only a method can be nointerface.
go_assert(imp->peek_char() == '(');
}
@@ -7158,6 +7190,8 @@ Function_declaration::import_function_bo
Named_type* rtype = fntype->receiver()->type()->deref()->named_type();
go_assert(rtype != NULL);
no = rtype->add_method(no->name(), fn);
+ const Package* package = rtype->named_object()->package();
+ package->bindings()->add_method(no);
}
Import_function_body ifb(gogo, this->imp_, no, body, nl + 1, outer, indent);
Index: gcc/go/gofrontend/gogo.h
===================================================================
--- gcc/go/gofrontend/gogo.h (revision 271894)
+++ gcc/go/gofrontend/gogo.h (working copy)
@@ -1573,7 +1573,7 @@ class Function
static void
export_func_with_type(Export*, const Named_object*,
const Function_type*, Results*, bool nointerface,
- Block* block, Location);
+ const std::string& asm_name, Block* block, Location);
// Import a function. Reports whether the import succeeded.
static bool
@@ -1581,7 +1581,7 @@ class Function
bool* is_exported, Typed_identifier** receiver,
Typed_identifier_list** pparameters,
Typed_identifier_list** presults, bool* is_varargs,
- bool* nointerface, std::string* body);
+ bool* nointerface, std::string* asm_name, std::string* body);
private:
// Type for mapping from label names to Label objects.
@@ -1805,7 +1805,7 @@ class Function_declaration
{
Function::export_func_with_type(exp, no, this->fntype_, NULL,
this->is_method() && this->nointerface(),
- NULL, this->location_);
+ this->asm_name_, NULL, this->location_);
}
// Check that the types used in this declaration's signature are defined.
Index: gcc/go/gofrontend/import.cc
===================================================================
--- gcc/go/gofrontend/import.cc (revision 271891)
+++ gcc/go/gofrontend/import.cc (working copy)
@@ -757,10 +757,11 @@ Import::import_func(Package* package)
Typed_identifier_list* results;
bool is_varargs;
bool nointerface;
+ std::string asm_name;
std::string body;
if (!Function::import_func(this, &name, &fpkg, &is_exported, &receiver,
¶meters, &results, &is_varargs, &nointerface,
- &body))
+ &asm_name, &body))
return;
if (fpkg == NULL)
fpkg = package;
@@ -802,12 +803,14 @@ Import::import_func(Package* package)
else
{
no = fpkg->add_function_declaration(name, fntype, loc);
- if (this->add_to_globals_)
+ if (this->add_to_globals_ && fpkg == package)
this->gogo_->add_dot_import_object(no);
}
if (nointerface)
no->func_declaration_value()->set_nointerface();
+ if (!asm_name.empty())
+ no->func_declaration_value()->set_asm_name(asm_name);
if (!body.empty() && !no->func_declaration_value()->has_imported_body())
no->func_declaration_value()->set_imported_body(this, body);
}
@@ -1231,6 +1234,12 @@ Import::register_builtin_type(Gogo* gogo
this->builtin_types_[index] = named_object->type_value();
}
+// Characters that stop read_identifier. We base this on the
+// characters that stop an identifier, without worrying about
+// characters that are permitted in an identifier. That lets us skip
+// UTF-8 parsing.
+static const char * const identifier_stop = " \n;,()[]";
+
// Read an identifier from the stream.
std::string
@@ -1242,8 +1251,14 @@ Import::read_identifier()
while (true)
{
c = stream->peek_char();
- if (c == -1 || c == ' ' || c == '\n' || c == ';' || c == ')')
+ if (c == -1 || strchr(identifier_stop, c) != NULL)
+ break;
+
+ // FIXME: Probably we shouldn't accept '.', but that might break
+ // some existing imports.
+ if (c == '.' && stream->match_c_string("..."))
break;
+
ret += c;
stream->advance(1);
}
@@ -1521,7 +1536,18 @@ Import_function_body::read_identifier()
for (size_t i = start; i < this->body_.length(); i++)
{
int c = static_cast<unsigned char>(this->body_[i]);
- if (c == ' ' || c == '\n' || c == ';' || c == ')')
+ if (strchr(identifier_stop, c) != NULL)
+ {
+ this->off_ = i;
+ return this->body_.substr(start, i - start);
+ }
+
+ // FIXME: Probably we shouldn't accept '.', but that might break
+ // some existing imports.
+ if (c == '.'
+ && i + 2 < this->body_.length()
+ && this->body_[i + 1] == '.'
+ && this->body_[i + 2] == '.')
{
this->off_ = i;
return this->body_.substr(start, i - start);
Index: libgo/go/go/internal/gccgoimporter/parser.go
===================================================================
--- libgo/go/go/internal/gccgoimporter/parser.go (revision 271669)
+++ libgo/go/go/internal/gccgoimporter/parser.go (working copy)
@@ -539,10 +539,12 @@ func (p *parser) parseNamedType(nlist []
for p.tok == scanner.Ident {
p.expectKeyword("func")
if p.tok == '/' {
- // Skip a /*nointerface*/ comment.
+ // Skip a /*nointerface*/ or /*asm ID */ comment.
p.expect('/')
p.expect('*')
- p.expect(scanner.Ident)
+ if p.expect(scanner.Ident) == "asm" {
+ p.parseUnquotedString()
+ }
p.expect('*')
p.expect('/')
}
@@ -727,6 +729,17 @@ func (p *parser) parseFunctionType(pkg *
// Func = Name FunctionType [InlineBody] .
func (p *parser) parseFunc(pkg *types.Package) *types.Func {
+ if p.tok == '/' {
+ // Skip an /*asm ID */ comment.
+ p.expect('/')
+ p.expect('*')
+ if p.expect(scanner.Ident) == "asm" {
+ p.parseUnquotedString()
+ }
+ p.expect('*')
+ p.expect('/')
+ }
+
name := p.parseName()
if strings.ContainsRune(name, '$') {
// This is a Type$equal or Type$hash function, which we don't want to parse,
^ permalink raw reply [flat|nested] only message in thread
only message in thread, other threads:[~2019-06-05 21:06 UTC | newest]
Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-06-05 21:06 Go patch committed: Make calls and functions inlinable Ian Lance Taylor
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).