aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIan Lance Taylor <ian@gcc.gnu.org>2012-01-11 21:20:41 +0000
committerIan Lance Taylor <ian@gcc.gnu.org>2012-01-11 21:20:41 +0000
commit11d8e1a4cbf1a47eb3fe0a673cff6b820ffdda2c (patch)
tree42ef542db68c33e13f4e261277beb7a8be2ffbdc
parentef079a56e47d898ae7ad347c5d84a4ccfd9cba91 (diff)
downloadgcc-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.cc23
-rw-r--r--gcc/go/gofrontend/gogo.cc67
-rw-r--r--gcc/go/gofrontend/types.cc147
-rw-r--r--gcc/go/gofrontend/types.h50
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