From 1196b60f8fc5a169e01ac859712013a4d3d8de96 Mon Sep 17 00:00:00 2001 From: Ian Lance Taylor Date: Fri, 6 Aug 2021 18:04:43 -0700 Subject: compiler: store pointers to go:notinheap types indirectly This is the gofrontend version of https://golang.org/cl/264480. For golang/go#42076 Reviewed-on: https://go-review.googlesource.com/c/gofrontend/+/340609 --- gcc/go/gofrontend/MERGE | 2 +- gcc/go/gofrontend/expressions.cc | 30 ++++++++++++---- gcc/go/gofrontend/types.cc | 75 +++++++++++++++++++++++++++++++++++----- gcc/go/gofrontend/types.h | 3 +- 4 files changed, 92 insertions(+), 18 deletions(-) (limited to 'gcc/go') diff --git a/gcc/go/gofrontend/MERGE b/gcc/go/gofrontend/MERGE index be092de..539d886 100644 --- a/gcc/go/gofrontend/MERGE +++ b/gcc/go/gofrontend/MERGE @@ -1,4 +1,4 @@ -7e092d2cc5af7648036496485b639f2c9db2f2d8 +5edbb624b2595d644eb6842c952a292c41f7d6fa 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 67917da..8d4d168 100644 --- a/gcc/go/gofrontend/expressions.cc +++ b/gcc/go/gofrontend/expressions.cc @@ -408,7 +408,14 @@ Expression::convert_type_to_interface(Type* lhs_type, Expression* rhs, { // We are assigning a non-pointer value to the interface; the // interface gets a copy of the value in the heap if it escapes. - if (rhs->is_constant()) + + // An exception is &global if global is notinheap, which is a + // pointer value but not a direct-iface type and we can't simply + // take its address. + bool is_address = (rhs->unary_expression() != NULL + && rhs->unary_expression()->op() == OPERATOR_AND); + + if (rhs->is_constant() && !is_address) obj = Expression::make_unary(OPERATOR_AND, rhs, location); else { @@ -11331,6 +11338,7 @@ Call_expression::do_lower(Gogo* gogo, Named_object* function, // We always pass a pointer when calling a method, except for // direct interface types when calling a value method. if (!first_arg->type()->is_error() + && first_arg->type()->points_to() == NULL && !first_arg->type()->is_direct_iface_type()) { first_arg = Expression::make_unary(OPERATOR_AND, first_arg, loc); @@ -18630,12 +18638,20 @@ Interface_mtable_expression::do_get_backend(Translate_context* context) else m = st->method_function(p->name(), &is_ambiguous); go_assert(m != NULL); - Named_object* no = - (this->is_pointer_ - && this->type_->is_direct_iface_type() - && m->is_value_method() - ? m->iface_stub_object() - : m->named_object()); + + // See the comment in Type::method_constructor. + bool use_direct_iface_stub = false; + if (m->is_value_method() + && this->is_pointer_ + && this->type_->is_direct_iface_type()) + use_direct_iface_stub = true; + if (!m->is_value_method() + && this->is_pointer_ + && !this->type_->in_heap()) + use_direct_iface_stub = true; + Named_object* no = (use_direct_iface_stub + ? m->iface_stub_object() + : m->named_object()); go_assert(no->is_function() || no->is_function_declaration()); diff --git a/gcc/go/gofrontend/types.cc b/gcc/go/gofrontend/types.cc index 0c44186..e76600d 100644 --- a/gcc/go/gofrontend/types.cc +++ b/gcc/go/gofrontend/types.cc @@ -2464,8 +2464,16 @@ Type::is_direct_iface_type() const bool Type::is_direct_iface_type_helper(Unordered_set(const Type*)* visited) const { - if (this->points_to() != NULL - || this->channel_type() != NULL + if (this->points_to() != NULL) + { + // Pointers to notinheap types must be stored indirectly. See + // https://golang.org/issue/42076. + if (!this->points_to()->in_heap()) + return false; + return true; + } + + if (this->channel_type() != NULL || this->function_type() != NULL || this->map_type() != NULL) return true; @@ -3597,10 +3605,36 @@ Type::method_constructor(Gogo*, Type* method_type, vals->push_back(Expression::make_unary(OPERATOR_AND, s, bloc)); } - bool use_direct_iface_stub = - this->points_to() != NULL - && this->points_to()->is_direct_iface_type() - && m->is_value_method(); + // The direct_iface_stub dereferences the value stored in the + // interface when calling the method. + // + // We need this for a value method if this type is a pointer to a + // direct-iface type. For example, if we have "type C chan int" and M + // is a value method on C, then since a channel is a direct-iface type + // M expects a value of type C. We are generating the method table + // for *C, so the value stored in the interface is *C. We have to + // call the direct-iface stub to dereference *C to get C to pass to M. + // + // We also need this for a pointer method if the pointer itself is not + // a direct-iface type, as arises for notinheap types. In this case + // we have "type NIH ..." where NIH is go:notinheap. Since NIH is + // notinheap, *NIH is a pointer type that is not a direct-iface type, + // so the value stored in the interface is actually **NIH. The method + // expects *NIH, so we have to call the direct-iface stub to + // dereference **NIH to get *NIH to pass to M. (This case doesn't + // arise for value methods because pointer types can't have methods, + // so there is no such thing as a value method for type *NIH.) + + bool use_direct_iface_stub = false; + if (m->is_value_method() + && this->points_to() != NULL + && this->points_to()->is_direct_iface_type()) + use_direct_iface_stub = true; + if (!m->is_value_method() + && this->points_to() != NULL + && !this->is_direct_iface_type()) + use_direct_iface_stub = true; + Named_object* no = (use_direct_iface_stub ? m->iface_stub_object() : (m->needs_stub_method() @@ -10902,6 +10936,20 @@ Named_type::do_needs_key_update() return ret; } +// Return whether this type is permitted in the heap. +bool +Named_type::do_in_heap() const +{ + if (!this->in_heap_) + return false; + if (this->seen_) + return true; + this->seen_ = true; + bool ret = this->type_->in_heap(); + this->seen_ = false; + return ret; +} + // Return a hash code. This is used for method lookup. We simply // hash on the name itself. @@ -11434,7 +11482,7 @@ Type::finalize_methods(Gogo* gogo, const Type* type, Location location, *all_methods = NULL; } Type::build_stub_methods(gogo, type, *all_methods, location); - if (type->is_direct_iface_type()) + if (type->is_direct_iface_type() || !type->in_heap()) Type::build_direct_iface_stub_methods(gogo, type, *all_methods, location); } @@ -11814,12 +11862,23 @@ Type::build_direct_iface_stub_methods(Gogo* gogo, const Type* type, if (methods == NULL) return; + bool is_direct_iface = type->is_direct_iface_type(); + bool in_heap = type->in_heap(); for (Methods::const_iterator p = methods->begin(); p != methods->end(); ++p) { Method* m = p->second; - if (!m->is_value_method()) + + // We need a direct-iface stub for a value method for a + // direct-iface type, and for a pointer method for a not-in-heap + // type. + bool need_stub = false; + if (is_direct_iface && m->is_value_method()) + need_stub = true; + if (!in_heap && !m->is_value_method()) + need_stub = true; + if (!need_stub) continue; Type* receiver_type = const_cast(type); diff --git a/gcc/go/gofrontend/types.h b/gcc/go/gofrontend/types.h index f2880f9..ca1ab49 100644 --- a/gcc/go/gofrontend/types.h +++ b/gcc/go/gofrontend/types.h @@ -3605,8 +3605,7 @@ class Named_type : public Type do_needs_key_update(); bool - do_in_heap() const - { return this->in_heap_ && this->type_->in_heap(); } + do_in_heap() const; unsigned int do_hash_for_method(Gogo*, int) const; -- cgit v1.1