tint/writer: Replace use of strings for cache keys

Concatenating strings to use for cache keys is horribly inefficent and very error prone.

Add a UnorderedKeyWrapper helper to allow types to be used as a unordered_map and unordered_set key. Use this for the type_constructor_to_id_ map.

Produces SPIR-V with some duplicate SPIR-V instructions for constructors removed.

Change-Id: Ib072d485ca28bb07f03e979c133cdce1f69ee482
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/88300
Kokoro: Kokoro <noreply+kokoro@google.com>
Commit-Queue: Ben Clayton <bclayton@google.com>
Reviewed-by: Dan Sinclair <dsinclair@chromium.org>
This commit is contained in:
Ben Clayton 2022-04-28 13:08:22 +00:00 committed by Dawn LUCI CQ
parent 89fe801dff
commit 046abc08e8
8 changed files with 189 additions and 84 deletions

View File

@ -18,6 +18,7 @@
#include <stdint.h>
#include <cstdio>
#include <functional>
#include <utility>
#include <vector>
namespace tint::utils {
@ -76,6 +77,53 @@ size_t Hash(const ARGS&... args) {
return hash;
}
/// Wrapper for a hashable type enabling the wrapped value to be used as a key
/// for an unordered_map or unordered_set.
template <typename T>
struct UnorderedKeyWrapper {
/// The wrapped value
const T value;
/// The hash of value
const size_t hash;
/// Constructor
/// @param v the value to wrap
explicit UnorderedKeyWrapper(const T& v) : value(v), hash(Hash(v)) {}
/// Move constructor
/// @param v the value to wrap
explicit UnorderedKeyWrapper(T&& v)
: value(std::move(v)), hash(Hash(value)) {}
/// @returns true if this wrapper comes before other
/// @param other the RHS of the operator
bool operator<(const UnorderedKeyWrapper& other) const {
return hash < other.hash;
}
/// @returns true if this wrapped value is equal to the other wrapped value
/// @param other the RHS of the operator
bool operator==(const UnorderedKeyWrapper& other) const {
return value == other.value;
}
};
} // namespace tint::utils
namespace std {
/// Custom std::hash specialization for tint::utils::UnorderedKeyWrapper
template <typename T>
class hash<tint::utils::UnorderedKeyWrapper<T>> {
public:
/// @param w the UnorderedKeyWrapper
/// @return the hash value
inline std::size_t operator()(
const tint::utils::UnorderedKeyWrapper<T>& w) const {
return w.hash;
}
};
} // namespace std
#endif // SRC_TINT_UTILS_HASH_H_

View File

@ -15,6 +15,7 @@
#include "src/tint/utils/hash.h"
#include <string>
#include <unordered_map>
#include "gtest/gtest.h"
@ -43,5 +44,30 @@ TEST(HashTests, Vector) {
Hash(std::vector<int>({1, 2, 3, 4})));
}
TEST(HashTests, UnorderedKeyWrapper) {
using W = UnorderedKeyWrapper<std::vector<int>>;
std::unordered_map<W, int> m;
m.emplace(W{{1, 2}}, -1);
EXPECT_EQ(m.size(), 1u);
EXPECT_EQ(m[W({1, 2})], -1);
m.emplace(W{{3, 2}}, 1);
EXPECT_EQ(m.size(), 2u);
EXPECT_EQ(m[W({3, 2})], 1);
EXPECT_EQ(m[W({1, 2})], -1);
m.emplace(W{{100}}, 100);
EXPECT_EQ(m.size(), 3u);
EXPECT_EQ(m[W({100})], 100);
EXPECT_EQ(m[W({3, 2})], 1);
EXPECT_EQ(m[W({1, 2})], -1);
// Reversed vector element order
EXPECT_EQ(m[W({2, 3})], 0);
EXPECT_EQ(m[W({2, 1})], 0);
}
} // namespace
} // namespace tint::utils

View File

