From e7a8d69a557124c41a350c1f52f0422fd4b5a230 Mon Sep 17 00:00:00 2001 From: Ian Lance Taylor Date: Sun, 14 Feb 2021 18:59:07 -0800 Subject: compiler: unalias receiver type in export data Test case is https://golang.org/cl/292009. Reviewed-on: https://go-review.googlesource.com/c/gofrontend/+/291991 --- gcc/go/gofrontend/MERGE | 2 +- gcc/go/gofrontend/gogo.cc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'gcc/go') diff --git a/gcc/go/gofrontend/MERGE b/gcc/go/gofrontend/MERGE index 3175ba0..eed9ce0 100644 --- a/gcc/go/gofrontend/MERGE +++ b/gcc/go/gofrontend/MERGE @@ -1,4 +1,4 @@ -a5d7c4225fbbd06b97db6fa424cc0cb5191082d4 +c406de0594782b1d6782a732a50f5b76387852dc 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/gogo.cc b/gcc/go/gofrontend/gogo.cc index 62b06be..38a2f6f 100644 --- a/gcc/go/gofrontend/gogo.cc +++ b/gcc/go/gofrontend/gogo.cc @@ -5898,7 +5898,7 @@ Function::export_func_with_type(Export* exp, const Named_object* no, exp->write_name(receiver->name()); exp->write_escape(receiver->note()); exp->write_c_string(" "); - exp->write_type(receiver->type()); + exp->write_type(receiver->type()->unalias()); exp->write_c_string(") "); } -- cgit v1.1 From 13e6fadd96dc00c611a3d2f26a1a6d7a0a29ea27 Mon Sep 17 00:00:00 2001 From: Ian Lance Taylor Date: Thu, 18 Feb 2021 18:54:38 -0800 Subject: libgo: update to Go1.16 release Reviewed-on: https://go-review.googlesource.com/c/gofrontend/+/293793 --- gcc/go/gofrontend/MERGE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'gcc/go') diff --git a/gcc/go/gofrontend/MERGE b/gcc/go/gofrontend/MERGE index eed9ce0..217bdd5 100644 --- a/gcc/go/gofrontend/MERGE +++ b/gcc/go/gofrontend/MERGE @@ -1,4 +1,4 @@ -c406de0594782b1d6782a732a50f5b76387852dc +78a840e4940159a66072237f6b002ab79f441b79 The first line of this file holds the git revision number of the last merge done from the gofrontend repository. -- cgit v1.1 From 2fbed0dc6c4dffcfd586a990fc3d2aeb7e00d521 Mon Sep 17 00:00:00 2001 From: Lynn Boger Date: Mon, 22 Feb 2021 09:12:01 -0600 Subject: libgo: ensure memmove, memset 8 byte atomicity on ppc64x Go requires that pointer moves are done 8 bytes at a time, but gccgo uses libc's memmove and memset which does not require that, and there are some cases where an 8 byte move might be done as 4+4. To enforce 8 byte moves for memmove and memset, this adds a C implementation in libgo/runtime for memmove and memset to be used on ppc64le and ppc64. Asm implementations were considered but discarded to avoid different implementations for different target ISAs. Fixes golang/go#41428 Reviewed-on: https://go-review.googlesource.com/c/gofrontend/+/294931 --- gcc/go/gofrontend/MERGE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'gcc/go') diff --git a/gcc/go/gofrontend/MERGE b/gcc/go/gofrontend/MERGE index 217bdd5..9806b9d 100644 --- a/gcc/go/gofrontend/MERGE +++ b/gcc/go/gofrontend/MERGE @@ -1,4 +1,4 @@ -78a840e4940159a66072237f6b002ab79f441b79 +56cf388da8d04bbd3824c4df34d77a8afa69749b The first line of this file holds the git revision number of the last merge done from the gofrontend repository. -- cgit v1.1 From d1776b7757001d9da47efcfd64d3bb20afb8d96d Mon Sep 17 00:00:00 2001 From: Michael Matloob Date: Mon, 1 Mar 2021 19:02:38 -0500 Subject: compiler: allow //go:embed in files underscore-importing embed The embed spec allows for //go:embed to be used in files that underscore-import package "embed". This is useful for embeds to []byte and string vars because the embed.FS type may not be referenced if those are the only types of embeds in a file. Because the compiler previously checked whether there were any aliases to the embed package to decide if //go:embed could be used, it would reject files with only underscore imports of embed. Instead, record whether the embed import is encountered at all, similar to what is done with unsafe, to decide whether //go:embed is allowed. Reviewed-on: https://go-review.googlesource.com/c/gofrontend/+/297553 --- gcc/go/gofrontend/MERGE | 2 +- gcc/go/gofrontend/embed.cc | 15 --------------- gcc/go/gofrontend/gogo.cc | 5 +++++ gcc/go/gofrontend/gogo.h | 11 +++++++---- gcc/go/gofrontend/parse.cc | 2 +- 5 files changed, 14 insertions(+), 21 deletions(-) (limited to 'gcc/go') diff --git a/gcc/go/gofrontend/MERGE b/gcc/go/gofrontend/MERGE index 9806b9d..5c9fc7d 100644 --- a/gcc/go/gofrontend/MERGE +++ b/gcc/go/gofrontend/MERGE @@ -1,4 +1,4 @@ -56cf388da8d04bbd3824c4df34d77a8afa69749b +2c5188b5ad6143e791f2ba42f02a4ea7887d87b6 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/embed.cc b/gcc/go/gofrontend/embed.cc index 0a7df05..0584f70 100644 --- a/gcc/go/gofrontend/embed.cc +++ b/gcc/go/gofrontend/embed.cc @@ -663,21 +663,6 @@ Embedcfg_reader::error(const char* msg) this->filename_, msg); } -// Return whether the current file imports "embed". - -bool -Gogo::is_embed_imported() const -{ - Packages::const_iterator p = this->packages_.find("embed"); - if (p == this->packages_.end()) - return false; - - // We track current file imports in the package aliases, where a - // typical import will just list the package name in aliases. So - // the package has been imported if there is at least one alias. - return !p->second->aliases().empty(); -} - // Implement the sort order for a list of embedded files, as discussed // at the docs for embed.FS. diff --git a/gcc/go/gofrontend/gogo.cc b/gcc/go/gofrontend/gogo.cc index 38a2f6f..9d4150e 100644 --- a/gcc/go/gofrontend/gogo.cc +++ b/gcc/go/gofrontend/gogo.cc @@ -37,6 +37,7 @@ Gogo::Gogo(Backend* backend, Linemap* linemap, int, int pointer_size) imports_(), imported_unsafe_(false), current_file_imported_unsafe_(false), + current_file_imported_embed_(false), packages_(), init_functions_(), var_deps_(), @@ -469,6 +470,9 @@ Gogo::import_package(const std::string& filename, return; } + if (filename == "embed") + this->current_file_imported_embed_ = true; + Imports::const_iterator p = this->imports_.find(filename); if (p != this->imports_.end()) { @@ -2717,6 +2721,7 @@ Gogo::clear_file_scope() } this->current_file_imported_unsafe_ = false; + this->current_file_imported_embed_ = false; } // Queue up a type-specific hash function for later writing. These diff --git a/gcc/go/gofrontend/gogo.h b/gcc/go/gofrontend/gogo.h index 51b6575..f4155a2 100644 --- a/gcc/go/gofrontend/gogo.h +++ b/gcc/go/gofrontend/gogo.h @@ -397,10 +397,6 @@ class Gogo void read_embedcfg(const char* filename); - // Return whether the current file imports "embed". - bool - is_embed_imported() const; - // Build an initializer for a variable with a go:embed directive. Expression* initializer_for_embeds(Type*, const std::vector*, Location); @@ -709,6 +705,11 @@ class Gogo current_file_imported_unsafe() const { return this->current_file_imported_unsafe_; } + // Return whether the current file imported the embed package. + bool + current_file_imported_embed() const + { return this->current_file_imported_embed_; } + // Clear out all names in file scope. This is called when we start // parsing a new file. void @@ -1251,6 +1252,8 @@ class Gogo bool imported_unsafe_; // Whether the magic unsafe package was imported by the current file. bool current_file_imported_unsafe_; + // Whether the embed package was imported by the current file. + bool current_file_imported_embed_; // Mapping from package names we have seen to packages. This does // not include the package we are compiling. Packages packages_; diff --git a/gcc/go/gofrontend/parse.cc b/gcc/go/gofrontend/parse.cc index fd81a85..e43b5f2 100644 --- a/gcc/go/gofrontend/parse.cc +++ b/gcc/go/gofrontend/parse.cc @@ -1321,7 +1321,7 @@ Parse::declaration() embeds = new(std::vector); this->lex_->get_and_clear_embeds(embeds); - if (!this->gogo_->is_embed_imported()) + if (!this->gogo_->current_file_imported_embed()) { go_error_at(token->location(), "invalid go:embed: missing import %"); -- cgit v1.1 From d5d3f15a0e04c30d5dbec09b56c14ad923a3e8da Mon Sep 17 00:00:00 2001 From: Ian Lance Taylor Date: Mon, 8 Mar 2021 13:58:14 -0800 Subject: runtime: cast SIGSTKSZ to uintptr In newer versions of glibc it is long, which causes a signed comparison warning. Fixes PR go/99458 --- gcc/go/gofrontend/MERGE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'gcc/go') diff --git a/gcc/go/gofrontend/MERGE b/gcc/go/gofrontend/MERGE index 5c9fc7d..5b45f03 100644 --- a/gcc/go/gofrontend/MERGE +++ b/gcc/go/gofrontend/MERGE @@ -1,4 +1,4 @@ -2c5188b5ad6143e791f2ba42f02a4ea7887d87b6 +93380a9126e76b71fa208e62c31c7914084c0e37 The first line of this file holds the git revision number of the last merge done from the gofrontend repository. -- cgit v1.1 From 7ad5a72c8bc6aa71a0d195ddfa207db01265fe0b Mon Sep 17 00:00:00 2001 From: Ian Lance Taylor Date: Wed, 10 Mar 2021 19:38:21 -0800 Subject: compiler: create temporaries for heap variables The compiler generally doesn't create a temporary for an expression that is a variable, because it's normally valid to simply reload the value from the variable. However, if the variable is in the heap, then loading the value is a pointer indirection. The process of creating GCC IR can cause the variable load and the pointer indirection to be split, such that the second evaluation only does the pointer indirection. If there are conditionals in between the two uses, this can cause the second use to load the pointer from an uninitialized register. Avoid this by introducing a new Expression method that returns whether it is safe to evaluate an expression multiple times, and use it everywhere. The test case is https://golang.org/cl/300789. Fixes golang/go#44383 Reviewed-on: https://go-review.googlesource.com/c/gofrontend/+/300809 --- gcc/go/gofrontend/MERGE | 2 +- gcc/go/gofrontend/expressions.cc | 136 +++++++++++++++++++++++++-------------- gcc/go/gofrontend/expressions.h | 5 ++ gcc/go/gofrontend/gogo.cc | 2 +- gcc/go/gofrontend/statements.cc | 6 +- gcc/go/gofrontend/wb.cc | 4 +- 6 files changed, 99 insertions(+), 56 deletions(-) (limited to 'gcc/go') diff --git a/gcc/go/gofrontend/MERGE b/gcc/go/gofrontend/MERGE index 5b45f03..58c881a 100644 --- a/gcc/go/gofrontend/MERGE +++ b/gcc/go/gofrontend/MERGE @@ -1,4 +1,4 @@ -93380a9126e76b71fa208e62c31c7914084c0e37 +bf35249a7c752836741b1cab43a312f87916fcb0 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/expressions.cc b/gcc/go/gofrontend/expressions.cc index 17b4cfd..101cbe7 100644 --- a/gcc/go/gofrontend/expressions.cc +++ b/gcc/go/gofrontend/expressions.cc @@ -526,7 +526,7 @@ Expression::convert_interface_to_interface(Type *lhs_type, Expression* rhs, // method table. // We are going to evaluate RHS multiple times. - go_assert(rhs->is_variable()); + go_assert(rhs->is_multi_eval_safe()); // Get the type descriptor for the right hand side. This will be // NULL for a nil interface. @@ -569,7 +569,7 @@ Expression::convert_interface_to_type(Gogo* gogo, Type *lhs_type, Expression* rh Location location) { // We are going to evaluate RHS multiple times. - go_assert(rhs->is_variable()); + go_assert(rhs->is_multi_eval_safe()); // Build an expression to check that the type is valid. It will // panic with an appropriate runtime type error if the type is not @@ -707,8 +707,8 @@ Expression::check_bounds(Expression* val, Operator op, Expression* bound, Statement_inserter* inserter, Location loc) { - go_assert(val->is_variable() || val->is_constant()); - go_assert(bound->is_variable() || bound->is_constant()); + go_assert(val->is_multi_eval_safe()); + go_assert(bound->is_multi_eval_safe()); Type* int_type = Type::lookup_integer_type("int"); int int_type_size = int_type->integer_type()->bits(); @@ -3976,7 +3976,7 @@ Type_conversion_expression::do_flatten(Gogo*, Named_object*, if (((this->type()->is_string_type() && this->expr_->type()->is_slice_type()) || this->expr_->type()->interface_type() != NULL) - && !this->expr_->is_variable()) + && !this->expr_->is_multi_eval_safe()) { Temporary_statement* temp = Statement::make_temporary(NULL, this->expr_, this->location()); @@ -4264,7 +4264,7 @@ Type_conversion_expression::do_get_backend(Translate_context* context) Array_type* a = expr_type->array_type(); Type* e = a->element_type()->forwarded(); go_assert(e->integer_type() != NULL); - go_assert(this->expr_->is_variable()); + go_assert(this->expr_->is_multi_eval_safe()); Expression* buf; if (this->no_escape_ && !this->no_copy_) @@ -4711,7 +4711,7 @@ Unary_expression::do_flatten(Gogo* gogo, Named_object*, Location location = this->location(); if (this->op_ == OPERATOR_MULT - && !this->expr_->is_variable()) + && !this->expr_->is_multi_eval_safe()) { go_assert(this->expr_->type()->points_to() != NULL); switch (this->requires_nil_check(gogo)) @@ -4731,7 +4731,7 @@ Unary_expression::do_flatten(Gogo* gogo, Named_object*, } } - if (this->create_temp_ && !this->expr_->is_variable()) + if (this->create_temp_ && !this->expr_->is_multi_eval_safe()) { Temporary_statement* temp = Statement::make_temporary(NULL, this->expr_, location); @@ -5326,7 +5326,7 @@ Unary_expression::do_get_backend(Translate_context* context) bexpr = gogo->backend()->var_expression(decl, loc); } - go_assert(!this->create_temp_ || this->expr_->is_variable()); + go_assert(!this->create_temp_ || this->expr_->is_multi_eval_safe()); ret = gogo->backend()->address_expression(bexpr, loc); break; @@ -5347,7 +5347,7 @@ Unary_expression::do_get_backend(Translate_context* context) } case NIL_CHECK_NEEDED: { - go_assert(this->expr_->is_variable()); + go_assert(this->expr_->is_multi_eval_safe()); // If we're nil-checking the result of a set-and-use-temporary // expression, then pick out the target temp and use that @@ -6496,13 +6496,13 @@ Binary_expression::do_flatten(Gogo* gogo, Named_object*, && (gogo->check_divide_by_zero() || gogo->check_divide_overflow())) || is_string_op) { - if (!this->left_->is_variable() && !this->left_->is_constant()) + if (!this->left_->is_multi_eval_safe()) { temp = Statement::make_temporary(NULL, this->left_, loc); inserter->insert(temp); this->left_ = Expression::make_temporary_reference(temp, loc); } - if (!this->right_->is_variable() && !this->right_->is_constant()) + if (!this->right_->is_multi_eval_safe()) { temp = Statement::make_temporary(NULL, this->right_, loc); @@ -7478,8 +7478,8 @@ Expression::comparison(Translate_context* context, Type* result_type, if (left_type->is_string_type() && right_type->is_string_type()) { - go_assert(left->is_variable() || left->is_constant()); - go_assert(right->is_variable() || right->is_constant()); + go_assert(left->is_multi_eval_safe()); + go_assert(right->is_multi_eval_safe()); if (op == OPERATOR_EQEQ || op == OPERATOR_NOTEQ) { @@ -8078,7 +8078,7 @@ Bound_method_expression::do_flatten(Gogo* gogo, Named_object*, // we are going to do nil checks below, but it's easy enough to // always do it. Expression* expr = this->expr_; - if (!expr->is_variable()) + if (!expr->is_multi_eval_safe()) { Temporary_statement* etemp = Statement::make_temporary(NULL, expr, loc); inserter->insert(etemp); @@ -8419,7 +8419,7 @@ Builtin_call_expression::do_lower(Gogo*, Named_object* function, pa != this->args()->end(); ++pa) { - if (!(*pa)->is_variable() && !(*pa)->is_constant()) + if (!(*pa)->is_multi_eval_safe()) { Temporary_statement* temp = Statement::make_temporary(NULL, *pa, loc); @@ -8470,7 +8470,7 @@ Builtin_call_expression::do_flatten(Gogo* gogo, Named_object* function, Expression* zero = Expression::make_integer_ul(0, NULL, loc); *pa = Expression::make_slice_value(at, nil, zero, zero, loc); } - if (!(*pa)->is_variable()) + if (!(*pa)->is_multi_eval_safe()) { Temporary_statement* temp = Statement::make_temporary(NULL, *pa, loc); @@ -8484,8 +8484,8 @@ Builtin_call_expression::do_flatten(Gogo* gogo, Named_object* function, go_assert(args != NULL && args->size() == 2); Expression* arg1 = args->front(); Expression* arg2 = args->back(); - go_assert(arg1->is_variable()); - go_assert(arg2->is_variable()); + go_assert(arg1->is_multi_eval_safe()); + go_assert(arg2->is_multi_eval_safe()); bool arg2_is_string = arg2->type()->is_string_type(); Expression* ret; @@ -8573,7 +8573,8 @@ Builtin_call_expression::do_flatten(Gogo* gogo, Named_object* function, pa != this->args()->end(); ++pa) { - if (!(*pa)->is_variable() && (*pa)->type()->interface_type() != NULL) + if (!(*pa)->is_multi_eval_safe() + && (*pa)->type()->interface_type() != NULL) { Temporary_statement* temp = Statement::make_temporary(NULL, *pa, loc); @@ -8587,7 +8588,7 @@ Builtin_call_expression::do_flatten(Gogo* gogo, Named_object* function, case BUILTIN_CAP: { Expression_list::iterator pa = this->args()->begin(); - if (!(*pa)->is_variable() + if (!(*pa)->is_multi_eval_safe() && ((*pa)->type()->map_type() != NULL || (*pa)->type()->channel_type() != NULL)) { @@ -9024,7 +9025,7 @@ Builtin_call_expression::flatten_append(Gogo* gogo, Named_object* function, Expression_list::const_iterator pa = args->begin(); for (++pa; pa != args->end(); ++pa) { - if ((*pa)->is_variable()) + if ((*pa)->is_multi_eval_safe()) add->push_back(*pa); else { @@ -11235,7 +11236,7 @@ Call_expression::do_flatten(Gogo* gogo, Named_object*, { Location loc = (*pa)->location(); Expression* arg = *pa; - if (!arg->is_variable()) + if (!arg->is_multi_eval_safe()) { Temporary_statement *temp = Statement::make_temporary(NULL, arg, loc); @@ -11457,7 +11458,7 @@ Call_expression::intrinsify(Gogo* gogo, && this->args_ != NULL && this->args_->size() == 1) { Expression* arg = this->args_->front(); - if (!arg->is_variable()) + if (!arg->is_multi_eval_safe()) { Temporary_statement* ts = Statement::make_temporary(uint32_type, arg, loc); inserter->insert(ts); @@ -11476,7 +11477,7 @@ Call_expression::intrinsify(Gogo* gogo, && this->args_ != NULL && this->args_->size() == 1) { Expression* arg = this->args_->front(); - if (!arg->is_variable()) + if (!arg->is_multi_eval_safe()) { Temporary_statement* ts = Statement::make_temporary(uint64_type, arg, loc); inserter->insert(ts); @@ -11526,7 +11527,7 @@ Call_expression::intrinsify(Gogo* gogo, && this->args_ != NULL && this->args_->size() == 1) { Expression* arg = this->args_->front(); - if (!arg->is_variable()) + if (!arg->is_multi_eval_safe()) { Temporary_statement* ts = Statement::make_temporary(uint32_type, arg, loc); inserter->insert(ts); @@ -11549,7 +11550,7 @@ Call_expression::intrinsify(Gogo* gogo, && this->args_ != NULL && this->args_->size() == 1) { Expression* arg = this->args_->front(); - if (!arg->is_variable()) + if (!arg->is_multi_eval_safe()) { Temporary_statement* ts = Statement::make_temporary(uint64_type, arg, loc); inserter->insert(ts); @@ -13087,14 +13088,14 @@ Array_index_expression::do_flatten(Gogo* gogo, Named_object*, } Temporary_statement* temp; - if (array_type->is_slice_type() && !array->is_variable()) + if (array_type->is_slice_type() && !array->is_multi_eval_safe()) { temp = Statement::make_temporary(NULL, array, loc); inserter->insert(temp); this->array_ = Expression::make_temporary_reference(temp, loc); array = this->array_; } - if (!start->is_variable() && !start->is_constant()) + if (!start->is_multi_eval_safe()) { temp = Statement::make_temporary(NULL, start, loc); inserter->insert(temp); @@ -13103,15 +13104,14 @@ Array_index_expression::do_flatten(Gogo* gogo, Named_object*, } if (end != NULL && !end->is_nil_expression() - && !end->is_variable() - && !end->is_constant()) + && !end->is_multi_eval_safe()) { temp = Statement::make_temporary(NULL, end, loc); inserter->insert(temp); this->end_ = Expression::make_temporary_reference(temp, loc); end = this->end_; } - if (cap != NULL && !cap->is_variable() && !cap->is_constant()) + if (cap != NULL && !cap->is_multi_eval_safe()) { temp = Statement::make_temporary(NULL, cap, loc); inserter->insert(temp); @@ -13270,7 +13270,8 @@ Array_index_expression::do_get_backend(Translate_context* context) go_assert(this->array_->type()->is_error()); return context->backend()->error_expression(); } - go_assert(!array_type->is_slice_type() || this->array_->is_variable()); + go_assert(!array_type->is_slice_type() + || this->array_->is_multi_eval_safe()); Location loc = this->location(); Gogo* gogo = context->gogo(); @@ -13484,14 +13485,14 @@ String_index_expression::do_flatten(Gogo*, Named_object*, } Temporary_statement* temp; - if (!string->is_variable()) + if (!string->is_multi_eval_safe()) { temp = Statement::make_temporary(NULL, string, loc); inserter->insert(temp); this->string_ = Expression::make_temporary_reference(temp, loc); string = this->string_; } - if (!start->is_variable()) + if (!start->is_multi_eval_safe()) { temp = Statement::make_temporary(NULL, start, loc); inserter->insert(temp); @@ -13500,7 +13501,7 @@ String_index_expression::do_flatten(Gogo*, Named_object*, } if (end != NULL && !end->is_nil_expression() - && !end->is_variable()) + && !end->is_multi_eval_safe()) { temp = Statement::make_temporary(NULL, end, loc); inserter->insert(temp); @@ -13659,8 +13660,8 @@ String_index_expression::do_get_backend(Translate_context* context) return context->backend()->error_expression(); } - go_assert(this->string_->is_variable()); - go_assert(this->start_->is_variable()); + go_assert(this->string_->is_multi_eval_safe()); + go_assert(this->start_->is_multi_eval_safe()); Expression* start = Expression::make_cast(int_type, this->start_, loc); Bfunction* bfn = context->function()->func_value()->get_decl(); @@ -13685,7 +13686,7 @@ String_index_expression::do_get_backend(Translate_context* context) end = length; else { - go_assert(this->end_->is_variable()); + go_assert(this->end_->is_multi_eval_safe()); end = Expression::make_cast(int_type, this->end_, loc); } @@ -13815,7 +13816,7 @@ Map_index_expression::do_flatten(Gogo* gogo, Named_object*, NULL)) { if (this->index_->type()->interface_type() != NULL - && !this->index_->is_variable()) + && !this->index_->is_multi_eval_safe()) { Temporary_statement* temp = Statement::make_temporary(NULL, this->index_, loc); @@ -13826,7 +13827,7 @@ Map_index_expression::do_flatten(Gogo* gogo, Named_object*, this->index_, loc); } - if (!this->index_->is_variable()) + if (!this->index_->is_multi_eval_safe()) { Temporary_statement* temp = Statement::make_temporary(NULL, this->index_, loc); @@ -13839,7 +13840,7 @@ Map_index_expression::do_flatten(Gogo* gogo, Named_object*, if (this->value_pointer_->is_error_expression() || this->value_pointer_->type()->is_error_type()) return Expression::make_error(loc); - if (!this->value_pointer_->is_variable()) + if (!this->value_pointer_->is_multi_eval_safe()) { Temporary_statement* temp = Statement::make_temporary(NULL, this->value_pointer_, loc); @@ -13923,7 +13924,7 @@ Map_index_expression::do_get_backend(Translate_context* context) } go_assert(this->value_pointer_ != NULL - && this->value_pointer_->is_variable()); + && this->value_pointer_->is_multi_eval_safe()); Expression* val = Expression::make_dereference(this->value_pointer_, NIL_CHECK_NOT_NEEDED, @@ -14268,7 +14269,7 @@ Interface_field_reference_expression::do_flatten(Gogo*, Named_object*, return Expression::make_error(this->location()); } - if (!this->expr_->is_variable()) + if (!this->expr_->is_multi_eval_safe()) { Temporary_statement* temp = Statement::make_temporary(NULL, this->expr_, this->location()); @@ -15165,7 +15166,7 @@ Struct_construction_expression::do_flatten(Gogo*, Named_object*, go_assert(saw_errors()); return Expression::make_error(loc); } - if (!(*pv)->is_variable()) + if (!(*pv)->is_multi_eval_safe()) { Temporary_statement* temp = Statement::make_temporary(NULL, *pv, loc); @@ -15471,7 +15472,7 @@ Array_construction_expression::do_flatten(Gogo*, Named_object*, go_assert(saw_errors()); return Expression::make_error(loc); } - if (!(*pv)->is_variable()) + if (!(*pv)->is_multi_eval_safe()) { Temporary_statement* temp = Statement::make_temporary(NULL, *pv, loc); @@ -15914,7 +15915,8 @@ Map_construction_expression::do_flatten(Gogo* gogo, Named_object*, go_assert(saw_errors()); return Expression::make_error(loc); } - if (key->type()->interface_type() != NULL && !key->is_variable()) + if (key->type()->interface_type() != NULL + && !key->is_multi_eval_safe()) { Temporary_statement* temp = Statement::make_temporary(NULL, key, loc); @@ -15930,7 +15932,8 @@ Map_construction_expression::do_flatten(Gogo* gogo, Named_object*, go_assert(saw_errors()); return Expression::make_error(loc); } - if (val->type()->interface_type() != NULL && !val->is_variable()) + if (val->type()->interface_type() != NULL + && !val->is_multi_eval_safe()) { Temporary_statement* temp = Statement::make_temporary(NULL, val, loc); @@ -17037,6 +17040,41 @@ Expression::is_local_variable() const || (no->is_variable() && !no->var_value()->is_global())); } +// Return true if multiple evaluations are OK. + +bool +Expression::is_multi_eval_safe() +{ + switch (this->classification_) + { + case EXPRESSION_VAR_REFERENCE: + { + // A variable is a simple reference if not stored in the heap. + const Named_object* no = this->var_expression()->named_object(); + if (no->is_variable()) + return !no->var_value()->is_in_heap(); + else if (no->is_result_variable()) + return !no->result_var_value()->is_in_heap(); + else + go_unreachable(); + } + + case EXPRESSION_TEMPORARY_REFERENCE: + return true; + + default: + break; + } + + if (!this->is_constant()) + return false; + + // Only numeric and boolean constants are really multi-evaluation + // safe. We don't want multiple copies of string constants. + Type* type = this->type(); + return type->is_numeric_type() || type->is_boolean_type(); +} + const Named_object* Expression::named_constant() const { @@ -17070,7 +17108,7 @@ Type_guard_expression::do_flatten(Gogo*, Named_object*, return Expression::make_error(this->location()); } - if (!this->expr_->is_variable()) + if (!this->expr_->is_multi_eval_safe()) { Temporary_statement* temp = Statement::make_temporary(NULL, this->expr_, this->location()); diff --git a/gcc/go/gofrontend/expressions.h b/gcc/go/gofrontend/expressions.h index 712f687..e3747cc 100644 --- a/gcc/go/gofrontend/expressions.h +++ b/gcc/go/gofrontend/expressions.h @@ -916,6 +916,11 @@ class Expression bool is_local_variable() const; + // Return true if multiple evaluations of this expression are OK. + // This is true for simple variable references and constants. + bool + is_multi_eval_safe(); + // Return true if two expressions refer to the same variable or // struct field. static bool diff --git a/gcc/go/gofrontend/gogo.cc b/gcc/go/gofrontend/gogo.cc index 9d4150e..93b54fd 100644 --- a/gcc/go/gofrontend/gogo.cc +++ b/gcc/go/gofrontend/gogo.cc @@ -7579,7 +7579,7 @@ Variable::flatten_init_expression(Gogo* gogo, Named_object* function, Type::COMPARE_ERRORS | Type::COMPARE_TAGS, NULL) && this->init_->type()->interface_type() != NULL - && !this->init_->is_variable()) + && !this->init_->is_multi_eval_safe()) { Temporary_statement* temp = Statement::make_temporary(NULL, this->init_, this->location_); diff --git a/gcc/go/gofrontend/statements.cc b/gcc/go/gofrontend/statements.cc index da0e084..7ad7339 100644 --- a/gcc/go/gofrontend/statements.cc +++ b/gcc/go/gofrontend/statements.cc @@ -588,7 +588,7 @@ Temporary_statement::do_flatten(Gogo*, Named_object*, Block*, Type::COMPARE_ERRORS | Type::COMPARE_TAGS, NULL) && this->init_->type()->interface_type() != NULL - && !this->init_->is_variable()) + && !this->init_->is_multi_eval_safe()) { Temporary_statement *temp = Statement::make_temporary(NULL, this->init_, this->location()); @@ -1125,7 +1125,7 @@ Assignment_statement::do_flatten(Gogo*, Named_object*, Block*, Type::COMPARE_ERRORS | Type::COMPARE_TAGS, NULL) && this->rhs_->type()->interface_type() != NULL - && !this->rhs_->is_variable()) + && !this->rhs_->is_multi_eval_safe()) { Temporary_statement* temp = Statement::make_temporary(NULL, this->rhs_, this->location()); @@ -5116,7 +5116,7 @@ Send_statement::do_flatten(Gogo*, Named_object*, Block*, Type::COMPARE_ERRORS | Type::COMPARE_TAGS, NULL) && this->val_->type()->interface_type() != NULL - && !this->val_->is_variable()) + && !this->val_->is_multi_eval_safe()) { Temporary_statement* temp = Statement::make_temporary(NULL, this->val_, this->location()); diff --git a/gcc/go/gofrontend/wb.cc b/gcc/go/gofrontend/wb.cc index ac1f0a1..104c5db 100644 --- a/gcc/go/gofrontend/wb.cc +++ b/gcc/go/gofrontend/wb.cc @@ -883,7 +883,7 @@ Gogo::assign_with_write_barrier(Function* function, Block* enclosing, Type::COMPARE_ERRORS | Type::COMPARE_TAGS, NULL) && rhs->type()->interface_type() != NULL - && !rhs->is_variable()) + && !rhs->is_multi_eval_safe()) { // May need a temporary for interface conversion. Temporary_statement* temp = Statement::make_temporary(NULL, rhs, loc); @@ -892,7 +892,7 @@ Gogo::assign_with_write_barrier(Function* function, Block* enclosing, } rhs = Expression::convert_for_assignment(this, type, rhs, loc); Temporary_statement* rhs_temp = NULL; - if (!rhs->is_variable() && !rhs->is_constant()) + if (!rhs->is_multi_eval_safe()) { rhs_temp = Statement::make_temporary(NULL, rhs, loc); inserter->insert(rhs_temp); -- cgit v1.1