From 8586635cd8c75f3aa096e64953d08f0dfbe6d4f6 Mon Sep 17 00:00:00 2001 From: Ian Lance Taylor Date: Mon, 1 Aug 2011 01:44:36 +0000 Subject: [PATCH] Use temporary variables for calls with multiple results. From-SVN: r176998 --- gcc/go/gofrontend/expressions.cc | 224 ++++++++++++++------ gcc/go/gofrontend/expressions.h | 76 +++++-- gcc/go/gofrontend/gogo.cc | 342 +++++++++++++++++++------------ gcc/go/gofrontend/gogo.h | 47 ++++- gcc/go/gofrontend/statements.cc | 181 ++++++++++------ gcc/go/gofrontend/statements.h | 24 ++- 6 files changed, 611 insertions(+), 283 deletions(-) diff --git a/gcc/go/gofrontend/expressions.cc b/gcc/go/gofrontend/expressions.cc index bd437c4ce4c..433a6d7d431 100644 --- a/gcc/go/gofrontend/expressions.cc +++ b/gcc/go/gofrontend/expressions.cc @@ -926,7 +926,8 @@ Parser_expression::do_type() // if necessary. Expression* -Var_expression::do_lower(Gogo* gogo, Named_object* function, int) +Var_expression::do_lower(Gogo* gogo, Named_object* function, + Statement_inserter* inserter, int) { if (this->variable_->is_variable()) { @@ -935,8 +936,11 @@ Var_expression::do_lower(Gogo* gogo, Named_object* function, int) // reference to a variable which is local to an enclosing // function will be a reference to a field in a closure. if (var->is_global()) - function = NULL; - var->lower_init_expression(gogo, function); + { + function = NULL; + inserter = NULL; + } + var->lower_init_expression(gogo, function, inserter); } return this; } @@ -1061,7 +1065,9 @@ Temporary_reference_expression::do_get_tree(Translate_context* context) // that here by adding a type cast. We need to use base() to push // the circularity down one level. tree ret = var_to_tree(bvar); - if (POINTER_TYPE_P(TREE_TYPE(ret)) && VOID_TYPE_P(TREE_TYPE(TREE_TYPE(ret)))) + if (!this->is_lvalue_ + && POINTER_TYPE_P(TREE_TYPE(ret)) + && VOID_TYPE_P(TREE_TYPE(TREE_TYPE(ret)))) { Btype* type_btype = this->type()->base()->get_backend(context->gogo()); tree type_tree = type_to_tree(type_btype); @@ -1072,7 +1078,7 @@ Temporary_reference_expression::do_get_tree(Translate_context* context) // Make a reference to a temporary variable. -Expression* +Temporary_reference_expression* Expression::make_temporary_reference(Temporary_statement* statement, source_location location) { @@ -1302,7 +1308,7 @@ Unknown_expression::name() const // Lower a reference to an unknown name. Expression* -Unknown_expression::do_lower(Gogo*, Named_object*, int) +Unknown_expression::do_lower(Gogo*, Named_object*, Statement_inserter*, int) { source_location location = this->location(); Named_object* no = this->named_object_; @@ -2394,7 +2400,7 @@ class Const_expression : public Expression do_traverse(Traverse*); Expression* - do_lower(Gogo*, Named_object*, int); + do_lower(Gogo*, Named_object*, Statement_inserter*, int); bool do_is_constant() const @@ -2462,7 +2468,8 @@ Const_expression::do_traverse(Traverse* traverse) // predeclared constant iota into an integer value. Expression* -Const_expression::do_lower(Gogo* gogo, Named_object*, int iota_value) +Const_expression::do_lower(Gogo* gogo, Named_object*, + Statement_inserter*, int iota_value) { if (this->constant_->const_value()->expr()->classification() == EXPRESSION_IOTA) @@ -2931,7 +2938,7 @@ class Iota_expression : public Parser_expression protected: Expression* - do_lower(Gogo*, Named_object*, int) + do_lower(Gogo*, Named_object*, Statement_inserter*, int) { go_unreachable(); } // There should only ever be one of these. @@ -2988,7 +2995,7 @@ class Type_conversion_expression : public Expression do_traverse(Traverse* traverse); Expression* - do_lower(Gogo*, Named_object*, int); + do_lower(Gogo*, Named_object*, Statement_inserter*, int); bool do_is_constant() const @@ -3057,7 +3064,8 @@ Type_conversion_expression::do_traverse(Traverse* traverse) // Convert to a constant at lowering time. Expression* -Type_conversion_expression::do_lower(Gogo*, Named_object*, int) +Type_conversion_expression::do_lower(Gogo*, Named_object*, + Statement_inserter*, int) { Type* type = this->type_; Expression* val = this->expr_; @@ -3753,7 +3761,7 @@ class Unary_expression : public Expression { return Expression::traverse(&this->expr_, traverse); } Expression* - do_lower(Gogo*, Named_object*, int); + do_lower(Gogo*, Named_object*, Statement_inserter*, int); bool do_is_constant() const; @@ -3808,7 +3816,7 @@ class Unary_expression : public Expression // instead. Expression* -Unary_expression::do_lower(Gogo*, Named_object*, int) +Unary_expression::do_lower(Gogo*, Named_object*, Statement_inserter*, int) { source_location loc = this->location(); Operator op = this->op_; @@ -5137,7 +5145,7 @@ Binary_expression::eval_complex(Operator op, Type* left_type, // constants. Expression* -Binary_expression::do_lower(Gogo*, Named_object*, int) +Binary_expression::do_lower(Gogo*, Named_object*, Statement_inserter*, int) { source_location location = this->location(); Operator op = this->op_; @@ -6656,7 +6664,7 @@ class Builtin_call_expression : public Call_expression protected: // This overrides Call_expression::do_lower. Expression* - do_lower(Gogo*, Named_object*, int); + do_lower(Gogo*, Named_object*, Statement_inserter*, int); bool do_is_constant() const; @@ -6864,7 +6872,8 @@ Find_call_expression::expression(Expression** pexpr) // specific expressions. We also convert to a constant if we can. Expression* -Builtin_call_expression::do_lower(Gogo* gogo, Named_object* function, int) +Builtin_call_expression::do_lower(Gogo* gogo, Named_object* function, + Statement_inserter* inserter, int) { if (this->classification() == EXPRESSION_ERROR) return this; @@ -6974,7 +6983,7 @@ Builtin_call_expression::do_lower(Gogo* gogo, Named_object* function, int) this->set_is_error(); return this; } - return this->lower_varargs(gogo, function, slice_type, 2); + return this->lower_varargs(gogo, function, inserter, slice_type, 2); } return this; @@ -8553,9 +8562,10 @@ Call_expression::do_traverse(Traverse* traverse) // Lower a call statement. Expression* -Call_expression::do_lower(Gogo* gogo, Named_object* function, int) +Call_expression::do_lower(Gogo* gogo, Named_object* function, + Statement_inserter* inserter, int) { - // A type case can look like a function call. + // A type cast can look like a function call. if (this->fn_->is_type_expression() && this->args_ != NULL && this->args_->size() == 1) @@ -8597,6 +8607,29 @@ Call_expression::do_lower(Gogo* gogo, Named_object* function, int) } } + // If this call returns multiple results, create a temporary + // variable for each result. + size_t rc = this->result_count(); + if (rc > 1 && this->results_ == NULL) + { + std::vector* temps = + new std::vector; + temps->reserve(rc); + const Typed_identifier_list* results = + this->fn_->type()->function_type()->results(); + for (Typed_identifier_list::const_iterator p = results->begin(); + p != results->end(); + ++p) + { + Temporary_statement* temp = Statement::make_temporary(p->type(), + NULL, + p->location()); + inserter->insert(temp); + temps->push_back(temp); + } + this->results_ = temps; + } + // Handle a call to a varargs function by packaging up the extra // parameters. if (this->fn_->type()->function_type() != NULL @@ -8606,7 +8639,7 @@ Call_expression::do_lower(Gogo* gogo, Named_object* function, int) const Typed_identifier_list* parameters = fntype->parameters(); go_assert(parameters != NULL && !parameters->empty()); Type* varargs_type = parameters->back().type(); - return this->lower_varargs(gogo, function, varargs_type, + return this->lower_varargs(gogo, function, inserter, varargs_type, parameters->size()); } @@ -8622,6 +8655,7 @@ Call_expression::do_lower(Gogo* gogo, Named_object* function, int) Expression* Call_expression::lower_varargs(Gogo* gogo, Named_object* function, + Statement_inserter* inserter, Type* varargs_type, size_t param_count) { if (this->varargs_are_lowered_) @@ -8702,13 +8736,12 @@ Call_expression::lower_varargs(Gogo* gogo, Named_object* function, // Lower all the new subexpressions. Expression* ret = this; - gogo->lower_expression(function, &ret); + gogo->lower_expression(function, inserter, &ret); go_assert(ret == this); return ret; } -// Get the function type. Returns NULL if we don't know the type. If -// this returns NULL, and if_ERROR is true, issues an error. +// Get the function type. This can return NULL in error cases. Function_type* Call_expression::get_function_type() const @@ -8729,6 +8762,16 @@ Call_expression::result_count() const return fntype->results()->size(); } +// Return the temporary which holds a result. + +Temporary_statement* +Call_expression::result(size_t i) const +{ + go_assert(this->results_ != NULL + && this->results_->size() > i); + return (*this->results_)[i]; +} + // Return whether this is a call to the predeclared function recover. bool @@ -8759,6 +8802,21 @@ Call_expression::do_set_recover_arg(Expression*) go_unreachable(); } +// We have found an error with this call expression; return true if +// we should report it. + +bool +Call_expression::issue_error() +{ + if (this->issued_error_) + return false; + else + { + this->issued_error_ = true; + return true; + } +} + // Get the type. Type* @@ -8941,15 +8999,12 @@ Call_expression::do_check_types(Gogo*) // Return whether we have to use a temporary variable to ensure that // we evaluate this call expression in order. If the call returns no -// results then it will inevitably be executed last. If the call -// returns more than one result then it will be used with Call_result -// expressions. So we only have to use a temporary variable if the -// call returns exactly one result. +// results then it will inevitably be executed last. bool Call_expression::do_must_eval_in_order() const { - return this->result_count() == 1; + return this->result_count() > 0; } // Get the function and the first argument to use when calling a bound @@ -9193,16 +9248,56 @@ Call_expression::do_get_tree(Translate_context* context) ret = build1(NOP_EXPR, rettype, ret); } - // If there is more than one result, we will refer to the call - // multiple times. - if (fntype->results() != NULL && fntype->results()->size() > 1) - ret = save_expr(ret); + if (this->results_ != NULL) + ret = this->set_results(context, ret); this->tree_ = ret; return ret; } +// Set the result variables if this call returns multiple results. + +tree +Call_expression::set_results(Translate_context* context, tree call_tree) +{ + tree stmt_list = NULL_TREE; + + call_tree = save_expr(call_tree); + + if (TREE_CODE(TREE_TYPE(call_tree)) != RECORD_TYPE) + { + go_assert(saw_errors()); + return call_tree; + } + + source_location loc = this->location(); + tree field = TYPE_FIELDS(TREE_TYPE(call_tree)); + size_t rc = this->result_count(); + for (size_t i = 0; i < rc; ++i, field = DECL_CHAIN(field)) + { + go_assert(field != NULL_TREE); + + Temporary_statement* temp = this->result(i); + Temporary_reference_expression* ref = + Expression::make_temporary_reference(temp, loc); + ref->set_is_lvalue(); + tree temp_tree = ref->get_tree(context); + if (temp_tree == error_mark_node) + continue; + + tree val_tree = build3_loc(loc, COMPONENT_REF, TREE_TYPE(field), + call_tree, field, NULL_TREE); + tree set_tree = build2_loc(loc, MODIFY_EXPR, void_type_node, temp_tree, + val_tree); + + append_to_statement_list(set_tree, &stmt_list); + } + go_assert(field == NULL_TREE); + + return save_expr(stmt_list); +} + // Make a call expression. Call_expression* @@ -9292,10 +9387,11 @@ Call_result_expression::do_type() return Type::make_error_type(); } const Typed_identifier_list* results = fntype->results(); - if (results == NULL) + if (results == NULL || results->size() < 2) { - this->report_error(_("number of results does not match " - "number of values")); + if (ce->issue_error()) + this->report_error(_("number of results does not match " + "number of values")); return Type::make_error_type(); } Typed_identifier_list::const_iterator pr = results->begin(); @@ -9307,8 +9403,9 @@ Call_result_expression::do_type() } if (pr == results->end()) { - this->report_error(_("number of results does not match " - "number of values")); + if (ce->issue_error()) + this->report_error(_("number of results does not match " + "number of values")); return Type::make_error_type(); } return pr->type(); @@ -9332,27 +9429,18 @@ Call_result_expression::do_determine_type(const Type_context*) this->call_->determine_type_no_context(); } -// Return the tree. +// Return the tree. We just refer to the temporary set by the call +// expression. We don't do this at lowering time because it makes it +// hard to evaluate the call at the right time. tree Call_result_expression::do_get_tree(Translate_context* context) { - tree call_tree = this->call_->get_tree(context); - if (call_tree == error_mark_node) - return error_mark_node; - if (TREE_CODE(TREE_TYPE(call_tree)) != RECORD_TYPE) - { - go_assert(saw_errors()); - return error_mark_node; - } - tree field = TYPE_FIELDS(TREE_TYPE(call_tree)); - for (unsigned int i = 0; i < this->index_; ++i) - { - go_assert(field != NULL_TREE); - field = DECL_CHAIN(field); - } - go_assert(field != NULL_TREE); - return build3(COMPONENT_REF, TREE_TYPE(field), call_tree, field, NULL_TREE); + Call_expression* ce = this->call_->call_expression(); + go_assert(ce != NULL); + Temporary_statement* ts = ce->result(this->index_); + Expression* ref = Expression::make_temporary_reference(ts, this->location()); + return ref->get_tree(context); } // Make a reference to a single result of a call which returns @@ -9383,7 +9471,7 @@ Index_expression::do_traverse(Traverse* traverse) // expression into an array index, a string index, or a map index. Expression* -Index_expression::do_lower(Gogo*, Named_object*, int) +Index_expression::do_lower(Gogo*, Named_object*, Statement_inserter*, int) { source_location location = this->location(); Expression* left = this->left_; @@ -10542,7 +10630,7 @@ class Selector_expression : public Parser_expression { return Expression::traverse(&this->left_, traverse); } Expression* - do_lower(Gogo*, Named_object*, int); + do_lower(Gogo*, Named_object*, Statement_inserter*, int); Expression* do_copy() @@ -10565,7 +10653,8 @@ class Selector_expression : public Parser_expression // hand side. Expression* -Selector_expression::do_lower(Gogo* gogo, Named_object*, int) +Selector_expression::do_lower(Gogo* gogo, Named_object*, Statement_inserter*, + int) { Expression* left = this->left_; if (left->is_type_expression()) @@ -10734,6 +10823,8 @@ Selector_expression::lower_method_expression(Gogo* gogo) } } + gogo->start_block(location); + Call_expression* call = Expression::make_call(bm, args, method_type->is_varargs(), location); @@ -10756,6 +10847,13 @@ Selector_expression::lower_method_expression(Gogo* gogo) } gogo->add_statement(s); + Block* b = gogo->finish_block(location); + + gogo->add_block(b, location); + + // Lower the call in case there are multiple results. + gogo->lower_block(no, b); + gogo->finish_function(location); return Expression::make_func_reference(no, NULL, location); @@ -11860,7 +11958,7 @@ class Composite_literal_expression : public Parser_expression do_traverse(Traverse* traverse); Expression* - do_lower(Gogo*, Named_object*, int); + do_lower(Gogo*, Named_object*, Statement_inserter*, int); Expression* do_copy() @@ -11884,7 +11982,7 @@ class Composite_literal_expression : public Parser_expression make_array(Type*, Expression_list*); Expression* - lower_map(Gogo*, Named_object*, Type*); + lower_map(Gogo*, Named_object*, Statement_inserter*, Type*); // The type of the composite literal. Type* type_; @@ -11913,7 +12011,8 @@ Composite_literal_expression::do_traverse(Traverse* traverse) // the type. Expression* -Composite_literal_expression::do_lower(Gogo* gogo, Named_object* function, int) +Composite_literal_expression::do_lower(Gogo* gogo, Named_object* function, + Statement_inserter* inserter, int) { Type* type = this->type_; @@ -11940,7 +12039,7 @@ Composite_literal_expression::do_lower(Gogo* gogo, Named_object* function, int) else if (type->array_type() != NULL) return this->lower_array(type); else if (type->map_type() != NULL) - return this->lower_map(gogo, function, type); + return this->lower_map(gogo, function, inserter, type); else { error_at(this->location(), @@ -12244,6 +12343,7 @@ Composite_literal_expression::make_array(Type* type, Expression_list* vals) Expression* Composite_literal_expression::lower_map(Gogo* gogo, Named_object* function, + Statement_inserter* inserter, Type* type) { source_location location = this->location(); @@ -12272,7 +12372,7 @@ Composite_literal_expression::lower_map(Gogo* gogo, Named_object* function, if ((*p)->unknown_expression() != NULL) { (*p)->unknown_expression()->clear_is_composite_literal_key(); - gogo->lower_expression(function, &*p); + gogo->lower_expression(function, inserter, &*p); go_assert((*p)->is_error_expression()); return Expression::make_error(location); } diff --git a/gcc/go/gofrontend/expressions.h b/gcc/go/gofrontend/expressions.h index 271b1bbbe36..da31f149ae0 100644 --- a/gcc/go/gofrontend/expressions.h +++ b/gcc/go/gofrontend/expressions.h @@ -15,6 +15,7 @@ class Gogo; class Translate_context; class Traverse; +class Statement_inserter; class Type; struct Type_context; class Function_type; @@ -128,7 +129,7 @@ class Expression // Make a reference to a temporary variable. Temporary variables // are always created by a single statement, which is what we use to // refer to them. - static Expression* + static Temporary_reference_expression* make_temporary_reference(Temporary_statement*, source_location); // Make a sink expression--a reference to the blank identifier _. @@ -521,13 +522,18 @@ class Expression traverse_subexpressions(Traverse*); // Lower an expression. This is called immediately after parsing. - // IOTA_VALUE is the value that we should give to any iota - // expressions. This function must resolve expressions which could - // not be fully parsed into their final form. It returns the same - // Expression or a new one. + // FUNCTION is the function we are in; it will be NULL for an + // expression initializing a global variable. INSERTER may be used + // to insert statements before the statement or initializer + // containing this expression; it is normally used to create + // temporary variables. IOTA_VALUE is the value that we should give + // to any iota expressions. This function must resolve expressions + // which could not be fully parsed into their final form. It + // returns the same Expression or a new one. Expression* - lower(Gogo* gogo, Named_object* function, int iota_value) - { return this->do_lower(gogo, function, iota_value); } + lower(Gogo* gogo, Named_object* function, Statement_inserter* inserter, + int iota_value) + { return this->do_lower(gogo, function, inserter, iota_value); } // Determine the real type of an expression with abstract integer, // floating point, or complex type. TYPE_CONTEXT describes the @@ -636,7 +642,7 @@ class Expression // Return a lowered expression. virtual Expression* - do_lower(Gogo*, Named_object*, int) + do_lower(Gogo*, Named_object*, Statement_inserter*, int) { return this; } // Return whether this is a constant expression. @@ -871,7 +877,7 @@ class Parser_expression : public Expression protected: virtual Expression* - do_lower(Gogo*, Named_object*, int) = 0; + do_lower(Gogo*, Named_object*, Statement_inserter*, int) = 0; Type* do_type(); @@ -906,7 +912,7 @@ class Var_expression : public Expression protected: Expression* - do_lower(Gogo*, Named_object*, int); + do_lower(Gogo*, Named_object*, Statement_inserter*, int); Type* do_type(); @@ -941,9 +947,15 @@ class Temporary_reference_expression : public Expression Temporary_reference_expression(Temporary_statement* statement, source_location location) : Expression(EXPRESSION_TEMPORARY_REFERENCE, location), - statement_(statement) + statement_(statement), is_lvalue_(false) { } + // Indicate that this reference appears on the left hand side of an + // assignment statement. + void + set_is_lvalue() + { this->is_lvalue_ = true; } + protected: Type* do_type(); @@ -969,6 +981,9 @@ class Temporary_reference_expression : public Expression private: // The statement where the temporary variable is defined. Temporary_statement* statement_; + // Whether this reference appears on the left hand side of an + // assignment statement. + bool is_lvalue_; }; // A string expression. @@ -1099,7 +1114,7 @@ class Binary_expression : public Expression do_traverse(Traverse* traverse); Expression* - do_lower(Gogo*, Named_object*, int); + do_lower(Gogo*, Named_object*, Statement_inserter*, int); bool do_is_constant() const @@ -1156,9 +1171,9 @@ class Call_expression : public Expression Call_expression(Expression* fn, Expression_list* args, bool is_varargs, source_location location) : Expression(EXPRESSION_CALL, location), - fn_(fn), args_(args), type_(NULL), tree_(NULL), is_varargs_(is_varargs), - varargs_are_lowered_(false), types_are_determined_(false), - is_deferred_(false) + fn_(fn), args_(args), type_(NULL), results_(NULL), tree_(NULL), + is_varargs_(is_varargs), varargs_are_lowered_(false), + types_are_determined_(false), is_deferred_(false), issued_error_(false) { } // The function to call. @@ -1183,6 +1198,12 @@ class Call_expression : public Expression size_t result_count() const; + // Return the temporary variable which holds result I. This is only + // valid after the expression has been lowered, and is only valid + // for calls which return multiple results. + Temporary_statement* + result(size_t i) const; + // Return whether this is a call to the predeclared function // recover. bool @@ -1207,12 +1228,17 @@ class Call_expression : public Expression set_is_deferred() { this->is_deferred_ = true; } + // We have found an error with this call expression; return true if + // we should report it. + bool + issue_error(); + protected: int do_traverse(Traverse*); virtual Expression* - do_lower(Gogo*, Named_object*, int); + do_lower(Gogo*, Named_object*, Statement_inserter*, int); void do_discarding_value() @@ -1256,8 +1282,8 @@ class Call_expression : public Expression // Let a builtin expression lower varargs. Expression* - lower_varargs(Gogo*, Named_object* function, Type* varargs_type, - size_t param_count); + lower_varargs(Gogo*, Named_object* function, Statement_inserter* inserter, + Type* varargs_type, size_t param_count); // Let a builtin expression check whether types have been // determined. @@ -1276,6 +1302,9 @@ class Call_expression : public Expression Interface_field_reference_expression*, tree*); + tree + set_results(Translate_context*, tree); + // The function to call. Expression* fn_; // The arguments to pass. This may be NULL if there are no @@ -1283,6 +1312,9 @@ class Call_expression : public Expression Expression_list* args_; // The type of the expression, to avoid recomputing it. Type* type_; + // The list of temporaries which will hold the results if the + // function returns a tuple. + std::vector* results_; // The tree for the call, used for a call which returns a tuple. tree tree_; // True if the last argument is a varargs argument (f(a...)). @@ -1293,6 +1325,10 @@ class Call_expression : public Expression bool types_are_determined_; // True if the call is an argument to a defer statement. bool is_deferred_; + // True if we reported an error about a mismatch between call + // results and uses. This is to avoid producing multiple errors + // when there are multiple Call_result_expressions. + bool issued_error_; }; // An expression which represents a pointer to a function. @@ -1390,7 +1426,7 @@ class Unknown_expression : public Parser_expression protected: Expression* - do_lower(Gogo*, Named_object*, int); + do_lower(Gogo*, Named_object*, Statement_inserter*, int); Expression* do_copy() @@ -1425,7 +1461,7 @@ class Index_expression : public Parser_expression do_traverse(Traverse*); Expression* - do_lower(Gogo*, Named_object*, int); + do_lower(Gogo*, Named_object*, Statement_inserter*, int); Expression* do_copy() diff --git a/gcc/go/gofrontend/gogo.cc b/gcc/go/gofrontend/gogo.cc index 194cacafcaf..4aafe412bc5 100644 --- a/gcc/go/gofrontend/gogo.cc +++ b/gcc/go/gofrontend/gogo.cc @@ -1148,9 +1148,13 @@ class Lower_parse_tree : public Traverse | traverse_functions | traverse_statements | traverse_expressions), - gogo_(gogo), function_(function), iota_value_(-1) + gogo_(gogo), function_(function), iota_value_(-1), inserter_() { } + void + set_inserter(const Statement_inserter* inserter) + { this->inserter_ = *inserter; } + int variable(Named_object*); @@ -1173,18 +1177,44 @@ class Lower_parse_tree : public Traverse Named_object* function_; // Value to use for the predeclared constant iota. int iota_value_; + // Current statement inserter for use by expressions. + Statement_inserter inserter_; }; -// Lower variables. We handle variables specially to break loops in -// which a variable initialization expression refers to itself. The -// loop breaking is in lower_init_expression. +// Lower variables. int Lower_parse_tree::variable(Named_object* no) { - if (no->is_variable()) - no->var_value()->lower_init_expression(this->gogo_, this->function_); - return TRAVERSE_CONTINUE; + if (!no->is_variable()) + return TRAVERSE_CONTINUE; + + if (no->is_variable() && no->var_value()->is_global()) + { + // Global variables can have loops in their initialization + // expressions. This is handled in lower_init_expression. + no->var_value()->lower_init_expression(this->gogo_, this->function_, + &this->inserter_); + return TRAVERSE_CONTINUE; + } + + // This is a local variable. We are going to return + // TRAVERSE_SKIP_COMPONENTS here because we want to traverse the + // initialization expression when we reach the variable declaration + // statement. However, that means that we need to traverse the type + // ourselves. + if (no->var_value()->has_type()) + { + Type* type = no->var_value()->type(); + if (type != NULL) + { + if (Type::traverse(type, this) == TRAVERSE_EXIT) + return TRAVERSE_EXIT; + } + } + go_assert(!no->var_value()->has_pre_init()); + + return TRAVERSE_SKIP_COMPONENTS; } // Lower constants. We handle constants specially so that we can set @@ -1238,27 +1268,38 @@ Lower_parse_tree::function(Named_object* no) int Lower_parse_tree::statement(Block* block, size_t* pindex, Statement* sorig) { + Statement_inserter hold_inserter(this->inserter_); + this->inserter_ = Statement_inserter(block, pindex); + // Lower the expressions first. int t = sorig->traverse_contents(this); if (t == TRAVERSE_EXIT) - return t; + { + this->inserter_ = hold_inserter; + return t; + } // Keep lowering until nothing changes. Statement* s = sorig; while (true) { - Statement* snew = s->lower(this->gogo_, this->function_, block); + Statement* snew = s->lower(this->gogo_, this->function_, block, + &this->inserter_); if (snew == s) break; s = snew; t = s->traverse_contents(this); if (t == TRAVERSE_EXIT) - return t; + { + this->inserter_ = hold_inserter; + return t; + } } if (s != sorig) block->replace_statement(*pindex, s); + this->inserter_ = hold_inserter; return TRAVERSE_SKIP_COMPONENTS; } @@ -1277,7 +1318,7 @@ Lower_parse_tree::expression(Expression** pexpr) { Expression* e = *pexpr; Expression* enew = e->lower(this->gogo_, this->function_, - this->iota_value_); + &this->inserter_, this->iota_value_); if (enew == e) break; *pexpr = enew; @@ -1304,12 +1345,16 @@ Gogo::lower_block(Named_object* function, Block* block) block->traverse(&lower_parse_tree); } -// Lower an expression. +// Lower an expression. INSERTER may be NULL, in which case the +// expression had better not need to create any temporaries. void -Gogo::lower_expression(Named_object* function, Expression** pexpr) +Gogo::lower_expression(Named_object* function, Statement_inserter* inserter, + Expression** pexpr) { Lower_parse_tree lower_parse_tree(this, function); + if (inserter != NULL) + lower_parse_tree.set_inserter(inserter); lower_parse_tree.expression(pexpr); } @@ -1951,11 +1996,26 @@ Order_eval::statement(Block* block, size_t* pindex, Statement* s) break; source_location loc = (*pexpr)->location(); - Temporary_statement* ts = Statement::make_temporary(NULL, *pexpr, loc); - block->insert_statement_before(*pindex, ts); - ++*pindex; + Statement* s; + if ((*pexpr)->call_expression() == NULL + || (*pexpr)->call_expression()->result_count() < 2) + { + Temporary_statement* ts = Statement::make_temporary(NULL, *pexpr, + loc); + s = ts; + *pexpr = Expression::make_temporary_reference(ts, loc); + } + else + { + // A call expression which returns multiple results needs to + // be handled specially. We can't create a temporary + // because there is no type to give it. Any actual uses of + // the values will be done via Call_result_expressions. + s = Statement::make_statement(*pexpr); + } - *pexpr = Expression::make_temporary_reference(ts, loc); + block->insert_statement_before(*pindex, s); + ++*pindex; } if (init != orig_init) @@ -1978,7 +2038,7 @@ Order_eval::variable(Named_object* no) return TRAVERSE_CONTINUE; Find_eval_ordering find_eval_ordering; - init->traverse_subexpressions(&find_eval_ordering); + Expression::traverse(&init, &find_eval_ordering); if (find_eval_ordering.size() <= 1) { @@ -1993,9 +2053,22 @@ Order_eval::variable(Named_object* no) { Expression** pexpr = *p; source_location loc = (*pexpr)->location(); - Temporary_statement* ts = Statement::make_temporary(NULL, *pexpr, loc); - var->add_preinit_statement(this->gogo_, ts); - *pexpr = Expression::make_temporary_reference(ts, loc); + Statement* s; + if ((*pexpr)->call_expression() == NULL + || (*pexpr)->call_expression()->result_count() < 2) + { + Temporary_statement* ts = Statement::make_temporary(NULL, *pexpr, + loc); + s = ts; + *pexpr = Expression::make_temporary_reference(ts, loc); + } + else + { + // A call expression which returns multiple results needs to + // be handled specially. + s = Statement::make_statement(*pexpr); + } + var->add_preinit_statement(this->gogo_, s); } return TRAVERSE_SKIP_COMPONENTS; @@ -2181,6 +2254,8 @@ Build_recover_thunks::function(Named_object* orig_no) } args->push_back(this->can_recover_arg(location)); + gogo->start_block(location); + Call_expression* call = Expression::make_call(fn, args, false, location); Statement* s; @@ -2202,6 +2277,13 @@ Build_recover_thunks::function(Named_object* orig_no) s->determine_types(); gogo->add_statement(s); + Block* b = gogo->finish_block(location); + + gogo->add_block(b, location); + + // Lower the call in case it returns multiple results. + gogo->lower_block(new_no, b); + gogo->finish_function(location); // Swap the function bodies and types. @@ -3152,78 +3234,64 @@ Block::traverse(Traverse* traverse) | Traverse::traverse_expressions | Traverse::traverse_types)) != 0) { + const unsigned int e_or_t = (Traverse::traverse_expressions + | Traverse::traverse_types); + const unsigned int e_or_t_or_s = (e_or_t + | Traverse::traverse_statements); for (Bindings::const_definitions_iterator pb = this->bindings_->begin_definitions(); pb != this->bindings_->end_definitions(); ++pb) { + int t = TRAVERSE_CONTINUE; switch ((*pb)->classification()) { case Named_object::NAMED_OBJECT_CONST: if ((traverse_mask & Traverse::traverse_constants) != 0) + t = traverse->constant(*pb, false); + if (t == TRAVERSE_CONTINUE + && (traverse_mask & e_or_t) != 0) { - if (traverse->constant(*pb, false) == TRAVERSE_EXIT) - return TRAVERSE_EXIT; - } - if ((traverse_mask & Traverse::traverse_types) != 0 - || (traverse_mask & Traverse::traverse_expressions) != 0) - { - Type* t = (*pb)->const_value()->type(); - if (t != NULL - && Type::traverse(t, traverse) == TRAVERSE_EXIT) - return TRAVERSE_EXIT; - } - if ((traverse_mask & Traverse::traverse_expressions) != 0 - || (traverse_mask & Traverse::traverse_types) != 0) - { - if ((*pb)->const_value()->traverse_expression(traverse) - == TRAVERSE_EXIT) + Type* tc = (*pb)->const_value()->type(); + if (tc != NULL + && Type::traverse(tc, traverse) == TRAVERSE_EXIT) return TRAVERSE_EXIT; + t = (*pb)->const_value()->traverse_expression(traverse); } break; case Named_object::NAMED_OBJECT_VAR: case Named_object::NAMED_OBJECT_RESULT_VAR: if ((traverse_mask & Traverse::traverse_variables) != 0) + t = traverse->variable(*pb); + if (t == TRAVERSE_CONTINUE + && (traverse_mask & e_or_t) != 0) { - if (traverse->variable(*pb) == TRAVERSE_EXIT) - return TRAVERSE_EXIT; - } - if (((traverse_mask & Traverse::traverse_types) != 0 - || (traverse_mask & Traverse::traverse_expressions) != 0) - && ((*pb)->is_result_variable() - || (*pb)->var_value()->has_type())) - { - Type* t = ((*pb)->is_variable() - ? (*pb)->var_value()->type() - : (*pb)->result_var_value()->type()); - if (t != NULL - && Type::traverse(t, traverse) == TRAVERSE_EXIT) - return TRAVERSE_EXIT; - } - if ((*pb)->is_variable() - && ((traverse_mask & Traverse::traverse_expressions) != 0 - || (traverse_mask & Traverse::traverse_types) != 0)) - { - if ((*pb)->var_value()->traverse_expression(traverse) - == TRAVERSE_EXIT) - return TRAVERSE_EXIT; + if ((*pb)->is_result_variable() + || (*pb)->var_value()->has_type()) + { + Type* tv = ((*pb)->is_variable() + ? (*pb)->var_value()->type() + : (*pb)->result_var_value()->type()); + if (tv != NULL + && Type::traverse(tv, traverse) == TRAVERSE_EXIT) + return TRAVERSE_EXIT; + } } + if (t == TRAVERSE_CONTINUE + && (traverse_mask & e_or_t_or_s) != 0 + && (*pb)->is_variable()) + t = (*pb)->var_value()->traverse_expression(traverse, + traverse_mask); break; case Named_object::NAMED_OBJECT_FUNC: case Named_object::NAMED_OBJECT_FUNC_DECLARATION: - // FIXME: Where will nested functions be found? go_unreachable(); case Named_object::NAMED_OBJECT_TYPE: - if ((traverse_mask & Traverse::traverse_types) != 0 - || (traverse_mask & Traverse::traverse_expressions) != 0) - { - if (Type::traverse((*pb)->type_value(), traverse) - == TRAVERSE_EXIT) - return TRAVERSE_EXIT; - } + if ((traverse_mask & e_or_t) != 0) + t = Type::traverse((*pb)->type_value(), traverse); break; case Named_object::NAMED_OBJECT_TYPE_DECLARATION: @@ -3237,6 +3305,9 @@ Block::traverse(Traverse* traverse) default: go_unreachable(); } + + if (t == TRAVERSE_EXIT) + return TRAVERSE_EXIT; } } @@ -3351,14 +3422,17 @@ Variable::Variable(Type* type, Expression* init, bool is_global, // Traverse the initializer expression. int -Variable::traverse_expression(Traverse* traverse) +Variable::traverse_expression(Traverse* traverse, unsigned int traverse_mask) { if (this->preinit_ != NULL) { if (this->preinit_->traverse(traverse) == TRAVERSE_EXIT) return TRAVERSE_EXIT; } - if (this->init_ != NULL) + if (this->init_ != NULL + && ((traverse_mask + & (Traverse::traverse_expressions | Traverse::traverse_types)) + != 0)) { if (Expression::traverse(&this->init_, traverse) == TRAVERSE_EXIT) return TRAVERSE_EXIT; @@ -3369,7 +3443,8 @@ Variable::traverse_expression(Traverse* traverse) // Lower the initialization expression after parsing is complete. void -Variable::lower_init_expression(Gogo* gogo, Named_object* function) +Variable::lower_init_expression(Gogo* gogo, Named_object* function, + Statement_inserter* inserter) { if (this->init_ != NULL && !this->init_is_lowered_) { @@ -3381,7 +3456,14 @@ Variable::lower_init_expression(Gogo* gogo, Named_object* function) } this->seen_ = true; - gogo->lower_expression(function, &this->init_); + Statement_inserter global_inserter; + if (this->is_global_) + { + global_inserter = Statement_inserter(gogo, this); + inserter = &global_inserter; + } + + gogo->lower_expression(function, inserter, &this->init_); this->seen_ = false; @@ -4508,77 +4590,67 @@ Bindings::traverse(Traverse* traverse, bool is_global) // We don't use an iterator because we permit the traversal to add // new global objects. + const unsigned int e_or_t = (Traverse::traverse_expressions + | Traverse::traverse_types); + const unsigned int e_or_t_or_s = (e_or_t + | Traverse::traverse_statements); for (size_t i = 0; i < this->named_objects_.size(); ++i) { Named_object* p = this->named_objects_[i]; + int t = TRAVERSE_CONTINUE; switch (p->classification()) { case Named_object::NAMED_OBJECT_CONST: if ((traverse_mask & Traverse::traverse_constants) != 0) + t = traverse->constant(p, is_global); + if (t == TRAVERSE_CONTINUE + && (traverse_mask & e_or_t) != 0) { - if (traverse->constant(p, is_global) == TRAVERSE_EXIT) - return TRAVERSE_EXIT; - } - if ((traverse_mask & Traverse::traverse_types) != 0 - || (traverse_mask & Traverse::traverse_expressions) != 0) - { - Type* t = p->const_value()->type(); - if (t != NULL - && Type::traverse(t, traverse) == TRAVERSE_EXIT) - return TRAVERSE_EXIT; - if (p->const_value()->traverse_expression(traverse) - == TRAVERSE_EXIT) + Type* tc = p->const_value()->type(); + if (tc != NULL + && Type::traverse(tc, traverse) == TRAVERSE_EXIT) return TRAVERSE_EXIT; + t = p->const_value()->traverse_expression(traverse); } break; case Named_object::NAMED_OBJECT_VAR: case Named_object::NAMED_OBJECT_RESULT_VAR: if ((traverse_mask & Traverse::traverse_variables) != 0) + t = traverse->variable(p); + if (t == TRAVERSE_CONTINUE + && (traverse_mask & e_or_t) != 0) { - if (traverse->variable(p) == TRAVERSE_EXIT) - return TRAVERSE_EXIT; - } - if (((traverse_mask & Traverse::traverse_types) != 0 - || (traverse_mask & Traverse::traverse_expressions) != 0) - && (p->is_result_variable() - || p->var_value()->has_type())) - { - Type* t = (p->is_variable() - ? p->var_value()->type() - : p->result_var_value()->type()); - if (t != NULL - && Type::traverse(t, traverse) == TRAVERSE_EXIT) - return TRAVERSE_EXIT; - } - if (p->is_variable() - && ((traverse_mask & Traverse::traverse_types) != 0 - || (traverse_mask & Traverse::traverse_expressions) != 0)) - { - if (p->var_value()->traverse_expression(traverse) - == TRAVERSE_EXIT) - return TRAVERSE_EXIT; + if (p->is_result_variable() + || p->var_value()->has_type()) + { + Type* tv = (p->is_variable() + ? p->var_value()->type() + : p->result_var_value()->type()); + if (tv != NULL + && Type::traverse(tv, traverse) == TRAVERSE_EXIT) + return TRAVERSE_EXIT; + } } + if (t == TRAVERSE_CONTINUE + && (traverse_mask & e_or_t_or_s) != 0 + && p->is_variable()) + t = p->var_value()->traverse_expression(traverse, traverse_mask); break; case Named_object::NAMED_OBJECT_FUNC: if ((traverse_mask & Traverse::traverse_functions) != 0) - { - int t = traverse->function(p); - if (t == TRAVERSE_EXIT) - return TRAVERSE_EXIT; - else if (t == TRAVERSE_SKIP_COMPONENTS) - break; - } - - if ((traverse_mask - & (Traverse::traverse_variables - | Traverse::traverse_constants - | Traverse::traverse_functions - | Traverse::traverse_blocks - | Traverse::traverse_statements - | Traverse::traverse_expressions - | Traverse::traverse_types)) != 0) + t = traverse->function(p); + + if (t == TRAVERSE_CONTINUE + && (traverse_mask + & (Traverse::traverse_variables + | Traverse::traverse_constants + | Traverse::traverse_functions + | Traverse::traverse_blocks + | Traverse::traverse_statements + | Traverse::traverse_expressions + | Traverse::traverse_types)) != 0) { if (p->func_value()->traverse(traverse) == TRAVERSE_EXIT) return TRAVERSE_EXIT; @@ -4591,12 +4663,8 @@ Bindings::traverse(Traverse* traverse, bool is_global) break; case Named_object::NAMED_OBJECT_TYPE: - if ((traverse_mask & Traverse::traverse_types) != 0 - || (traverse_mask & Traverse::traverse_expressions) != 0) - { - if (Type::traverse(p->type_value(), traverse) == TRAVERSE_EXIT) - return TRAVERSE_EXIT; - } + if ((traverse_mask & e_or_t) != 0) + t = Type::traverse(p->type_value(), traverse); break; case Named_object::NAMED_OBJECT_TYPE_DECLARATION: @@ -4608,6 +4676,9 @@ Bindings::traverse(Traverse* traverse, bool is_global) default: go_unreachable(); } + + if (t == TRAVERSE_EXIT) + return TRAVERSE_EXIT; } return TRAVERSE_CONTINUE; @@ -4805,3 +4876,20 @@ Traverse::type(Type*) { go_unreachable(); } + +// Class Statement_inserter. + +void +Statement_inserter::insert(Statement* s) +{ + if (this->block_ != NULL) + { + go_assert(this->pindex_ != NULL); + this->block_->insert_statement_before(*this->pindex_, s); + ++*this->pindex_; + } + else if (this->var_ != NULL) + this->var_->add_preinit_statement(this->gogo_, s); + else + go_unreachable(); +} diff --git a/gcc/go/gofrontend/gogo.h b/gcc/go/gofrontend/gogo.h index cc349afd064..c9d2971daf5 100644 --- a/gcc/go/gofrontend/gogo.h +++ b/gcc/go/gofrontend/gogo.h @@ -8,6 +8,7 @@ #define GO_GOGO_H class Traverse; +class Statement_inserter; class Type; class Type_hash_identical; class Type_equal; @@ -366,7 +367,7 @@ class Gogo // Lower an expression. void - lower_expression(Named_object* function, Expression**); + lower_expression(Named_object* function, Statement_inserter*, Expression**); // Lower a constant. void @@ -1157,7 +1158,7 @@ class Variable // Lower the initialization expression after parsing is complete. void - lower_init_expression(Gogo*, Named_object*); + lower_init_expression(Gogo*, Named_object*, Statement_inserter*); // A special case: the init value is used only to determine the // type. This is used if the variable is defined using := with the @@ -1208,7 +1209,7 @@ class Variable // Traverse the initializer expression. int - traverse_expression(Traverse*); + traverse_expression(Traverse*, unsigned int traverse_mask); // Determine the type of the variable if necessary. void @@ -2463,6 +2464,46 @@ class Traverse Expressions_seen* expressions_seen_; }; +// A class which makes it easier to insert new statements before the +// current statement during a traversal. + +class Statement_inserter +{ + public: + // Empty constructor. + Statement_inserter() + : block_(NULL), pindex_(NULL), gogo_(NULL), var_(NULL) + { } + + // Constructor for a statement in a block. + Statement_inserter(Block* block, size_t *pindex) + : block_(block), pindex_(pindex), gogo_(NULL), var_(NULL) + { } + + // Constructor for a global variable. + Statement_inserter(Gogo* gogo, Variable* var) + : block_(NULL), pindex_(NULL), gogo_(gogo), var_(var) + { go_assert(var->is_global()); } + + // We use the default copy constructor and assignment operator. + + // Insert S before the statement we are traversing, or before the + // initialization expression of a global variable. + void + insert(Statement* s); + + private: + // The block that the statement is in. + Block* block_; + // The index of the statement that we are traversing. + size_t* pindex_; + // The IR, needed when looking at an initializer expression for a + // global variable. + Gogo* gogo_; + // The global variable, when looking at an initializer expression. + Variable* var_; +}; + // When translating the gogo IR into the backend data structure, this // is the context we pass down the blocks and statements. diff --git a/gcc/go/gofrontend/statements.cc b/gcc/go/gofrontend/statements.cc index 4d335bb4507..dd2aef6f10a 100644 --- a/gcc/go/gofrontend/statements.cc +++ b/gcc/go/gofrontend/statements.cc @@ -217,6 +217,16 @@ Variable_declaration_statement::do_traverse_assignments( return true; } +// Lower the variable's initialization expression. + +Statement* +Variable_declaration_statement::do_lower(Gogo* gogo, Named_object* function, + Block*, Statement_inserter* inserter) +{ + this->var_->var_value()->lower_init_expression(gogo, function, inserter); + return this; +} + // Convert a variable declaration to the backend representation. Bstatement* @@ -244,7 +254,7 @@ Variable_declaration_statement::do_get_backend(Translate_context* context) Expression_list* params = new Expression_list(); params->push_back(Expression::make_type(var->type(), loc)); Expression* call = Expression::make_call(func, params, false, loc); - context->gogo()->lower_expression(context->function(), &call); + context->gogo()->lower_expression(context->function(), NULL, &call); Temporary_statement* temp = Statement::make_temporary(NULL, call, loc); Bstatement* btemp = temp->get_backend(context); @@ -386,7 +396,7 @@ Temporary_statement::do_get_backend(Translate_context* context) { Expression* init = Expression::make_cast(this->type_, this->init_, this->location()); - context->gogo()->lower_expression(context->function(), &init); + context->gogo()->lower_expression(context->function(), NULL, &init); binit = tree_to_expr(init->get_tree(context)); } @@ -598,7 +608,7 @@ class Assignment_operation_statement : public Statement { go_unreachable(); } Statement* - do_lower(Gogo*, Named_object*, Block*); + do_lower(Gogo*, Named_object*, Block*, Statement_inserter*); Bstatement* do_get_backend(Translate_context*) @@ -628,7 +638,7 @@ Assignment_operation_statement::do_traverse(Traverse* traverse) Statement* Assignment_operation_statement::do_lower(Gogo*, Named_object*, - Block* enclosing) + Block* enclosing, Statement_inserter*) { source_location loc = this->location(); @@ -725,7 +735,7 @@ class Tuple_assignment_statement : public Statement { go_unreachable(); } Statement* - do_lower(Gogo*, Named_object*, Block*); + do_lower(Gogo*, Named_object*, Block*, Statement_inserter*); Bstatement* do_get_backend(Translate_context*) @@ -752,7 +762,8 @@ Tuple_assignment_statement::do_traverse(Traverse* traverse) // up into a set of single assignments. Statement* -Tuple_assignment_statement::do_lower(Gogo*, Named_object*, Block* enclosing) +Tuple_assignment_statement::do_lower(Gogo*, Named_object*, Block* enclosing, + Statement_inserter*) { source_location loc = this->location(); @@ -852,7 +863,7 @@ public: { go_unreachable(); } Statement* - do_lower(Gogo*, Named_object*, Block*); + do_lower(Gogo*, Named_object*, Block*, Statement_inserter*); Bstatement* do_get_backend(Translate_context*) @@ -882,7 +893,7 @@ Tuple_map_assignment_statement::do_traverse(Traverse* traverse) Statement* Tuple_map_assignment_statement::do_lower(Gogo*, Named_object*, - Block* enclosing) + Block* enclosing, Statement_inserter*) { source_location loc = this->location(); @@ -923,7 +934,8 @@ Tuple_map_assignment_statement::do_lower(Gogo*, Named_object*, b->add_statement(present_temp); // present_temp = mapaccess2(MAP, &key_temp, &val_temp) - Expression* ref = Expression::make_temporary_reference(key_temp, loc); + Temporary_reference_expression* ref = + Expression::make_temporary_reference(key_temp, loc); Expression* a1 = Expression::make_unary(OPERATOR_AND, ref, loc); ref = Expression::make_temporary_reference(val_temp, loc); Expression* a2 = Expression::make_unary(OPERATOR_AND, ref, loc); @@ -931,6 +943,7 @@ Tuple_map_assignment_statement::do_lower(Gogo*, Named_object*, map_index->map(), a1, a2); ref = Expression::make_temporary_reference(present_temp, loc); + ref->set_is_lvalue(); Statement* s = Statement::make_assignment(ref, call, loc); b->add_statement(s); @@ -979,7 +992,7 @@ class Map_assignment_statement : public Statement { go_unreachable(); } Statement* - do_lower(Gogo*, Named_object*, Block*); + do_lower(Gogo*, Named_object*, Block*, Statement_inserter*); Bstatement* do_get_backend(Translate_context*) @@ -1008,7 +1021,8 @@ Map_assignment_statement::do_traverse(Traverse* traverse) // Lower a map assignment to a function call. Statement* -Map_assignment_statement::do_lower(Gogo*, Named_object*, Block* enclosing) +Map_assignment_statement::do_lower(Gogo*, Named_object*, Block* enclosing, + Statement_inserter*) { source_location loc = this->location(); @@ -1093,7 +1107,7 @@ class Tuple_receive_assignment_statement : public Statement { go_unreachable(); } Statement* - do_lower(Gogo*, Named_object*, Block*); + do_lower(Gogo*, Named_object*, Block*, Statement_inserter*); Bstatement* do_get_backend(Translate_context*) @@ -1125,7 +1139,8 @@ Tuple_receive_assignment_statement::do_traverse(Traverse* traverse) Statement* Tuple_receive_assignment_statement::do_lower(Gogo*, Named_object*, - Block* enclosing) + Block* enclosing, + Statement_inserter*) { source_location loc = this->location(); @@ -1160,13 +1175,15 @@ Tuple_receive_assignment_statement::do_lower(Gogo*, Named_object*, b->add_statement(closed_temp); // closed_temp = chanrecv[23](channel, &val_temp) - Expression* ref = Expression::make_temporary_reference(val_temp, loc); + Temporary_reference_expression* ref = + Expression::make_temporary_reference(val_temp, loc); Expression* p2 = Expression::make_unary(OPERATOR_AND, ref, loc); Expression* call = Runtime::make_call((this->for_select_ ? Runtime::CHANRECV3 : Runtime::CHANRECV2), loc, 2, this->channel_, p2); ref = Expression::make_temporary_reference(closed_temp, loc); + ref->set_is_lvalue(); Statement* s = Statement::make_assignment(ref, call, loc); b->add_statement(s); @@ -1217,7 +1234,7 @@ class Tuple_type_guard_assignment_statement : public Statement { go_unreachable(); } Statement* - do_lower(Gogo*, Named_object*, Block*); + do_lower(Gogo*, Named_object*, Block*, Statement_inserter*); Bstatement* do_get_backend(Translate_context*) @@ -1256,7 +1273,8 @@ Tuple_type_guard_assignment_statement::do_traverse(Traverse* traverse) Statement* Tuple_type_guard_assignment_statement::do_lower(Gogo*, Named_object*, - Block* enclosing) + Block* enclosing, + Statement_inserter*) { source_location loc = this->location(); @@ -1378,6 +1396,10 @@ class Expression_statement : public Statement expr_(expr) { } + Expression* + expr() + { return this->expr_; } + protected: int do_traverse(Traverse* traverse) @@ -1513,7 +1535,7 @@ class Inc_dec_statement : public Statement { go_unreachable(); } Statement* - do_lower(Gogo*, Named_object*, Block*); + do_lower(Gogo*, Named_object*, Block*, Statement_inserter*); Bstatement* do_get_backend(Translate_context*) @@ -1529,7 +1551,7 @@ class Inc_dec_statement : public Statement // Lower to += or -=. Statement* -Inc_dec_statement::do_lower(Gogo*, Named_object*, Block*) +Inc_dec_statement::do_lower(Gogo*, Named_object*, Block*, Statement_inserter*) { source_location loc = this->location(); @@ -2017,6 +2039,8 @@ Thunk_statement::build_thunk(Gogo* gogo, const std::string& thunk_name, Named_object* function = gogo->start_function(thunk_name, thunk_type, true, location); + gogo->start_block(location); + // For a defer statement, start with a call to // __go_set_defer_retaddr. */ Label* retaddr_label = NULL; @@ -2122,26 +2146,10 @@ Thunk_statement::build_thunk(Gogo* gogo, const std::string& thunk_name, call_params = NULL; } - Expression* call = Expression::make_call(func_to_call, call_params, false, - location); - // We need to lower in case this is a builtin function. - call = call->lower(gogo, function, -1); - Call_expression* call_ce = call->call_expression(); - if (call_ce != NULL && may_call_recover) - call_ce->set_is_deferred(); - + Call_expression* call = Expression::make_call(func_to_call, call_params, + false, location); Statement* call_statement = Statement::make_statement(call); - // We already ran the determine_types pass, so we need to run it - // just for this statement now. - call_statement->determine_types(); - - // Sanity check. - call->check_types(gogo); - - if (call_ce != NULL && recover_arg != NULL) - call_ce->set_recover_arg(recover_arg); - gogo->add_statement(call_statement); // If this is a defer statement, the label comes immediately after @@ -2155,6 +2163,31 @@ Thunk_statement::build_thunk(Gogo* gogo, const std::string& thunk_name, gogo->add_statement(Statement::make_return_statement(vals, location)); } + Block* b = gogo->finish_block(location); + + gogo->add_block(b, location); + + gogo->lower_block(function, b); + + // We already ran the determine_types pass, so we need to run it + // just for the call statement now. The other types are known. + call_statement->determine_types(); + + if (may_call_recover || recover_arg != NULL) + { + // Dig up the call expression, which may have been changed + // during lowering. + go_assert(call_statement->classification() == STATEMENT_EXPRESSION); + Expression_statement* es = + static_cast(call_statement); + Call_expression* ce = es->expr()->call_expression(); + go_assert(ce != NULL); + if (may_call_recover) + ce->set_is_deferred(); + if (recover_arg != NULL) + ce->set_recover_arg(recover_arg); + } + // That is all the thunk has to do. gogo->finish_function(location); } @@ -2265,7 +2298,8 @@ Return_statement::do_traverse_assignments(Traverse_assignments* tassign) // panic/recover work correctly. Statement* -Return_statement::do_lower(Gogo*, Named_object* function, Block* enclosing) +Return_statement::do_lower(Gogo*, Named_object* function, Block* enclosing, + Statement_inserter*) { if (this->is_lowered_) return this; @@ -3305,7 +3339,8 @@ Switch_statement::do_traverse(Traverse* traverse) // of if statements. Statement* -Switch_statement::do_lower(Gogo*, Named_object*, Block* enclosing) +Switch_statement::do_lower(Gogo*, Named_object*, Block* enclosing, + Statement_inserter*) { source_location loc = this->location(); @@ -3578,7 +3613,8 @@ Type_switch_statement::do_traverse(Traverse* traverse) // equality testing. Statement* -Type_switch_statement::do_lower(Gogo*, Named_object*, Block* enclosing) +Type_switch_statement::do_lower(Gogo*, Named_object*, Block* enclosing, + Statement_inserter*) { const source_location loc = this->location(); @@ -3629,8 +3665,9 @@ Type_switch_statement::do_lower(Gogo*, Named_object*, Block* enclosing) ? Runtime::EFACETYPE : Runtime::IFACETYPE), loc, 1, ref); - Expression* lhs = Expression::make_temporary_reference(descriptor_temp, - loc); + Temporary_reference_expression* lhs = + Expression::make_temporary_reference(descriptor_temp, loc); + lhs->set_is_lvalue(); Statement* s = Statement::make_assignment(lhs, call, loc); b->add_statement(s); } @@ -3815,7 +3852,7 @@ Send_statement::do_get_backend(Translate_context* context) call = Runtime::make_call(code, loc, 3, this->channel_, val, Expression::make_boolean(this->for_select_, loc)); - context->gogo()->lower_expression(context->function(), &call); + context->gogo()->lower_expression(context->function(), NULL, &call); Bexpression* bcall = tree_to_expr(call->get_tree(context)); Bstatement* s = context->backend()->expression_statement(bcall); @@ -4154,7 +4191,7 @@ Select_clauses::get_backend(Translate_context* context, Expression* nil2 = nil1->copy(); Expression* call = Runtime::make_call(Runtime::SELECT, location, 4, zero, default_arg, nil1, nil2); - context->gogo()->lower_expression(context->function(), &call); + context->gogo()->lower_expression(context->function(), NULL, &call); Bexpression* bcall = tree_to_expr(call->get_tree(context)); s = context->backend()->expression_statement(bcall); } @@ -4175,7 +4212,7 @@ Select_clauses::get_backend(Translate_context* context, Expression* chans = Expression::make_composite_literal(chan_array_type, 0, false, chan_init, location); - context->gogo()->lower_expression(context->function(), &chans); + context->gogo()->lower_expression(context->function(), NULL, &chans); Temporary_statement* chan_temp = Statement::make_temporary(chan_array_type, chans, location); @@ -4187,7 +4224,7 @@ Select_clauses::get_backend(Translate_context* context, 0, false, is_send_init, location); - context->gogo()->lower_expression(context->function(), &is_sends); + context->gogo()->lower_expression(context->function(), NULL, &is_sends); Temporary_statement* is_send_temp = Statement::make_temporary(is_send_array_type, is_sends, location); statements.push_back(is_send_temp->get_backend(context)); @@ -4213,7 +4250,7 @@ Select_clauses::get_backend(Translate_context* context, Expression* call = Runtime::make_call(Runtime::SELECT, location, 4, ecount->copy(), default_arg, chan_arg, is_send_arg); - context->gogo()->lower_expression(context->function(), &call); + context->gogo()->lower_expression(context->function(), NULL, &call); Bexpression* bcall = tree_to_expr(call->get_tree(context)); std::vector > cases; @@ -4309,7 +4346,7 @@ Select_statement::break_label() Statement* Select_statement::do_lower(Gogo* gogo, Named_object* function, - Block* enclosing) + Block* enclosing, Statement_inserter*) { if (this->is_lowered_) return this; @@ -4366,7 +4403,8 @@ For_statement::do_traverse(Traverse* traverse) // complex statements make it easier to handle garbage collection. Statement* -For_statement::do_lower(Gogo*, Named_object*, Block* enclosing) +For_statement::do_lower(Gogo*, Named_object*, Block* enclosing, + Statement_inserter*) { Statement* s; source_location loc = this->location(); @@ -4497,7 +4535,8 @@ For_range_statement::do_traverse(Traverse* traverse) // statements. Statement* -For_range_statement::do_lower(Gogo* gogo, Named_object*, Block* enclosing) +For_range_statement::do_lower(Gogo* gogo, Named_object*, Block* enclosing, + Statement_inserter*) { Type* range_type = this->range_->type(); if (range_type->points_to() != NULL @@ -4711,8 +4750,10 @@ For_range_statement::lower_range_array(Gogo* gogo, Expression* zexpr = Expression::make_integer(&zval, NULL, loc); mpz_clear(zval); - ref = Expression::make_temporary_reference(index_temp, loc); - Statement* s = Statement::make_assignment(ref, zexpr, loc); + Temporary_reference_expression* tref = + Expression::make_temporary_reference(index_temp, loc); + tref->set_is_lvalue(); + Statement* s = Statement::make_assignment(tref, zexpr, loc); init->add_statement(s); *pinit = init; @@ -4738,8 +4779,9 @@ For_range_statement::lower_range_array(Gogo* gogo, Expression* ref2 = Expression::make_temporary_reference(index_temp, loc); Expression* index = Expression::make_index(ref, ref2, NULL, loc); - ref = Expression::make_temporary_reference(value_temp, loc); - s = Statement::make_assignment(ref, index, loc); + tref = Expression::make_temporary_reference(value_temp, loc); + tref->set_is_lvalue(); + s = Statement::make_assignment(tref, index, loc); iter_init->add_statement(s); } @@ -4749,8 +4791,9 @@ For_range_statement::lower_range_array(Gogo* gogo, // index_temp++ Block* post = new Block(enclosing, loc); - ref = Expression::make_temporary_reference(index_temp, loc); - s = Statement::make_inc_statement(ref); + tref = Expression::make_temporary_reference(index_temp, loc); + tref->set_is_lvalue(); + s = Statement::make_inc_statement(tref); post->add_statement(s); *ppost = post; } @@ -4798,7 +4841,9 @@ For_range_statement::lower_range_string(Gogo*, mpz_init_set_ui(zval, 0UL); Expression* zexpr = Expression::make_integer(&zval, NULL, loc); - Expression* ref = Expression::make_temporary_reference(index_temp, loc); + Temporary_reference_expression* ref = + Expression::make_temporary_reference(index_temp, loc); + ref->set_is_lvalue(); Statement* s = Statement::make_assignment(ref, zexpr, loc); init->add_statement(s); @@ -4829,14 +4874,20 @@ For_range_statement::lower_range_string(Gogo*, if (value_temp == NULL) { ref = Expression::make_temporary_reference(next_index_temp, loc); + ref->set_is_lvalue(); s = Statement::make_assignment(ref, call, loc); } else { Expression_list* lhs = new Expression_list(); - lhs->push_back(Expression::make_temporary_reference(next_index_temp, - loc)); - lhs->push_back(Expression::make_temporary_reference(value_temp, loc)); + + ref = Expression::make_temporary_reference(next_index_temp, loc); + ref->set_is_lvalue(); + lhs->push_back(ref); + + ref = Expression::make_temporary_reference(value_temp, loc); + ref->set_is_lvalue(); + lhs->push_back(ref); Expression_list* rhs = new Expression_list(); rhs->push_back(Expression::make_call_result(call, 0)); @@ -4865,7 +4916,9 @@ For_range_statement::lower_range_string(Gogo*, Block* post = new Block(enclosing, loc); - Expression* lhs = Expression::make_temporary_reference(index_temp, loc); + Temporary_reference_expression* lhs = + Expression::make_temporary_reference(index_temp, loc); + lhs->set_is_lvalue(); Expression* rhs = Expression::make_temporary_reference(next_index_temp, loc); s = Statement::make_assignment(lhs, rhs, loc); @@ -5024,8 +5077,12 @@ For_range_statement::lower_range_channel(Gogo*, iter_init->add_statement(ok_temp); Expression* cref = this->make_range_ref(range_object, range_temp, loc); - Expression* iref = Expression::make_temporary_reference(index_temp, loc); - Expression* oref = Expression::make_temporary_reference(ok_temp, loc); + Temporary_reference_expression* iref = + Expression::make_temporary_reference(index_temp, loc); + iref->set_is_lvalue(); + Temporary_reference_expression* oref = + Expression::make_temporary_reference(ok_temp, loc); + oref->set_is_lvalue(); Statement* s = Statement::make_tuple_receive_assignment(iref, oref, cref, false, loc); iter_init->add_statement(s); diff --git a/gcc/go/gofrontend/statements.h b/gcc/go/gofrontend/statements.h index 5c27c117967..44241ab1152 100644 --- a/gcc/go/gofrontend/statements.h +++ b/gcc/go/gofrontend/statements.h @@ -11,6 +11,7 @@ class Gogo; class Traverse; +class Statement_inserter; class Block; class Function; class Unnamed_label; @@ -290,9 +291,11 @@ class Statement // simplify statements for further processing. It returns the same // Statement or a new one. FUNCTION is the function containing this // statement. BLOCK is the block containing this statement. + // INSERTER can be used to insert new statements before this one. Statement* - lower(Gogo* gogo, Named_object* function, Block* block) - { return this->do_lower(gogo, function, block); } + lower(Gogo* gogo, Named_object* function, Block* block, + Statement_inserter* inserter) + { return this->do_lower(gogo, function, block, inserter); } // Set type information for unnamed constants. void @@ -385,7 +388,7 @@ class Statement // Implemented by the child class: lower this statement to a simpler // one. virtual Statement* - do_lower(Gogo*, Named_object*, Block*) + do_lower(Gogo*, Named_object*, Block*, Statement_inserter*) { return this; } // Implemented by child class: set type information for unnamed @@ -535,6 +538,9 @@ class Variable_declaration_statement : public Statement bool do_traverse_assignments(Traverse_assignments*); + Statement* + do_lower(Gogo*, Named_object*, Block*, Statement_inserter*); + Bstatement* do_get_backend(Translate_context*); @@ -566,7 +572,7 @@ class Return_statement : public Statement do_traverse_assignments(Traverse_assignments*); Statement* - do_lower(Gogo*, Named_object*, Block*); + do_lower(Gogo*, Named_object*, Block*, Statement_inserter*); bool do_may_fall_through() const @@ -806,7 +812,7 @@ class Select_statement : public Statement { return this->clauses_->traverse(traverse); } Statement* - do_lower(Gogo*, Named_object*, Block*); + do_lower(Gogo*, Named_object*, Block*, Statement_inserter*); void do_determine_types() @@ -993,7 +999,7 @@ class For_statement : public Statement { go_unreachable(); } Statement* - do_lower(Gogo*, Named_object*, Block*); + do_lower(Gogo*, Named_object*, Block*, Statement_inserter*); Bstatement* do_get_backend(Translate_context*) @@ -1051,7 +1057,7 @@ class For_range_statement : public Statement { go_unreachable(); } Statement* - do_lower(Gogo*, Named_object*, Block*); + do_lower(Gogo*, Named_object*, Block*, Statement_inserter*); Bstatement* do_get_backend(Translate_context*) @@ -1280,7 +1286,7 @@ class Switch_statement : public Statement do_traverse(Traverse*); Statement* - do_lower(Gogo*, Named_object*, Block*); + do_lower(Gogo*, Named_object*, Block*, Statement_inserter*); Bstatement* do_get_backend(Translate_context*) @@ -1426,7 +1432,7 @@ class Type_switch_statement : public Statement do_traverse(Traverse*); Statement* - do_lower(Gogo*, Named_object*, Block*); + do_lower(Gogo*, Named_object*, Block*, Statement_inserter*); Bstatement* do_get_backend(Translate_context*) -- 2.30.2