From fc917b42658000f0c4dc87f6602336eef899fadf Mon Sep 17 00:00:00 2001 From: Ian Lance Taylor Date: Mon, 10 Jun 2019 21:02:20 +0000 Subject: [PATCH] compiler: permit inlining functions with labels and goto statements This permits inlining functions with for loops and some switches, as they are lowered to if and goto statements before exporting them. This by itself only adds three new inlinable functions in the standard library: sort.Search, context.(*emptyCtx).String, and cmd/go/internal/work.(*Builder).disableBuildID. Reviewed-on: https://go-review.googlesource.com/c/gofrontend/+/181197 From-SVN: r272131 --- gcc/go/gofrontend/MERGE | 2 +- gcc/go/gofrontend/export.cc | 23 ++++ gcc/go/gofrontend/export.h | 13 +- gcc/go/gofrontend/import.cc | 46 +++++-- gcc/go/gofrontend/import.h | 14 ++- gcc/go/gofrontend/statements.cc | 204 ++++++++++++++++++++++++++++++-- gcc/go/gofrontend/statements.h | 44 ++++++- 7 files changed, 321 insertions(+), 25 deletions(-) diff --git a/gcc/go/gofrontend/MERGE b/gcc/go/gofrontend/MERGE index 05959f2c55a..9dfa0670c5a 100644 --- a/gcc/go/gofrontend/MERGE +++ b/gcc/go/gofrontend/MERGE @@ -1,4 +1,4 @@ -3f7dcb98df3ce1d4e02d0072fd21e70dc08351db +11d96c36198b75b0485d16524d521e558cf03312 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.cc b/gcc/go/gofrontend/export.cc index 0890b0961f7..a48ca0767e6 100644 --- a/gcc/go/gofrontend/export.cc +++ b/gcc/go/gofrontend/export.cc @@ -1326,3 +1326,26 @@ Export_function_body::temporary_index(const Temporary_statement* temp) go_assert(p != this->temporary_indexes_.end()); return p->second; } + +// Return the index of an unnamed label. If it doesn't already have +// an index, give it one. + +unsigned int +Export_function_body::unnamed_label_index(const Unnamed_label* label) +{ + unsigned int next = this->next_label_index_; + std::pair val(label, next); + std::pair ins = + this->label_indexes_.insert(val); + if (!ins.second) + return ins.first->second; + else + { + if (next > 0x7fffffff) + go_error_at(label->location(), + "too many unnamed labels in export data"); + ++this->next_label_index_; + return next; + } +} diff --git a/gcc/go/gofrontend/export.h b/gcc/go/gofrontend/export.h index 92d0180dde6..910b1db43cd 100644 --- a/gcc/go/gofrontend/export.h +++ b/gcc/go/gofrontend/export.h @@ -21,6 +21,7 @@ class Package; class Import_init_set; class Backend; class Temporary_statement; +class Unnamed_label; // Codes used for the builtin types. These are all negative to make // them easily distinct from the codes assigned by Export::write_type. @@ -309,7 +310,8 @@ class Export_function_body : public String_dump public: Export_function_body(Export* exp, int indent) : exp_(exp), body_(), type_context_(NULL), next_temporary_index_(0), - temporary_indexes_(), indent_(indent) + temporary_indexes_(), next_label_index_(0), label_indexes_(), + indent_(indent) { } // Write a character to the body. @@ -373,6 +375,11 @@ class Export_function_body : public String_dump unsigned int temporary_index(const Temporary_statement*); + // Return the index of an unnamed label. If it doesn't already have + // an index, give it one. + unsigned int + unnamed_label_index(const Unnamed_label*); + // Return a reference to the completed body. const std::string& body() const @@ -389,6 +396,10 @@ class Export_function_body : public String_dump unsigned int next_temporary_index_; // Map temporary statements to indexes. Unordered_map(const Temporary_statement*, unsigned int) temporary_indexes_; + // Index to give to the next unnamed label. + unsigned int next_label_index_; + // Map unnamed labels to indexes. + Unordered_map(const Unnamed_label*, unsigned int) label_indexes_; // Current indentation level: the number of spaces before each statement. int indent_; }; diff --git a/gcc/go/gofrontend/import.cc b/gcc/go/gofrontend/import.cc index 70e92ab9d3b..02c1c48191b 100644 --- a/gcc/go/gofrontend/import.cc +++ b/gcc/go/gofrontend/import.cc @@ -1612,6 +1612,19 @@ Import_function_body::read_type() return type; } +// Return the next size to use for a vector mapping indexes to values. + +size_t +Import_function_body::next_size(size_t have) +{ + if (have == 0) + return 8; + else if (have < 256) + return have * 2; + else + return have + 64; +} + // Record the index of a temporary statement. void @@ -1619,16 +1632,11 @@ Import_function_body::record_temporary(Temporary_statement* temp, unsigned int idx) { size_t have = this->temporaries_.size(); - if (static_cast(idx) >= have) + while (static_cast(idx) >= have) { - size_t want; - if (have == 0) - want = 8; - else if (have < 256) - want = have * 2; - else - want = have + 64; + size_t want = Import_function_body::next_size(have); this->temporaries_.resize(want, NULL); + have = want; } this->temporaries_[idx] = temp; } @@ -1642,3 +1650,25 @@ Import_function_body::temporary_statement(unsigned int idx) return NULL; return this->temporaries_[idx]; } + +// Return an unnamed label given an index, defining the label if we +// haven't seen it already. + +Unnamed_label* +Import_function_body::unnamed_label(unsigned int idx, Location loc) +{ + size_t have = this->labels_.size(); + while (static_cast(idx) >= have) + { + size_t want = Import_function_body::next_size(have); + this->labels_.resize(want, NULL); + have = want; + } + Unnamed_label* label = this->labels_[idx]; + if (label == NULL) + { + label = new Unnamed_label(loc); + this->labels_[idx] = label; + } + return label; +} diff --git a/gcc/go/gofrontend/import.h b/gcc/go/gofrontend/import.h index 4afeb4a2958..db51f726bf9 100644 --- a/gcc/go/gofrontend/import.h +++ b/gcc/go/gofrontend/import.h @@ -18,6 +18,8 @@ class Named_object; class Named_type; class Expression; class Import_function_body; +class Temporary_statement; +class Unnamed_label; // Expressions can be imported either directly from import data (for // simple constant expressions that can appear in a const declaration @@ -587,7 +589,7 @@ class Import_function_body : public Import_expression const std::string& body, size_t off, Block* block, int indent) : gogo_(gogo), imp_(imp), named_object_(named_object), body_(body), - off_(off), block_(block), indent_(indent), temporaries_(), + off_(off), block_(block), indent_(indent), temporaries_(), labels_(), saw_error_(false) { } @@ -704,6 +706,11 @@ class Import_function_body : public Import_expression Temporary_statement* temporary_statement(unsigned int); + // Return an unnamed label given an index, defining the label if we + // haven't seen it already. + Unnamed_label* + unnamed_label(unsigned int, Location); + // Implement Import_expression. Import_function_body* ifb() @@ -730,6 +737,9 @@ class Import_function_body : public Import_expression { this->saw_error_ = true; } private: + static size_t + next_size(size_t); + // The IR. Gogo* gogo_; // The importer. @@ -747,6 +757,8 @@ class Import_function_body : public Import_expression int indent_; // Temporary statements by index. std::vector temporaries_; + // Unnamed labels by index. + std::vector labels_; // Whether we've seen an error. Used to avoid reporting excess // errors. bool saw_error_; diff --git a/gcc/go/gofrontend/statements.cc b/gcc/go/gofrontend/statements.cc index f680a0a1c3e..9ab11727fe9 100644 --- a/gcc/go/gofrontend/statements.cc +++ b/gcc/go/gofrontend/statements.cc @@ -129,10 +129,15 @@ Statement::import_statement(Import_function_body* ifb, Location loc) { if (ifb->match_c_string("{")) { - Block* block = Block_statement::do_import(ifb, loc); + bool is_lowered_for_statement; + Block* block = Block_statement::do_import(ifb, loc, + &is_lowered_for_statement); if (block == NULL) return Statement::make_error_statement(loc); - return Statement::make_block_statement(block, loc); + Block_statement* s = Statement::make_block_statement(block, loc); + if (is_lowered_for_statement) + s->set_is_lowered_for_statement(); + return s; } else if (ifb->match_c_string("return")) { @@ -147,6 +152,10 @@ Statement::import_statement(Import_function_body* ifb, Location loc) return Variable_declaration_statement::do_import(ifb, loc); else if (ifb->match_c_string("if ")) return If_statement::do_import(ifb, loc); + else if (ifb->match_c_string(":")) + return Label_statement::do_import(ifb, loc); + else if (ifb->match_c_string("goto ")) + return Goto_statement::do_import(ifb, loc); Expression* lhs = Expression::import_expression(ifb, loc); ifb->require_c_string(" = "); @@ -2109,15 +2118,20 @@ Statement::make_statement(Expression* expr, bool is_ignored) void Block_statement::do_export_statement(Export_function_body* efb) { - Block_statement::export_block(efb, this->block_); + Block_statement::export_block(efb, this->block_, + this->is_lowered_for_statement_); } void -Block_statement::export_block(Export_function_body* efb, Block* block) +Block_statement::export_block(Export_function_body* efb, Block* block, + bool is_lowered_for_statement) { // We are already indented to the right position. char buf[50]; - snprintf(buf, sizeof buf, "{ //%d\n", + efb->write_c_string("{"); + if (is_lowered_for_statement) + efb->write_c_string(" /*for*/"); + snprintf(buf, sizeof buf, " //%d\n", Linemap::location_to_line(block->start_location())); efb->write_c_string(buf); @@ -2134,9 +2148,16 @@ Block_statement::export_block(Export_function_body* efb, Block* block) // Import a block statement, returning the block. Block* -Block_statement::do_import(Import_function_body* ifb, Location loc) +Block_statement::do_import(Import_function_body* ifb, Location loc, + bool* is_lowered_for_statement) { go_assert(ifb->match_c_string("{")); + *is_lowered_for_statement = false; + if (ifb->match_c_string(" /*for*/")) + { + ifb->advance(8); + *is_lowered_for_statement = true; + } size_t nl = ifb->body().find('\n', ifb->off()); if (nl == std::string::npos) { @@ -2176,7 +2197,7 @@ Block_statement::do_dump_statement(Ast_dump_context*) const // Make a block statement. -Statement* +Block_statement* Statement::make_block_statement(Block* block, Location location) { return new Block_statement(block, location); @@ -3344,6 +3365,61 @@ Goto_statement::do_get_backend(Translate_context* context) return context->backend()->goto_statement(blabel, this->location()); } +// Export a goto statement. + +void +Goto_statement::do_export_statement(Export_function_body *efb) +{ + efb->write_c_string("goto "); + efb->write_string(this->label_->name()); +} + +// Import a goto or goto unnamed statement. + +Statement* +Goto_statement::do_import(Import_function_body* ifb, Location loc) +{ + ifb->require_c_string("goto "); + std::string id = ifb->read_identifier(); + if (id[0] != '$') + { + Function* fn = ifb->function()->func_value(); + Label* label = fn->add_label_reference(ifb->gogo(), id, loc, false); + return Statement::make_goto_statement(label, loc); + } + else + { + if (id[1] != 'l') + { + if (!ifb->saw_error()) + go_error_at(loc, + ("invalid export data for %qs: " + "bad unnamed label at %lu"), + ifb->name().c_str(), + static_cast(ifb->off())); + ifb->set_saw_error(); + return Statement::make_error_statement(loc); + } + const char* p = id.c_str(); + char* end; + long idx = strtol(p + 2, &end, 10); + if (*end != '\0' || idx > 0x7fffffff) + { + if (!ifb->saw_error()) + go_error_at(loc, + ("invalid export data for %qs: " + "bad unnamed label index at %lu"), + ifb->name().c_str(), + static_cast(ifb->off())); + ifb->set_saw_error(); + return Statement::make_error_statement(loc); + } + + Unnamed_label* label = ifb->unnamed_label(idx, loc); + return Statement::make_goto_unnamed_statement(label, loc); + } +} + // Dump the AST representation for a goto statement. void @@ -3377,6 +3453,17 @@ Goto_unnamed_statement::do_get_backend(Translate_context* context) return this->label_->get_goto(context, this->location()); } +// Export a goto unnamed statement. + +void +Goto_unnamed_statement::do_export_statement(Export_function_body *efb) +{ + unsigned int index = efb->unnamed_label_index(this->label_); + char buf[100]; + snprintf(buf, sizeof buf, "goto $l%u", index); + efb->write_c_string(buf); +} + // Dump the AST representation for an unnamed goto statement void @@ -3424,6 +3511,64 @@ Label_statement::do_get_backend(Translate_context* context) return context->backend()->label_definition_statement(blabel); } +// Export a label. + +void +Label_statement::do_export_statement(Export_function_body* efb) +{ + if (this->label_->is_dummy_label()) + return; + // We use a leading colon, not a trailing one, to simplify import. + efb->write_c_string(":"); + efb->write_string(this->label_->name()); +} + +// Import a label or an unnamed label. + +Statement* +Label_statement::do_import(Import_function_body* ifb, Location loc) +{ + ifb->require_c_string(":"); + std::string id = ifb->read_identifier(); + if (id[0] != '$') + { + Function* fn = ifb->function()->func_value(); + Label* label = fn->add_label_definition(ifb->gogo(), id, loc); + return Statement::make_label_statement(label, loc); + } + else + { + if (id[1] != 'l') + { + if (!ifb->saw_error()) + go_error_at(loc, + ("invalid export data for %qs: " + "bad unnamed label at %lu"), + ifb->name().c_str(), + static_cast(ifb->off())); + ifb->set_saw_error(); + return Statement::make_error_statement(loc); + } + const char* p = id.c_str(); + char* end; + long idx = strtol(p + 2, &end, 10); + if (*end != '\0' || idx > 0x7fffffff) + { + if (!ifb->saw_error()) + go_error_at(loc, + ("invalid export data for %qs: " + "bad unnamed label index at %lu"), + ifb->name().c_str(), + static_cast(ifb->off())); + ifb->set_saw_error(); + return Statement::make_error_statement(loc); + } + + Unnamed_label* label = ifb->unnamed_label(idx, loc); + return Statement::make_unnamed_label_statement(label); + } +} + // Dump the AST for a label definition statement. void @@ -3462,6 +3607,18 @@ Unnamed_label_statement::do_get_backend(Translate_context* context) return this->label_->get_definition(context); } +// Export an unnamed label. + +void +Unnamed_label_statement::do_export_statement(Export_function_body* efb) +{ + unsigned int index = efb->unnamed_label_index(this->label_); + char buf[50]; + // We use a leading colon, not a trailing one, to simplify import. + snprintf(buf, sizeof buf, ":$l%u", index); + efb->write_c_string(buf); +} + // Dump the AST representation for an unnamed label definition statement. void @@ -3557,11 +3714,11 @@ If_statement::do_export_statement(Export_function_body* efb) efb->write_c_string("if "); this->cond_->export_expression(efb); efb->write_c_string(" "); - Block_statement::export_block(efb, this->then_block_); + Block_statement::export_block(efb, this->then_block_, false); if (this->else_block_ != NULL) { efb->write_c_string(" else "); - Block_statement::export_block(efb, this->else_block_); + Block_statement::export_block(efb, this->else_block_, false); } } @@ -3588,9 +3745,22 @@ If_statement::do_import(Import_function_body* ifb, Location loc) return Statement::make_error_statement(loc); } - Block* then_block = Block_statement::do_import(ifb, loc); + bool is_lowered_for_statement; + Block* then_block = Block_statement::do_import(ifb, loc, + &is_lowered_for_statement); if (then_block == NULL) return Statement::make_error_statement(loc); + if (is_lowered_for_statement) + { + if (!ifb->saw_error()) + go_error_at(ifb->location(), + ("import error for %qs: " + "unexpected lowered for in if statement at %lu"), + ifb->name().c_str(), + static_cast(ifb->off())); + ifb->set_saw_error(); + return Statement::make_error_statement(loc); + } Block* else_block = NULL; if (ifb->match_c_string(" else ")) @@ -3608,9 +3778,21 @@ If_statement::do_import(Import_function_body* ifb, Location loc) return Statement::make_error_statement(loc); } - else_block = Block_statement::do_import(ifb, loc); + else_block = Block_statement::do_import(ifb, loc, + &is_lowered_for_statement); if (else_block == NULL) return Statement::make_error_statement(loc); + if (is_lowered_for_statement) + { + if (!ifb->saw_error()) + go_error_at(ifb->location(), + ("import error for %qs: " + "unexpected lowered for in if statement at %lu"), + ifb->name().c_str(), + static_cast(ifb->off())); + ifb->set_saw_error(); + return Statement::make_error_statement(loc); + } } return Statement::make_if_statement(cond, then_block, else_block, loc); diff --git a/gcc/go/gofrontend/statements.h b/gcc/go/gofrontend/statements.h index 3e85243b084..432da30f7e6 100644 --- a/gcc/go/gofrontend/statements.h +++ b/gcc/go/gofrontend/statements.h @@ -187,7 +187,7 @@ class Statement // Make a block statement from a Block. This is an embedded list of // statements which may also include variable definitions. - static Statement* + static Block_statement* make_block_statement(Block*, Location); // Make an increment statement. @@ -956,11 +956,13 @@ class Block_statement : public Statement // Export a block for a block statement. static void - export_block(Export_function_body*, Block*); + export_block(Export_function_body*, Block*, bool is_lowered_for_statement); // Import a block statement, returning the block. + // *IS_LOWERED_FOR_STATEMENT reports whether this block statement + // was lowered from a for statement. static Block* - do_import(Import_function_body*, Location); + do_import(Import_function_body*, Location, bool* is_lowered_for_statement); protected: int @@ -1409,6 +1411,10 @@ class Goto_statement : public Statement label() const { return this->label_; } + // Import a goto statement. + static Statement* + do_import(Import_function_body*, Location); + protected: int do_traverse(Traverse*); @@ -1423,6 +1429,13 @@ class Goto_statement : public Statement Bstatement* do_get_backend(Translate_context*); + int + do_inlining_cost() + { return 5; } + + void + do_export_statement(Export_function_body*); + void do_dump_statement(Ast_dump_context*) const; @@ -1455,6 +1468,13 @@ class Goto_unnamed_statement : public Statement Bstatement* do_get_backend(Translate_context* context); + int + do_inlining_cost() + { return 5; } + + void + do_export_statement(Export_function_body*); + void do_dump_statement(Ast_dump_context*) const; @@ -1477,6 +1497,10 @@ class Label_statement : public Statement label() const { return this->label_; } + // Import a label or unnamed label. + static Statement* + do_import(Import_function_body*, Location); + protected: int do_traverse(Traverse*); @@ -1484,6 +1508,13 @@ class Label_statement : public Statement Bstatement* do_get_backend(Translate_context*); + int + do_inlining_cost() + { return 1; } + + void + do_export_statement(Export_function_body*); + void do_dump_statement(Ast_dump_context*) const; @@ -1506,6 +1537,13 @@ class Unnamed_label_statement : public Statement Bstatement* do_get_backend(Translate_context* context); + int + do_inlining_cost() + { return 1; } + + void + do_export_statement(Export_function_body*); + void do_dump_statement(Ast_dump_context*) const; -- 2.30.2