compiler: import inlinable functions from package data
Start reading the export data generated by the last change in this series. At this point we will inline direct calls to empty functions and methods defined in different packages. Reviewed-on: https://go-review.googlesource.com/c/150062 From-SVN: r266517
This commit is contained in:
parent
56c79e7f5d
commit
862ec76377
13 changed files with 512 additions and 33 deletions
|
@ -1,3 +1,8 @@
|
|||
2018-11-27 Ian Lance Taylor <iant@golang.org>
|
||||
|
||||
* go-gcc.cc (Gcc_backend::function): Handle function_only_inline
|
||||
flag.
|
||||
|
||||
2018-11-13 David Malcolm <dmalcolm@redhat.com>
|
||||
|
||||
* go-gcc-diagnostics.cc: Replace "source_location" with "location_t".
|
||||
|
|
|
@ -3086,6 +3086,11 @@ Gcc_backend::function(Btype* fntype, const std::string& name,
|
|||
TREE_THIS_VOLATILE(decl) = 1;
|
||||
if ((flags & function_in_unique_section) != 0)
|
||||
resolve_unique_section(decl, 0, 1);
|
||||
if ((flags & function_only_inline) != 0)
|
||||
{
|
||||
DECL_EXTERNAL(decl) = 1;
|
||||
DECL_DECLARED_INLINE_P(decl) = 1;
|
||||
}
|
||||
|
||||
go_preserve_from_gc(decl);
|
||||
return new Bfunction(decl);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
3ecc845c337c15d9a19ed8d277e5ee9eaf49c3ad
|
||||
f551ab95f46c3d7bb7c032711e10b03bfa995ee2
|
||||
|
||||
The first line of this file holds the git revision number of the last
|
||||
merge done from the gofrontend repository.
|
||||
|
|
|
@ -726,6 +726,13 @@ class Backend
|
|||
// possible. This is used for field tracking.
|
||||
static const unsigned int function_in_unique_section = 1 << 5;
|
||||
|
||||
// Set if the function should be available for inlining in the
|
||||
// backend, but should not be emitted as a standalone function. Any
|
||||
// call to the function that is not inlined should be treated as a
|
||||
// call to a function defined in a different compilation unit. This
|
||||
// is like a C99 function marked inline but not extern.
|
||||
static const unsigned int function_only_inline = 1 << 6;
|
||||
|
||||
// Declare or define a function of FNTYPE.
|
||||
// NAME is the Go name of the function. ASM_NAME, if not the empty
|
||||
// string, is the name that should be used in the symbol table; this
|
||||
|
|
|
@ -9785,6 +9785,15 @@ Call_expression::do_lower(Gogo* gogo, Named_object* function,
|
|||
}
|
||||
}
|
||||
|
||||
// If this is a call to an imported function for which we have an
|
||||
// inlinable function body, add it to the list of functions to give
|
||||
// to the backend as inlining opportunities.
|
||||
Func_expression* fe = this->fn_->func_expression();
|
||||
if (fe != NULL
|
||||
&& fe->named_object()->is_function_declaration()
|
||||
&& fe->named_object()->func_declaration_value()->has_imported_body())
|
||||
gogo->add_imported_inlinable_function(fe->named_object());
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
|
|
|
@ -97,8 +97,6 @@ go_parse_input_files(const char** filenames, unsigned int filename_count,
|
|||
}
|
||||
}
|
||||
|
||||
::gogo->linemap()->stop();
|
||||
|
||||
::gogo->clear_file_scope();
|
||||
|
||||
// If the global predeclared names are referenced but not defined,
|
||||
|
@ -122,6 +120,10 @@ go_parse_input_files(const char** filenames, unsigned int filename_count,
|
|||
// form which is easier to use.
|
||||
::gogo->lower_parse_tree();
|
||||
|
||||
// At this point we have handled all inline functions, so we no
|
||||
// longer need the linemap.
|
||||
::gogo->linemap()->stop();
|
||||
|
||||
// Create function descriptors as needed.
|
||||
::gogo->create_function_descriptors();
|
||||
|
||||
|
|
|
@ -62,7 +62,9 @@ Gogo::Gogo(Backend* backend, Linemap* linemap, int, int pointer_size)
|
|||
specific_type_functions_are_written_(false),
|
||||
named_types_are_converted_(false),
|
||||
analysis_sets_(),
|
||||
gc_roots_()
|
||||
gc_roots_(),
|
||||
imported_inlinable_functions_(),
|
||||
imported_inline_functions_()
|
||||
{
|
||||
const Location loc = Linemap::predeclared_location();
|
||||
|
||||
|
@ -1557,6 +1559,13 @@ Gogo::write_globals()
|
|||
}
|
||||
}
|
||||
|
||||
// Output inline functions, which are in different packages.
|
||||
for (std::vector<Named_object*>::const_iterator p =
|
||||
this->imported_inline_functions_.begin();
|
||||
p != this->imported_inline_functions_.end();
|
||||
++p)
|
||||
(*p)->get_backend(this, const_decls, type_decls, func_decls);
|
||||
|
||||
// Register global variables with the garbage collector.
|
||||
this->register_gc_vars(var_gc, init_stmts, init_bfn);
|
||||
|
||||
|
@ -2234,6 +2243,20 @@ Gogo::declare_package_function(const std::string& name, Function_type* type,
|
|||
location);
|
||||
}
|
||||
|
||||
// Add a function declaration to the list of functions we may want to
|
||||
// inline.
|
||||
|
||||
void
|
||||
Gogo::add_imported_inlinable_function(Named_object* no)
|
||||
{
|
||||
go_assert(no->is_function_declaration());
|
||||
Function_declaration* fd = no->func_declaration_value();
|
||||
if (fd->is_on_inlinable_list())
|
||||
return;
|
||||
this->imported_inlinable_functions_.push_back(no);
|
||||
fd->set_is_on_inlinable_list();
|
||||
}
|
||||
|
||||
// Define a type which was already declared.
|
||||
|
||||
void
|
||||
|
@ -2881,6 +2904,17 @@ Gogo::lower_parse_tree()
|
|||
Lower_parse_tree lower_parse_tree(this, NULL);
|
||||
this->traverse(&lower_parse_tree);
|
||||
|
||||
// If we found any functions defined in other packages that are
|
||||
// inlinables, import their bodies and turn them into functions.
|
||||
//
|
||||
// Note that as we import inlinable functions we may find more
|
||||
// inlinable functions, so don't use an iterator.
|
||||
for (size_t i = 0; i < this->imported_inlinable_functions_.size(); i++)
|
||||
{
|
||||
Named_object* no = this->imported_inlinable_functions_[i];
|
||||
no->func_declaration_value()->import_function_body(this, no);
|
||||
}
|
||||
|
||||
// There might be type definitions that involve expressions such as the
|
||||
// array length. Make sure to lower these expressions as well. Otherwise,
|
||||
// errors hidden within a type can introduce unexpected errors into later
|
||||
|
@ -5081,7 +5115,8 @@ 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), export_for_inlining_(false)
|
||||
in_unique_section_(false), export_for_inlining_(false),
|
||||
is_inline_only_(false)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -5440,7 +5475,7 @@ Function::export_func(Export* exp, const std::string& name) const
|
|||
block = this->block_;
|
||||
Function::export_func_with_type(exp, name, this->type_,
|
||||
this->is_method() && this->nointerface(),
|
||||
block);
|
||||
block, this->location_);
|
||||
}
|
||||
|
||||
// Export a function with a type.
|
||||
|
@ -5448,7 +5483,7 @@ Function::export_func(Export* exp, const std::string& name) const
|
|||
void
|
||||
Function::export_func_with_type(Export* exp, const std::string& name,
|
||||
const Function_type* fntype, bool nointerface,
|
||||
Block* block)
|
||||
Block* block, Location loc)
|
||||
{
|
||||
exp->write_c_string("func ");
|
||||
|
||||
|
@ -5542,12 +5577,15 @@ Function::export_func_with_type(Export* exp, const std::string& name,
|
|||
efb.indent();
|
||||
efb.write_c_string("// ");
|
||||
efb.write_string(Linemap::location_to_file(block->start_location()));
|
||||
efb.write_char(':');
|
||||
char buf[100];
|
||||
snprintf(buf, sizeof buf, "%d", Linemap::location_to_line(loc));
|
||||
efb.write_c_string(buf);
|
||||
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);
|
||||
|
@ -5564,7 +5602,8 @@ Function::import_func(Import* imp, std::string* pname,
|
|||
Typed_identifier_list** pparameters,
|
||||
Typed_identifier_list** presults,
|
||||
bool* is_varargs,
|
||||
bool* nointerface)
|
||||
bool* nointerface,
|
||||
std::string* body)
|
||||
{
|
||||
imp->require_c_string("func ");
|
||||
|
||||
|
@ -5666,6 +5705,7 @@ Function::import_func(Import* imp, std::string* pname,
|
|||
{
|
||||
imp->require_semicolon_if_old_version();
|
||||
imp->require_c_string("\n");
|
||||
body->clear();
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -5694,11 +5734,7 @@ Function::import_func(Import* imp, std::string* pname,
|
|||
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.
|
||||
*body = imp->read(static_cast<size_t>(llen));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5711,7 +5747,6 @@ Function::get_or_make_decl(Gogo* gogo, Named_object* no)
|
|||
{
|
||||
unsigned int flags = 0;
|
||||
bool is_init_fn = false;
|
||||
Type* rtype = NULL;
|
||||
if (no->package() != NULL)
|
||||
;
|
||||
else if (this->enclosing_ != NULL || Gogo::is_thunk(no))
|
||||
|
@ -5735,10 +5770,12 @@ Function::get_or_make_decl(Gogo* gogo, Named_object* no)
|
|||
{
|
||||
if (!this->is_unnamed_type_stub_method_)
|
||||
flags |= Backend::function_is_visible;
|
||||
if (this->type_->is_method())
|
||||
rtype = this->type_->receiver()->type();
|
||||
}
|
||||
|
||||
Type* rtype = NULL;
|
||||
if (this->type_->is_method())
|
||||
rtype = this->type_->receiver()->type();
|
||||
|
||||
std::string asm_name;
|
||||
if (!this->asm_name_.empty())
|
||||
{
|
||||
|
@ -5757,7 +5794,7 @@ Function::get_or_make_decl(Gogo* gogo, Named_object* no)
|
|||
asm_name = no->name();
|
||||
}
|
||||
else
|
||||
asm_name = gogo->function_asm_name(no->name(), NULL, rtype);
|
||||
asm_name = gogo->function_asm_name(no->name(), no->package(), rtype);
|
||||
|
||||
// If a function calls the predeclared recover function, we
|
||||
// can't inline it, because recover behaves differently in a
|
||||
|
@ -5803,6 +5840,9 @@ Function::get_or_make_decl(Gogo* gogo, Named_object* no)
|
|||
|| (this->is_method() && this->nointerface()))
|
||||
flags |= Backend::function_in_unique_section;
|
||||
|
||||
if (this->is_inline_only_)
|
||||
flags |= Backend::function_only_inline;
|
||||
|
||||
Btype* functype = this->type_->get_backend_fntype(gogo);
|
||||
this->fndecl_ =
|
||||
gogo->backend()->function(functype, no->get_id(gogo), asm_name,
|
||||
|
@ -6449,6 +6489,108 @@ Block::export_block(Export_function_body* efb)
|
|||
}
|
||||
}
|
||||
|
||||
// Add exported block data to SET, reading from BODY starting at OFF.
|
||||
// Returns whether the import succeeded.
|
||||
|
||||
bool
|
||||
Block::import_block(Block* set, Import_function_body *ifb, Location loc)
|
||||
{
|
||||
Location eloc = ifb->location();
|
||||
Location sloc = loc;
|
||||
const std::string& body(ifb->body());
|
||||
size_t off = ifb->off();
|
||||
while (off < body.length())
|
||||
{
|
||||
int indent = ifb->indent();
|
||||
if (off + indent >= body.length())
|
||||
{
|
||||
go_error_at(eloc,
|
||||
"invalid export data for %qs: insufficient indentation",
|
||||
ifb->name().c_str());
|
||||
return false;
|
||||
}
|
||||
for (int i = 0; i < indent - 1; i++)
|
||||
{
|
||||
if (body[off + i] != ' ')
|
||||
{
|
||||
go_error_at(eloc,
|
||||
"invalid export data for %qs: bad indentation",
|
||||
ifb->name().c_str());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool at_end = false;
|
||||
if (body[off + indent - 1] == '}')
|
||||
at_end = true;
|
||||
else if (body[off + indent - 1] != ' ')
|
||||
{
|
||||
go_error_at(eloc,
|
||||
"invalid export data for %qs: bad indentation",
|
||||
ifb->name().c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
off += indent;
|
||||
|
||||
size_t nl = body.find('\n', off);
|
||||
if (nl == std::string::npos)
|
||||
{
|
||||
go_error_at(eloc, "invalid export data for %qs: missing newline",
|
||||
ifb->name().c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t lineno_pos = body.find(" //", off);
|
||||
if (lineno_pos == std::string::npos || lineno_pos >= nl)
|
||||
{
|
||||
go_error_at(eloc, "invalid export data for %qs: missing line number",
|
||||
ifb->name().c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
unsigned int lineno = 0;
|
||||
for (size_t i = lineno_pos + 3; i < nl; ++i)
|
||||
{
|
||||
char c = body[i];
|
||||
if (c < '0' || c > '9')
|
||||
{
|
||||
go_error_at(loc,
|
||||
"invalid export data for %qs: invalid line number",
|
||||
ifb->name().c_str());
|
||||
return false;
|
||||
}
|
||||
lineno = lineno * 10 + c - '0';
|
||||
}
|
||||
|
||||
ifb->gogo()->linemap()->start_line(lineno, 1);
|
||||
sloc = ifb->gogo()->linemap()->get_location(0);
|
||||
|
||||
if (at_end)
|
||||
{
|
||||
off = nl + 1;
|
||||
break;
|
||||
}
|
||||
|
||||
ifb->set_off(off);
|
||||
Statement* s = Statement::import_statement(ifb, sloc);
|
||||
if (s == NULL)
|
||||
return false;
|
||||
|
||||
set->add_statement(s);
|
||||
|
||||
size_t at = ifb->off();
|
||||
if (at < nl + 1)
|
||||
off = nl + 1;
|
||||
else
|
||||
off = at;
|
||||
}
|
||||
|
||||
ifb->set_off(off);
|
||||
set->set_end_location(sloc);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Convert a block to the backend representation.
|
||||
|
||||
Bblock*
|
||||
|
@ -6607,6 +6749,144 @@ Function_declaration::set_nointerface()
|
|||
this->pragmas_ |= GOPRAGMA_NOINTERFACE;
|
||||
}
|
||||
|
||||
// Import an inlinable function. This is used for an inlinable
|
||||
// function whose body is recorded in the export data. Parse the
|
||||
// export data into a Block and create a regular function using that
|
||||
// Block as its body. Redeclare this function declaration as the
|
||||
// function.
|
||||
|
||||
void
|
||||
Function_declaration::import_function_body(Gogo* gogo, Named_object* no)
|
||||
{
|
||||
go_assert(no->func_declaration_value() == this);
|
||||
go_assert(no->package() != NULL);
|
||||
const std::string& body(this->imported_body_);
|
||||
go_assert(!body.empty());
|
||||
|
||||
Location orig_loc = no->location();
|
||||
|
||||
// Read the "//FILE:LINE" comment starts the export data.
|
||||
|
||||
size_t indent = 1;
|
||||
if (this->is_method())
|
||||
indent = 2;
|
||||
size_t i = 0;
|
||||
for (; i < indent; i++)
|
||||
{
|
||||
if (body.at(i) != ' ')
|
||||
{
|
||||
go_error_at(this->location_,
|
||||
"invalid export body for %qs: bad initial indentation",
|
||||
no->message_name().c_str());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (body.substr(i, 2) != "//")
|
||||
{
|
||||
go_error_at(this->location_,
|
||||
"invalid export body for %qs: missing file comment",
|
||||
no->message_name().c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
size_t colon = body.find(':', i + 2);
|
||||
size_t nl = body.find('\n', i + 2);
|
||||
if (nl == std::string::npos)
|
||||
{
|
||||
go_error_at(this->location_,
|
||||
"invalid export body for %qs: missing file name",
|
||||
no->message_name().c_str());
|
||||
return;
|
||||
}
|
||||
if (colon == std::string::npos || nl < colon)
|
||||
{
|
||||
go_error_at(this->location_,
|
||||
"invalid export body for %qs: missing initial line number",
|
||||
no->message_name().c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
std::string file = body.substr(i + 2, colon - (i + 2));
|
||||
std::string linestr = body.substr(colon + 1, nl - (colon + 1));
|
||||
char* end;
|
||||
long linenol = strtol(linestr.c_str(), &end, 10);
|
||||
if (*end != '\0')
|
||||
{
|
||||
go_error_at(this->location_,
|
||||
"invalid export body for %qs: invalid initial line number",
|
||||
no->message_name().c_str());
|
||||
return;
|
||||
}
|
||||
unsigned int lineno = static_cast<unsigned int>(linenol);
|
||||
|
||||
// Turn the file/line into a location.
|
||||
|
||||
char* alc = new char[file.length() + 1];
|
||||
memcpy(alc, file.data(), file.length());
|
||||
alc[file.length()] = '\0';
|
||||
gogo->linemap()->start_file(alc, lineno);
|
||||
gogo->linemap()->start_line(lineno, 1);
|
||||
Location start_loc = gogo->linemap()->get_location(0);
|
||||
|
||||
// Define the function with an outer block that declares the
|
||||
// parameters.
|
||||
|
||||
Function_type* fntype = this->fntype_;
|
||||
|
||||
Block* outer = new Block(NULL, start_loc);
|
||||
|
||||
Function* fn = new Function(fntype, NULL, outer, start_loc);
|
||||
fn->set_is_inline_only();
|
||||
|
||||
if (fntype->is_method())
|
||||
{
|
||||
const Typed_identifier* receiver = fntype->receiver();
|
||||
Variable* recv_param = new Variable(receiver->type(), NULL, false,
|
||||
true, true, start_loc);
|
||||
outer->bindings()->add_variable(receiver->name(), NULL, recv_param);
|
||||
}
|
||||
|
||||
const Typed_identifier_list* params = fntype->parameters();
|
||||
bool is_varargs = fntype->is_varargs();
|
||||
if (params != NULL)
|
||||
{
|
||||
for (Typed_identifier_list::const_iterator p = params->begin();
|
||||
p != params->end();
|
||||
++p)
|
||||
{
|
||||
Variable* param = new Variable(p->type(), NULL, false, true, false,
|
||||
start_loc);
|
||||
if (is_varargs && p + 1 == params->end())
|
||||
param->set_is_varargs_parameter();
|
||||
outer->bindings()->add_variable(p->name(), NULL, param);
|
||||
}
|
||||
}
|
||||
|
||||
fn->create_result_variables(gogo);
|
||||
|
||||
if (!fntype->is_method())
|
||||
{
|
||||
const Package* package = no->package();
|
||||
no = package->bindings()->add_function(no->name(), package, fn);
|
||||
}
|
||||
else
|
||||
{
|
||||
Named_type* rtype = fntype->receiver()->type()->deref()->named_type();
|
||||
go_assert(rtype != NULL);
|
||||
no = rtype->add_method(no->name(), fn);
|
||||
}
|
||||
|
||||
Import_function_body ifb(gogo, orig_loc, no, body, nl + 1, outer, indent);
|
||||
|
||||
if (!Block::import_block(outer, &ifb, start_loc))
|
||||
return;
|
||||
|
||||
gogo->lower_block(no, outer);
|
||||
|
||||
gogo->add_imported_inline_function(no);
|
||||
}
|
||||
|
||||
// Return the function descriptor.
|
||||
|
||||
Expression*
|
||||
|
@ -8121,14 +8401,21 @@ Bindings::new_definition(Named_object* old_object, Named_object* new_object)
|
|||
{
|
||||
// We declare the hash and equality functions before defining
|
||||
// them, because we sometimes see that we need the declaration
|
||||
// while we are in the middle of a different function. We
|
||||
// declare the main function before the user defines it, to
|
||||
// while we are in the middle of a different function.
|
||||
//
|
||||
// We declare the main function before the user defines it, to
|
||||
// give better error messages.
|
||||
//
|
||||
// We declare inline functions before we define them, as we
|
||||
// only define them if we need them.
|
||||
if (new_object->is_function()
|
||||
&& ((Linemap::is_predeclared_location(old_object->location())
|
||||
&& Linemap::is_predeclared_location(new_object->location()))
|
||||
|| (Gogo::unpack_hidden_name(old_object->name()) == "main"
|
||||
&& Linemap::is_unknown_location(old_object->location()))))
|
||||
&& Linemap::is_unknown_location(old_object->location()))
|
||||
|| (new_object->package() != NULL
|
||||
&& old_object->func_declaration_value()->has_imported_body()
|
||||
&& new_object->func_value()->is_inline_only())))
|
||||
{
|
||||
Function_type* old_type =
|
||||
old_object->func_declaration_value()->type();
|
||||
|
|
|
@ -43,6 +43,7 @@ class Backend;
|
|||
class Export;
|
||||
class Export_function_body;
|
||||
class Import;
|
||||
class Import_function_body;
|
||||
class Bexpression;
|
||||
class Btype;
|
||||
class Bstatement;
|
||||
|
@ -420,6 +421,17 @@ class Gogo
|
|||
Named_object*
|
||||
declare_package_function(const std::string&, Function_type*, Location);
|
||||
|
||||
// Add a function declaration to the list of functions we may want
|
||||
// to inline.
|
||||
void
|
||||
add_imported_inlinable_function(Named_object*);
|
||||
|
||||
// Add a function to the list of functions that we do want to
|
||||
// inline.
|
||||
void
|
||||
add_imported_inline_function(Named_object* no)
|
||||
{ this->imported_inline_functions_.push_back(no); }
|
||||
|
||||
// Add a label.
|
||||
Label*
|
||||
add_label_definition(const std::string&, Location);
|
||||
|
@ -661,7 +673,7 @@ class Gogo
|
|||
propagate_escape(Escape_context*, Node*);
|
||||
|
||||
// Add notes about the escape level of a function's input and output
|
||||
// parameters for exporting and importing top level functions.
|
||||
// parameters for exporting and importing top level functions.
|
||||
void
|
||||
tag_function(Escape_context*, Named_object*);
|
||||
|
||||
|
@ -726,7 +738,7 @@ class Gogo
|
|||
void
|
||||
simplify_thunk_statements();
|
||||
|
||||
// Dump AST if -fgo-dump-ast is set
|
||||
// Dump AST if -fgo-dump-ast is set.
|
||||
void
|
||||
dump_ast(const char* basename);
|
||||
|
||||
|
@ -1062,6 +1074,12 @@ class Gogo
|
|||
std::vector<Analysis_set> analysis_sets_;
|
||||
// A list of objects to add to the GC roots.
|
||||
std::vector<Expression*> gc_roots_;
|
||||
// A list of function declarations with imported bodies that we may
|
||||
// want to inline.
|
||||
std::vector<Named_object*> imported_inlinable_functions_;
|
||||
// A list of functions that we want to inline. These will be sent
|
||||
// to the backend.
|
||||
std::vector<Named_object*> imported_inline_functions_;
|
||||
};
|
||||
|
||||
// A block of statements.
|
||||
|
@ -1144,6 +1162,10 @@ class Block
|
|||
void
|
||||
export_block(Export_function_body*);
|
||||
|
||||
// Turn exported block data into a block.
|
||||
static bool
|
||||
import_block(Block*, Import_function_body*, Location);
|
||||
|
||||
// Convert the block to the backend representation.
|
||||
Bblock*
|
||||
get_backend(Translate_context*);
|
||||
|
@ -1419,6 +1441,17 @@ class Function
|
|||
set_export_for_inlining()
|
||||
{ this->export_for_inlining_ = true; }
|
||||
|
||||
// Return whether this function is inline only.
|
||||
bool
|
||||
is_inline_only() const
|
||||
{ return this->is_inline_only_; }
|
||||
|
||||
// Mark the function as inline only: the body should not be emitted
|
||||
// if it is not inlined.
|
||||
void
|
||||
set_is_inline_only()
|
||||
{ this->is_inline_only_ = true; }
|
||||
|
||||
// Swap with another function. Used only for the thunk which calls
|
||||
// recover.
|
||||
void
|
||||
|
@ -1476,14 +1509,15 @@ class Function
|
|||
// Export a function with a type.
|
||||
static void
|
||||
export_func_with_type(Export*, const std::string& name,
|
||||
const Function_type*, bool nointerface, Block* block);
|
||||
const Function_type*, bool nointerface, Block* block,
|
||||
Location);
|
||||
|
||||
// Import a function.
|
||||
static void
|
||||
import_func(Import*, std::string* pname, Typed_identifier** receiver,
|
||||
Typed_identifier_list** pparameters,
|
||||
Typed_identifier_list** presults, bool* is_varargs,
|
||||
bool* nointerface);
|
||||
bool* nointerface, std::string* body);
|
||||
|
||||
private:
|
||||
// Type for mapping from label names to Label objects.
|
||||
|
@ -1557,6 +1591,9 @@ class Function
|
|||
// True if we should export the body of this function for
|
||||
// cross-package inlining.
|
||||
bool export_for_inlining_ : 1;
|
||||
// True if this function is inline only: if it should not be emitted
|
||||
// if it is not inlined.
|
||||
bool is_inline_only_ : 1;
|
||||
};
|
||||
|
||||
// A snapshot of the current binding state.
|
||||
|
@ -1600,7 +1637,8 @@ class Function_declaration
|
|||
public:
|
||||
Function_declaration(Function_type* fntype, Location location)
|
||||
: fntype_(fntype), location_(location), asm_name_(), descriptor_(NULL),
|
||||
fndecl_(NULL), pragmas_(0)
|
||||
fndecl_(NULL), pragmas_(0), imported_body_(),
|
||||
is_on_inlinable_list_(false)
|
||||
{ }
|
||||
|
||||
Function_type*
|
||||
|
@ -1646,6 +1684,30 @@ class Function_declaration
|
|||
void
|
||||
set_nointerface();
|
||||
|
||||
// Whether we have an imported function body.
|
||||
bool
|
||||
has_imported_body() const
|
||||
{ return !this->imported_body_.empty(); }
|
||||
|
||||
// Record the imported body of this function.
|
||||
void
|
||||
set_imported_body(const std::string& imported_body)
|
||||
{ this->imported_body_ = imported_body; }
|
||||
|
||||
// Whether this declaration is on the list of inlinable functions.
|
||||
bool
|
||||
is_on_inlinable_list() const
|
||||
{ return this->is_on_inlinable_list_; }
|
||||
|
||||
// Set that this function is on the list of inlinable functions.
|
||||
void
|
||||
set_is_on_inlinable_list()
|
||||
{ this->is_on_inlinable_list_ = true; }
|
||||
|
||||
// Import the function body, creating a function.
|
||||
void
|
||||
import_function_body(Gogo*, Named_object*);
|
||||
|
||||
// Return an expression for the function descriptor, given the named
|
||||
// object for this function. This may only be called for functions
|
||||
// without a closure. This will be an immutable struct with one
|
||||
|
@ -1673,7 +1735,7 @@ class Function_declaration
|
|||
{
|
||||
Function::export_func_with_type(exp, name, this->fntype_,
|
||||
this->is_method() && this->nointerface(),
|
||||
NULL);
|
||||
NULL, this->location_);
|
||||
}
|
||||
|
||||
// Check that the types used in this declaration's signature are defined.
|
||||
|
@ -1694,6 +1756,10 @@ class Function_declaration
|
|||
Bfunction* fndecl_;
|
||||
// Pragmas for this function. This is a set of GOPRAGMA bits.
|
||||
unsigned int pragmas_;
|
||||
// Export data for function body if imported from a different package.
|
||||
std::string imported_body_;
|
||||
// Whether this declaration is already on the list of inlinable functions.
|
||||
bool is_on_inlinable_list_;
|
||||
};
|
||||
|
||||
// A variable.
|
||||
|
@ -1789,7 +1855,7 @@ class Variable
|
|||
bool
|
||||
is_in_heap() const
|
||||
{
|
||||
return this->is_address_taken_
|
||||
return this->is_address_taken_
|
||||
&& this->escapes_
|
||||
&& !this->is_global_;
|
||||
}
|
||||
|
@ -2103,7 +2169,7 @@ class Result_variable
|
|||
void
|
||||
set_non_escaping_address_taken()
|
||||
{ this->is_non_escaping_address_taken_ = true; }
|
||||
|
||||
|
||||
// Return whether this variable escapes the function it is declared in.
|
||||
bool
|
||||
escapes()
|
||||
|
@ -3200,7 +3266,7 @@ class Package
|
|||
|
||||
// Return the bindings.
|
||||
Bindings*
|
||||
bindings()
|
||||
bindings() const
|
||||
{ return this->bindings_; }
|
||||
|
||||
// Type used to map import names to package aliases.
|
||||
|
|
|
@ -744,8 +744,9 @@ Import::import_func(Package* package)
|
|||
Typed_identifier_list* results;
|
||||
bool is_varargs;
|
||||
bool nointerface;
|
||||
Function::import_func(this, &name, &receiver,
|
||||
¶meters, &results, &is_varargs, &nointerface);
|
||||
std::string body;
|
||||
Function::import_func(this, &name, &receiver, ¶meters, &results,
|
||||
&is_varargs, &nointerface, &body);
|
||||
Function_type *fntype = Type::make_function_type(receiver, parameters,
|
||||
results, this->location_);
|
||||
if (is_varargs)
|
||||
|
@ -788,6 +789,8 @@ Import::import_func(Package* package)
|
|||
|
||||
if (nointerface)
|
||||
no->func_declaration_value()->set_nointerface();
|
||||
if (!body.empty() && !no->func_declaration_value()->has_imported_body())
|
||||
no->func_declaration_value()->set_imported_body(body);
|
||||
|
||||
return no;
|
||||
}
|
||||
|
@ -1395,3 +1398,13 @@ Stream_from_file::do_advance(size_t skip)
|
|||
this->data_.clear();
|
||||
}
|
||||
}
|
||||
|
||||
// Class Import_function_body.
|
||||
|
||||
// The name of the function we are parsing.
|
||||
|
||||
const std::string&
|
||||
Import_function_body::name() const
|
||||
{
|
||||
return this->named_object_->name();
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include "go-linemap.h"
|
||||
|
||||
class Gogo;
|
||||
class Block;
|
||||
class Package;
|
||||
class Type;
|
||||
class Named_object;
|
||||
|
@ -464,4 +465,74 @@ class Stream_from_string_ref : public Import::Stream
|
|||
size_t end_;
|
||||
};
|
||||
|
||||
// Class to manage importing a function body. This is passed around
|
||||
// to Statements and Expressions. It parses the function into the IR.
|
||||
|
||||
class Import_function_body
|
||||
{
|
||||
public:
|
||||
Import_function_body(Gogo* gogo, Location loc, Named_object* named_object,
|
||||
const std::string& body, size_t off, Block* block,
|
||||
int indent)
|
||||
: gogo_(gogo), loc_(loc), named_object_(named_object), body_(body),
|
||||
off_(off), block_(block), indent_(indent)
|
||||
{ }
|
||||
|
||||
// The IR.
|
||||
Gogo*
|
||||
gogo()
|
||||
{ return this->gogo_; }
|
||||
|
||||
// The location to report in an error message.
|
||||
Location
|
||||
location() const
|
||||
{ return this->loc_; }
|
||||
|
||||
// A reference to the body we are reading.
|
||||
const std::string&
|
||||
body() const
|
||||
{ return this->body_; }
|
||||
|
||||
// The current offset into the body.
|
||||
size_t
|
||||
off()
|
||||
{ return this->off_; }
|
||||
|
||||
// Update the offset into the body.
|
||||
void
|
||||
set_off(size_t off)
|
||||
{ this->off_ = off; }
|
||||
|
||||
// The current block.
|
||||
Block*
|
||||
block()
|
||||
{ return this->block_; }
|
||||
|
||||
// The current indentation.
|
||||
int
|
||||
indent() const
|
||||
{ return this->indent_; }
|
||||
|
||||
// The name of the function we are parsing.
|
||||
const std::string&
|
||||
name() const;
|
||||
|
||||
private:
|
||||
// The IR.
|
||||
Gogo* gogo_;
|
||||
// The location to report in an error message.
|
||||
Location loc_;
|
||||
// The function we are parsing.
|
||||
Named_object* named_object_;
|
||||
// The exported data we are parsing. Note that this is a reference;
|
||||
// the body string must laster longer than this object.
|
||||
const std::string& body_;
|
||||
// The current offset into body_.
|
||||
size_t off_;
|
||||
// Current block.
|
||||
Block* block_;
|
||||
// Current expected indentation level.
|
||||
int indent_;
|
||||
};
|
||||
|
||||
#endif // !defined(GO_IMPORT_H)
|
||||
|
|
|
@ -121,6 +121,14 @@ Statement::determine_types()
|
|||
this->do_determine_types();
|
||||
}
|
||||
|
||||
// Read a statement from export data.
|
||||
|
||||
Statement*
|
||||
Statement::import_statement(Import_function_body*, Location)
|
||||
{
|
||||
go_unreachable();
|
||||
}
|
||||
|
||||
// If this is a thunk statement, return it.
|
||||
|
||||
Thunk_statement*
|
||||
|
|
|
@ -338,6 +338,10 @@ class Statement
|
|||
export_statement(Export_function_body* efb)
|
||||
{ this->do_export_statement(efb); }
|
||||
|
||||
// Read a statement from export data.
|
||||
static Statement*
|
||||
import_statement(Import_function_body*, Location);
|
||||
|
||||
// Return whether this is a block statement.
|
||||
bool
|
||||
is_block_statement() const
|
||||
|
|
|
@ -9865,7 +9865,9 @@ Named_type::add_method(const std::string& name, Function* function)
|
|||
go_assert(!this->is_alias_);
|
||||
if (this->local_methods_ == NULL)
|
||||
this->local_methods_ = new Bindings(NULL);
|
||||
return this->local_methods_->add_function(name, NULL, function);
|
||||
return this->local_methods_->add_function(name,
|
||||
this->named_object_->package(),
|
||||
function);
|
||||
}
|
||||
|
||||
// Add a method declaration to this type.
|
||||
|
|
Loading…
Add table
Reference in a new issue