compiler: split writing of equal and hash functions for types
Separate the generation of type equality and hash functions, rather than doing them in a single operation. This is almost entirely a pure refactoring in preparation for generating hash functions only for types that are map keys. The only change in generated code is that for types that are the size of numeric types, but not aligned like numeric types, such as [8]byte, now use standard hash functions. They previously used special-purpose hash functions because they required special-purpose equal functions. Reviewed-on: https://go-review.googlesource.com/c/gofrontend/+/212842 From-SVN: r279847
This commit is contained in:
parent
45b6395b20
commit
9279b5ba45
@ -1,4 +1,4 @@
|
|||||||
393957c8b68e370504209eb901aa0c3874e256d4
|
b5c950fb98042fe434edca0c2403234692f25cd4
|
||||||
|
|
||||||
The first line of this file holds the git revision number of the last
|
The first line of this file holds the git revision number of the last
|
||||||
merge done from the gofrontend repository.
|
merge done from the gofrontend repository.
|
||||||
|
@ -6238,10 +6238,8 @@ Binary_expression::lower_array_comparison(Gogo* gogo,
|
|||||||
return this->lower_compare_to_memcmp(gogo, inserter);
|
return this->lower_compare_to_memcmp(gogo, inserter);
|
||||||
|
|
||||||
// Call the array comparison function.
|
// Call the array comparison function.
|
||||||
Named_object* hash_fn;
|
Named_object* equal_fn =
|
||||||
Named_object* equal_fn;
|
at->equal_function(gogo, this->left_->type()->named_type(), NULL);
|
||||||
at->type_functions(gogo, this->left_->type()->named_type(), NULL, NULL,
|
|
||||||
&hash_fn, &equal_fn);
|
|
||||||
|
|
||||||
Location loc = this->location();
|
Location loc = this->location();
|
||||||
|
|
||||||
|
@ -2717,23 +2717,40 @@ Gogo::clear_file_scope()
|
|||||||
this->current_file_imported_unsafe_ = false;
|
this->current_file_imported_unsafe_ = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Queue up a type specific function for later writing. These are
|
// Queue up a type-specific hash function for later writing. These
|
||||||
// written out in write_specific_type_functions, called after the
|
// are written out in write_specific_type_functions, called after the
|
||||||
// parse tree is lowered.
|
// parse tree is lowered.
|
||||||
|
|
||||||
void
|
void
|
||||||
Gogo::queue_specific_type_function(Type* type, Named_type* name, int64_t size,
|
Gogo::queue_hash_function(Type* type, Named_type* name, int64_t size,
|
||||||
const std::string& hash_name,
|
const std::string& hash_name,
|
||||||
Function_type* hash_fntype,
|
Function_type* hash_fntype)
|
||||||
const std::string& equal_name,
|
|
||||||
Function_type* equal_fntype)
|
|
||||||
{
|
{
|
||||||
go_assert(!this->specific_type_functions_are_written_);
|
go_assert(!this->specific_type_functions_are_written_);
|
||||||
go_assert(!this->in_global_scope());
|
go_assert(!this->in_global_scope());
|
||||||
|
Specific_type_function::Specific_type_function_kind kind =
|
||||||
|
Specific_type_function::SPECIFIC_HASH;
|
||||||
Specific_type_function* tsf = new Specific_type_function(type, name, size,
|
Specific_type_function* tsf = new Specific_type_function(type, name, size,
|
||||||
hash_name,
|
kind, hash_name,
|
||||||
hash_fntype,
|
hash_fntype);
|
||||||
equal_name,
|
this->specific_type_functions_.push_back(tsf);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Queue up a type-specific equal function for later writing. These
|
||||||
|
// are written out in write_specific_type_functions, called after the
|
||||||
|
// parse tree is lowered.
|
||||||
|
|
||||||
|
void
|
||||||
|
Gogo::queue_equal_function(Type* type, Named_type* name, int64_t size,
|
||||||
|
const std::string& equal_name,
|
||||||
|
Function_type* equal_fntype)
|
||||||
|
{
|
||||||
|
go_assert(!this->specific_type_functions_are_written_);
|
||||||
|
go_assert(!this->in_global_scope());
|
||||||
|
Specific_type_function::Specific_type_function_kind kind =
|
||||||
|
Specific_type_function::SPECIFIC_EQUAL;
|
||||||
|
Specific_type_function* tsf = new Specific_type_function(type, name, size,
|
||||||
|
kind, equal_name,
|
||||||
equal_fntype);
|
equal_fntype);
|
||||||
this->specific_type_functions_.push_back(tsf);
|
this->specific_type_functions_.push_back(tsf);
|
||||||
}
|
}
|
||||||
@ -2758,8 +2775,6 @@ class Specific_type_functions : public Traverse
|
|||||||
int
|
int
|
||||||
Specific_type_functions::type(Type* t)
|
Specific_type_functions::type(Type* t)
|
||||||
{
|
{
|
||||||
Named_object* hash_fn;
|
|
||||||
Named_object* equal_fn;
|
|
||||||
switch (t->classification())
|
switch (t->classification())
|
||||||
{
|
{
|
||||||
case Type::TYPE_NAMED:
|
case Type::TYPE_NAMED:
|
||||||
@ -2768,7 +2783,10 @@ Specific_type_functions::type(Type* t)
|
|||||||
if (nt->is_alias())
|
if (nt->is_alias())
|
||||||
return TRAVERSE_CONTINUE;
|
return TRAVERSE_CONTINUE;
|
||||||
if (t->needs_specific_type_functions(this->gogo_))
|
if (t->needs_specific_type_functions(this->gogo_))
|
||||||
t->type_functions(this->gogo_, nt, NULL, NULL, &hash_fn, &equal_fn);
|
{
|
||||||
|
t->equal_function(this->gogo_, nt, NULL);
|
||||||
|
t->hash_function(this->gogo_, nt, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
// If this is a struct type, we don't want to make functions
|
// If this is a struct type, we don't want to make functions
|
||||||
// for the unnamed struct.
|
// for the unnamed struct.
|
||||||
@ -2802,7 +2820,10 @@ Specific_type_functions::type(Type* t)
|
|||||||
case Type::TYPE_STRUCT:
|
case Type::TYPE_STRUCT:
|
||||||
case Type::TYPE_ARRAY:
|
case Type::TYPE_ARRAY:
|
||||||
if (t->needs_specific_type_functions(this->gogo_))
|
if (t->needs_specific_type_functions(this->gogo_))
|
||||||
t->type_functions(this->gogo_, NULL, NULL, NULL, &hash_fn, &equal_fn);
|
{
|
||||||
|
t->equal_function(this->gogo_, NULL, NULL);
|
||||||
|
t->hash_function(this->gogo_, NULL, NULL);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@ -2824,11 +2845,12 @@ Gogo::write_specific_type_functions()
|
|||||||
{
|
{
|
||||||
Specific_type_function* tsf = this->specific_type_functions_.back();
|
Specific_type_function* tsf = this->specific_type_functions_.back();
|
||||||
this->specific_type_functions_.pop_back();
|
this->specific_type_functions_.pop_back();
|
||||||
tsf->type->write_specific_type_functions(this, tsf->name, tsf->size,
|
if (tsf->kind == Specific_type_function::SPECIFIC_HASH)
|
||||||
tsf->hash_name,
|
tsf->type->write_hash_function(this, tsf->name, tsf->size,
|
||||||
tsf->hash_fntype,
|
tsf->fnname, tsf->fntype);
|
||||||
tsf->equal_name,
|
else
|
||||||
tsf->equal_fntype);
|
tsf->type->write_equal_function(this, tsf->name, tsf->size,
|
||||||
|
tsf->fnname, tsf->fntype);
|
||||||
delete tsf;
|
delete tsf;
|
||||||
}
|
}
|
||||||
this->specific_type_functions_are_written_ = true;
|
this->specific_type_functions_are_written_ = true;
|
||||||
|
@ -598,15 +598,21 @@ class Gogo
|
|||||||
return p != this->var_deps_.end() ? p->second : NULL;
|
return p != this->var_deps_.end() ? p->second : NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Queue up a type-specific function to be written out. This is
|
// Queue up a type-specific hash function to be written out. This
|
||||||
// used when a type-specific function is needed when not at the top
|
// is used when a type-specific hash function is needed when not at
|
||||||
// level.
|
// top level.
|
||||||
void
|
void
|
||||||
queue_specific_type_function(Type* type, Named_type* name, int64_t size,
|
queue_hash_function(Type* type, Named_type* name, int64_t size,
|
||||||
const std::string& hash_name,
|
const std::string& hash_name,
|
||||||
Function_type* hash_fntype,
|
Function_type* hash_fntype);
|
||||||
const std::string& equal_name,
|
|
||||||
Function_type* equal_fntype);
|
// Queue up a type-specific equal function to be written out. This
|
||||||
|
// is used when a type-specific equal function is needed when not at
|
||||||
|
// top level.
|
||||||
|
void
|
||||||
|
queue_equal_function(Type* type, Named_type* name, int64_t size,
|
||||||
|
const std::string& equal_name,
|
||||||
|
Function_type* equal_fntype);
|
||||||
|
|
||||||
// Write out queued specific type functions.
|
// Write out queued specific type functions.
|
||||||
void
|
void
|
||||||
@ -871,11 +877,13 @@ class Gogo
|
|||||||
std::string
|
std::string
|
||||||
stub_method_name(const Package*, const std::string& method_name);
|
stub_method_name(const Package*, const std::string& method_name);
|
||||||
|
|
||||||
// Return the names of the hash and equality functions for TYPE.
|
// Return the name of the hash function for TYPE.
|
||||||
void
|
std::string
|
||||||
specific_type_function_names(const Type*, const Named_type*,
|
hash_function_name(const Type*, const Named_type*);
|
||||||
std::string* hash_name,
|
|
||||||
std::string* equal_name);
|
// Return the name of the equal function for TYPE.
|
||||||
|
std::string
|
||||||
|
equal_function_name(const Type*, const Named_type*);
|
||||||
|
|
||||||
// Return the assembler name to use for a global variable.
|
// Return the assembler name to use for a global variable.
|
||||||
std::string
|
std::string
|
||||||
@ -1059,22 +1067,21 @@ class Gogo
|
|||||||
// Type used to queue writing a type specific function.
|
// Type used to queue writing a type specific function.
|
||||||
struct Specific_type_function
|
struct Specific_type_function
|
||||||
{
|
{
|
||||||
|
enum Specific_type_function_kind { SPECIFIC_HASH, SPECIFIC_EQUAL };
|
||||||
|
|
||||||
Type* type;
|
Type* type;
|
||||||
Named_type* name;
|
Named_type* name;
|
||||||
int64_t size;
|
int64_t size;
|
||||||
std::string hash_name;
|
Specific_type_function_kind kind;
|
||||||
Function_type* hash_fntype;
|
std::string fnname;
|
||||||
std::string equal_name;
|
Function_type* fntype;
|
||||||
Function_type* equal_fntype;
|
|
||||||
|
|
||||||
Specific_type_function(Type* atype, Named_type* aname, int64_t asize,
|
Specific_type_function(Type* atype, Named_type* aname, int64_t asize,
|
||||||
const std::string& ahash_name,
|
Specific_type_function_kind akind,
|
||||||
Function_type* ahash_fntype,
|
const std::string afnname,
|
||||||
const std::string& aequal_name,
|
Function_type* afntype)
|
||||||
Function_type* aequal_fntype)
|
: type(atype), name(aname), size(asize), kind(akind),
|
||||||
: type(atype), name(aname), size(asize), hash_name(ahash_name),
|
fnname(afnname), fntype(afntype)
|
||||||
hash_fntype(ahash_fntype), equal_name(aequal_name),
|
|
||||||
equal_fntype(aequal_fntype)
|
|
||||||
{ }
|
{ }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -287,21 +287,30 @@ Gogo::stub_method_name(const Package* package, const std::string& mname)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return the names of the hash and equality functions for TYPE. If
|
// Return the name of the hash function for TYPE. If NAME is not NULL
|
||||||
// NAME is not NULL it is the name of the type. Set *HASH_NAME and
|
// it is the name of the type.
|
||||||
// *EQUAL_NAME.
|
|
||||||
|
|
||||||
void
|
std::string
|
||||||
Gogo::specific_type_function_names(const Type* type, const Named_type* name,
|
Gogo::hash_function_name(const Type* type, const Named_type* name)
|
||||||
std::string *hash_name,
|
|
||||||
std::string *equal_name)
|
|
||||||
{
|
{
|
||||||
const Type* rtype = type;
|
const Type* rtype = type;
|
||||||
if (name != NULL)
|
if (name != NULL)
|
||||||
rtype = name;
|
rtype = name;
|
||||||
std::string tname = rtype->mangled_name(this);
|
std::string tname = rtype->mangled_name(this);
|
||||||
*hash_name = tname + "..hash";
|
return tname + "..hash";
|
||||||
*equal_name = tname + "..eq";
|
}
|
||||||
|
|
||||||
|
// Return the name of the equal function for TYPE. If NAME is not
|
||||||
|
// NULL it is the name of the type.
|
||||||
|
|
||||||
|
std::string
|
||||||
|
Gogo::equal_function_name(const Type* type, const Named_type* name)
|
||||||
|
{
|
||||||
|
const Type* rtype = type;
|
||||||
|
if (name != NULL)
|
||||||
|
rtype = name;
|
||||||
|
std::string tname = rtype->mangled_name(this);
|
||||||
|
return tname + "..eq";
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return the assembler name to use for a global variable. GO_NAME is
|
// Return the assembler name to use for a global variable. GO_NAME is
|
||||||
|
@ -1722,7 +1722,7 @@ Type::needs_specific_type_functions(Gogo* gogo)
|
|||||||
go_assert(saw_errors());
|
go_assert(saw_errors());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// This switch matches the one in Type::type_functions.
|
// This switch matches the one in Type::equal_function.
|
||||||
switch (size)
|
switch (size)
|
||||||
{
|
{
|
||||||
case 0:
|
case 0:
|
||||||
@ -1740,16 +1740,13 @@ Type::needs_specific_type_functions(Gogo* gogo)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set *HASH_FN and *EQUAL_FN to the runtime functions which compute a
|
// Return the runtime function that computes the hash of this type.
|
||||||
// hash code for this type and which compare whether two values of
|
// If NAME is not NULL it is the name of this type. HASH_FNTYPE is
|
||||||
// this type are equal. If NAME is not NULL it is the name of this
|
// the type of the hash function function, for convenience; it may be
|
||||||
// type. HASH_FNTYPE and EQUAL_FNTYPE are the types of these
|
// NULL. This returns NULL if the type is not comparable.
|
||||||
// functions, for convenience; they may be NULL.
|
|
||||||
|
|
||||||
void
|
Named_object*
|
||||||
Type::type_functions(Gogo* gogo, Named_type* name, Function_type* hash_fntype,
|
Type::hash_function(Gogo* gogo, Named_type* name, Function_type* hash_fntype)
|
||||||
Function_type* equal_fntype, Named_object** hash_fn,
|
|
||||||
Named_object** equal_fn)
|
|
||||||
{
|
{
|
||||||
// If the unaliased type is not a named type, then the type does not
|
// If the unaliased type is not a named type, then the type does not
|
||||||
// have a name after all.
|
// have a name after all.
|
||||||
@ -1757,122 +1754,56 @@ Type::type_functions(Gogo* gogo, Named_type* name, Function_type* hash_fntype,
|
|||||||
name = name->unalias()->named_type();
|
name = name->unalias()->named_type();
|
||||||
|
|
||||||
if (!this->is_comparable())
|
if (!this->is_comparable())
|
||||||
{
|
return NULL;
|
||||||
*hash_fn = NULL;
|
|
||||||
*equal_fn = NULL;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hash_fntype == NULL || equal_fntype == NULL)
|
if (hash_fntype == NULL)
|
||||||
{
|
{
|
||||||
Location bloc = Linemap::predeclared_location();
|
Location bloc = Linemap::predeclared_location();
|
||||||
|
|
||||||
Type* uintptr_type = Type::lookup_integer_type("uintptr");
|
Type* uintptr_type = Type::lookup_integer_type("uintptr");
|
||||||
Type* void_type = Type::make_void_type();
|
Type* void_type = Type::make_void_type();
|
||||||
Type* unsafe_pointer_type = Type::make_pointer_type(void_type);
|
Type* unsafe_pointer_type = Type::make_pointer_type(void_type);
|
||||||
|
Typed_identifier_list* params = new Typed_identifier_list();
|
||||||
if (hash_fntype == NULL)
|
params->push_back(Typed_identifier("key", unsafe_pointer_type, bloc));
|
||||||
{
|
params->push_back(Typed_identifier("seed", uintptr_type, bloc));
|
||||||
Typed_identifier_list* params = new Typed_identifier_list();
|
Typed_identifier_list* results = new Typed_identifier_list();
|
||||||
params->push_back(Typed_identifier("key", unsafe_pointer_type,
|
results->push_back(Typed_identifier("", uintptr_type, bloc));
|
||||||
bloc));
|
hash_fntype = Type::make_function_type(NULL, params, results, bloc);
|
||||||
params->push_back(Typed_identifier("seed", uintptr_type, bloc));
|
|
||||||
|
|
||||||
Typed_identifier_list* results = new Typed_identifier_list();
|
|
||||||
results->push_back(Typed_identifier("", uintptr_type, bloc));
|
|
||||||
|
|
||||||
hash_fntype = Type::make_function_type(NULL, params, results, bloc);
|
|
||||||
}
|
|
||||||
if (equal_fntype == NULL)
|
|
||||||
{
|
|
||||||
Typed_identifier_list* params = new Typed_identifier_list();
|
|
||||||
params->push_back(Typed_identifier("key1", unsafe_pointer_type,
|
|
||||||
bloc));
|
|
||||||
params->push_back(Typed_identifier("key2", unsafe_pointer_type,
|
|
||||||
bloc));
|
|
||||||
|
|
||||||
Typed_identifier_list* results = new Typed_identifier_list();
|
|
||||||
results->push_back(Typed_identifier("", Type::lookup_bool_type(),
|
|
||||||
bloc));
|
|
||||||
|
|
||||||
equal_fntype = Type::make_function_type(NULL, params, results, bloc);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* hash_fnname;
|
const char* hash_fnname;
|
||||||
const char* equal_fnname;
|
|
||||||
if (this->compare_is_identity(gogo))
|
if (this->compare_is_identity(gogo))
|
||||||
{
|
{
|
||||||
int64_t size, align;
|
int64_t size;
|
||||||
if (!this->backend_type_size(gogo, &size)
|
if (!this->backend_type_size(gogo, &size))
|
||||||
|| !this->backend_type_align(gogo, &align))
|
|
||||||
{
|
{
|
||||||
go_assert(saw_errors());
|
go_assert(saw_errors());
|
||||||
return;
|
return NULL;
|
||||||
}
|
}
|
||||||
bool build_functions = false;
|
|
||||||
// This switch matches the one in Type::needs_specific_type_functions.
|
|
||||||
// The alignment tests are because of the memequal functions,
|
|
||||||
// which assume that the values are aligned as required for an
|
|
||||||
// integer of that size.
|
|
||||||
switch (size)
|
switch (size)
|
||||||
{
|
{
|
||||||
case 0:
|
case 0:
|
||||||
hash_fnname = "runtime.memhash0";
|
hash_fnname = "runtime.memhash0";
|
||||||
equal_fnname = "runtime.memequal0";
|
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
hash_fnname = "runtime.memhash8";
|
hash_fnname = "runtime.memhash8";
|
||||||
equal_fnname = "runtime.memequal8";
|
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
if (align < Type::memequal_align(gogo, 16))
|
hash_fnname = "runtime.memhash16";
|
||||||
build_functions = true;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
hash_fnname = "runtime.memhash16";
|
|
||||||
equal_fnname = "runtime.memequal16";
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case 4:
|
case 4:
|
||||||
if (align < Type::memequal_align(gogo, 32))
|
hash_fnname = "runtime.memhash32";
|
||||||
build_functions = true;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
hash_fnname = "runtime.memhash32";
|
|
||||||
equal_fnname = "runtime.memequal32";
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case 8:
|
case 8:
|
||||||
if (align < Type::memequal_align(gogo, 64))
|
hash_fnname = "runtime.memhash64";
|
||||||
build_functions = true;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
hash_fnname = "runtime.memhash64";
|
|
||||||
equal_fnname = "runtime.memequal64";
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case 16:
|
case 16:
|
||||||
if (align < Type::memequal_align(gogo, 128))
|
hash_fnname = "runtime.memhash128";
|
||||||
build_functions = true;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
hash_fnname = "runtime.memhash128";
|
|
||||||
equal_fnname = "runtime.memequal128";
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
build_functions = true;
|
// We don't have a built-in function for a type of this
|
||||||
break;
|
// size. Build a function to use that calls the generic
|
||||||
}
|
// hash functions for identity, passing the size.
|
||||||
if (build_functions)
|
return this->build_hash_function(gogo, name, size, hash_fntype);
|
||||||
{
|
|
||||||
// We don't have a built-in function for a type of this size
|
|
||||||
// and alignment. Build a function to use that calls the
|
|
||||||
// generic hash/equality functions for identity, passing the size.
|
|
||||||
this->specific_type_functions(gogo, name, size, hash_fntype,
|
|
||||||
equal_fntype, hash_fn, equal_fn);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -1899,11 +1830,9 @@ Type::type_functions(Gogo* gogo, Named_type* name, Function_type* hash_fntype,
|
|||||||
{
|
{
|
||||||
case 32:
|
case 32:
|
||||||
hash_fnname = "runtime.f32hash";
|
hash_fnname = "runtime.f32hash";
|
||||||
equal_fnname = "runtime.f32equal";
|
|
||||||
break;
|
break;
|
||||||
case 64:
|
case 64:
|
||||||
hash_fnname = "runtime.f64hash";
|
hash_fnname = "runtime.f64hash";
|
||||||
equal_fnname = "runtime.f64equal";
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
go_unreachable();
|
go_unreachable();
|
||||||
@ -1915,11 +1844,9 @@ Type::type_functions(Gogo* gogo, Named_type* name, Function_type* hash_fntype,
|
|||||||
{
|
{
|
||||||
case 64:
|
case 64:
|
||||||
hash_fnname = "runtime.c64hash";
|
hash_fnname = "runtime.c64hash";
|
||||||
equal_fnname = "runtime.c64equal";
|
|
||||||
break;
|
break;
|
||||||
case 128:
|
case 128:
|
||||||
hash_fnname = "runtime.c128hash";
|
hash_fnname = "runtime.c128hash";
|
||||||
equal_fnname = "runtime.c128equal";
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
go_unreachable();
|
go_unreachable();
|
||||||
@ -1928,18 +1855,13 @@ Type::type_functions(Gogo* gogo, Named_type* name, Function_type* hash_fntype,
|
|||||||
|
|
||||||
case Type::TYPE_STRING:
|
case Type::TYPE_STRING:
|
||||||
hash_fnname = "runtime.strhash";
|
hash_fnname = "runtime.strhash";
|
||||||
equal_fnname = "runtime.strequal";
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Type::TYPE_STRUCT:
|
case Type::TYPE_STRUCT:
|
||||||
{
|
// This is a struct which can not be compared using a simple
|
||||||
// This is a struct which can not be compared using a
|
// identity function. We need to build a function to
|
||||||
// simple identity function. We need to build a function
|
// compute the hash.
|
||||||
// for comparison.
|
return this->build_hash_function(gogo, name, -1, hash_fntype);
|
||||||
this->specific_type_functions(gogo, name, -1, hash_fntype,
|
|
||||||
equal_fntype, hash_fn, equal_fn);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
case Type::TYPE_ARRAY:
|
case Type::TYPE_ARRAY:
|
||||||
if (this->is_slice_type())
|
if (this->is_slice_type())
|
||||||
@ -1952,24 +1874,16 @@ Type::type_functions(Gogo* gogo, Named_type* name, Function_type* hash_fntype,
|
|||||||
{
|
{
|
||||||
// This is an array which can not be compared using a
|
// This is an array which can not be compared using a
|
||||||
// simple identity function. We need to build a
|
// simple identity function. We need to build a
|
||||||
// function for comparison.
|
// function to compute the hash.
|
||||||
this->specific_type_functions(gogo, name, -1, hash_fntype,
|
return this->build_hash_function(gogo, name, -1, hash_fntype);
|
||||||
equal_fntype, hash_fn, equal_fn);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Type::TYPE_INTERFACE:
|
case Type::TYPE_INTERFACE:
|
||||||
if (this->interface_type()->is_empty())
|
if (this->interface_type()->is_empty())
|
||||||
{
|
hash_fnname = "runtime.nilinterhash";
|
||||||
hash_fnname = "runtime.nilinterhash";
|
|
||||||
equal_fnname = "runtime.nilinterequal";
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
hash_fnname = "runtime.interhash";
|
||||||
hash_fnname = "runtime.interhash";
|
|
||||||
equal_fnname = "runtime.interequal";
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Type::TYPE_NAMED:
|
case Type::TYPE_NAMED:
|
||||||
@ -1983,90 +1897,70 @@ Type::type_functions(Gogo* gogo, Named_type* name, Function_type* hash_fntype,
|
|||||||
|
|
||||||
|
|
||||||
Location bloc = Linemap::predeclared_location();
|
Location bloc = Linemap::predeclared_location();
|
||||||
*hash_fn = Named_object::make_function_declaration(hash_fnname, NULL,
|
Named_object *hash_fn = Named_object::make_function_declaration(hash_fnname,
|
||||||
hash_fntype, bloc);
|
NULL,
|
||||||
(*hash_fn)->func_declaration_value()->set_asm_name(hash_fnname);
|
hash_fntype,
|
||||||
*equal_fn = Named_object::make_function_declaration(equal_fnname, NULL,
|
bloc);
|
||||||
equal_fntype, bloc);
|
hash_fn->func_declaration_value()->set_asm_name(hash_fnname);
|
||||||
(*equal_fn)->func_declaration_value()->set_asm_name(equal_fnname);
|
return hash_fn;
|
||||||
}
|
}
|
||||||
|
|
||||||
// A hash table mapping types to the specific hash functions.
|
// A hash table mapping types to the specific hash functions.
|
||||||
|
|
||||||
Type::Type_functions Type::type_functions_table;
|
Type::Type_function Type::type_hash_functions_table;
|
||||||
|
|
||||||
// Handle a type function which is specific to a type: if SIZE == -1,
|
// Build a hash function that is specific to a type: if SIZE == -1,
|
||||||
// this is a struct or array that can not use an identity comparison.
|
// this is a struct or array type that cannot use an identity
|
||||||
// Otherwise, it is a type that uses an identity comparison but is not
|
// comparison. Otherwise, it is a type that uses an identity
|
||||||
// one of the standard supported sizes.
|
// comparison but is not one of the standard supported sizes.
|
||||||
|
|
||||||
void
|
Named_object*
|
||||||
Type::specific_type_functions(Gogo* gogo, Named_type* name, int64_t size,
|
Type::build_hash_function(Gogo* gogo, Named_type* name, int64_t size,
|
||||||
Function_type* hash_fntype,
|
Function_type* hash_fntype)
|
||||||
Function_type* equal_fntype,
|
|
||||||
Named_object** hash_fn,
|
|
||||||
Named_object** equal_fn)
|
|
||||||
{
|
{
|
||||||
Hash_equal_fn fnull(NULL, NULL);
|
std::pair<Type*, Named_object*> val(name != NULL ? name : this, NULL);
|
||||||
std::pair<Type*, Hash_equal_fn> val(name != NULL ? name : this, fnull);
|
std::pair<Type_function::iterator, bool> ins =
|
||||||
std::pair<Type_functions::iterator, bool> ins =
|
Type::type_hash_functions_table.insert(val);
|
||||||
Type::type_functions_table.insert(val);
|
|
||||||
if (!ins.second)
|
if (!ins.second)
|
||||||
{
|
{
|
||||||
// We already have functions for this type
|
// We already have a function for this type.
|
||||||
*hash_fn = ins.first->second.first;
|
return ins.first->second;
|
||||||
*equal_fn = ins.first->second.second;
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string hash_name;
|
std::string hash_name = gogo->hash_function_name(this, name);
|
||||||
std::string equal_name;
|
|
||||||
gogo->specific_type_function_names(this, name, &hash_name, &equal_name);
|
|
||||||
|
|
||||||
Location bloc = Linemap::predeclared_location();
|
Location bloc = Linemap::predeclared_location();
|
||||||
|
|
||||||
const Package* package = NULL;
|
const Package* package = NULL;
|
||||||
bool is_defined_elsewhere =
|
bool is_defined_elsewhere =
|
||||||
this->type_descriptor_defined_elsewhere(name, &package);
|
this->type_descriptor_defined_elsewhere(name, &package);
|
||||||
if (is_defined_elsewhere)
|
|
||||||
{
|
|
||||||
*hash_fn = Named_object::make_function_declaration(hash_name, package,
|
|
||||||
hash_fntype, bloc);
|
|
||||||
*equal_fn = Named_object::make_function_declaration(equal_name, package,
|
|
||||||
equal_fntype, bloc);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
*hash_fn = gogo->declare_package_function(hash_name, hash_fntype, bloc);
|
|
||||||
*equal_fn = gogo->declare_package_function(equal_name, equal_fntype,
|
|
||||||
bloc);
|
|
||||||
}
|
|
||||||
|
|
||||||
ins.first->second.first = *hash_fn;
|
Named_object* hash_fn;
|
||||||
ins.first->second.second = *equal_fn;
|
if (is_defined_elsewhere)
|
||||||
|
hash_fn = Named_object::make_function_declaration(hash_name, package,
|
||||||
|
hash_fntype, bloc);
|
||||||
|
else
|
||||||
|
hash_fn = gogo->declare_package_function(hash_name, hash_fntype, bloc);
|
||||||
|
|
||||||
|
ins.first->second = hash_fn;
|
||||||
|
|
||||||
if (!is_defined_elsewhere)
|
if (!is_defined_elsewhere)
|
||||||
{
|
{
|
||||||
if (gogo->in_global_scope())
|
if (gogo->in_global_scope())
|
||||||
this->write_specific_type_functions(gogo, name, size, hash_name,
|
this->write_hash_function(gogo, name, size, hash_name, hash_fntype);
|
||||||
hash_fntype, equal_name,
|
|
||||||
equal_fntype);
|
|
||||||
else
|
else
|
||||||
gogo->queue_specific_type_function(this, name, size, hash_name,
|
gogo->queue_hash_function(this, name, size, hash_name, hash_fntype);
|
||||||
hash_fntype, equal_name,
|
|
||||||
equal_fntype);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return hash_fn;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write the hash and equality functions for a type which needs to be
|
// Write the hash function for a type that needs it written specially.
|
||||||
// written specially.
|
|
||||||
|
|
||||||
void
|
void
|
||||||
Type::write_specific_type_functions(Gogo* gogo, Named_type* name, int64_t size,
|
Type::write_hash_function(Gogo* gogo, Named_type* name, int64_t size,
|
||||||
const std::string& hash_name,
|
const std::string& hash_name,
|
||||||
Function_type* hash_fntype,
|
Function_type* hash_fntype)
|
||||||
const std::string& equal_name,
|
|
||||||
Function_type* equal_fntype)
|
|
||||||
{
|
{
|
||||||
Location bloc = Linemap::predeclared_location();
|
Location bloc = Linemap::predeclared_location();
|
||||||
|
|
||||||
@ -2086,13 +1980,11 @@ Type::write_specific_type_functions(Gogo* gogo, Named_type* name, int64_t size,
|
|||||||
if (size != -1)
|
if (size != -1)
|
||||||
this->write_identity_hash(gogo, size);
|
this->write_identity_hash(gogo, size);
|
||||||
else if (name != NULL && name->real_type()->named_type() != NULL)
|
else if (name != NULL && name->real_type()->named_type() != NULL)
|
||||||
this->write_named_hash(gogo, name, hash_fntype, equal_fntype);
|
this->write_named_hash(gogo, name, hash_fntype);
|
||||||
else if (this->struct_type() != NULL)
|
else if (this->struct_type() != NULL)
|
||||||
this->struct_type()->write_hash_function(gogo, name, hash_fntype,
|
this->struct_type()->write_hash_function(gogo, name, hash_fntype);
|
||||||
equal_fntype);
|
|
||||||
else if (this->array_type() != NULL)
|
else if (this->array_type() != NULL)
|
||||||
this->array_type()->write_hash_function(gogo, name, hash_fntype,
|
this->array_type()->write_hash_function(gogo, name, hash_fntype);
|
||||||
equal_fntype);
|
|
||||||
else
|
else
|
||||||
go_unreachable();
|
go_unreachable();
|
||||||
|
|
||||||
@ -2103,32 +1995,8 @@ Type::write_specific_type_functions(Gogo* gogo, Named_type* name, int64_t size,
|
|||||||
gogo->remove_shortcuts_in_block(b);
|
gogo->remove_shortcuts_in_block(b);
|
||||||
gogo->finish_function(bloc);
|
gogo->finish_function(bloc);
|
||||||
|
|
||||||
Named_object *equal_fn = gogo->start_function(equal_name, equal_fntype,
|
// Build the function descriptor for the type descriptor to refer to.
|
||||||
false, bloc);
|
|
||||||
equal_fn->func_value()->set_is_type_specific_function();
|
|
||||||
gogo->start_block(bloc);
|
|
||||||
|
|
||||||
if (size != -1)
|
|
||||||
this->write_identity_equal(gogo, size);
|
|
||||||
else if (name != NULL && name->real_type()->named_type() != NULL)
|
|
||||||
this->write_named_equal(gogo, name);
|
|
||||||
else if (this->struct_type() != NULL)
|
|
||||||
this->struct_type()->write_equal_function(gogo, name);
|
|
||||||
else if (this->array_type() != NULL)
|
|
||||||
this->array_type()->write_equal_function(gogo, name);
|
|
||||||
else
|
|
||||||
go_unreachable();
|
|
||||||
|
|
||||||
b = gogo->finish_block(bloc);
|
|
||||||
gogo->add_block(b, bloc);
|
|
||||||
gogo->lower_block(equal_fn, b);
|
|
||||||
gogo->order_block(b);
|
|
||||||
gogo->remove_shortcuts_in_block(b);
|
|
||||||
gogo->finish_function(bloc);
|
|
||||||
|
|
||||||
// Build the function descriptors for the type descriptor to refer to.
|
|
||||||
hash_fn->func_value()->descriptor(gogo, hash_fn);
|
hash_fn->func_value()->descriptor(gogo, hash_fn);
|
||||||
equal_fn->func_value()->descriptor(gogo, equal_fn);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write a hash function for a type that can use an identity hash but
|
// Write a hash function for a type that can use an identity hash but
|
||||||
@ -2184,6 +2052,336 @@ Type::write_identity_hash(Gogo* gogo, int64_t size)
|
|||||||
gogo->add_statement(s);
|
gogo->add_statement(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Write a hash function that simply calls the hash function for a
|
||||||
|
// named type. This is used when one named type is defined as
|
||||||
|
// another. This ensures that this case works when the other named
|
||||||
|
// type is defined in another package and relies on calling hash
|
||||||
|
// functions defined only in that package.
|
||||||
|
|
||||||
|
void
|
||||||
|
Type::write_named_hash(Gogo* gogo, Named_type* name,
|
||||||
|
Function_type* hash_fntype)
|
||||||
|
{
|
||||||
|
Location bloc = Linemap::predeclared_location();
|
||||||
|
|
||||||
|
Named_type* base_type = name->real_type()->named_type();
|
||||||
|
while (base_type->is_alias())
|
||||||
|
{
|
||||||
|
base_type = base_type->real_type()->named_type();
|
||||||
|
go_assert(base_type != NULL);
|
||||||
|
}
|
||||||
|
go_assert(base_type != NULL);
|
||||||
|
|
||||||
|
// The pointer to the type we are going to hash. This is an
|
||||||
|
// unsafe.Pointer.
|
||||||
|
Named_object* key_arg = gogo->lookup("key", NULL);
|
||||||
|
go_assert(key_arg != NULL);
|
||||||
|
|
||||||
|
// The seed argument to the hash function.
|
||||||
|
Named_object* seed_arg = gogo->lookup("seed", NULL);
|
||||||
|
go_assert(seed_arg != NULL);
|
||||||
|
|
||||||
|
Named_object* hash_fn = name->real_type()->hash_function(gogo, base_type,
|
||||||
|
hash_fntype);
|
||||||
|
|
||||||
|
// Call the hash function for the base type.
|
||||||
|
Expression* key_ref = Expression::make_var_reference(key_arg, bloc);
|
||||||
|
Expression* seed_ref = Expression::make_var_reference(seed_arg, bloc);
|
||||||
|
Expression_list* args = new Expression_list();
|
||||||
|
args->push_back(key_ref);
|
||||||
|
args->push_back(seed_ref);
|
||||||
|
Expression* func = Expression::make_func_reference(hash_fn, NULL, bloc);
|
||||||
|
Expression* call = Expression::make_call(func, args, false, bloc);
|
||||||
|
|
||||||
|
// Return the hash of the base type.
|
||||||
|
Expression_list* vals = new Expression_list();
|
||||||
|
vals->push_back(call);
|
||||||
|
Statement* s = Statement::make_return_statement(vals, bloc);
|
||||||
|
gogo->add_statement(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the runtime function that compares whether two values of
|
||||||
|
// this type are equal. If NAME is not NULL it is the name of this
|
||||||
|
// type. EQUAL_FNTYPE is the type of the equality function, for
|
||||||
|
// convenience; it may be NULL. This returns NULL if the type is not
|
||||||
|
// comparable.
|
||||||
|
|
||||||
|
Named_object*
|
||||||
|
Type::equal_function(Gogo* gogo, Named_type* name, Function_type* equal_fntype)
|
||||||
|
{
|
||||||
|
// If the unaliased type is not a named type, then the type does not
|
||||||
|
// have a name after all.
|
||||||
|
if (name != NULL)
|
||||||
|
name = name->unalias()->named_type();
|
||||||
|
|
||||||
|
if (!this->is_comparable())
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (equal_fntype == NULL)
|
||||||
|
{
|
||||||
|
Location bloc = Linemap::predeclared_location();
|
||||||
|
Type* void_type = Type::make_void_type();
|
||||||
|
Type* unsafe_pointer_type = Type::make_pointer_type(void_type);
|
||||||
|
Typed_identifier_list* params = new Typed_identifier_list();
|
||||||
|
params->push_back(Typed_identifier("key1", unsafe_pointer_type, bloc));
|
||||||
|
params->push_back(Typed_identifier("key2", unsafe_pointer_type, bloc));
|
||||||
|
Typed_identifier_list* results = new Typed_identifier_list();
|
||||||
|
results->push_back(Typed_identifier("", Type::lookup_bool_type(), bloc));
|
||||||
|
equal_fntype = Type::make_function_type(NULL, params, results, bloc);
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* equal_fnname;
|
||||||
|
if (this->compare_is_identity(gogo))
|
||||||
|
{
|
||||||
|
int64_t size, align;
|
||||||
|
if (!this->backend_type_size(gogo, &size)
|
||||||
|
|| !this->backend_type_align(gogo, &align))
|
||||||
|
{
|
||||||
|
go_assert(saw_errors());
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
bool build_function = false;
|
||||||
|
// This switch matches the one in Type::needs_specific_type_functions.
|
||||||
|
// The alignment tests are because of the memequal functions,
|
||||||
|
// which assume that the values are aligned as required for an
|
||||||
|
// integer of that size.
|
||||||
|
switch (size)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
equal_fnname = "runtime.memequal0";
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
equal_fnname = "runtime.memequal8";
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
if (align < Type::memequal_align(gogo, 16))
|
||||||
|
build_function = true;
|
||||||
|
else
|
||||||
|
equal_fnname = "runtime.memequal16";
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
if (align < Type::memequal_align(gogo, 32))
|
||||||
|
build_function = true;
|
||||||
|
else
|
||||||
|
equal_fnname = "runtime.memequal32";
|
||||||
|
break;
|
||||||
|
case 8:
|
||||||
|
if (align < Type::memequal_align(gogo, 64))
|
||||||
|
build_function = true;
|
||||||
|
else
|
||||||
|
equal_fnname = "runtime.memequal64";
|
||||||
|
break;
|
||||||
|
case 16:
|
||||||
|
if (align < Type::memequal_align(gogo, 128))
|
||||||
|
build_function = true;
|
||||||
|
else
|
||||||
|
equal_fnname = "runtime.memequal128";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
build_function = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (build_function)
|
||||||
|
{
|
||||||
|
// We don't have a built-in function for a type of this size
|
||||||
|
// and alignment. Build a function to use that calls the
|
||||||
|
// generic equality functions for identity, passing the size.
|
||||||
|
return this->build_equal_function(gogo, name, size, equal_fntype);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
switch (this->base()->classification())
|
||||||
|
{
|
||||||
|
case Type::TYPE_ERROR:
|
||||||
|
case Type::TYPE_VOID:
|
||||||
|
case Type::TYPE_NIL:
|
||||||
|
case Type::TYPE_FUNCTION:
|
||||||
|
case Type::TYPE_MAP:
|
||||||
|
// For these types is_comparable should have returned false.
|
||||||
|
go_unreachable();
|
||||||
|
|
||||||
|
case Type::TYPE_BOOLEAN:
|
||||||
|
case Type::TYPE_INTEGER:
|
||||||
|
case Type::TYPE_POINTER:
|
||||||
|
case Type::TYPE_CHANNEL:
|
||||||
|
// For these types compare_is_identity should have returned true.
|
||||||
|
go_unreachable();
|
||||||
|
|
||||||
|
case Type::TYPE_FLOAT:
|
||||||
|
switch (this->float_type()->bits())
|
||||||
|
{
|
||||||
|
case 32:
|
||||||
|
equal_fnname = "runtime.f32equal";
|
||||||
|
break;
|
||||||
|
case 64:
|
||||||
|
equal_fnname = "runtime.f64equal";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
go_unreachable();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Type::TYPE_COMPLEX:
|
||||||
|
switch (this->complex_type()->bits())
|
||||||
|
{
|
||||||
|
case 64:
|
||||||
|
equal_fnname = "runtime.c64equal";
|
||||||
|
break;
|
||||||
|
case 128:
|
||||||
|
equal_fnname = "runtime.c128equal";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
go_unreachable();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Type::TYPE_STRING:
|
||||||
|
equal_fnname = "runtime.strequal";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Type::TYPE_STRUCT:
|
||||||
|
// This is a struct which can not be compared using a simple
|
||||||
|
// identity function. We need to build a function for
|
||||||
|
// comparison.
|
||||||
|
return this->build_equal_function(gogo, name, -1, equal_fntype);
|
||||||
|
|
||||||
|
case Type::TYPE_ARRAY:
|
||||||
|
if (this->is_slice_type())
|
||||||
|
{
|
||||||
|
// Type::is_compatible_for_comparison should have
|
||||||
|
// returned false.
|
||||||
|
go_unreachable();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// This is an array which can not be compared using a
|
||||||
|
// simple identity function. We need to build a
|
||||||
|
// function for comparison.
|
||||||
|
return this->build_equal_function(gogo, name, -1, equal_fntype);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Type::TYPE_INTERFACE:
|
||||||
|
if (this->interface_type()->is_empty())
|
||||||
|
equal_fnname = "runtime.nilinterequal";
|
||||||
|
else
|
||||||
|
equal_fnname = "runtime.interequal";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Type::TYPE_NAMED:
|
||||||
|
case Type::TYPE_FORWARD:
|
||||||
|
go_unreachable();
|
||||||
|
|
||||||
|
default:
|
||||||
|
go_unreachable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Location bloc = Linemap::predeclared_location();
|
||||||
|
Named_object* equal_fn =
|
||||||
|
Named_object::make_function_declaration(equal_fnname, NULL, equal_fntype,
|
||||||
|
bloc);
|
||||||
|
equal_fn->func_declaration_value()->set_asm_name(equal_fnname);
|
||||||
|
return equal_fn;
|
||||||
|
}
|
||||||
|
|
||||||
|
// A hash table mapping types to the specific equal functions.
|
||||||
|
|
||||||
|
Type::Type_function Type::type_equal_functions_table;
|
||||||
|
|
||||||
|
// Build an equality function that is specific to a type: if SIZE ==
|
||||||
|
// -1, this is a struct or array type that cannot use an identity
|
||||||
|
// comparison. Otherwise, it is a type that uses an identity
|
||||||
|
// comparison but is not one of the standard supported sizes or it is
|
||||||
|
// not aligned as needed.
|
||||||
|
|
||||||
|
Named_object*
|
||||||
|
Type::build_equal_function(Gogo* gogo, Named_type* name, int64_t size,
|
||||||
|
Function_type* equal_fntype)
|
||||||
|
{
|
||||||
|
std::pair<Type*, Named_object*> val(name != NULL ? name : this, NULL);
|
||||||
|
std::pair<Type_function::iterator, bool> ins =
|
||||||
|
Type::type_equal_functions_table.insert(val);
|
||||||
|
if (!ins.second)
|
||||||
|
{
|
||||||
|
// We already have a function for this type.
|
||||||
|
return ins.first->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string equal_name = gogo->equal_function_name(this, name);
|
||||||
|
|
||||||
|
Location bloc = Linemap::predeclared_location();
|
||||||
|
|
||||||
|
const Package* package = NULL;
|
||||||
|
bool is_defined_elsewhere =
|
||||||
|
this->type_descriptor_defined_elsewhere(name, &package);
|
||||||
|
|
||||||
|
Named_object* equal_fn;
|
||||||
|
if (is_defined_elsewhere)
|
||||||
|
equal_fn = Named_object::make_function_declaration(equal_name, package,
|
||||||
|
equal_fntype, bloc);
|
||||||
|
else
|
||||||
|
equal_fn = gogo->declare_package_function(equal_name, equal_fntype, bloc);
|
||||||
|
|
||||||
|
ins.first->second = equal_fn;
|
||||||
|
|
||||||
|
if (!is_defined_elsewhere)
|
||||||
|
{
|
||||||
|
if (gogo->in_global_scope())
|
||||||
|
this->write_equal_function(gogo, name, size, equal_name, equal_fntype);
|
||||||
|
else
|
||||||
|
gogo->queue_equal_function(this, name, size, equal_name, equal_fntype);
|
||||||
|
}
|
||||||
|
|
||||||
|
return equal_fn;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write the equal function for a type that needs it written
|
||||||
|
// specially.
|
||||||
|
|
||||||
|
void
|
||||||
|
Type::write_equal_function(Gogo* gogo, Named_type* name, int64_t size,
|
||||||
|
const std::string& equal_name,
|
||||||
|
Function_type* equal_fntype)
|
||||||
|
{
|
||||||
|
Location bloc = Linemap::predeclared_location();
|
||||||
|
|
||||||
|
if (gogo->specific_type_functions_are_written())
|
||||||
|
{
|
||||||
|
go_assert(saw_errors());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
go_assert(this->is_comparable());
|
||||||
|
|
||||||
|
Named_object* equal_fn = gogo->start_function(equal_name, equal_fntype,
|
||||||
|
false, bloc);
|
||||||
|
equal_fn->func_value()->set_is_type_specific_function();
|
||||||
|
gogo->start_block(bloc);
|
||||||
|
|
||||||
|
if (size != -1)
|
||||||
|
this->write_identity_equal(gogo, size);
|
||||||
|
else if (name != NULL && name->real_type()->named_type() != NULL)
|
||||||
|
this->write_named_equal(gogo, name);
|
||||||
|
else if (this->struct_type() != NULL)
|
||||||
|
this->struct_type()->write_equal_function(gogo, name);
|
||||||
|
else if (this->array_type() != NULL)
|
||||||
|
this->array_type()->write_equal_function(gogo, name);
|
||||||
|
else
|
||||||
|
go_unreachable();
|
||||||
|
|
||||||
|
Block* b = gogo->finish_block(bloc);
|
||||||
|
gogo->add_block(b, bloc);
|
||||||
|
gogo->lower_block(equal_fn, b);
|
||||||
|
gogo->order_block(b);
|
||||||
|
gogo->remove_shortcuts_in_block(b);
|
||||||
|
gogo->finish_function(bloc);
|
||||||
|
|
||||||
|
// Build the function descriptor for the type descriptor to refer to.
|
||||||
|
equal_fn->func_value()->descriptor(gogo, equal_fn);
|
||||||
|
}
|
||||||
|
|
||||||
// Write an equality function for a type that can use an identity
|
// Write an equality function for a type that can use an identity
|
||||||
// equality comparison but is not one of the standard supported sizes.
|
// equality comparison but is not one of the standard supported sizes.
|
||||||
// For example, this would be used for the type [3]byte. This builds
|
// For example, this would be used for the type [3]byte. This builds
|
||||||
@ -2237,56 +2435,6 @@ Type::write_identity_equal(Gogo* gogo, int64_t size)
|
|||||||
gogo->add_statement(s);
|
gogo->add_statement(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write a hash function that simply calls the hash function for a
|
|
||||||
// named type. This is used when one named type is defined as
|
|
||||||
// another. This ensures that this case works when the other named
|
|
||||||
// type is defined in another package and relies on calling hash
|
|
||||||
// functions defined only in that package.
|
|
||||||
|
|
||||||
void
|
|
||||||
Type::write_named_hash(Gogo* gogo, Named_type* name,
|
|
||||||
Function_type* hash_fntype, Function_type* equal_fntype)
|
|
||||||
{
|
|
||||||
Location bloc = Linemap::predeclared_location();
|
|
||||||
|
|
||||||
Named_type* base_type = name->real_type()->named_type();
|
|
||||||
while (base_type->is_alias())
|
|
||||||
{
|
|
||||||
base_type = base_type->real_type()->named_type();
|
|
||||||
go_assert(base_type != NULL);
|
|
||||||
}
|
|
||||||
go_assert(base_type != NULL);
|
|
||||||
|
|
||||||
// The pointer to the type we are going to hash. This is an
|
|
||||||
// unsafe.Pointer.
|
|
||||||
Named_object* key_arg = gogo->lookup("key", NULL);
|
|
||||||
go_assert(key_arg != NULL);
|
|
||||||
|
|
||||||
// The seed argument to the hash function.
|
|
||||||
Named_object* seed_arg = gogo->lookup("seed", NULL);
|
|
||||||
go_assert(seed_arg != NULL);
|
|
||||||
|
|
||||||
Named_object* hash_fn;
|
|
||||||
Named_object* equal_fn;
|
|
||||||
name->real_type()->type_functions(gogo, base_type, hash_fntype, equal_fntype,
|
|
||||||
&hash_fn, &equal_fn);
|
|
||||||
|
|
||||||
// Call the hash function for the base type.
|
|
||||||
Expression* key_ref = Expression::make_var_reference(key_arg, bloc);
|
|
||||||
Expression* seed_ref = Expression::make_var_reference(seed_arg, bloc);
|
|
||||||
Expression_list* args = new Expression_list();
|
|
||||||
args->push_back(key_ref);
|
|
||||||
args->push_back(seed_ref);
|
|
||||||
Expression* func = Expression::make_func_reference(hash_fn, NULL, bloc);
|
|
||||||
Expression* call = Expression::make_call(func, args, false, bloc);
|
|
||||||
|
|
||||||
// Return the hash of the base type.
|
|
||||||
Expression_list* vals = new Expression_list();
|
|
||||||
vals->push_back(call);
|
|
||||||
Statement* s = Statement::make_return_statement(vals, bloc);
|
|
||||||
gogo->add_statement(s);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write an equality function that simply calls the equality function
|
// Write an equality function that simply calls the equality function
|
||||||
// for a named type. This is used when one named type is defined as
|
// for a named type. This is used when one named type is defined as
|
||||||
// another. This ensures that this case works when the other named
|
// another. This ensures that this case works when the other named
|
||||||
@ -2441,21 +2589,18 @@ Type::type_descriptor_constructor(Gogo* gogo, int runtime_type_kind,
|
|||||||
++p;
|
++p;
|
||||||
go_assert(p->is_field_name("hashfn"));
|
go_assert(p->is_field_name("hashfn"));
|
||||||
Function_type* hash_fntype = p->type()->function_type();
|
Function_type* hash_fntype = p->type()->function_type();
|
||||||
|
Named_object* hash_fn = this->hash_function(gogo, name, hash_fntype);
|
||||||
++p;
|
|
||||||
go_assert(p->is_field_name("equalfn"));
|
|
||||||
Function_type* equal_fntype = p->type()->function_type();
|
|
||||||
|
|
||||||
Named_object* hash_fn;
|
|
||||||
Named_object* equal_fn;
|
|
||||||
this->type_functions(gogo, name, hash_fntype, equal_fntype, &hash_fn,
|
|
||||||
&equal_fn);
|
|
||||||
if (hash_fn == NULL)
|
if (hash_fn == NULL)
|
||||||
vals->push_back(Expression::make_cast(hash_fntype,
|
vals->push_back(Expression::make_cast(hash_fntype,
|
||||||
Expression::make_nil(bloc),
|
Expression::make_nil(bloc),
|
||||||
bloc));
|
bloc));
|
||||||
else
|
else
|
||||||
vals->push_back(Expression::make_func_reference(hash_fn, NULL, bloc));
|
vals->push_back(Expression::make_func_reference(hash_fn, NULL, bloc));
|
||||||
|
|
||||||
|
++p;
|
||||||
|
go_assert(p->is_field_name("equalfn"));
|
||||||
|
Function_type* equal_fntype = p->type()->function_type();
|
||||||
|
Named_object* equal_fn = this->equal_function(gogo, name, equal_fntype);
|
||||||
if (equal_fn == NULL)
|
if (equal_fn == NULL)
|
||||||
vals->push_back(Expression::make_cast(equal_fntype,
|
vals->push_back(Expression::make_cast(equal_fntype,
|
||||||
Expression::make_nil(bloc),
|
Expression::make_nil(bloc),
|
||||||
@ -6459,8 +6604,7 @@ Struct_type::do_type_descriptor(Gogo* gogo, Named_type* name)
|
|||||||
|
|
||||||
void
|
void
|
||||||
Struct_type::write_hash_function(Gogo* gogo, Named_type*,
|
Struct_type::write_hash_function(Gogo* gogo, Named_type*,
|
||||||
Function_type* hash_fntype,
|
Function_type* hash_fntype)
|
||||||
Function_type* equal_fntype)
|
|
||||||
{
|
{
|
||||||
Location bloc = Linemap::predeclared_location();
|
Location bloc = Linemap::predeclared_location();
|
||||||
|
|
||||||
@ -6506,10 +6650,8 @@ Struct_type::write_hash_function(Gogo* gogo, Named_type*,
|
|||||||
subkey = Expression::make_cast(key_arg_type, subkey, bloc);
|
subkey = Expression::make_cast(key_arg_type, subkey, bloc);
|
||||||
|
|
||||||
// Get the hash function to use for the type of this field.
|
// Get the hash function to use for the type of this field.
|
||||||
Named_object* hash_fn;
|
Named_object* hash_fn =
|
||||||
Named_object* equal_fn;
|
pf->type()->hash_function(gogo, pf->type()->named_type(), hash_fntype);
|
||||||
pf->type()->type_functions(gogo, pf->type()->named_type(), hash_fntype,
|
|
||||||
equal_fntype, &hash_fn, &equal_fn);
|
|
||||||
|
|
||||||
// Call the hash function for the field, passing retval as the seed.
|
// Call the hash function for the field, passing retval as the seed.
|
||||||
ref = Expression::make_temporary_reference(retval, bloc);
|
ref = Expression::make_temporary_reference(retval, bloc);
|
||||||
@ -7306,8 +7448,7 @@ Array_type::do_hash_for_method(Gogo* gogo, int flags) const
|
|||||||
|
|
||||||
void
|
void
|
||||||
Array_type::write_hash_function(Gogo* gogo, Named_type* name,
|
Array_type::write_hash_function(Gogo* gogo, Named_type* name,
|
||||||
Function_type* hash_fntype,
|
Function_type* hash_fntype)
|
||||||
Function_type* equal_fntype)
|
|
||||||
{
|
{
|
||||||
Location bloc = Linemap::predeclared_location();
|
Location bloc = Linemap::predeclared_location();
|
||||||
|
|
||||||
@ -7356,11 +7497,9 @@ Array_type::write_hash_function(Gogo* gogo, Named_type* name,
|
|||||||
gogo->start_block(bloc);
|
gogo->start_block(bloc);
|
||||||
|
|
||||||
// Get the hash function for the element type.
|
// Get the hash function for the element type.
|
||||||
Named_object* hash_fn;
|
Named_object* hash_fn =
|
||||||
Named_object* equal_fn;
|
this->element_type_->hash_function(gogo, this->element_type_->named_type(),
|
||||||
this->element_type_->type_functions(gogo, this->element_type_->named_type(),
|
hash_fntype);
|
||||||
hash_fntype, equal_fntype, &hash_fn,
|
|
||||||
&equal_fn);
|
|
||||||
|
|
||||||
// Get a pointer to this element in the loop.
|
// Get a pointer to this element in the loop.
|
||||||
Expression* subkey = Expression::make_temporary_reference(key, bloc);
|
Expression* subkey = Expression::make_temporary_reference(key, bloc);
|
||||||
|
@ -1053,19 +1053,27 @@ class Type
|
|||||||
bool
|
bool
|
||||||
needs_specific_type_functions(Gogo*);
|
needs_specific_type_functions(Gogo*);
|
||||||
|
|
||||||
// Get the hash and equality functions for a type.
|
// Get the equality function for a type. Returns NULL if the type
|
||||||
void
|
// is not comparable.
|
||||||
type_functions(Gogo*, Named_type* name, Function_type* hash_fntype,
|
Named_object*
|
||||||
Function_type* equal_fntype, Named_object** hash_fn,
|
equal_function(Gogo*, Named_type* name, Function_type* equal_fntype);
|
||||||
Named_object** equal_fn);
|
|
||||||
|
|
||||||
// Write the hash and equality type functions.
|
// Get the hash function for a type. Returns NULL if the type is
|
||||||
|
// not comparable.
|
||||||
|
Named_object*
|
||||||
|
hash_function(Gogo*, Named_type* name, Function_type* hash_fntype);
|
||||||
|
|
||||||
|
// Write the equal function for a type.
|
||||||
void
|
void
|
||||||
write_specific_type_functions(Gogo*, Named_type*, int64_t size,
|
write_equal_function(Gogo*, Named_type*, int64_t size,
|
||||||
const std::string& hash_name,
|
const std::string& equal_name,
|
||||||
Function_type* hash_fntype,
|
Function_type* equal_fntype);
|
||||||
const std::string& equal_name,
|
|
||||||
Function_type* equal_fntype);
|
// Write the hash function for a type.
|
||||||
|
void
|
||||||
|
write_hash_function(Gogo*, Named_type*, int64_t size,
|
||||||
|
const std::string& hash_name,
|
||||||
|
Function_type* hash_fntype);
|
||||||
|
|
||||||
// Return the alignment required by the memequalN function.
|
// Return the alignment required by the memequalN function.
|
||||||
static int64_t memequal_align(Gogo*, int size);
|
static int64_t memequal_align(Gogo*, int size);
|
||||||
@ -1274,13 +1282,15 @@ class Type
|
|||||||
Expression*
|
Expression*
|
||||||
gcprog_constructor(Gogo*, int64_t ptrsize, int64_t ptrdata);
|
gcprog_constructor(Gogo*, int64_t ptrsize, int64_t ptrdata);
|
||||||
|
|
||||||
// Build the hash and equality type functions for a type which needs
|
// Build the hash function for a type that needs specific functions.
|
||||||
// specific functions.
|
Named_object*
|
||||||
void
|
build_hash_function(Gogo*, Named_type*, int64_t size,
|
||||||
specific_type_functions(Gogo*, Named_type*, int64_t size,
|
Function_type* hash_fntype);
|
||||||
Function_type* hash_fntype,
|
|
||||||
Function_type* equal_fntype, Named_object** hash_fn,
|
// Build the equal function for a type that needs specific functions.
|
||||||
Named_object** equal_fn);
|
Named_object*
|
||||||
|
build_equal_function(Gogo*, Named_type*, int64_t size,
|
||||||
|
Function_type* equal_fntype);
|
||||||
|
|
||||||
void
|
void
|
||||||
write_identity_hash(Gogo*, int64_t size);
|
write_identity_hash(Gogo*, int64_t size);
|
||||||
@ -1289,8 +1299,7 @@ class Type
|
|||||||
write_identity_equal(Gogo*, int64_t size);
|
write_identity_equal(Gogo*, int64_t size);
|
||||||
|
|
||||||
void
|
void
|
||||||
write_named_hash(Gogo*, Named_type*, Function_type* hash_fntype,
|
write_named_hash(Gogo*, Named_type*, Function_type* hash_fntype);
|
||||||
Function_type* equal_fntype);
|
|
||||||
|
|
||||||
void
|
void
|
||||||
write_named_equal(Gogo*, Named_type*);
|
write_named_equal(Gogo*, Named_type*);
|
||||||
@ -1394,13 +1403,13 @@ class Type
|
|||||||
// A list of builtin named types.
|
// A list of builtin named types.
|
||||||
static std::vector<Named_type*> named_builtin_types;
|
static std::vector<Named_type*> named_builtin_types;
|
||||||
|
|
||||||
// A map from types which need specific type functions to the type
|
// A map from types that need a specific hash or equality function
|
||||||
// functions themselves.
|
// to the hash or equality function.
|
||||||
typedef std::pair<Named_object*, Named_object*> Hash_equal_fn;
|
typedef Unordered_map_hash(const Type*, Named_object*, Type_hash_identical,
|
||||||
typedef Unordered_map_hash(const Type*, Hash_equal_fn, Type_hash_identical,
|
Type_identical) Type_function;
|
||||||
Type_identical) Type_functions;
|
|
||||||
|
|
||||||
static Type_functions type_functions_table;
|
static Type_function type_hash_functions_table;
|
||||||
|
static Type_function type_equal_functions_table;
|
||||||
|
|
||||||
// Cache for reusing existing pointer types; maps from pointed-to-type
|
// Cache for reusing existing pointer types; maps from pointed-to-type
|
||||||
// to pointer type.
|
// to pointer type.
|
||||||
@ -2619,7 +2628,7 @@ class Struct_type : public Type
|
|||||||
|
|
||||||
// Write the hash function for this type.
|
// Write the hash function for this type.
|
||||||
void
|
void
|
||||||
write_hash_function(Gogo*, Named_type*, Function_type*, Function_type*);
|
write_hash_function(Gogo*, Named_type*, Function_type*);
|
||||||
|
|
||||||
// Write the equality function for this type.
|
// Write the equality function for this type.
|
||||||
void
|
void
|
||||||
@ -2806,7 +2815,7 @@ class Array_type : public Type
|
|||||||
|
|
||||||
// Write the hash function for this type.
|
// Write the hash function for this type.
|
||||||
void
|
void
|
||||||
write_hash_function(Gogo*, Named_type*, Function_type*, Function_type*);
|
write_hash_function(Gogo*, Named_type*, Function_type*);
|
||||||
|
|
||||||
// Write the equality function for this type.
|
// Write the equality function for this type.
|
||||||
void
|
void
|
||||||
|
Loading…
Reference in New Issue
Block a user