@ -1346,9 +1346,6 @@ uint32_t Builder::GenerateTypeConstructorOrConversion(
return GenerateConstantNullIfNeeded(result_type->UnwrapRef());
}
std::ostringstream out;
out << "__const_" << result_type->FriendlyName(builder_.Symbols()) << "_";
result_type = result_type->UnwrapRef();
bool constructor_is_const = IsConstructorConst(call->Declaration());
if (has_error()) {
@ -1386,6 +1383,12 @@ uint32_t Builder::GenerateTypeConstructorOrConversion(
}
OperandList ops;
static constexpr size_t kOpsResultIdx = 1;
static constexpr size_t kOpsFirstValueIdx = 2;
ops.reserve(8);
ops.push_back(Operand::Int(type_id));
ops.push_back(Operand::Int(0)); // Placeholder for the result ID
for (auto* e : args) {
uint32_t id = 0;
id = GenerateExpressionWithLoadIfNeeded(e);
@ -1399,8 +1402,6 @@ uint32_t Builder::GenerateTypeConstructorOrConversion(
// value type is a correctly sized vector so we can just use it directly.
if (result_type == value_type || result_type->Is<sem::Matrix>() ||
result_type->Is<sem::Array>() || result_type->Is<sem::Struct>()) {
out << "_" << id;
ops.push_back(Operand::Int(id));
continue;
}
@ -1410,7 +1411,6 @@ uint32_t Builder::GenerateTypeConstructorOrConversion(
if (value_type->is_scalar() && result_type->is_scalar()) {
id = GenerateCastOrCopyOrPassthrough(result_type, args[0]->Declaration(),
global_var);
out << "_" << id;
ops.push_back(Operand::Int(id));
continue;
}
@ -1461,7 +1461,6 @@ uint32_t Builder::GenerateTypeConstructorOrConversion(
result_is_spec_composite = true;
}
out << "_" << extract_id;
ops.push_back(Operand::Int(extract_id));
}
} else {
@ -1476,21 +1475,14 @@ uint32_t Builder::GenerateTypeConstructorOrConversion(
args[0]->Type()->UnwrapRef()->is_scalar()) {
size_t vec_size = init_result_type->As<sem::Vector>()->Width();
for (size_t i = 0; i < (vec_size - 1); ++i) {
ops.push_back(ops[0]);
ops.push_back(ops[kOpsFirstValueIdx]);
}
}
auto str = out.str();
auto val = type_constructor_to_id_.find(str);
if (val != type_constructor_to_id_.end()) {
return val->second;
}
return utils::GetOrCreate(
type_constructor_to_id_, OperandListKey{ops}, [&]() -> uint32_t {
auto result = result_op();
ops.insert(ops.begin(), result);
ops.insert(ops.begin(), Operand::Int(type_id));
type_constructor_to_id_[str] = result.to_i();
ops[kOpsResultIdx] = result;
if (result_is_spec_composite) {
push_type(spv::Op::OpSpecConstantComposite, ops);
@ -1503,6 +1495,7 @@ uint32_t Builder::GenerateTypeConstructorOrConversion(
}
return result.to_i();
});
}
uint32_t Builder::GenerateCastOrCopyOrPassthrough(

View File

@ -604,7 +604,7 @@ class Builder {
std::unordered_map<sem::CallTargetSignature, uint32_t> func_sig_to_id_;
std::unordered_map<const sem::Type*, uint32_t> type_to_id_;
std::unordered_map<ScalarConstant, uint32_t> const_to_id_;
std::unordered_map<std::string, uint32_t> type_constructor_to_id_;
std::unordered_map<OperandListKey, uint32_t> type_constructor_to_id_;
std::unordered_map<const sem::Type*, uint32_t> const_null_to_id_;
std::unordered_map<uint64_t, uint32_t> const_splat_to_id_;
std::unordered_map<const sem::Type*, uint32_t>

View File

@ -19,21 +19,21 @@ namespace tint::writer::spirv {
// static
Operand Operand::Float(float val) {
Operand o(Kind::kFloat);
o.set_float(val);
o.float_val_ = val;
return o;
}
// static
Operand Operand::Int(uint32_t val) {
Operand o(Kind::kInt);
o.set_int(val);
o.int_val_ = val;
return o;
}
// static
Operand Operand::String(const std::string& val) {
Operand o(Kind::kString);
o.set_string(val);
o.str_val_ = val;
return o;
}

View File

@ -15,9 +15,12 @@
#ifndef SRC_TINT_WRITER_SPIRV_OPERAND_H_
#define SRC_TINT_WRITER_SPIRV_OPERAND_H_
#include <cstring>
#include <string>
#include <vector>
#include "src/tint/utils/hash.h"
namespace tint::writer::spirv {
/// A single SPIR-V instruction operand
@ -53,16 +56,29 @@ class Operand {
/// @returns a copy of this operand
Operand& operator=(const Operand& b) = default;
/// Sets the float value
/// @param val the value to set
void set_float(float val) { float_val_ = val; }
/// Sets the int value
/// @param val the value to set
void set_int(uint32_t val) { int_val_ = val; }
/// Sets the string value
/// @param val the value to set
void set_string(const std::string& val) { str_val_ = val; }
/// Equality operator
/// @param other the RHS of the operator
/// @returns true if this operand is equal to other
bool operator==(const Operand& other) const {
if (kind_ == other.kind_) {
switch (kind_) {
case tint::writer::spirv::Operand::Kind::kFloat:
// Use memcmp to work around:
// error: comparing floating point with == or != is unsafe
// [-Werror,-Wfloat-equal]
return memcmp(&float_val_, &other.float_val_, sizeof(float_val_)) ==
0;
case tint::writer::spirv::Operand::Kind::kInt:
return int_val_ == other.int_val_;
case tint::writer::spirv::Operand::Kind::kString:
return str_val_ == other.str_val_;
}
}
return false;
}
/// @returns the kind of the operand
Kind GetKind() const { return kind_; }
/// @returns true if this is a float operand
bool IsFloat() const { return kind_ == Kind::kFloat; }
/// @returns true if this is an integer operand
@ -90,6 +106,30 @@ class Operand {
/// A list of operands
using OperandList = std::vector<Operand>;
using OperandListKey = utils::UnorderedKeyWrapper<OperandList>;
} // namespace tint::writer::spirv
namespace std {
/// Custom std::hash specialization for tint::writer::spirv::Operand
template <>
class hash<tint::writer::spirv::Operand> {
public:
/// @param o the Operand
/// @return the hash value
inline std::size_t operator()(const tint::writer::spirv::Operand& o) const {
switch (o.GetKind()) {
case tint::writer::spirv::Operand::Kind::kFloat:
return tint::utils::Hash(o.to_f());
case tint::writer::spirv::Operand::Kind::kInt:
return tint::utils::Hash(o.to_i());
case tint::writer::spirv::Operand::Kind::kString:
return tint::utils::Hash(o.to_s());
}
return 0;
}
};
} // namespace std
#endif // SRC_TINT_WRITER_SPIRV_OPERAND_H_

View File

@ -1,7 +1,7 @@
; SPIR-V
; Version: 1.3
; Generator: Google Tint Compiler; 0
; Bound: 73
; Bound: 72
; Schema: 0
OpCapability Shader
OpMemoryModel Logical GLSL450
@ -69,12 +69,11 @@
%_ptr_Function_v3uint = OpTypePointer Function %v3uint
%61 = OpConstantNull %v3uint
%63 = OpConstantComposite %v3uint %uint_1 %uint_1 %uint_1
%65 = OpConstantComposite %v3bool %true %true %true
%v4bool = OpTypeVector %bool 4
%false = OpConstantFalse %bool
%69 = OpConstantComposite %v4bool %true %false %true %false
%68 = OpConstantComposite %v4bool %true %false %true %false
%_ptr_Function_v4bool = OpTypePointer Function %v4bool
%72 = OpConstantNull %v4bool
%71 = OpConstantNull %v4bool
%constant_with_non_constant = OpFunction %void None %1
%4 = OpLabel
%a = OpVariable %_ptr_Function_float Function %6
@ -107,7 +106,7 @@
%v3u32_var2 = OpVariable %_ptr_Function_v3uint Function %61
%v3u32_var3 = OpVariable %_ptr_Function_v3uint Function %61
%v3bool_var4 = OpVariable %_ptr_Function_v3bool Function %45
%v4bool_var5 = OpVariable %_ptr_Function_v4bool Function %72
%v4bool_var5 = OpVariable %_ptr_Function_v4bool Function %71
OpStore %bool_var1 %true
OpStore %bool_var2 %true
OpStore %bool_var3 %true
@ -127,7 +126,7 @@
OpStore %v3u32_var1 %58
OpStore %v3u32_var2 %58
OpStore %v3u32_var3 %63
OpStore %v3bool_var4 %65
OpStore %v4bool_var5 %69
OpStore %v3bool_var4 %42
OpStore %v4bool_var5 %68
OpReturn
OpFunctionEnd

View File

@ -1,7 +1,7 @@
; SPIR-V
; Version: 1.3
; Generator: Google Tint Compiler; 0
; Bound: 55
; Bound: 54
; Schema: 0
OpCapability Shader
OpMemoryModel Logical GLSL450
@ -64,43 +64,42 @@
%v3u32_var1 = OpVariable %_ptr_Private_v3uint Private %32
%v3u32_var2 = OpVariable %_ptr_Private_v3uint Private %32
%v3u32_var3 = OpVariable %_ptr_Private_v3uint Private %32
%37 = OpConstantComposite %v3bool %true %true %true
%v3bool_var4 = OpVariable %_ptr_Private_v3bool Private %37
%v3bool_var4 = OpVariable %_ptr_Private_v3bool Private %20
%v4bool = OpTypeVector %bool 4
%false = OpConstantFalse %bool
%41 = OpConstantComposite %v4bool %true %false %true %false
%40 = OpConstantComposite %v4bool %true %false %true %false
%_ptr_Private_v4bool = OpTypePointer Private %v4bool
%v4bool_var5 = OpVariable %_ptr_Private_v4bool Private %41
%v4bool_var5 = OpVariable %_ptr_Private_v4bool Private %40
%void = OpTypeVoid
%44 = OpTypeFunction %void
%48 = OpConstantNull %bool
%49 = OpConstantNull %int
%50 = OpConstantNull %uint
%51 = OpConstantNull %v3bool
%52 = OpConstantNull %v4bool
%53 = OpConstantNull %v3int
%54 = OpConstantNull %v3uint
%main = OpFunction %void None %44
%47 = OpLabel
OpStore %bool_var1 %48
OpStore %bool_var2 %48
OpStore %bool_var3 %48
OpStore %i32_var1 %49
OpStore %i32_var2 %49
OpStore %i32_var3 %49
OpStore %u32_var1 %50
OpStore %u32_var2 %50
OpStore %u32_var3 %50
OpStore %v3bool_var1 %51
OpStore %v3bool_var2 %51
OpStore %v3bool_var3 %51
OpStore %v3bool_var4 %51
OpStore %v4bool_var5 %52
OpStore %v3i32_var1 %53
OpStore %v3i32_var2 %53
OpStore %v3i32_var3 %53
OpStore %v3u32_var1 %54
OpStore %v3u32_var2 %54
OpStore %v3u32_var3 %54
%43 = OpTypeFunction %void
%47 = OpConstantNull %bool
%48 = OpConstantNull %int
%49 = OpConstantNull %uint
%50 = OpConstantNull %v3bool
%51 = OpConstantNull %v4bool
%52 = OpConstantNull %v3int
%53 = OpConstantNull %v3uint
%main = OpFunction %void None %43
%46 = OpLabel
OpStore %bool_var1 %47
OpStore %bool_var2 %47
OpStore %bool_var3 %47
OpStore %i32_var1 %48
OpStore %i32_var2 %48
OpStore %i32_var3 %48
OpStore %u32_var1 %49
OpStore %u32_var2 %49
OpStore %u32_var3 %49
OpStore %v3bool_var1 %50
OpStore %v3bool_var2 %50
OpStore %v3bool_var3 %50
OpStore %v3bool_var4 %50
OpStore %v4bool_var5 %51
OpStore %v3i32_var1 %52
OpStore %v3i32_var2 %52
OpStore %v3i32_var3 %52
OpStore %v3u32_var1 %53
OpStore %v3u32_var2 %53
OpStore %v3u32_var3 %53
OpReturn
OpFunctionEnd