aboutsummaryrefslogtreecommitdiff
path: root/gcc/go/gofrontend/gogo.cc
diff options
context:
space:
mode:
authorIan Lance Taylor <ian@gcc.gnu.org>2018-11-26 21:44:20 +0000
committerIan Lance Taylor <ian@gcc.gnu.org>2018-11-26 21:44:20 +0000
commit1e4cc1d4b097f346b4cda611782fe83eda0df18f (patch)
tree2d4e3368b005eaf1e957c8b3437f6e03d7026f42 /gcc/go/gofrontend/gogo.cc
parent50e99db39196a0567e844f35ce1cd0f36d066b8f (diff)
downloadgcc-1e4cc1d4b097f346b4cda611782fe83eda0df18f.zip
gcc-1e4cc1d4b097f346b4cda611782fe83eda0df18f.tar.gz
gcc-1e4cc1d4b097f346b4cda611782fe83eda0df18f.tar.bz2
compiler: initial support for exporting function bodies
Create a framework for putting function bodies in export data. At present only empty functions will be put there, and they will be ignored on import. Later patches will get this to the point of supporting inlining of (some) functions defined in other packages. Reviewed-on: https://go-review.googlesource.com/c/150061 From-SVN: r266490
Diffstat (limited to 'gcc/go/gofrontend/gogo.cc')
-rw-r--r--gcc/go/gofrontend/gogo.cc229
1 files changed, 222 insertions, 7 deletions
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*