This patch to the Go frontend adds support for inlining functions that contain only assignments and return statements, with expressions of either constants or parameters. Functions that contain other kinds of statements or expressions are not yet inlined. With this change, about 100 functions in the standard library are inlinable. Bootstrapped and ran Go testsuite on x86_64-pc-linux-gnu. Committed to mainline.
Ian
Index: gcc/go/gofrontend/MERGE =================================================================== --- gcc/go/gofrontend/MERGE (revision 266536) +++ gcc/go/gofrontend/MERGE (working copy) @@ -1,4 +1,4 @@ -b013405f2c66596c47cb9be493c798db1087c0f0 +a8f768d68760768da5e86a8e63ef1ad5691c3ae8 The first line of this file holds the git revision number of the last merge done from the gofrontend repository. Index: gcc/go/gofrontend/expressions.cc =================================================================== --- gcc/go/gofrontend/expressions.cc (revision 266534) +++ gcc/go/gofrontend/expressions.cc (working copy) @@ -786,6 +786,31 @@ Var_expression::do_address_taken(bool es } } +// The cost to inline a variable reference. We currently only support +// references to parameters. + +int +Var_expression::do_inlining_cost() const +{ + if (this->variable_->is_variable()) + { + if (this->variable_->var_value()->is_parameter()) + return 1; + } + else if (this->variable_->is_result_variable()) + return 1; + + return 0x100000; +} + +// Export a reference to a variable. + +void +Var_expression::do_export(Export_function_body* efb) const +{ + efb->write_string(Gogo::unpack_hidden_name(this->variable_->name())); +} + // Get the backend representation for a reference to a variable. Bexpression* @@ -1608,6 +1633,10 @@ class Boolean_expression : public Expres do_get_backend(Translate_context* context) { return context->backend()->boolean_constant_expression(this->val_); } + int + do_inlining_cost() const + { return 1; } + void do_export(Export_function_body* efb) const { efb->write_c_string(this->val_ ? "$true" : "$false"); } @@ -1997,6 +2026,10 @@ class Integer_expression : public Expres this->location()); } + int + do_inlining_cost() const + { return 1; } + void do_export(Export_function_body*) const; @@ -2408,6 +2441,10 @@ class Float_expression : public Expressi Bexpression* do_get_backend(Translate_context*); + int + do_inlining_cost() const + { return 1; } + void do_export(Export_function_body*) const; @@ -2617,6 +2654,10 @@ class Complex_expression : public Expres Bexpression* do_get_backend(Translate_context*); + int + do_inlining_cost() const + { return 2; } + void do_export(Export_function_body*) const; @@ -3204,6 +3245,10 @@ class Nil_expression : public Expression do_get_backend(Translate_context* context) { return context->backend()->nil_pointer_expression(); } + int + do_inlining_cost() const + { return 1; } + void do_export(Export_function_body* efb) const { efb->write_c_string("$nil"); } @@ -3654,6 +3699,25 @@ Type_conversion_expression::do_get_backe } } +// Cost of inlining a type conversion. + +int +Type_conversion_expression::do_inlining_cost() const +{ + Type* type = this->type_; + Type* expr_type = this->expr_->type(); + if (type->interface_type() != NULL || expr_type->interface_type() != NULL) + return 10; + else if (type->is_string_type() && expr_type->integer_type() != NULL) + return 10; + else if (type->is_string_type() && expr_type->is_slice_type()) + return 10; + else if (type->is_slice_type() && expr_type->is_string_type()) + return 10; + else + return 1; +} + // Output a type conversion in a constant expression. void @@ -4677,7 +4741,11 @@ Unary_expression::do_export(Export_funct efb->write_c_string("^"); break; case OPERATOR_AND: + efb->write_c_string("&"); + break; case OPERATOR_MULT: + efb->write_c_string("*"); + break; default: go_unreachable(); } @@ -4704,6 +4772,12 @@ Unary_expression::do_import(Import_expre case '^': op = OPERATOR_XOR; break; + case '&': + op = OPERATOR_AND; + break; + case '*': + op = OPERATOR_MULT; + break; default: go_unreachable(); } @@ -16195,7 +16269,7 @@ Expression* Expression::import_expression(Import_expression* imp, Location loc) { int c = imp->peek_char(); - if (c == '+' || c == '-' || c == '!' || c == '^') + if (c == '+' || c == '-' || c == '!' || c == '^' || c == '&' || c == '*') return Unary_expression::do_import(imp, loc); else if (c == '(') return Binary_expression::do_import(imp, loc); @@ -16220,11 +16294,35 @@ Expression::import_expression(Import_exp || (imp->version() < EXPORT_FORMAT_V3 && imp->match_c_string("convert"))) return Type_conversion_expression::do_import(imp, loc); - else + + Import_function_body* ifb = imp->ifb(); + if (ifb == NULL) { go_error_at(imp->location(), "import error: expected expression"); return Expression::make_error(loc); } + if (ifb->saw_error()) + return Expression::make_error(loc); + std::string id = ifb->read_identifier(); + if (id.empty()) + { + if (!ifb->saw_error()) + go_error_at(imp->location(), + "import error: expected identifier at %lu", + static_cast<unsigned long>(ifb->off())); + ifb->set_saw_error(); + return Expression::make_error(loc); + } + Named_object* var = ifb->block()->bindings()->lookup(id); + if (var == NULL) + { + if (!ifb->saw_error()) + go_error_at(imp->location(), "import error: lookup of %qs failed", + id.c_str()); + ifb->set_saw_error(); + return Expression::make_error(loc); + } + return Expression::make_var_reference(var, loc); } // Class Expression_list. Index: gcc/go/gofrontend/expressions.h =================================================================== --- gcc/go/gofrontend/expressions.h (revision 266526) +++ gcc/go/gofrontend/expressions.h (working copy) @@ -941,7 +941,7 @@ class Expression // Return the cost of this statement for inlining purposes. int - inlining_cost() + inlining_cost() const { return this->do_inlining_cost(); } // Return whether the expression is addressable--something which may @@ -1093,7 +1093,7 @@ class Expression // 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() + do_inlining_cost() const { return 0x100000; } // Child class implements whether the expression is addressable. @@ -1355,6 +1355,12 @@ class Var_expression : public Expression do_copy() { return this; } + int + do_inlining_cost() const; + + void + do_export(Export_function_body*) const; + bool do_is_addressable() const { return true; } @@ -1602,6 +1608,12 @@ class String_expression : public Express static void export_string(String_dump* exp, const String_expression* str); + // Set the inlining cost a bit high since inlining may cause + // duplicated string literals. + int + do_inlining_cost() const + { return 5; } + void do_export(Export_function_body*) const; @@ -1686,6 +1698,9 @@ class Type_conversion_expression : publi Bexpression* do_get_backend(Translate_context* context); + int + do_inlining_cost() const; + void do_export(Export_function_body*) const; @@ -1877,6 +1892,10 @@ class Unary_expression : public Expressi Bexpression* do_get_backend(Translate_context*); + int + do_inlining_cost() const + { return 1; } + void do_export(Export_function_body*) const; @@ -2022,6 +2041,10 @@ class Binary_expression : public Express Bexpression* do_get_backend(Translate_context*); + int + do_inlining_cost() const + { return 1; } + void do_export(Export_function_body*) const; Index: gcc/go/gofrontend/statements.cc =================================================================== --- gcc/go/gofrontend/statements.cc (revision 266517) +++ gcc/go/gofrontend/statements.cc (working copy) @@ -12,6 +12,7 @@ #include "expressions.h" #include "gogo.h" #include "export.h" +#include "import.h" #include "runtime.h" #include "backend.h" #include "statements.h" @@ -124,9 +125,41 @@ Statement::determine_types() // Read a statement from export data. Statement* -Statement::import_statement(Import_function_body*, Location) +Statement::import_statement(Import_function_body* ifb, Location loc) { - go_unreachable(); + if (ifb->match_c_string("{")) + { + size_t nl = ifb->body().find('\n', ifb->off()); + if (nl == std::string::npos) + { + if (!ifb->saw_error()) + go_error_at(ifb->location(), + "import error: no newline after { at %lu", + static_cast<unsigned long>(ifb->off())); + ifb->set_saw_error(); + return Statement::make_error_statement(loc); + } + ifb->set_off(nl + 1); + ifb->increment_indent(); + Block* block = new Block(ifb->block(), loc); + bool ok = Block::import_block(block, ifb, loc); + ifb->decrement_indent(); + if (!ok) + return Statement::make_error_statement(loc); + return Statement::make_block_statement(block, loc); + } + else if (ifb->match_c_string("return")) + { + // After lowering return statements have no expressions. The + // return expressions are assigned to result parameters. + ifb->advance(6); + return Statement::make_return_statement(NULL, loc); + } + + Expression* lhs = Expression::import_expression(ifb, loc); + ifb->require_c_string(" = "); + Expression* rhs = Expression::import_expression(ifb, loc); + return Statement::make_assignment(lhs, rhs, loc); } // If this is a thunk statement, return it. @@ -834,6 +867,14 @@ Assignment_statement::do_check_types(Gog this->set_is_error(); } +void +Assignment_statement::do_export_statement(Export_function_body* efb) +{ + this->lhs_->export_expression(efb); + efb->write_c_string(" = "); + this->rhs_->export_expression(efb); +} + // Flatten an assignment statement. We may need a temporary for // interface conversion. @@ -2844,6 +2885,16 @@ Return_statement::do_get_backend(Transla retvals, loc); } +// Export a return statement. At this point all the expressions have +// been converted to assignments to the result variables, so this is +// simple. + +void +Return_statement::do_export_statement(Export_function_body* efb) +{ + efb->write_c_string("return"); +} + // Dump the AST representation for a return statement. void Index: gcc/go/gofrontend/statements.h =================================================================== --- gcc/go/gofrontend/statements.h (revision 266525) +++ gcc/go/gofrontend/statements.h (working copy) @@ -631,6 +631,13 @@ class Assignment_statement : public Stat void do_check_types(Gogo*); + int + do_inlining_cost() + { return 1; } + + void + do_export_statement(Export_function_body*); + Statement* do_flatten(Gogo*, Named_object*, Block*, Statement_inserter*); @@ -792,6 +799,13 @@ class Return_statement : public Statemen do_may_fall_through() const { return false; } + int + do_inlining_cost() + { return 1; } + + void + do_export_statement(Export_function_body*); + Bstatement* do_get_backend(Translate_context*);