diff options
author | Ian Lance Taylor <ian@gcc.gnu.org> | 2012-01-11 21:20:41 +0000 |
---|---|---|
committer | Ian Lance Taylor <ian@gcc.gnu.org> | 2012-01-11 21:20:41 +0000 |
commit | 11d8e1a4cbf1a47eb3fe0a673cff6b820ffdda2c (patch) | |
tree | 42ef542db68c33e13f4e261277beb7a8be2ffbdc | |
parent | ef079a56e47d898ae7ad347c5d84a4ccfd9cba91 (diff) | |
download | gcc-11d8e1a4cbf1a47eb3fe0a673cff6b820ffdda2c.zip gcc-11d8e1a4cbf1a47eb3fe0a673cff6b820ffdda2c.tar.gz gcc-11d8e1a4cbf1a47eb3fe0a673cff6b820ffdda2c.tar.bz2 |
compiler: Don't use memcmp for structs/arrays with padding.
From-SVN: r183109
-rw-r--r-- | gcc/go/gofrontend/expressions.cc | 23 | ||||
-rw-r--r-- | gcc/go/gofrontend/gogo.cc | 67 | ||||
-rw-r--r-- | gcc/go/gofrontend/types.cc | 147 | ||||
-rw-r--r-- | gcc/go/gofrontend/types.h | 50 |
4 files changed, 170 insertions, 117 deletions
diff --git a/gcc/go/gofrontend/expressions.cc b/gcc/go/gofrontend/expressions.cc index 337b949..7166a56 100644 --- a/gcc/go/gofrontend/expressions.cc +++ b/gcc/go/gofrontend/expressions.cc @@ -5844,7 +5844,7 @@ Binary_expression::lower_struct_comparison(Gogo* gogo, // See if we can compare using memcmp. As a heuristic, we use // memcmp rather than field references and comparisons if there are // more than two fields. - if (st->compare_is_identity() && st->total_field_count() > 2) + if (st->compare_is_identity(gogo) && st->total_field_count() > 2) return this->lower_compare_to_memcmp(gogo, inserter); Location loc = this->location(); @@ -5919,7 +5919,7 @@ Binary_expression::lower_array_comparison(Gogo* gogo, // Call memcmp directly if possible. This may let the middle-end // optimize the call. - if (at->compare_is_identity()) + if (at->compare_is_identity(gogo)) return this->lower_compare_to_memcmp(gogo, inserter); // Call the array comparison function. @@ -12966,10 +12966,10 @@ class Composite_literal_expression : public Parser_expression lower_struct(Gogo*, Type*); Expression* - lower_array(Gogo*, Type*); + lower_array(Type*); Expression* - make_array(Gogo*, Type*, Expression_list*); + make_array(Type*, Expression_list*); Expression* lower_map(Gogo*, Named_object*, Statement_inserter*, Type*); @@ -13036,7 +13036,7 @@ Composite_literal_expression::do_lower(Gogo* gogo, Named_object* function, else if (type->struct_type() != NULL) ret = this->lower_struct(gogo, type); else if (type->array_type() != NULL) - ret = this->lower_array(gogo, type); + ret = this->lower_array(type); else if (type->map_type() != NULL) ret = this->lower_map(gogo, function, inserter, type); else @@ -13249,11 +13249,11 @@ Composite_literal_expression::lower_struct(Gogo* gogo, Type* type) // Lower an array composite literal. Expression* -Composite_literal_expression::lower_array(Gogo* gogo, Type* type) +Composite_literal_expression::lower_array(Type* type) { Location location = this->location(); if (this->vals_ == NULL || !this->has_keys_) - return this->make_array(gogo, type, this->vals_); + return this->make_array(type, this->vals_); std::vector<Expression*> vals; vals.reserve(this->vals_->size()); @@ -13353,15 +13353,14 @@ Composite_literal_expression::lower_array(Gogo* gogo, Type* type) for (size_t i = 0; i < size; ++i) list->push_back(vals[i]); - return this->make_array(gogo, type, list); + return this->make_array(type, list); } // Actually build the array composite literal. This handles // [...]{...}. Expression* -Composite_literal_expression::make_array(Gogo* gogo, Type* type, - Expression_list* vals) +Composite_literal_expression::make_array(Type* type, Expression_list* vals) { Location location = this->location(); Array_type* at = type->array_type(); @@ -13373,10 +13372,6 @@ Composite_literal_expression::make_array(Gogo* gogo, Type* type, Expression* elen = Expression::make_integer(&vlen, NULL, location); mpz_clear(vlen); at = Type::make_array_type(at->element_type(), elen); - - // This is after the finalize_methods pass, so run that now. - at->finalize_methods(gogo); - type = at; } if (at->length() != NULL) diff --git a/gcc/go/gofrontend/gogo.cc b/gcc/go/gofrontend/gogo.cc index eb24947..f6f22d4 100644 --- a/gcc/go/gofrontend/gogo.cc +++ b/gcc/go/gofrontend/gogo.cc @@ -1149,11 +1149,74 @@ Gogo::queue_specific_type_function(Type* type, Named_type* name, this->specific_type_functions_.push_back(tsf); } +// Look for types which need specific hash or equality functions. + +class Specific_type_functions : public Traverse +{ + public: + Specific_type_functions(Gogo* gogo) + : Traverse(traverse_types), + gogo_(gogo) + { } + + int + type(Type*); + + private: + Gogo* gogo_; +}; + +int +Specific_type_functions::type(Type* t) +{ + Named_object* hash_fn; + Named_object* equal_fn; + switch (t->classification()) + { + case Type::TYPE_NAMED: + { + if (!t->compare_is_identity(this->gogo_) && t->is_comparable()) + t->type_functions(this->gogo_, t->named_type(), NULL, NULL, &hash_fn, + &equal_fn); + + // If this is a struct type, we don't want to make functions + // for the unnamed struct. + Type* rt = t->named_type()->real_type(); + if (rt->struct_type() == NULL) + { + if (Type::traverse(rt, this) == TRAVERSE_EXIT) + return TRAVERSE_EXIT; + } + else + { + if (rt->struct_type()->traverse_field_types(this) == TRAVERSE_EXIT) + return TRAVERSE_EXIT; + } + + return TRAVERSE_SKIP_COMPONENTS; + } + + case Type::TYPE_STRUCT: + case Type::TYPE_ARRAY: + if (!t->compare_is_identity(this->gogo_) && t->is_comparable()) + t->type_functions(this->gogo_, NULL, NULL, NULL, &hash_fn, &equal_fn); + break; + + default: + break; + } + + return TRAVERSE_CONTINUE; +} + // Write out type specific functions. void Gogo::write_specific_type_functions() { + Specific_type_functions stf(this); + this->traverse(&stf); + while (!this->specific_type_functions_.empty()) { Specific_type_function* tsf = this->specific_type_functions_.back(); @@ -1520,10 +1583,6 @@ Finalize_methods::type(Type* t) t->struct_type()->finalize_methods(this->gogo_); break; - case Type::TYPE_ARRAY: - t->array_type()->finalize_methods(this->gogo_); - break; - default: break; } diff --git a/gcc/go/gofrontend/types.cc b/gcc/go/gofrontend/types.cc index 21dac51..c8600ac 100644 --- a/gcc/go/gofrontend/types.cc +++ b/gcc/go/gofrontend/types.cc @@ -571,7 +571,8 @@ Type::are_compatible_for_comparison(bool is_equality_op, const Type *t1, } else if (t1->array_type() != NULL) { - if (!t1->array_type()->element_type()->is_comparable()) + if (t1->array_type()->length()->is_nil_expression() + || !t1->array_type()->element_type()->is_comparable()) { if (reason != NULL) *reason = _("invalid comparison of non-comparable array"); @@ -1372,7 +1373,7 @@ Type::type_functions(Gogo* gogo, Named_type* name, Function_type* hash_fntype, const char* hash_fnname; const char* equal_fnname; - if (this->compare_is_identity()) + if (this->compare_is_identity(gogo)) { hash_fnname = "__go_type_hash_identity"; equal_fnname = "__go_type_equal_identity"; @@ -1963,7 +1964,7 @@ Type::mangled_name(Gogo* gogo) const // Return whether the backend size of the type is known. bool -Type::is_backend_type_size_known(Gogo* gogo) const +Type::is_backend_type_size_known(Gogo* gogo) { switch (this->classification_) { @@ -2014,11 +2015,14 @@ Type::is_backend_type_size_known(Gogo* gogo) const } case TYPE_NAMED: + // Begin converting this type to the backend representation. + // This will create a placeholder if necessary. + this->get_backend(gogo); return this->named_type()->is_named_backend_type_size_known(); case TYPE_FORWARD: { - const Forward_declaration_type* fdt = this->forward_declaration_type(); + Forward_declaration_type* fdt = this->forward_declaration_type(); return fdt->real_type()->is_backend_type_size_known(gogo); } @@ -2038,10 +2042,9 @@ Type::is_backend_type_size_known(Gogo* gogo) const bool Type::backend_type_size(Gogo* gogo, unsigned int *psize) { - Btype* btype = this->get_backend(gogo); if (!this->is_backend_type_size_known(gogo)) return false; - size_t size = gogo->backend()->type_size(btype); + size_t size = gogo->backend()->type_size(this->get_backend(gogo)); *psize = static_cast<unsigned int>(size); if (*psize != size) return false; @@ -2054,10 +2057,9 @@ Type::backend_type_size(Gogo* gogo, unsigned int *psize) bool Type::backend_type_align(Gogo* gogo, unsigned int *palign) { - Btype* btype = this->get_backend(gogo); if (!this->is_backend_type_size_known(gogo)) return false; - size_t align = gogo->backend()->type_alignment(btype); + size_t align = gogo->backend()->type_alignment(this->get_backend(gogo)); *palign = static_cast<unsigned int>(align); if (*palign != align) return false; @@ -2070,10 +2072,9 @@ Type::backend_type_align(Gogo* gogo, unsigned int *palign) bool Type::backend_type_field_align(Gogo* gogo, unsigned int *palign) { - Btype* btype = this->get_backend(gogo); if (!this->is_backend_type_size_known(gogo)) return false; - size_t a = gogo->backend()->type_field_alignment(btype); + size_t a = gogo->backend()->type_field_alignment(this->get_backend(gogo)); *palign = static_cast<unsigned int>(a); if (*palign != a) return false; @@ -2126,7 +2127,7 @@ class Error_type : public Type protected: bool - do_compare_is_identity() const + do_compare_is_identity(Gogo*) const { return false; } Btype* @@ -2164,7 +2165,7 @@ class Void_type : public Type protected: bool - do_compare_is_identity() const + do_compare_is_identity(Gogo*) const { return false; } Btype* @@ -2202,7 +2203,7 @@ class Boolean_type : public Type protected: bool - do_compare_is_identity() const + do_compare_is_identity(Gogo*) const { return true; } Btype* @@ -2793,7 +2794,7 @@ class Sink_type : public Type protected: bool - do_compare_is_identity() const + do_compare_is_identity(Gogo*) const { return false; } Btype* @@ -3705,7 +3706,7 @@ class Nil_type : public Type protected: bool - do_compare_is_identity() const + do_compare_is_identity(Gogo*) const { return false; } Btype* @@ -3756,7 +3757,7 @@ class Call_multiple_result_type : public Type } bool - do_compare_is_identity() const + do_compare_is_identity(Gogo*) const { return false; } Btype* @@ -4037,16 +4038,35 @@ Struct_type::struct_has_hidden_fields(const Named_type* within, // comparisons. bool -Struct_type::do_compare_is_identity() const +Struct_type::do_compare_is_identity(Gogo* gogo) const { const Struct_field_list* fields = this->fields_; if (fields == NULL) return true; + unsigned int offset = 0; for (Struct_field_list::const_iterator pf = fields->begin(); pf != fields->end(); ++pf) - if (!pf->type()->compare_is_identity()) - return false; + { + if (!pf->type()->compare_is_identity(gogo)) + return false; + + unsigned int field_align; + if (!pf->type()->backend_type_align(gogo, &field_align)) + return false; + if ((offset & (field_align - 1)) != 0) + { + // This struct has padding. We don't guarantee that that + // padding is zero-initialized for a stack variable, so we + // can't use memcmp to compare struct values. + return false; + } + + unsigned int field_size; + if (!pf->type()->backend_type_size(gogo, &field_size)) + return false; + offset += field_size; + } return true; } @@ -4231,7 +4251,7 @@ Struct_type::total_field_count() const pf != this->fields_->end(); ++pf) { - if (!pf->is_anonymous() || pf->type()->deref()->struct_type() == NULL) + if (!pf->is_anonymous() || pf->type()->struct_type() == NULL) ++ret; else ret += pf->type()->struct_type()->total_field_count(); @@ -4267,17 +4287,6 @@ Struct_type::is_unexported_local_field(Gogo* gogo, void Struct_type::finalize_methods(Gogo* gogo) { - // If this type needs explicit comparison and hash functions, create - // them now. It would be a bit better to do this only if the - // functions are needed, but they will be static so the backend can - // discard them if they are not used. - if (!this->compare_is_identity() && this->is_comparable()) - { - Named_object* hash_fn; - Named_object* equal_fn; - this->type_functions(gogo, NULL, NULL, NULL, &hash_fn, &equal_fn); - } - if (this->all_methods_ != NULL) return; Type::finalize_methods(gogo, this, this->location_, &this->all_methods_); @@ -4727,10 +4736,10 @@ bool Struct_type::backend_field_offset(Gogo* gogo, unsigned int index, unsigned int* poffset) { - Btype* btype = this->get_backend(gogo); if (!this->is_backend_type_size_known(gogo)) return false; - size_t offset = gogo->backend()->type_field_offset(btype, index); + size_t offset = gogo->backend()->type_field_offset(this->get_backend(gogo), + index); *poffset = static_cast<unsigned int>(offset); if (*poffset != offset) return false; @@ -4871,25 +4880,6 @@ Array_type::is_identical(const Array_type* t, bool errors_are_identical) const return false; } -// If this type needs explicit comparison and hash functions, create -// them now. It would be a bit better to do this only if the -// functions are needed, but they will be static so the backend can -// discard them if they are not used. - -void -Array_type::finalize_methods(Gogo* gogo) -{ - if (this->length_ != NULL - && !this->length_->is_nil_expression() - && !this->compare_is_identity() - && this->is_comparable()) - { - Named_object* hash_fn; - Named_object* equal_fn; - this->type_functions(gogo, NULL, NULL, NULL, &hash_fn, &equal_fn); - } -} - // Traversal. int @@ -4988,6 +4978,33 @@ Array_type::do_verify() return true; } +// Whether we can use memcmp to compare this array. + +bool +Array_type::do_compare_is_identity(Gogo* gogo) const +{ + if (this->length_ == NULL) + return false; + + // Check for [...], which indicates that this is not a real type. + if (this->length_->is_nil_expression()) + return false; + + if (!this->element_type_->compare_is_identity(gogo)) + return false; + + // If there is any padding, then we can't use memcmp. + unsigned int size; + unsigned int align; + if (!this->element_type_->backend_type_size(gogo, &size) + || !this->element_type_->backend_type_align(gogo, &align)) + return false; + if ((size & (align - 1)) != 0) + return false; + + return true; +} + // Array type hash code. unsigned int @@ -7272,20 +7289,6 @@ Named_type::is_unexported_local_method(Gogo* gogo, void Named_type::finalize_methods(Gogo* gogo) { - // If this type needs explicit comparison and hash functions, create - // them now. It would be a bit better to do this only if the - // functions are needed, but they will be static so the backend can - // discard them if they are not used. - if ((this->struct_type() != NULL - || (this->array_type() != NULL && !this->is_slice_type())) - && !this->compare_is_identity() - && this->is_comparable()) - { - Named_object* hash_fn; - Named_object* equal_fn; - this->type_functions(gogo, this, NULL, NULL, &hash_fn, &equal_fn); - } - if (this->all_methods_ != NULL) return; @@ -7539,13 +7542,15 @@ Named_type::do_has_pointer() const // function. bool -Named_type::do_compare_is_identity() const +Named_type::do_compare_is_identity(Gogo* gogo) const { - if (this->seen_) + // We don't use this->seen_ here because compare_is_identity may + // call base() later, and that will mess up if seen_ is set here. + if (this->seen_in_compare_is_identity_) return false; - this->seen_ = true; - bool ret = this->type_->compare_is_identity(); - this->seen_ = false; + this->seen_in_compare_is_identity_ = true; + bool ret = this->type_->compare_is_identity(gogo); + this->seen_in_compare_is_identity_ = false; return ret; } diff --git a/gcc/go/gofrontend/types.h b/gcc/go/gofrontend/types.h index 200164c..9de1b84 100644 --- a/gcc/go/gofrontend/types.h +++ b/gcc/go/gofrontend/types.h @@ -568,8 +568,8 @@ class Type // identity function which gets nothing but a pointer to the value // and a size. bool - compare_is_identity() const - { return this->do_compare_is_identity(); } + compare_is_identity(Gogo* gogo) const + { return this->do_compare_is_identity(gogo); } // Return a hash code for this type for the method hash table. // Types which are equivalent according to are_identical will have @@ -880,7 +880,7 @@ class Type // Whether the backend size is known. bool - is_backend_type_size_known(Gogo*) const; + is_backend_type_size_known(Gogo*); // Get the hash and equality functions for a type. void @@ -924,7 +924,7 @@ class Type { return false; } virtual bool - do_compare_is_identity() const = 0; + do_compare_is_identity(Gogo*) const = 0; virtual unsigned int do_hash_for_method(Gogo*) const; @@ -1388,7 +1388,7 @@ class Integer_type : public Type protected: bool - do_compare_is_identity() const + do_compare_is_identity(Gogo*) const { return true; } unsigned int @@ -1461,7 +1461,7 @@ class Float_type : public Type protected: bool - do_compare_is_identity() const + do_compare_is_identity(Gogo*) const { return false; } unsigned int @@ -1530,7 +1530,7 @@ class Complex_type : public Type protected: bool - do_compare_is_identity() const + do_compare_is_identity(Gogo*) const { return false; } unsigned int @@ -1590,7 +1590,7 @@ class String_type : public Type { return true; } bool - do_compare_is_identity() const + do_compare_is_identity(Gogo*) const { return false; } Btype* @@ -1708,7 +1708,7 @@ class Function_type : public Type { return true; } bool - do_compare_is_identity() const + do_compare_is_identity(Gogo*) const { return false; } unsigned int @@ -1793,7 +1793,7 @@ class Pointer_type : public Type { return true; } bool - do_compare_is_identity() const + do_compare_is_identity(Gogo*) const { return true; } unsigned int @@ -1985,7 +1985,7 @@ class Struct_type : public Type Location) const; // Return the total number of fields, including embedded fields. - // This is the number of values which can appear in a conversion to + // This is the number of values that can appear in a conversion to // this type. unsigned int total_field_count() const; @@ -2066,7 +2066,7 @@ class Struct_type : public Type do_has_pointer() const; bool - do_compare_is_identity() const; + do_compare_is_identity(Gogo*) const; unsigned int do_hash_for_method(Gogo*) const; @@ -2136,10 +2136,6 @@ class Array_type : public Type array_has_hidden_fields(const Named_type* within, std::string* reason) const { return this->element_type_->has_hidden_fields(within, reason); } - // Build the hash and equality functions if necessary. - void - finalize_methods(Gogo*); - // Return a tree for the pointer to the values in an array. tree value_pointer_tree(Gogo*, tree array) const; @@ -2192,11 +2188,7 @@ class Array_type : public Type } bool - do_compare_is_identity() const - { - return (this->length_ != NULL - && this->element_type_->compare_is_identity()); - } + do_compare_is_identity(Gogo*) const; unsigned int do_hash_for_method(Gogo*) const; @@ -2289,7 +2281,7 @@ class Map_type : public Type { return true; } bool - do_compare_is_identity() const + do_compare_is_identity(Gogo*) const { return false; } unsigned int @@ -2375,7 +2367,7 @@ class Channel_type : public Type { return true; } bool - do_compare_is_identity() const + do_compare_is_identity(Gogo*) const { return true; } unsigned int @@ -2490,7 +2482,7 @@ class Interface_type : public Type { return true; } bool - do_compare_is_identity() const + do_compare_is_identity(Gogo*) const { return false; } unsigned int @@ -2536,7 +2528,7 @@ class Named_type : public Type location_(location), named_btype_(NULL), dependencies_(), is_visible_(true), is_error_(false), is_placeholder_(false), is_converted_(false), is_circular_(false), seen_(false), - seen_in_get_backend_(false) + seen_in_compare_is_identity_(false), seen_in_get_backend_(false) { } // Return the associated Named_object. This holds the actual name. @@ -2731,7 +2723,7 @@ class Named_type : public Type do_has_pointer() const; bool - do_compare_is_identity() const; + do_compare_is_identity(Gogo*) const; unsigned int do_hash_for_method(Gogo*) const; @@ -2815,6 +2807,8 @@ class Named_type : public Type // This is mutable because it is always reset to false when the // function exits. mutable bool seen_; + // Like seen_, but used only by do_compare_is_identity. + mutable bool seen_in_compare_is_identity_; // Like seen_, but used only by do_get_backend. bool seen_in_get_backend_; }; @@ -2869,8 +2863,8 @@ class Forward_declaration_type : public Type { return this->real_type()->has_pointer(); } bool - do_compare_is_identity() const - { return this->real_type()->compare_is_identity(); } + do_compare_is_identity(Gogo* gogo) const + { return this->real_type()->compare_is_identity(gogo); } unsigned int do_hash_for_method(Gogo* gogo) const |