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();
}
}
+ // 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);
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
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
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)
{
}
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.
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 ");
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);
Typed_identifier_list** pparameters,
Typed_identifier_list** presults,
bool* is_varargs,
- bool* nointerface)
+ bool* nointerface,
+ std::string* body)
{
imp->require_c_string("func ");
{
imp->require_semicolon_if_old_version();
imp->require_c_string("\n");
+ body->clear();
}
else
{
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));
}
}
{
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))
{
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())
{
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
|| (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,
}
}
+// 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*
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*
{
// 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();
class Export;
class Export_function_body;
class Import;
+class Import_function_body;
class Bexpression;
class Btype;
class Bstatement;
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);
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*);
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);
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.
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*);
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
// 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.
// 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.
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*
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
{
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.
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.
bool
is_in_heap() const
{
- return this->is_address_taken_
+ return this->is_address_taken_
&& this->escapes_
&& !this->is_global_;
}
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()
// Return the bindings.
Bindings*
- bindings()
+ bindings() const
{ return this->bindings_; }
// Type used to map import names to package aliases.