mirror of
https://github.com/KhronosGroup/SPIRV-Tools
synced 2024-10-18 11:10:05 +00:00
Optimize Type::HashValue (#4707)
Incrementally compute the hash instead of collecting words Avoids allocating temporary space in a std::vector and std::u32string, and making three passes over all the hashed data. Switch to using std::vector to prevent processing duplicates instead of std::unordered_set: avoids an allocation/deletion every call to ComputeHashValue, and ends up faster due to much better cache behaviour and smaller constant-factor when searching the (generally very small) list. In my test case, made Type::HashValue go from 7.5% of compilation time to .5%
This commit is contained in:
parent
471428a04f
commit
a123632ed9
1
BUILD.gn
1
BUILD.gn
@ -448,6 +448,7 @@ static_library("spvtools") {
|
|||||||
"source/util/bit_vector.cpp",
|
"source/util/bit_vector.cpp",
|
||||||
"source/util/bit_vector.h",
|
"source/util/bit_vector.h",
|
||||||
"source/util/bitutils.h",
|
"source/util/bitutils.h",
|
||||||
|
"source/util/hash_combine.h",
|
||||||
"source/util/hex_float.h",
|
"source/util/hex_float.h",
|
||||||
"source/util/ilist.h",
|
"source/util/ilist.h",
|
||||||
"source/util/ilist_node.h",
|
"source/util/ilist_node.h",
|
||||||
|
@ -224,6 +224,7 @@ set(SPIRV_SOURCES
|
|||||||
|
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/util/bitutils.h
|
${CMAKE_CURRENT_SOURCE_DIR}/util/bitutils.h
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/util/bit_vector.h
|
${CMAKE_CURRENT_SOURCE_DIR}/util/bit_vector.h
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/util/hash_combine.h
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/util/hex_float.h
|
${CMAKE_CURRENT_SOURCE_DIR}/util/hex_float.h
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/util/make_unique.h
|
${CMAKE_CURRENT_SOURCE_DIR}/util/make_unique.h
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/util/parse_number.h
|
${CMAKE_CURRENT_SOURCE_DIR}/util/parse_number.h
|
||||||
|
@ -21,6 +21,7 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <unordered_set>
|
#include <unordered_set>
|
||||||
|
|
||||||
|
#include "source/util/hash_combine.h"
|
||||||
#include "source/util/make_unique.h"
|
#include "source/util/make_unique.h"
|
||||||
#include "spirv/unified1/spirv.h"
|
#include "spirv/unified1/spirv.h"
|
||||||
|
|
||||||
@ -28,6 +29,7 @@ namespace spvtools {
|
|||||||
namespace opt {
|
namespace opt {
|
||||||
namespace analysis {
|
namespace analysis {
|
||||||
|
|
||||||
|
using spvtools::utils::hash_combine;
|
||||||
using U32VecVec = std::vector<std::vector<uint32_t>>;
|
using U32VecVec = std::vector<std::vector<uint32_t>>;
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
@ -182,23 +184,26 @@ bool Type::operator==(const Type& other) const {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Type::GetHashWords(std::vector<uint32_t>* words,
|
size_t Type::ComputeHashValue(size_t hash, SeenTypes* seen) const {
|
||||||
std::unordered_set<const Type*>* seen) const {
|
// Linear search through a dense, cache coherent vector is faster than O(log
|
||||||
if (!seen->insert(this).second) {
|
// n) search in a complex data structure (eg std::set) for the generally small
|
||||||
return;
|
// number of nodes. It also skips the overhead of an new/delete per Type
|
||||||
|
// (when inserting/removing from a set).
|
||||||
|
if (std::find(seen->begin(), seen->end(), this) != seen->end()) {
|
||||||
|
return hash;
|
||||||
}
|
}
|
||||||
|
|
||||||
words->push_back(kind_);
|
seen->push_back(this);
|
||||||
|
|
||||||
|
hash = hash_combine(hash, uint32_t(kind_));
|
||||||
for (const auto& d : decorations_) {
|
for (const auto& d : decorations_) {
|
||||||
for (auto w : d) {
|
hash = hash_combine(hash, d);
|
||||||
words->push_back(w);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (kind_) {
|
switch (kind_) {
|
||||||
#define DeclareKindCase(type) \
|
#define DeclareKindCase(type) \
|
||||||
case k##type: \
|
case k##type: \
|
||||||
As##type()->GetExtraHashWords(words, seen); \
|
hash = As##type()->ComputeExtraStateHash(hash, seen); \
|
||||||
break
|
break
|
||||||
DeclareKindCase(Void);
|
DeclareKindCase(Void);
|
||||||
DeclareKindCase(Bool);
|
DeclareKindCase(Bool);
|
||||||
@ -232,18 +237,13 @@ void Type::GetHashWords(std::vector<uint32_t>* words,
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
seen->erase(this);
|
seen->pop_back();
|
||||||
|
return hash;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t Type::HashValue() const {
|
size_t Type::HashValue() const {
|
||||||
std::u32string h;
|
SeenTypes seen;
|
||||||
std::vector<uint32_t> words;
|
return ComputeHashValue(0, &seen);
|
||||||
GetHashWords(&words);
|
|
||||||
for (auto w : words) {
|
|
||||||
h.push_back(w);
|
|
||||||
}
|
|
||||||
|
|
||||||
return std::hash<std::u32string>()(h);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Integer::IsSameImpl(const Type* that, IsSameCache*) const {
|
bool Integer::IsSameImpl(const Type* that, IsSameCache*) const {
|
||||||
@ -258,10 +258,8 @@ std::string Integer::str() const {
|
|||||||
return oss.str();
|
return oss.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Integer::GetExtraHashWords(std::vector<uint32_t>* words,
|
size_t Integer::ComputeExtraStateHash(size_t hash, SeenTypes*) const {
|
||||||
std::unordered_set<const Type*>*) const {
|
return hash_combine(hash, width_, signed_);
|
||||||
words->push_back(width_);
|
|
||||||
words->push_back(signed_);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Float::IsSameImpl(const Type* that, IsSameCache*) const {
|
bool Float::IsSameImpl(const Type* that, IsSameCache*) const {
|
||||||
@ -275,9 +273,8 @@ std::string Float::str() const {
|
|||||||
return oss.str();
|
return oss.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Float::GetExtraHashWords(std::vector<uint32_t>* words,
|
size_t Float::ComputeExtraStateHash(size_t hash, SeenTypes*) const {
|
||||||
std::unordered_set<const Type*>*) const {
|
return hash_combine(hash, width_);
|
||||||
words->push_back(width_);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector::Vector(const Type* type, uint32_t count)
|
Vector::Vector(const Type* type, uint32_t count)
|
||||||
@ -299,10 +296,11 @@ std::string Vector::str() const {
|
|||||||
return oss.str();
|
return oss.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Vector::GetExtraHashWords(std::vector<uint32_t>* words,
|
size_t Vector::ComputeExtraStateHash(size_t hash, SeenTypes* seen) const {
|
||||||
std::unordered_set<const Type*>* seen) const {
|
// prefer form that doesn't require push/pop from stack: add state and
|
||||||
element_type_->GetHashWords(words, seen);
|
// make tail call.
|
||||||
words->push_back(count_);
|
hash = hash_combine(hash, count_);
|
||||||
|
return element_type_->ComputeHashValue(hash, seen);
|
||||||
}
|
}
|
||||||
|
|
||||||
Matrix::Matrix(const Type* type, uint32_t count)
|
Matrix::Matrix(const Type* type, uint32_t count)
|
||||||
@ -324,10 +322,9 @@ std::string Matrix::str() const {
|
|||||||
return oss.str();
|
return oss.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Matrix::GetExtraHashWords(std::vector<uint32_t>* words,
|
size_t Matrix::ComputeExtraStateHash(size_t hash, SeenTypes* seen) const {
|
||||||
std::unordered_set<const Type*>* seen) const {
|
hash = hash_combine(hash, count_);
|
||||||
element_type_->GetHashWords(words, seen);
|
return element_type_->ComputeHashValue(hash, seen);
|
||||||
words->push_back(count_);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Image::Image(Type* type, SpvDim dimen, uint32_t d, bool array, bool multisample,
|
Image::Image(Type* type, SpvDim dimen, uint32_t d, bool array, bool multisample,
|
||||||
@ -362,16 +359,10 @@ std::string Image::str() const {
|
|||||||
return oss.str();
|
return oss.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Image::GetExtraHashWords(std::vector<uint32_t>* words,
|
size_t Image::ComputeExtraStateHash(size_t hash, SeenTypes* seen) const {
|
||||||
std::unordered_set<const Type*>* seen) const {
|
hash = hash_combine(hash, uint32_t(dim_), depth_, arrayed_, ms_, sampled_,
|
||||||
sampled_type_->GetHashWords(words, seen);
|
uint32_t(format_), uint32_t(access_qualifier_));
|
||||||
words->push_back(dim_);
|
return sampled_type_->ComputeHashValue(hash, seen);
|
||||||
words->push_back(depth_);
|
|
||||||
words->push_back(arrayed_);
|
|
||||||
words->push_back(ms_);
|
|
||||||
words->push_back(sampled_);
|
|
||||||
words->push_back(format_);
|
|
||||||
words->push_back(access_qualifier_);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SampledImage::IsSameImpl(const Type* that, IsSameCache* seen) const {
|
bool SampledImage::IsSameImpl(const Type* that, IsSameCache* seen) const {
|
||||||
@ -387,9 +378,8 @@ std::string SampledImage::str() const {
|
|||||||
return oss.str();
|
return oss.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SampledImage::GetExtraHashWords(
|
size_t SampledImage::ComputeExtraStateHash(size_t hash, SeenTypes* seen) const {
|
||||||
std::vector<uint32_t>* words, std::unordered_set<const Type*>* seen) const {
|
return image_type_->ComputeHashValue(hash, seen);
|
||||||
image_type_->GetHashWords(words, seen);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Array::Array(const Type* type, const Array::LengthInfo& length_info_arg)
|
Array::Array(const Type* type, const Array::LengthInfo& length_info_arg)
|
||||||
@ -422,11 +412,9 @@ std::string Array::str() const {
|
|||||||
return oss.str();
|
return oss.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Array::GetExtraHashWords(std::vector<uint32_t>* words,
|
size_t Array::ComputeExtraStateHash(size_t hash, SeenTypes* seen) const {
|
||||||
std::unordered_set<const Type*>* seen) const {
|
hash = hash_combine(hash, length_info_.words);
|
||||||
element_type_->GetHashWords(words, seen);
|
return element_type_->ComputeHashValue(hash, seen);
|
||||||
words->insert(words->end(), length_info_.words.begin(),
|
|
||||||
length_info_.words.end());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Array::ReplaceElementType(const Type* type) { element_type_ = type; }
|
void Array::ReplaceElementType(const Type* type) { element_type_ = type; }
|
||||||
@ -449,9 +437,8 @@ std::string RuntimeArray::str() const {
|
|||||||
return oss.str();
|
return oss.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
void RuntimeArray::GetExtraHashWords(
|
size_t RuntimeArray::ComputeExtraStateHash(size_t hash, SeenTypes* seen) const {
|
||||||
std::vector<uint32_t>* words, std::unordered_set<const Type*>* seen) const {
|
return element_type_->ComputeHashValue(hash, seen);
|
||||||
element_type_->GetHashWords(words, seen);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void RuntimeArray::ReplaceElementType(const Type* type) {
|
void RuntimeArray::ReplaceElementType(const Type* type) {
|
||||||
@ -508,19 +495,14 @@ std::string Struct::str() const {
|
|||||||
return oss.str();
|
return oss.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Struct::GetExtraHashWords(std::vector<uint32_t>* words,
|
size_t Struct::ComputeExtraStateHash(size_t hash, SeenTypes* seen) const {
|
||||||
std::unordered_set<const Type*>* seen) const {
|
|
||||||
for (auto* t : element_types_) {
|
for (auto* t : element_types_) {
|
||||||
t->GetHashWords(words, seen);
|
hash = t->ComputeHashValue(hash, seen);
|
||||||
}
|
}
|
||||||
for (const auto& pair : element_decorations_) {
|
for (const auto& pair : element_decorations_) {
|
||||||
words->push_back(pair.first);
|
hash = hash_combine(hash, pair.first, pair.second);
|
||||||
for (const auto& d : pair.second) {
|
|
||||||
for (auto w : d) {
|
|
||||||
words->push_back(w);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
return hash;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Opaque::IsSameImpl(const Type* that, IsSameCache*) const {
|
bool Opaque::IsSameImpl(const Type* that, IsSameCache*) const {
|
||||||
@ -535,11 +517,8 @@ std::string Opaque::str() const {
|
|||||||
return oss.str();
|
return oss.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Opaque::GetExtraHashWords(std::vector<uint32_t>* words,
|
size_t Opaque::ComputeExtraStateHash(size_t hash, SeenTypes*) const {
|
||||||
std::unordered_set<const Type*>*) const {
|
return hash_combine(hash, name_);
|
||||||
for (auto c : name_) {
|
|
||||||
words->push_back(static_cast<char32_t>(c));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Pointer::Pointer(const Type* type, SpvStorageClass sc)
|
Pointer::Pointer(const Type* type, SpvStorageClass sc)
|
||||||
@ -568,10 +547,9 @@ std::string Pointer::str() const {
|
|||||||
return os.str();
|
return os.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Pointer::GetExtraHashWords(std::vector<uint32_t>* words,
|
size_t Pointer::ComputeExtraStateHash(size_t hash, SeenTypes* seen) const {
|
||||||
std::unordered_set<const Type*>* seen) const {
|
hash = hash_combine(hash, uint32_t(storage_class_));
|
||||||
pointee_type_->GetHashWords(words, seen);
|
return pointee_type_->ComputeHashValue(hash, seen);
|
||||||
words->push_back(storage_class_);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Pointer::SetPointeeType(const Type* type) { pointee_type_ = type; }
|
void Pointer::SetPointeeType(const Type* type) { pointee_type_ = type; }
|
||||||
@ -605,12 +583,11 @@ std::string Function::str() const {
|
|||||||
return oss.str();
|
return oss.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Function::GetExtraHashWords(std::vector<uint32_t>* words,
|
size_t Function::ComputeExtraStateHash(size_t hash, SeenTypes* seen) const {
|
||||||
std::unordered_set<const Type*>* seen) const {
|
|
||||||
return_type_->GetHashWords(words, seen);
|
|
||||||
for (const auto* t : param_types_) {
|
for (const auto* t : param_types_) {
|
||||||
t->GetHashWords(words, seen);
|
hash = t->ComputeHashValue(hash, seen);
|
||||||
}
|
}
|
||||||
|
return return_type_->ComputeHashValue(hash, seen);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Function::SetReturnType(const Type* type) { return_type_ = type; }
|
void Function::SetReturnType(const Type* type) { return_type_ = type; }
|
||||||
@ -627,9 +604,8 @@ std::string Pipe::str() const {
|
|||||||
return oss.str();
|
return oss.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Pipe::GetExtraHashWords(std::vector<uint32_t>* words,
|
size_t Pipe::ComputeExtraStateHash(size_t hash, SeenTypes*) const {
|
||||||
std::unordered_set<const Type*>*) const {
|
return hash_combine(hash, uint32_t(access_qualifier_));
|
||||||
words->push_back(access_qualifier_);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ForwardPointer::IsSameImpl(const Type* that, IsSameCache*) const {
|
bool ForwardPointer::IsSameImpl(const Type* that, IsSameCache*) const {
|
||||||
@ -652,11 +628,11 @@ std::string ForwardPointer::str() const {
|
|||||||
return oss.str();
|
return oss.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ForwardPointer::GetExtraHashWords(
|
size_t ForwardPointer::ComputeExtraStateHash(size_t hash,
|
||||||
std::vector<uint32_t>* words, std::unordered_set<const Type*>* seen) const {
|
SeenTypes* seen) const {
|
||||||
words->push_back(target_id_);
|
hash = hash_combine(hash, target_id_, uint32_t(storage_class_));
|
||||||
words->push_back(storage_class_);
|
if (pointer_) hash = pointer_->ComputeHashValue(hash, seen);
|
||||||
if (pointer_) pointer_->GetHashWords(words, seen);
|
return hash;
|
||||||
}
|
}
|
||||||
|
|
||||||
CooperativeMatrixNV::CooperativeMatrixNV(const Type* type, const uint32_t scope,
|
CooperativeMatrixNV::CooperativeMatrixNV(const Type* type, const uint32_t scope,
|
||||||
@ -680,12 +656,10 @@ std::string CooperativeMatrixNV::str() const {
|
|||||||
return oss.str();
|
return oss.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CooperativeMatrixNV::GetExtraHashWords(
|
size_t CooperativeMatrixNV::ComputeExtraStateHash(size_t hash,
|
||||||
std::vector<uint32_t>* words, std::unordered_set<const Type*>* pSet) const {
|
SeenTypes* seen) const {
|
||||||
component_type_->GetHashWords(words, pSet);
|
hash = hash_combine(hash, scope_id_, rows_id_, columns_id_);
|
||||||
words->push_back(scope_id_);
|
return component_type_->ComputeHashValue(hash, seen);
|
||||||
words->push_back(rows_id_);
|
|
||||||
words->push_back(columns_id_);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CooperativeMatrixNV::IsSameImpl(const Type* that,
|
bool CooperativeMatrixNV::IsSameImpl(const Type* that,
|
||||||
|
@ -28,6 +28,7 @@
|
|||||||
|
|
||||||
#include "source/latest_version_spirv_header.h"
|
#include "source/latest_version_spirv_header.h"
|
||||||
#include "source/opt/instruction.h"
|
#include "source/opt/instruction.h"
|
||||||
|
#include "source/util/small_vector.h"
|
||||||
#include "spirv-tools/libspirv.h"
|
#include "spirv-tools/libspirv.h"
|
||||||
|
|
||||||
namespace spvtools {
|
namespace spvtools {
|
||||||
@ -67,6 +68,8 @@ class Type {
|
|||||||
public:
|
public:
|
||||||
typedef std::set<std::pair<const Pointer*, const Pointer*>> IsSameCache;
|
typedef std::set<std::pair<const Pointer*, const Pointer*>> IsSameCache;
|
||||||
|
|
||||||
|
using SeenTypes = spvtools::utils::SmallVector<const Type*, 8>;
|
||||||
|
|
||||||
// Available subtypes.
|
// Available subtypes.
|
||||||
//
|
//
|
||||||
// When adding a new derived class of Type, please add an entry to the enum.
|
// When adding a new derived class of Type, please add an entry to the enum.
|
||||||
@ -155,21 +158,7 @@ class Type {
|
|||||||
// Returns the hash value of this type.
|
// Returns the hash value of this type.
|
||||||
size_t HashValue() const;
|
size_t HashValue() const;
|
||||||
|
|
||||||
// Adds the necessary words to compute a hash value of this type to |words|.
|
size_t ComputeHashValue(size_t hash, SeenTypes* seen) const;
|
||||||
void GetHashWords(std::vector<uint32_t>* words) const {
|
|
||||||
std::unordered_set<const Type*> seen;
|
|
||||||
GetHashWords(words, &seen);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Adds the necessary words to compute a hash value of this type to |words|.
|
|
||||||
void GetHashWords(std::vector<uint32_t>* words,
|
|
||||||
std::unordered_set<const Type*>* seen) const;
|
|
||||||
|
|
||||||
// Adds necessary extra words for a subtype to calculate a hash value into
|
|
||||||
// |words|.
|
|
||||||
virtual void GetExtraHashWords(
|
|
||||||
std::vector<uint32_t>* words,
|
|
||||||
std::unordered_set<const Type*>* pSet) const = 0;
|
|
||||||
|
|
||||||
// A bunch of methods for casting this type to a given type. Returns this if the
|
// A bunch of methods for casting this type to a given type. Returns this if the
|
||||||
// cast can be done, nullptr otherwise.
|
// cast can be done, nullptr otherwise.
|
||||||
@ -205,6 +194,10 @@ class Type {
|
|||||||
DeclareCastMethod(RayQueryKHR)
|
DeclareCastMethod(RayQueryKHR)
|
||||||
#undef DeclareCastMethod
|
#undef DeclareCastMethod
|
||||||
|
|
||||||
|
protected:
|
||||||
|
// Add any type-specific state to |hash| and returns new hash.
|
||||||
|
virtual size_t ComputeExtraStateHash(size_t hash, SeenTypes* seen) const = 0;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// Decorations attached to this type. Each decoration is encoded as a vector
|
// Decorations attached to this type. Each decoration is encoded as a vector
|
||||||
// of uint32_t numbers. The first uint32_t number is the decoration value,
|
// of uint32_t numbers. The first uint32_t number is the decoration value,
|
||||||
@ -233,8 +226,7 @@ class Integer : public Type {
|
|||||||
uint32_t width() const { return width_; }
|
uint32_t width() const { return width_; }
|
||||||
bool IsSigned() const { return signed_; }
|
bool IsSigned() const { return signed_; }
|
||||||
|
|
||||||
void GetExtraHashWords(std::vector<uint32_t>* words,
|
size_t ComputeExtraStateHash(size_t hash, SeenTypes* seen) const override;
|
||||||
std::unordered_set<const Type*>* pSet) const override;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool IsSameImpl(const Type* that, IsSameCache*) const override;
|
bool IsSameImpl(const Type* that, IsSameCache*) const override;
|
||||||
@ -254,8 +246,7 @@ class Float : public Type {
|
|||||||
const Float* AsFloat() const override { return this; }
|
const Float* AsFloat() const override { return this; }
|
||||||
uint32_t width() const { return width_; }
|
uint32_t width() const { return width_; }
|
||||||
|
|
||||||
void GetExtraHashWords(std::vector<uint32_t>* words,
|
size_t ComputeExtraStateHash(size_t hash, SeenTypes* seen) const override;
|
||||||
std::unordered_set<const Type*>* pSet) const override;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool IsSameImpl(const Type* that, IsSameCache*) const override;
|
bool IsSameImpl(const Type* that, IsSameCache*) const override;
|
||||||
@ -275,8 +266,7 @@ class Vector : public Type {
|
|||||||
Vector* AsVector() override { return this; }
|
Vector* AsVector() override { return this; }
|
||||||
const Vector* AsVector() const override { return this; }
|
const Vector* AsVector() const override { return this; }
|
||||||
|
|
||||||
void GetExtraHashWords(std::vector<uint32_t>* words,
|
size_t ComputeExtraStateHash(size_t hash, SeenTypes* seen) const override;
|
||||||
std::unordered_set<const Type*>* pSet) const override;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool IsSameImpl(const Type* that, IsSameCache*) const override;
|
bool IsSameImpl(const Type* that, IsSameCache*) const override;
|
||||||
@ -297,8 +287,7 @@ class Matrix : public Type {
|
|||||||
Matrix* AsMatrix() override { return this; }
|
Matrix* AsMatrix() override { return this; }
|
||||||
const Matrix* AsMatrix() const override { return this; }
|
const Matrix* AsMatrix() const override { return this; }
|
||||||
|
|
||||||
void GetExtraHashWords(std::vector<uint32_t>* words,
|
size_t ComputeExtraStateHash(size_t hash, SeenTypes* seen) const override;
|
||||||
std::unordered_set<const Type*>* pSet) const override;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool IsSameImpl(const Type* that, IsSameCache*) const override;
|
bool IsSameImpl(const Type* that, IsSameCache*) const override;
|
||||||
@ -328,8 +317,7 @@ class Image : public Type {
|
|||||||
SpvImageFormat format() const { return format_; }
|
SpvImageFormat format() const { return format_; }
|
||||||
SpvAccessQualifier access_qualifier() const { return access_qualifier_; }
|
SpvAccessQualifier access_qualifier() const { return access_qualifier_; }
|
||||||
|
|
||||||
void GetExtraHashWords(std::vector<uint32_t>* words,
|
size_t ComputeExtraStateHash(size_t hash, SeenTypes* seen) const override;
|
||||||
std::unordered_set<const Type*>* pSet) const override;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool IsSameImpl(const Type* that, IsSameCache*) const override;
|
bool IsSameImpl(const Type* that, IsSameCache*) const override;
|
||||||
@ -356,8 +344,7 @@ class SampledImage : public Type {
|
|||||||
|
|
||||||
const Type* image_type() const { return image_type_; }
|
const Type* image_type() const { return image_type_; }
|
||||||
|
|
||||||
void GetExtraHashWords(std::vector<uint32_t>* words,
|
size_t ComputeExtraStateHash(size_t hash, SeenTypes* seen) const override;
|
||||||
std::unordered_set<const Type*>* pSet) const override;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool IsSameImpl(const Type* that, IsSameCache*) const override;
|
bool IsSameImpl(const Type* that, IsSameCache*) const override;
|
||||||
@ -400,8 +387,7 @@ class Array : public Type {
|
|||||||
Array* AsArray() override { return this; }
|
Array* AsArray() override { return this; }
|
||||||
const Array* AsArray() const override { return this; }
|
const Array* AsArray() const override { return this; }
|
||||||
|
|
||||||
void GetExtraHashWords(std::vector<uint32_t>* words,
|
size_t ComputeExtraStateHash(size_t hash, SeenTypes* seen) const override;
|
||||||
std::unordered_set<const Type*>* pSet) const override;
|
|
||||||
|
|
||||||
void ReplaceElementType(const Type* element_type);
|
void ReplaceElementType(const Type* element_type);
|
||||||
|
|
||||||
@ -423,8 +409,7 @@ class RuntimeArray : public Type {
|
|||||||
RuntimeArray* AsRuntimeArray() override { return this; }
|
RuntimeArray* AsRuntimeArray() override { return this; }
|
||||||
const RuntimeArray* AsRuntimeArray() const override { return this; }
|
const RuntimeArray* AsRuntimeArray() const override { return this; }
|
||||||
|
|
||||||
void GetExtraHashWords(std::vector<uint32_t>* words,
|
size_t ComputeExtraStateHash(size_t hash, SeenTypes* seen) const override;
|
||||||
std::unordered_set<const Type*>* pSet) const override;
|
|
||||||
|
|
||||||
void ReplaceElementType(const Type* element_type);
|
void ReplaceElementType(const Type* element_type);
|
||||||
|
|
||||||
@ -460,8 +445,7 @@ class Struct : public Type {
|
|||||||
Struct* AsStruct() override { return this; }
|
Struct* AsStruct() override { return this; }
|
||||||
const Struct* AsStruct() const override { return this; }
|
const Struct* AsStruct() const override { return this; }
|
||||||
|
|
||||||
void GetExtraHashWords(std::vector<uint32_t>* words,
|
size_t ComputeExtraStateHash(size_t hash, SeenTypes* seen) const override;
|
||||||
std::unordered_set<const Type*>* pSet) const override;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool IsSameImpl(const Type* that, IsSameCache*) const override;
|
bool IsSameImpl(const Type* that, IsSameCache*) const override;
|
||||||
@ -492,8 +476,7 @@ class Opaque : public Type {
|
|||||||
|
|
||||||
const std::string& name() const { return name_; }
|
const std::string& name() const { return name_; }
|
||||||
|
|
||||||
void GetExtraHashWords(std::vector<uint32_t>* words,
|
size_t ComputeExtraStateHash(size_t hash, SeenTypes* seen) const override;
|
||||||
std::unordered_set<const Type*>* pSet) const override;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool IsSameImpl(const Type* that, IsSameCache*) const override;
|
bool IsSameImpl(const Type* that, IsSameCache*) const override;
|
||||||
@ -513,8 +496,7 @@ class Pointer : public Type {
|
|||||||
Pointer* AsPointer() override { return this; }
|
Pointer* AsPointer() override { return this; }
|
||||||
const Pointer* AsPointer() const override { return this; }
|
const Pointer* AsPointer() const override { return this; }
|
||||||
|
|
||||||
void GetExtraHashWords(std::vector<uint32_t>* words,
|
size_t ComputeExtraStateHash(size_t hash, SeenTypes* seen) const override;
|
||||||
std::unordered_set<const Type*>* pSet) const override;
|
|
||||||
|
|
||||||
void SetPointeeType(const Type* type);
|
void SetPointeeType(const Type* type);
|
||||||
|
|
||||||
@ -540,8 +522,7 @@ class Function : public Type {
|
|||||||
const std::vector<const Type*>& param_types() const { return param_types_; }
|
const std::vector<const Type*>& param_types() const { return param_types_; }
|
||||||
std::vector<const Type*>& param_types() { return param_types_; }
|
std::vector<const Type*>& param_types() { return param_types_; }
|
||||||
|
|
||||||
void GetExtraHashWords(std::vector<uint32_t>* words,
|
size_t ComputeExtraStateHash(size_t hash, SeenTypes* seen) const override;
|
||||||
std::unordered_set<const Type*>*) const override;
|
|
||||||
|
|
||||||
void SetReturnType(const Type* type);
|
void SetReturnType(const Type* type);
|
||||||
|
|
||||||
@ -565,8 +546,7 @@ class Pipe : public Type {
|
|||||||
|
|
||||||
SpvAccessQualifier access_qualifier() const { return access_qualifier_; }
|
SpvAccessQualifier access_qualifier() const { return access_qualifier_; }
|
||||||
|
|
||||||
void GetExtraHashWords(std::vector<uint32_t>* words,
|
size_t ComputeExtraStateHash(size_t hash, SeenTypes* seen) const override;
|
||||||
std::unordered_set<const Type*>* pSet) const override;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool IsSameImpl(const Type* that, IsSameCache*) const override;
|
bool IsSameImpl(const Type* that, IsSameCache*) const override;
|
||||||
@ -593,8 +573,7 @@ class ForwardPointer : public Type {
|
|||||||
ForwardPointer* AsForwardPointer() override { return this; }
|
ForwardPointer* AsForwardPointer() override { return this; }
|
||||||
const ForwardPointer* AsForwardPointer() const override { return this; }
|
const ForwardPointer* AsForwardPointer() const override { return this; }
|
||||||
|
|
||||||
void GetExtraHashWords(std::vector<uint32_t>* words,
|
size_t ComputeExtraStateHash(size_t hash, SeenTypes* seen) const override;
|
||||||
std::unordered_set<const Type*>* pSet) const override;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool IsSameImpl(const Type* that, IsSameCache*) const override;
|
bool IsSameImpl(const Type* that, IsSameCache*) const override;
|
||||||
@ -617,8 +596,7 @@ class CooperativeMatrixNV : public Type {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GetExtraHashWords(std::vector<uint32_t>*,
|
size_t ComputeExtraStateHash(size_t hash, SeenTypes* seen) const override;
|
||||||
std::unordered_set<const Type*>*) const override;
|
|
||||||
|
|
||||||
const Type* component_type() const { return component_type_; }
|
const Type* component_type() const { return component_type_; }
|
||||||
uint32_t scope_id() const { return scope_id_; }
|
uint32_t scope_id() const { return scope_id_; }
|
||||||
@ -634,24 +612,25 @@ class CooperativeMatrixNV : public Type {
|
|||||||
const uint32_t columns_id_;
|
const uint32_t columns_id_;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define DefineParameterlessType(type, name) \
|
#define DefineParameterlessType(type, name) \
|
||||||
class type : public Type { \
|
class type : public Type { \
|
||||||
public: \
|
public: \
|
||||||
type() : Type(k##type) {} \
|
type() : Type(k##type) {} \
|
||||||
type(const type&) = default; \
|
type(const type&) = default; \
|
||||||
\
|
\
|
||||||
std::string str() const override { return #name; } \
|
std::string str() const override { return #name; } \
|
||||||
\
|
\
|
||||||
type* As##type() override { return this; } \
|
type* As##type() override { return this; } \
|
||||||
const type* As##type() const override { return this; } \
|
const type* As##type() const override { return this; } \
|
||||||
\
|
\
|
||||||
void GetExtraHashWords(std::vector<uint32_t>*, \
|
size_t ComputeExtraStateHash(size_t hash, SeenTypes*) const override { \
|
||||||
std::unordered_set<const Type*>*) const override {} \
|
return hash; \
|
||||||
\
|
} \
|
||||||
private: \
|
\
|
||||||
bool IsSameImpl(const Type* that, IsSameCache*) const override { \
|
private: \
|
||||||
return that->As##type() && HasSameDecorations(that); \
|
bool IsSameImpl(const Type* that, IsSameCache*) const override { \
|
||||||
} \
|
return that->As##type() && HasSameDecorations(that); \
|
||||||
|
} \
|
||||||
}
|
}
|
||||||
DefineParameterlessType(Void, void);
|
DefineParameterlessType(Void, void);
|
||||||
DefineParameterlessType(Bool, bool);
|
DefineParameterlessType(Bool, bool);
|
||||||
|
53
source/util/hash_combine.h
Normal file
53
source/util/hash_combine.h
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
// Copyright (c) 2022 The Khronos Group Inc.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
#ifndef SOURCE_UTIL_HASH_COMBINE_H_
|
||||||
|
#define SOURCE_UTIL_HASH_COMBINE_H_
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
#include <functional>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace spvtools {
|
||||||
|
namespace utils {
|
||||||
|
|
||||||
|
// Helpers for incrementally computing hashes.
|
||||||
|
// For reference, see
|
||||||
|
// http://open-std.org/jtc1/sc22/wg21/docs/papers/2014/n3876.pdf
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
inline size_t hash_combine(std::size_t seed, const T& val) {
|
||||||
|
return seed ^ (std::hash<T>()(val) + 0x9e3779b9 + (seed << 6) + (seed >> 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
inline size_t hash_combine(std::size_t hash, const std::vector<T>& vals) {
|
||||||
|
for (const T& val : vals) {
|
||||||
|
hash = hash_combine(hash, val);
|
||||||
|
}
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline size_t hash_combine(std::size_t hash) { return hash; }
|
||||||
|
|
||||||
|
template <typename T, typename... Types>
|
||||||
|
inline size_t hash_combine(std::size_t hash, const T& val,
|
||||||
|
const Types&... args) {
|
||||||
|
return hash_combine(hash_combine(hash, val), args...);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace utils
|
||||||
|
} // namespace spvtools
|
||||||
|
|
||||||
|
#endif // SOURCE_UTIL_HASH_COMBINE_H_
|
@ -333,6 +333,15 @@ class SmallVector {
|
|||||||
++size_;
|
++size_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void pop_back() {
|
||||||
|
if (large_data_) {
|
||||||
|
large_data_->pop_back();
|
||||||
|
} else {
|
||||||
|
--size_;
|
||||||
|
small_data_[size_].~T();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
template <class InputIt>
|
template <class InputIt>
|
||||||
iterator insert(iterator pos, InputIt first, InputIt last) {
|
iterator insert(iterator pos, InputIt first, InputIt last) {
|
||||||
size_t element_idx = (pos - begin());
|
size_t element_idx = (pos - begin());
|
||||||
|
@ -16,6 +16,7 @@ add_spvtools_unittest(TARGET utils
|
|||||||
SRCS ilist_test.cpp
|
SRCS ilist_test.cpp
|
||||||
bit_vector_test.cpp
|
bit_vector_test.cpp
|
||||||
bitutils_test.cpp
|
bitutils_test.cpp
|
||||||
|
hash_combine_test.cpp
|
||||||
small_vector_test.cpp
|
small_vector_test.cpp
|
||||||
LIBS SPIRV-Tools-opt
|
LIBS SPIRV-Tools-opt
|
||||||
)
|
)
|
||||||
|
43
test/util/hash_combine_test.cpp
Normal file
43
test/util/hash_combine_test.cpp
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
// Copyright (c) 2022 The Khronos Group Inc.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
#include <utility>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "gmock/gmock.h"
|
||||||
|
#include "source/util/hash_combine.h"
|
||||||
|
|
||||||
|
namespace spvtools {
|
||||||
|
namespace utils {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
using HashCombineTest = ::testing::Test;
|
||||||
|
|
||||||
|
TEST(HashCombineTest, Identity) { EXPECT_EQ(hash_combine(0), 0); }
|
||||||
|
|
||||||
|
TEST(HashCombineTest, Variadic) {
|
||||||
|
// Expect manual and variadic template versions be the same.
|
||||||
|
EXPECT_EQ(hash_combine(hash_combine(hash_combine(0, 1), 2), 3),
|
||||||
|
hash_combine(0, 1, 2, 3));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(HashCombineTest, Vector) {
|
||||||
|
// Expect variadic and vector versions be the same.
|
||||||
|
EXPECT_EQ(hash_combine(0, std::vector<uint32_t>({1, 2, 3})),
|
||||||
|
hash_combine(0, 1, 2, 3));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
} // namespace utils
|
||||||
|
} // namespace spvtools
|
@ -605,6 +605,68 @@ TEST(SmallVectorTest, Resize6) {
|
|||||||
EXPECT_EQ(vec, result);
|
EXPECT_EQ(vec, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(SmallVectorTest, Pop_back) {
|
||||||
|
SmallVector<uint32_t, 8> vec = {0, 1, 2, 10, 10, 10};
|
||||||
|
SmallVector<uint32_t, 8> result = {0, 1, 2};
|
||||||
|
|
||||||
|
EXPECT_EQ(vec.size(), 6);
|
||||||
|
vec.pop_back();
|
||||||
|
vec.pop_back();
|
||||||
|
vec.pop_back();
|
||||||
|
EXPECT_EQ(vec.size(), 3);
|
||||||
|
EXPECT_EQ(vec, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(SmallVectorTest, Pop_back_TestDestructor) {
|
||||||
|
// Tracks number of constructions and destructions to ensure they are called.
|
||||||
|
struct TracksDtor {
|
||||||
|
TracksDtor& operator=(TracksDtor&&) = delete;
|
||||||
|
TracksDtor& operator=(const TracksDtor&) = delete;
|
||||||
|
|
||||||
|
TracksDtor(int& num_ctors, int& num_dtors)
|
||||||
|
: num_ctors_(num_ctors), num_dtors_(num_dtors) {
|
||||||
|
num_ctors_++;
|
||||||
|
}
|
||||||
|
TracksDtor(const TracksDtor& that)
|
||||||
|
: TracksDtor(that.num_ctors_, that.num_dtors_) {}
|
||||||
|
TracksDtor(TracksDtor&& that)
|
||||||
|
: TracksDtor(that.num_ctors_, that.num_dtors_) {}
|
||||||
|
~TracksDtor() { num_dtors_++; }
|
||||||
|
|
||||||
|
int& num_ctors_;
|
||||||
|
int& num_dtors_;
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr int capacity = 4;
|
||||||
|
SmallVector<TracksDtor, capacity> vec;
|
||||||
|
|
||||||
|
int num_ctors = 0;
|
||||||
|
int num_dtors = 0;
|
||||||
|
|
||||||
|
// Make sure it works when staying within the smallvector capacity
|
||||||
|
for (int i = 0; i < capacity; ++i) {
|
||||||
|
vec.emplace_back(num_ctors, num_dtors);
|
||||||
|
}
|
||||||
|
|
||||||
|
EXPECT_EQ(num_ctors, capacity);
|
||||||
|
while (!vec.empty()) {
|
||||||
|
vec.pop_back();
|
||||||
|
}
|
||||||
|
|
||||||
|
EXPECT_EQ(num_ctors, capacity);
|
||||||
|
EXPECT_EQ(num_dtors, num_ctors);
|
||||||
|
|
||||||
|
// And when larger than builtin capacity
|
||||||
|
for (int i = 0; i < capacity * 2; ++i) {
|
||||||
|
vec.emplace_back(num_ctors, num_dtors);
|
||||||
|
}
|
||||||
|
|
||||||
|
while (!vec.empty()) {
|
||||||
|
vec.pop_back();
|
||||||
|
}
|
||||||
|
EXPECT_EQ(num_dtors, num_ctors);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
} // namespace utils
|
} // namespace utils
|
||||||
} // namespace spvtools
|
} // namespace spvtools
|
||||||
|
Loading…
Reference in New Issue
Block a user