aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gcc/go/gofrontend/MERGE2
-rw-r--r--gcc/go/gofrontend/export.h56
-rw-r--r--gcc/go/gofrontend/expressions.h12
-rw-r--r--gcc/go/gofrontend/gogo.cc229
-rw-r--r--gcc/go/gofrontend/gogo.h23
-rw-r--r--gcc/go/gofrontend/import.cc18
-rw-r--r--gcc/go/gofrontend/import.h5
-rw-r--r--gcc/go/gofrontend/statements.cc22
-rw-r--r--gcc/go/gofrontend/statements.h40
-rw-r--r--libgo/go/go/internal/gccgoimporter/parser.go63
10 files changed, 454 insertions, 16 deletions
diff --git a/gcc/go/gofrontend/MERGE b/gcc/go/gofrontend/MERGE
index 110cb84..89d79de 100644
--- a/gcc/go/gofrontend/MERGE
+++ b/gcc/go/gofrontend/MERGE
@@ -1,4 +1,4 @@
-37cb9763cbe8407b8c3a237b05a5272a226f14a0
+26639de5b48ca895f517b7b5f5720b2613f885ce
The first line of this file holds the git revision number of the last
merge done from the gofrontend repository.
diff --git a/gcc/go/gofrontend/export.h b/gcc/go/gofrontend/export.h
index 84077a2..5751488 100644
--- a/gcc/go/gofrontend/export.h
+++ b/gcc/go/gofrontend/export.h
@@ -286,4 +286,60 @@ class Stream_to_string : public Export::Stream
std::string string_;
};
+// Class to manage exporting a function body. This is passed around
+// to Statements and Expressions. It builds up the export data for
+// the function.
+
+class Export_function_body
+{
+ public:
+ Export_function_body(int indent)
+ : indent_(indent)
+ { }
+
+ // Write a character to the body.
+ void
+ write_char(char c)
+ { this->body_.append(1, c); }
+
+ // Write a NUL terminated string to the body.
+ void
+ write_c_string(const char* str)
+ { this->body_.append(str); }
+
+ // Write a string to the body.
+ void
+ write_string(const std::string& str)
+ { this->body_.append(str); }
+
+ // Append as many spaces as the current indentation level.
+ void
+ indent()
+ {
+ for (int i = this->indent_; i > 0; i--)
+ this->write_char(' ');
+ }
+
+ // Increment the indentation level.
+ void
+ increment_indent()
+ { ++this->indent_; }
+
+ // Decrement the indentation level.
+ void
+ decrement_indent()
+ { --this->indent_; }
+
+ // Return a reference to the completed body.
+ const std::string&
+ body() const
+ { return this->body_; }
+
+ private:
+ // The body we are building.
+ std::string body_;
+ // Current indentation level: the number of spaces before each statement.
+ int indent_;
+};
+
#endif // !defined(GO_EXPORT_H)
diff --git a/gcc/go/gofrontend/expressions.h b/gcc/go/gofrontend/expressions.h
index f53cc6e..33ea9b2 100644
--- a/gcc/go/gofrontend/expressions.h
+++ b/gcc/go/gofrontend/expressions.h
@@ -939,6 +939,11 @@ class Expression
copy()
{ return this->do_copy(); }
+ // Return the cost of this statement for inlining purposes.
+ int
+ inlining_cost()
+ { return this->do_inlining_cost(); }
+
// Return whether the expression is addressable--something which may
// be used as the operand of the unary & operator.
bool
@@ -1084,6 +1089,13 @@ class Expression
virtual Expression*
do_copy() = 0;
+ // Child class implements determining the cost of this statement for
+ // inlining. The default cost is high, so we only need to define
+ // this method for expressions that can be inlined.
+ virtual int
+ do_inlining_cost()
+ { return 0x100000; }
+
// Child class implements whether the expression is addressable.
virtual bool
do_is_addressable() const
diff --git a/gcc/go/gofrontend/gogo.cc b/gcc/go/gofrontend/gogo.cc
index 6ea247a..354eaba 100644
--- a/gcc/go/gofrontend/gogo.cc
+++ b/gcc/go/gofrontend/gogo.cc
@@ -4691,11 +4691,130 @@ Gogo::check_return_statements()
this->traverse(&traverse);
}
+// Traversal class to decide whether a function body is less than the
+// inlining budget. This adjusts *available as it goes, and stops the
+// traversal if it goes negative.
+
+class Inline_within_budget : public Traverse
+{
+ public:
+ Inline_within_budget(int* available)
+ : Traverse(traverse_statements
+ | traverse_expressions),
+ available_(available)
+ { }
+
+ int
+ statement(Block*, size_t*, Statement*);
+
+ int
+ expression(Expression**);
+
+ private:
+ // Pointer to remaining budget.
+ int* available_;
+};
+
+// Adjust the budget for the inlining cost of a statement.
+
+int
+Inline_within_budget::statement(Block*, size_t*, Statement* s)
+{
+ if (*this->available_ < 0)
+ return TRAVERSE_EXIT;
+ *this->available_ -= s->inlining_cost();
+ return TRAVERSE_CONTINUE;
+}
+
+// Adjust the budget for the inlining cost of an expression.
+
+int
+Inline_within_budget::expression(Expression** pexpr)
+{
+ if (*this->available_ < 0)
+ return TRAVERSE_EXIT;
+ *this->available_ -= (*pexpr)->inlining_cost();
+ return TRAVERSE_CONTINUE;
+}
+
+// Traversal class to find functions whose body should be exported for
+// inlining by other packages.
+
+class Mark_inline_candidates : public Traverse
+{
+ public:
+ Mark_inline_candidates()
+ : Traverse(traverse_functions
+ | traverse_types)
+ { }
+
+ int
+ function(Named_object*);
+
+ int
+ type(Type*);
+
+ private:
+ // We traverse the function body trying to determine how expensive
+ // it is for inlining. We start with a budget, and decrease that
+ // budget for each statement and expression. If the budget goes
+ // negative, we do not export the function body. The value of this
+ // budget is a heuristic. In the usual GCC spirit, we could
+ // consider setting this via a command line option.
+ const int budget_heuristic = 80;
+};
+
+// Mark a function if it is an inline candidate.
+
+int
+Mark_inline_candidates::function(Named_object* no)
+{
+ Function* func = no->func_value();
+ int budget = budget_heuristic;
+ Inline_within_budget iwb(&budget);
+ func->block()->traverse(&iwb);
+ if (budget >= 0)
+ func->set_export_for_inlining();
+ return TRAVERSE_CONTINUE;
+}
+
+// Mark methods if they are inline candidates.
+
+int
+Mark_inline_candidates::type(Type* t)
+{
+ Named_type* nt = t->named_type();
+ if (nt == NULL || nt->is_alias())
+ return TRAVERSE_CONTINUE;
+ const Bindings* methods = nt->local_methods();
+ if (methods == NULL)
+ return TRAVERSE_CONTINUE;
+ for (Bindings::const_definitions_iterator p = methods->begin_definitions();
+ p != methods->end_definitions();
+ ++p)
+ {
+ Named_object* no = *p;
+ go_assert(no->is_function());
+ Function *func = no->func_value();
+ int budget = budget_heuristic;
+ Inline_within_budget iwb(&budget);
+ func->block()->traverse(&iwb);
+ if (budget >= 0)
+ func->set_export_for_inlining();
+ }
+ return TRAVERSE_CONTINUE;
+}
+
// Export identifiers as requested.
void
Gogo::do_exports()
{
+ // Mark any functions whose body should be exported for inlining by
+ // other packages.
+ Mark_inline_candidates mic;
+ this->traverse(&mic);
+
// For now we always stream to a section. Later we may want to
// support streaming to a separate file.
Stream_to_section stream(this->backend());
@@ -4962,7 +5081,7 @@ Function::Function(Function_type* type, Named_object* enclosing, Block* block,
results_are_named_(false), is_unnamed_type_stub_method_(false),
calls_recover_(false), is_recover_thunk_(false), has_recover_thunk_(false),
calls_defer_retaddr_(false), is_type_specific_function_(false),
- in_unique_section_(false)
+ in_unique_section_(false), export_for_inlining_(false)
{
}
@@ -5316,15 +5435,20 @@ Function::defer_stack(Location location)
void
Function::export_func(Export* exp, const std::string& name) const
{
+ Block* block = NULL;
+ if (this->export_for_inlining())
+ block = this->block_;
Function::export_func_with_type(exp, name, this->type_,
- this->is_method() && this->nointerface());
+ this->is_method() && this->nointerface(),
+ block);
}
// Export a function with a type.
void
Function::export_func_with_type(Export* exp, const std::string& name,
- const Function_type* fntype, bool nointerface)
+ const Function_type* fntype, bool nointerface,
+ Block* block)
{
exp->write_c_string("func ");
@@ -5404,7 +5528,32 @@ Function::export_func_with_type(Export* exp, const std::string& name,
exp->write_c_string(")");
}
}
- exp->write_c_string("\n");
+
+ if (block == NULL)
+ exp->write_c_string("\n");
+ else
+ {
+ int indent = 1;
+ if (fntype->is_method())
+ indent++;
+
+ Export_function_body efb(indent);
+
+ efb.indent();
+ efb.write_c_string("// ");
+ efb.write_string(Linemap::location_to_file(block->start_location()));
+ efb.write_char('\n');
+ block->export_block(&efb);
+
+ const std::string& body(efb.body());
+
+ char buf[100];
+ snprintf(buf, sizeof buf, " <inl:%lu>\n",
+ static_cast<unsigned long>(body.length()));
+ exp->write_c_string(buf);
+
+ exp->write_string(body);
+ }
}
// Import a function.
@@ -5480,7 +5629,7 @@ Function::import_func(Import* imp, std::string* pname,
*pparameters = parameters;
Typed_identifier_list* results;
- if (imp->peek_char() != ' ')
+ if (imp->peek_char() != ' ' || imp->match_c_string(" <inl"))
results = NULL;
else
{
@@ -5511,9 +5660,46 @@ Function::import_func(Import* imp, std::string* pname,
imp->require_c_string(")");
}
}
- imp->require_semicolon_if_old_version();
- imp->require_c_string("\n");
*presults = results;
+
+ if (!imp->match_c_string(" <inl:"))
+ {
+ imp->require_semicolon_if_old_version();
+ imp->require_c_string("\n");
+ }
+ else
+ {
+ imp->require_c_string(" <inl:");
+ std::string lenstr;
+ int c;
+ while (true)
+ {
+ c = imp->peek_char();
+ if (c < '0' || c > '9')
+ break;
+ lenstr += c;
+ imp->get_char();
+ }
+ imp->require_c_string(">\n");
+
+ errno = 0;
+ char* end;
+ long llen = strtol(lenstr.c_str(), &end, 10);
+ if (*end != '\0'
+ || llen < 0
+ || (llen == LONG_MAX && errno == ERANGE))
+ {
+ go_error_at(imp->location(), "invalid inline function length %s",
+ lenstr.c_str());
+ return;
+ }
+
+ imp->read(static_cast<size_t>(llen));
+
+ // Here we should record the body for later parsing if we see a
+ // call to this function. This is not yet implemented. For now
+ // we just discard the information.
+ }
}
// Get the backend representation.
@@ -6234,6 +6420,35 @@ Block::may_fall_through() const
return this->statements_.back()->may_fall_through();
}
+// Write export data for a block.
+
+void
+Block::export_block(Export_function_body* efb)
+{
+ for (Block::iterator p = this->begin();
+ p != this->end();
+ ++p)
+ {
+ efb->indent();
+
+ efb->increment_indent();
+ (*p)->export_statement(efb);
+ efb->decrement_indent();
+
+ Location loc = (*p)->location();
+ if ((*p)->is_block_statement())
+ {
+ // For a block we put the start location on the first brace
+ // in Block_statement::do_export_statement. Here we put the
+ // end location on the final brace.
+ loc = (*p)->block_statement()->block()->end_location();
+ }
+ char buf[50];
+ snprintf(buf, sizeof buf, " //%d\n", Linemap::location_to_line(loc));
+ efb->write_c_string(buf);
+ }
+}
+
// Convert a block to the backend representation.
Bblock*
diff --git a/gcc/go/gofrontend/gogo.h b/gcc/go/gofrontend/gogo.h
index 48359eb..1b13e5c 100644
--- a/gcc/go/gofrontend/gogo.h
+++ b/gcc/go/gofrontend/gogo.h
@@ -41,6 +41,7 @@ class Label;
class Translate_context;
class Backend;
class Export;
+class Export_function_body;
class Import;
class Bexpression;
class Btype;
@@ -1139,6 +1140,10 @@ class Block
bool
may_fall_through() const;
+ // Write the export data for the block's statements to the string.
+ void
+ export_block(Export_function_body*);
+
// Convert the block to the backend representation.
Bblock*
get_backend(Translate_context*);
@@ -1404,6 +1409,16 @@ class Function
set_in_unique_section()
{ this->in_unique_section_ = true; }
+ // Return whether this function should be exported for inlining.
+ bool
+ export_for_inlining() const
+ { return this->export_for_inlining_; }
+
+ // Mark the function to be exported for inlining.
+ void
+ set_export_for_inlining()
+ { this->export_for_inlining_ = true; }
+
// Swap with another function. Used only for the thunk which calls
// recover.
void
@@ -1461,7 +1476,7 @@ class Function
// Export a function with a type.
static void
export_func_with_type(Export*, const std::string& name,
- const Function_type*, bool nointerface);
+ const Function_type*, bool nointerface, Block* block);
// Import a function.
static void
@@ -1539,6 +1554,9 @@ class Function
// True if this function should be put in a unique section. This is
// turned on for field tracking.
bool in_unique_section_ : 1;
+ // True if we should export the body of this function for
+ // cross-package inlining.
+ bool export_for_inlining_ : 1;
};
// A snapshot of the current binding state.
@@ -1654,7 +1672,8 @@ class Function_declaration
export_func(Export* exp, const std::string& name) const
{
Function::export_func_with_type(exp, name, this->fntype_,
- this->is_method() && this->nointerface());
+ this->is_method() && this->nointerface(),
+ NULL);
}
// Check that the types used in this declaration's signature are defined.
diff --git a/gcc/go/gofrontend/import.cc b/gcc/go/gofrontend/import.cc
index d30068f..a0a1f18 100644
--- a/gcc/go/gofrontend/import.cc
+++ b/gcc/go/gofrontend/import.cc
@@ -1230,6 +1230,24 @@ Import::read_name()
return ret;
}
+// Read LENGTH bytes from the stream.
+
+std::string
+Import::read(size_t length)
+{
+ const char* data;
+ if (!this->stream_->peek(length, &data))
+ {
+ if (!this->stream_->saw_error())
+ go_error_at(this->location_, "import error at %d: expected %d bytes",
+ this->stream_->pos(), static_cast<int>(length));
+ this->stream_->set_saw_error();
+ return "";
+ }
+ this->advance(length);
+ return std::string(data, length);
+}
+
// Turn a string into a integer with appropriate error handling.
bool
diff --git a/gcc/go/gofrontend/import.h b/gcc/go/gofrontend/import.h
index cc7703b..9c6cbfe 100644
--- a/gcc/go/gofrontend/import.h
+++ b/gcc/go/gofrontend/import.h
@@ -169,6 +169,11 @@ class Import
get_char()
{ return this->stream_->get_char(); }
+ // Read LENGTH characters into a string and advance past them. On
+ // EOF reports an error and returns an empty string.
+ std::string
+ read(size_t length);
+
// Return true at the end of the stream.
bool
at_eof()
diff --git a/gcc/go/gofrontend/statements.cc b/gcc/go/gofrontend/statements.cc
index c35002d..718139c 100644
--- a/gcc/go/gofrontend/statements.cc
+++ b/gcc/go/gofrontend/statements.cc
@@ -11,6 +11,7 @@
#include "types.h"
#include "expressions.h"
#include "gogo.h"
+#include "export.h"
#include "runtime.h"
#include "backend.h"
#include "statements.h"
@@ -1781,6 +1782,27 @@ Statement::make_statement(Expression* expr, bool is_ignored)
return new Expression_statement(expr, is_ignored);
}
+// Export data for a block.
+
+void
+Block_statement::do_export_statement(Export_function_body* efb)
+{
+ // We are already indented to the right position.
+ char buf[50];
+ snprintf(buf, sizeof buf, "{ //%d\n",
+ Linemap::location_to_line(this->block_->start_location()));
+ efb->write_c_string(buf);
+
+ this->block_->export_block(efb);
+ // The indentation is correct for the statements in the block, so
+ // subtract one for the closing curly brace.
+ efb->decrement_indent();
+ efb->indent();
+ efb->write_c_string("}");
+ // Increment back to the value the caller thinks it has.
+ efb->increment_indent();
+}
+
// Convert a block to the backend representation of a statement.
Bstatement*
diff --git a/gcc/go/gofrontend/statements.h b/gcc/go/gofrontend/statements.h
index 81b26b3..ff57a21 100644
--- a/gcc/go/gofrontend/statements.h
+++ b/gcc/go/gofrontend/statements.h
@@ -15,6 +15,7 @@ class Statement_inserter;
class Block;
class Function;
class Unnamed_label;
+class Export_function_body;
class Assignment_statement;
class Temporary_statement;
class Variable_declaration_statement;
@@ -326,6 +327,17 @@ class Statement
check_types(Gogo* gogo)
{ this->do_check_types(gogo); }
+ // Return the cost of this statement for inlining purposes.
+ int
+ inlining_cost()
+ { return this->do_inlining_cost(); }
+
+ // Export data for this statement to BODY. INDENT is an indentation
+ // level used if the export data requires multiple lines.
+ void
+ export_statement(Export_function_body* efb)
+ { this->do_export_statement(efb); }
+
// Return whether this is a block statement.
bool
is_block_statement() const
@@ -488,6 +500,22 @@ class Statement
do_check_types(Gogo*)
{ }
+ // Implemented by child class: return the cost of this statement for
+ // inlining. The default cost is high, so we only need to define
+ // this method for statements that can be inlined.
+ virtual int
+ do_inlining_cost()
+ { return 0x100000; }
+
+ // Implemented by child class: write export data for this statement
+ // to the string. The integer is an indentation level used if the
+ // export data requires multiple lines. This need only be
+ // implemented by classes that implement do_inlining_cost with a
+ // reasonable value.
+ virtual void
+ do_export_statement(Export_function_body*)
+ { go_unreachable(); }
+
// Implemented by child class: return true if this statement may
// fall through.
virtual bool
@@ -819,6 +847,11 @@ class Block_statement : public Statement
block_(block), is_lowered_for_statement_(false)
{ }
+ // Return the actual block.
+ Block*
+ block() const
+ { return this->block_; }
+
void
set_is_lowered_for_statement()
{ this->is_lowered_for_statement_ = true; }
@@ -836,6 +869,13 @@ class Block_statement : public Statement
do_determine_types()
{ this->block_->determine_types(); }
+ int
+ do_inlining_cost()
+ { return 0; }
+
+ void
+ do_export_statement(Export_function_body*);
+
bool
do_may_fall_through() const
{ return this->block_->may_fall_through(); }
diff --git a/libgo/go/go/internal/gccgoimporter/parser.go b/libgo/go/go/internal/gccgoimporter/parser.go
index dc61e4c..c23002f 100644
--- a/libgo/go/go/internal/gccgoimporter/parser.go
+++ b/libgo/go/go/internal/gccgoimporter/parser.go
@@ -15,6 +15,7 @@ import (
"strconv"
"strings"
"text/scanner"
+ "unicode/utf8"
)
type parser struct {
@@ -41,7 +42,7 @@ func (p *parser) init(filename string, src io.Reader, imports map[string]*types.
func (p *parser) initScanner(filename string, src io.Reader) {
p.scanner.Init(src)
p.scanner.Error = func(_ *scanner.Scanner, msg string) { p.error(msg) }
- p.scanner.Mode = scanner.ScanIdents | scanner.ScanInts | scanner.ScanFloats | scanner.ScanStrings | scanner.ScanComments | scanner.SkipComments
+ p.scanner.Mode = scanner.ScanIdents | scanner.ScanInts | scanner.ScanFloats | scanner.ScanStrings
p.scanner.Whitespace = 1<<'\t' | 1<<' '
p.scanner.Filename = filename // for good error messages
p.next()
@@ -393,7 +394,7 @@ func (p *parser) parseConst(pkg *types.Package) *types.Const {
// NamedType = TypeName [ "=" ] Type { Method } .
// TypeName = ExportedName .
-// Method = "func" "(" Param ")" Name ParamList ResultList ";" .
+// Method = "func" "(" Param ")" Name ParamList ResultList [InlineBody] ";" .
func (p *parser) parseNamedType(n int) types.Type {
pkg, name := p.parseExportedName()
scope := pkg.Scope()
@@ -455,6 +456,7 @@ func (p *parser) parseNamedType(n int) types.Type {
name := p.parseName()
params, isVariadic := p.parseParamList(pkg)
results := p.parseResultList(pkg)
+ p.skipInlineBody()
p.expectEOL()
sig := types.NewSignature(receiver, params, results, isVariadic)
@@ -566,7 +568,11 @@ func (p *parser) parseParamList(pkg *types.Package) (*types.Tuple, bool) {
func (p *parser) parseResultList(pkg *types.Package) *types.Tuple {
switch p.tok {
case '<':
- return types.NewTuple(types.NewParam(token.NoPos, pkg, "", p.parseType(pkg)))
+ p.next()
+ if p.tok == scanner.Ident && p.lit == "inl" {
+ return nil
+ }
+ return types.NewTuple(types.NewParam(token.NoPos, pkg, "", p.parseTypeAfterAngle(pkg)))
case '(':
params, _ := p.parseParamList(pkg)
@@ -584,7 +590,7 @@ func (p *parser) parseFunctionType(pkg *types.Package) *types.Signature {
return types.NewSignature(nil, params, results, isVariadic)
}
-// Func = Name FunctionType .
+// Func = Name FunctionType [InlineBody] .
func (p *parser) parseFunc(pkg *types.Package) *types.Func {
name := p.parseName()
if strings.ContainsRune(name, '$') {
@@ -593,7 +599,9 @@ func (p *parser) parseFunc(pkg *types.Package) *types.Func {
p.discardDirectiveWhileParsingTypes(pkg)
return nil
}
- return types.NewFunc(token.NoPos, pkg, name, p.parseFunctionType(pkg))
+ f := types.NewFunc(token.NoPos, pkg, name, p.parseFunctionType(pkg))
+ p.skipInlineBody()
+ return f
}
// InterfaceType = "interface" "{" { ("?" Type | Func) ";" } "}" .
@@ -717,8 +725,13 @@ func lookupBuiltinType(typ int) types.Type {
}
// Type = "<" "type" ( "-" int | int [ TypeDefinition ] ) ">" .
-func (p *parser) parseType(pkg *types.Package) (t types.Type) {
+func (p *parser) parseType(pkg *types.Package) types.Type {
p.expect('<')
+ return p.parseTypeAfterAngle(pkg)
+}
+
+// (*parser).Type after reading the "<".
+func (p *parser) parseTypeAfterAngle(pkg *types.Package) (t types.Type) {
p.expectKeyword("type")
switch p.tok {
@@ -748,6 +761,39 @@ func (p *parser) parseType(pkg *types.Package) (t types.Type) {
return
}
+// InlineBody = "<inl:NN>" .{NN}
+// Reports whether a body was skipped.
+func (p *parser) skipInlineBody() {
+ // We may or may not have seen the '<' already, depending on
+ // whether the function had a result type or not.
+ if p.tok == '<' {
+ p.next()
+ p.expectKeyword("inl")
+ } else if p.tok != scanner.Ident || p.lit != "inl" {
+ return
+ } else {
+ p.next()
+ }
+
+ p.expect(':')
+ want := p.parseInt()
+ p.expect('>')
+
+ defer func(w uint64) {
+ p.scanner.Whitespace = w
+ }(p.scanner.Whitespace)
+ p.scanner.Whitespace = 0
+
+ got := int64(0)
+ for got < want {
+ r := p.scanner.Next()
+ if r == scanner.EOF {
+ p.error("unexpected EOF")
+ }
+ got += int64(utf8.RuneLen(r))
+ }
+}
+
// Types = "types" maxp1 exportedp1 (offset length)* .
func (p *parser) parseTypes(pkg *types.Package) {
maxp1 := p.parseInt()
@@ -766,6 +812,11 @@ func (p *parser) parseTypes(pkg *types.Package) {
total += len
}
+ defer func(w uint64) {
+ p.scanner.Whitespace = w
+ }(p.scanner.Whitespace)
+ p.scanner.Whitespace = 0
+
// We should now have p.tok pointing to the final newline.
// The next runes from the scanner should be the type data.