tint: Extract intrinsic-table common type to helper

• Add sem::Type::Common() which returns the 'common' type for the list of
  types.
• Migrate intrisnic table to use this.
• Add a whole-lotta-tests.
• Deduplicate and improve the EXPECT_TEST() macro. Move it to a common home.

Bug: tint:1504
Change-Id: I1564f67ecf87fc594f3f54274da906ff0d822795
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/91020
Commit-Queue: Ben Clayton <bclayton@google.com>
Reviewed-by: David Neto <dneto@google.com>
This commit is contained in:
Ben Clayton 2022-05-19 22:54:00 +00:00 committed by Dawn LUCI CQ
parent 1b35e3f9a8
commit eee9f88ba2
6 changed files with 278 additions and 42 deletions

View File

@ -124,16 +124,11 @@ class TemplateState {
if (existing == ty) { if (existing == ty) {
return ty; return ty;
} }
if (sem::Type::ConversionRank(ty, existing) != sem::Type::kNoConversion) { ty = sem::Type::Common({existing, ty});
// ty can be converted to the existing type. Keep the existing type. if (ty) {
return existing; res.first->second = ty;
} }
if (sem::Type::ConversionRank(existing, ty) != sem::Type::kNoConversion) { return ty;
// template type can be converted to ty. Constrain the existing type.
types_[idx] = ty;
return ty;
}
return nullptr;
} }
/// If the number with index `idx` is undefined, then it is defined with the number `number` and /// If the number with index `idx` is undefined, then it is defined with the number `number` and

View File

@ -27,21 +27,13 @@
#include "src/tint/sem/reference.h" #include "src/tint/sem/reference.h"
#include "src/tint/sem/sampled_texture.h" #include "src/tint/sem/sampled_texture.h"
#include "src/tint/sem/storage_texture.h" #include "src/tint/sem/storage_texture.h"
#include "src/tint/sem/test_helper.h"
#include "src/tint/sem/type_constructor.h" #include "src/tint/sem/type_constructor.h"
#include "src/tint/sem/type_conversion.h" #include "src/tint/sem/type_conversion.h"
namespace tint::resolver { namespace tint::resolver {
namespace { namespace {
#define EXPECT_TYPE(GOT, EXPECT) \
if ((GOT) != (EXPECT)) { \
FAIL() << #GOT " != " #EXPECT "\n" \
<< " " #GOT ": " << NameOf(GOT) << "\n" \
<< " " #EXPECT ": " << NameOf(EXPECT); \
} \
do { \
} while (false)
using ::testing::HasSubstr; using ::testing::HasSubstr;
using BuiltinType = sem::BuiltinType; using BuiltinType = sem::BuiltinType;
@ -830,13 +822,8 @@ struct Case {
builder::sem_type_func_ptr arg_rhs; builder::sem_type_func_ptr arg_rhs;
}; };
class IntrinsicTableAbstractBinaryTest : public testing::TestWithParam<Case>, struct IntrinsicTableAbstractBinaryTest : public testing::TestWithParam<Case>,
public ProgramBuilder { public ProgramBuilder {
public:
std::string NameOf(const sem::Type* type) {
return type ? type->FriendlyName(Symbols()) : "<null>";
}
std::unique_ptr<IntrinsicTable> table = IntrinsicTable::Create(*this); std::unique_ptr<IntrinsicTable> table = IntrinsicTable::Create(*this);
}; };
@ -1017,13 +1004,8 @@ struct Case {
builder::sem_type_func_ptr arg_c; builder::sem_type_func_ptr arg_c;
}; };
class IntrinsicTableAbstractTernaryTest : public testing::TestWithParam<Case>, struct IntrinsicTableAbstractTernaryTest : public testing::TestWithParam<Case>,
public ProgramBuilder { public ProgramBuilder {
public:
std::string NameOf(const sem::Type* type) {
return type ? type->FriendlyName(Symbols()) : "<null>";
}
std::unique_ptr<IntrinsicTable> table = IntrinsicTable::Create(*this); std::unique_ptr<IntrinsicTable> table = IntrinsicTable::Create(*this);
}; };

View File

@ -44,4 +44,16 @@ using TestParamHelper = TestHelperBase<testing::TestWithParam<T>>;
} // namespace tint::sem } // namespace tint::sem
/// Helper macro for testing that a semantic type was as expected
#define EXPECT_TYPE(GOT, EXPECT) \
do { \
const sem::Type* got = GOT; \
const sem::Type* expect = EXPECT; \
if (got != expect) { \
ADD_FAILURE() << #GOT " != " #EXPECT "\n" \
<< " " #GOT ": " << FriendlyName(got) << "\n" \
<< " " #EXPECT ": " << FriendlyName(expect); \
} \
} while (false)
#endif // SRC_TINT_SEM_TEST_HELPER_H_ #endif // SRC_TINT_SEM_TEST_HELPER_H_

View File

@ -232,4 +232,26 @@ const Type* Type::ElementOf(const Type* ty, uint32_t* count /* = nullptr */) {
}); });
} }
const sem::Type* Type::Common(Type const* const* types, size_t count) {
if (count == 0) {
return nullptr;
}
const auto* common = types[0];
for (size_t i = 1; i < count; i++) {
auto* ty = types[i];
if (ty == common) {
continue; // ty == common
}
if (sem::Type::ConversionRank(ty, common) != sem::Type::kNoConversion) {
continue; // ty can be converted to common.
}
if (sem::Type::ConversionRank(common, ty) != sem::Type::kNoConversion) {
common = ty; // common can be converted to ty.
continue;
}
return nullptr; // Conversion is not valid.
}
return common;
}
} // namespace tint::sem } // namespace tint::sem

View File

@ -135,6 +135,21 @@ class Type : public Castable<Type, Node> {
/// or array, otherwise nullptr. /// or array, otherwise nullptr.
static const Type* ElementOf(const Type* ty, uint32_t* count = nullptr); static const Type* ElementOf(const Type* ty, uint32_t* count = nullptr);
/// @param types a pointer to a list of `const Type*`.
/// @param count the number of types in `types`.
/// @returns the lowest-ranking type that all types in `types` can be implicitly converted to,
/// or nullptr if there is no consistent common type across all types in `types`.
/// @see https://www.w3.org/TR/WGSL/#conversion-rank
static const sem::Type* Common(Type const* const* types, size_t count);
/// @param types an initializer_list of `const Type*`.
/// @returns the lowest-ranking type that all types in `types` can be implicitly converted to,
/// or nullptr if there is no consistent common type across all types in `types`.
/// @see https://www.w3.org/TR/WGSL/#conversion-rank
static const sem::Type* Common(std::initializer_list<const Type*> types) {
return Common(types.begin(), types.size());
}
protected: protected:
Type(); Type();
}; };

View File

@ -91,16 +91,6 @@ TEST_F(TypeTest, ConversionRank) {
EXPECT_EQ(Type::ConversionRank(f16, ai), Type::kNoConversion); EXPECT_EQ(Type::ConversionRank(f16, ai), Type::kNoConversion);
} }
/// Helper macro for testing that a semantic type was as expected
#define EXPECT_TYPE(GOT, EXPECT) \
if ((GOT) != (EXPECT)) { \
FAIL() << #GOT " != " #EXPECT "\n" \
<< " " #GOT ": " << FriendlyName(GOT) << "\n" \
<< " " #EXPECT ": " << FriendlyName(EXPECT); \
} \
do { \
} while (false)
TEST_F(TypeTest, ElementOf) { TEST_F(TypeTest, ElementOf) {
auto* f32 = create<F32>(); auto* f32 = create<F32>();
auto* f16 = create<F16>(); auto* f16 = create<F16>();
@ -179,5 +169,225 @@ TEST_F(TypeTest, ElementOf) {
EXPECT_EQ(count, 5u); EXPECT_EQ(count, 5u);
} }
TEST_F(TypeTest, Common2) {
auto* ai = create<AbstractInt>();
auto* af = create<AbstractFloat>();
auto* f32 = create<F32>();
auto* f16 = create<F16>();
auto* i32 = create<I32>();
auto* u32 = create<U32>();
EXPECT_TYPE(Type::Common({ai, ai}), ai);
EXPECT_TYPE(Type::Common({af, af}), af);
EXPECT_TYPE(Type::Common({f32, f32}), f32);
EXPECT_TYPE(Type::Common({f16, f16}), f16);
EXPECT_TYPE(Type::Common({i32, i32}), i32);
EXPECT_TYPE(Type::Common({u32, u32}), u32);
EXPECT_TYPE(Type::Common({i32, u32}), nullptr);
EXPECT_TYPE(Type::Common({u32, f32}), nullptr);
EXPECT_TYPE(Type::Common({f32, f16}), nullptr);
EXPECT_TYPE(Type::Common({f16, i32}), nullptr);
EXPECT_TYPE(Type::Common({ai, af}), af);
EXPECT_TYPE(Type::Common({ai, f32}), f32);
EXPECT_TYPE(Type::Common({ai, f16}), f16);
EXPECT_TYPE(Type::Common({ai, i32}), i32);
EXPECT_TYPE(Type::Common({ai, u32}), u32);
EXPECT_TYPE(Type::Common({af, ai}), af);
EXPECT_TYPE(Type::Common({f32, ai}), f32);
EXPECT_TYPE(Type::Common({f16, ai}), f16);
EXPECT_TYPE(Type::Common({i32, ai}), i32);
EXPECT_TYPE(Type::Common({u32, ai}), u32);
EXPECT_TYPE(Type::Common({ai, af}), af);
EXPECT_TYPE(Type::Common({f32, af}), f32);
EXPECT_TYPE(Type::Common({f16, af}), f16);
EXPECT_TYPE(Type::Common({i32, af}), nullptr);
EXPECT_TYPE(Type::Common({u32, af}), nullptr);
EXPECT_TYPE(Type::Common({af, ai}), af);
EXPECT_TYPE(Type::Common({af, f32}), f32);
EXPECT_TYPE(Type::Common({af, f16}), f16);
EXPECT_TYPE(Type::Common({af, i32}), nullptr);
EXPECT_TYPE(Type::Common({af, u32}), nullptr);
auto* vec3_ai = create<Vector>(ai, 3u);
auto* vec3_af = create<Vector>(af, 3u);
auto* vec3_f32 = create<Vector>(f32, 3u);
auto* vec3_f16 = create<Vector>(f16, 3u);
auto* vec4_f32 = create<Vector>(f32, 4u);
auto* vec3_u32 = create<Vector>(u32, 3u);
auto* vec3_i32 = create<Vector>(i32, 3u);
EXPECT_TYPE(Type::Common({vec3_ai, vec3_ai}), vec3_ai);
EXPECT_TYPE(Type::Common({vec3_af, vec3_af}), vec3_af);
EXPECT_TYPE(Type::Common({vec3_f32, vec3_f32}), vec3_f32);
EXPECT_TYPE(Type::Common({vec3_f16, vec3_f16}), vec3_f16);
EXPECT_TYPE(Type::Common({vec4_f32, vec4_f32}), vec4_f32);
EXPECT_TYPE(Type::Common({vec3_u32, vec3_u32}), vec3_u32);
EXPECT_TYPE(Type::Common({vec3_i32, vec3_i32}), vec3_i32);
EXPECT_TYPE(Type::Common({vec3_ai, vec3_f32}), vec3_f32);
EXPECT_TYPE(Type::Common({vec3_ai, vec3_f16}), vec3_f16);
EXPECT_TYPE(Type::Common({vec3_ai, vec4_f32}), nullptr);
EXPECT_TYPE(Type::Common({vec3_ai, vec3_u32}), vec3_u32);
EXPECT_TYPE(Type::Common({vec3_ai, vec3_i32}), vec3_i32);
EXPECT_TYPE(Type::Common({vec3_f32, vec3_ai}), vec3_f32);
EXPECT_TYPE(Type::Common({vec3_f16, vec3_ai}), vec3_f16);
EXPECT_TYPE(Type::Common({vec4_f32, vec3_ai}), nullptr);
EXPECT_TYPE(Type::Common({vec3_u32, vec3_ai}), vec3_u32);
EXPECT_TYPE(Type::Common({vec3_i32, vec3_ai}), vec3_i32);
EXPECT_TYPE(Type::Common({vec3_af, vec3_f32}), vec3_f32);
EXPECT_TYPE(Type::Common({vec3_af, vec3_f16}), vec3_f16);
EXPECT_TYPE(Type::Common({vec3_af, vec4_f32}), nullptr);
EXPECT_TYPE(Type::Common({vec3_af, vec3_u32}), nullptr);
EXPECT_TYPE(Type::Common({vec3_af, vec3_i32}), nullptr);
EXPECT_TYPE(Type::Common({vec3_f32, vec3_af}), vec3_f32);
EXPECT_TYPE(Type::Common({vec3_f16, vec3_af}), vec3_f16);
EXPECT_TYPE(Type::Common({vec4_f32, vec3_af}), nullptr);
EXPECT_TYPE(Type::Common({vec3_u32, vec3_af}), nullptr);
EXPECT_TYPE(Type::Common({vec3_i32, vec3_af}), nullptr);
auto* mat4x3_af = create<Matrix>(vec3_af, 4u);
auto* mat3x4_f32 = create<Matrix>(vec4_f32, 3u);
auto* mat4x3_f32 = create<Matrix>(vec3_f32, 4u);
auto* mat4x3_f16 = create<Matrix>(vec3_f16, 4u);
EXPECT_TYPE(Type::Common({mat4x3_af, mat4x3_af}), mat4x3_af);
EXPECT_TYPE(Type::Common({mat3x4_f32, mat3x4_f32}), mat3x4_f32);
EXPECT_TYPE(Type::Common({mat4x3_f32, mat4x3_f32}), mat4x3_f32);
EXPECT_TYPE(Type::Common({mat4x3_f16, mat4x3_f16}), mat4x3_f16);
EXPECT_TYPE(Type::Common({mat4x3_af, mat3x4_f32}), nullptr);
EXPECT_TYPE(Type::Common({mat4x3_af, mat4x3_f32}), mat4x3_f32);
EXPECT_TYPE(Type::Common({mat4x3_af, mat4x3_f16}), mat4x3_f16);
EXPECT_TYPE(Type::Common({mat3x4_f32, mat4x3_af}), nullptr);
EXPECT_TYPE(Type::Common({mat4x3_f32, mat4x3_af}), mat4x3_f32);
EXPECT_TYPE(Type::Common({mat4x3_f16, mat4x3_af}), mat4x3_f16);
}
TEST_F(TypeTest, Common3) {
auto* ai = create<AbstractInt>();
auto* af = create<AbstractFloat>();
auto* f32 = create<F32>();
auto* f16 = create<F16>();
auto* i32 = create<I32>();
auto* u32 = create<U32>();
EXPECT_TYPE(Type::Common({ai, ai, ai}), ai);
EXPECT_TYPE(Type::Common({af, af, af}), af);
EXPECT_TYPE(Type::Common({f32, f32, f32}), f32);
EXPECT_TYPE(Type::Common({f16, f16, f16}), f16);
EXPECT_TYPE(Type::Common({i32, i32, i32}), i32);
EXPECT_TYPE(Type::Common({u32, u32, u32}), u32);
EXPECT_TYPE(Type::Common({ai, af, ai}), af);
EXPECT_TYPE(Type::Common({ai, f32, ai}), f32);
EXPECT_TYPE(Type::Common({ai, f16, ai}), f16);
EXPECT_TYPE(Type::Common({ai, i32, ai}), i32);
EXPECT_TYPE(Type::Common({ai, u32, ai}), u32);
EXPECT_TYPE(Type::Common({af, ai, af}), af);
EXPECT_TYPE(Type::Common({f32, ai, f32}), f32);
EXPECT_TYPE(Type::Common({f16, ai, f16}), f16);
EXPECT_TYPE(Type::Common({i32, ai, i32}), i32);
EXPECT_TYPE(Type::Common({u32, ai, u32}), u32);
EXPECT_TYPE(Type::Common({ai, f32, ai}), f32);
EXPECT_TYPE(Type::Common({ai, f16, ai}), f16);
EXPECT_TYPE(Type::Common({ai, i32, ai}), i32);
EXPECT_TYPE(Type::Common({ai, u32, ai}), u32);
EXPECT_TYPE(Type::Common({f32, ai, f32}), f32);
EXPECT_TYPE(Type::Common({f16, ai, f16}), f16);
EXPECT_TYPE(Type::Common({i32, ai, i32}), i32);
EXPECT_TYPE(Type::Common({u32, ai, u32}), u32);
EXPECT_TYPE(Type::Common({af, f32, af}), f32);
EXPECT_TYPE(Type::Common({af, f16, af}), f16);
EXPECT_TYPE(Type::Common({af, i32, af}), nullptr);
EXPECT_TYPE(Type::Common({af, u32, af}), nullptr);
EXPECT_TYPE(Type::Common({f32, af, f32}), f32);
EXPECT_TYPE(Type::Common({f16, af, f16}), f16);
EXPECT_TYPE(Type::Common({i32, af, i32}), nullptr);
EXPECT_TYPE(Type::Common({u32, af, u32}), nullptr);
EXPECT_TYPE(Type::Common({ai, af, f32}), f32);
EXPECT_TYPE(Type::Common({ai, af, f16}), f16);
EXPECT_TYPE(Type::Common({ai, af, i32}), nullptr);
EXPECT_TYPE(Type::Common({ai, af, u32}), nullptr);
auto* vec3_ai = create<Vector>(ai, 3u);
auto* vec3_af = create<Vector>(af, 3u);
auto* vec3_f32 = create<Vector>(f32, 3u);
auto* vec3_f16 = create<Vector>(f16, 3u);
auto* vec4_f32 = create<Vector>(f32, 4u);
auto* vec3_u32 = create<Vector>(u32, 3u);
auto* vec3_i32 = create<Vector>(i32, 3u);
EXPECT_TYPE(Type::Common({vec3_ai, vec3_ai, vec3_ai}), vec3_ai);
EXPECT_TYPE(Type::Common({vec3_af, vec3_af, vec3_af}), vec3_af);
EXPECT_TYPE(Type::Common({vec3_f32, vec3_f32, vec3_f32}), vec3_f32);
EXPECT_TYPE(Type::Common({vec3_f16, vec3_f16, vec3_f16}), vec3_f16);
EXPECT_TYPE(Type::Common({vec4_f32, vec4_f32, vec4_f32}), vec4_f32);
EXPECT_TYPE(Type::Common({vec3_u32, vec3_u32, vec3_u32}), vec3_u32);
EXPECT_TYPE(Type::Common({vec3_i32, vec3_i32, vec3_i32}), vec3_i32);
EXPECT_TYPE(Type::Common({vec3_f32, vec3_ai, vec3_f32}), vec3_f32);
EXPECT_TYPE(Type::Common({vec3_f16, vec3_ai, vec3_f16}), vec3_f16);
EXPECT_TYPE(Type::Common({vec4_f32, vec3_ai, vec4_f32}), nullptr);
EXPECT_TYPE(Type::Common({vec3_u32, vec3_ai, vec3_u32}), vec3_u32);
EXPECT_TYPE(Type::Common({vec3_i32, vec3_ai, vec3_i32}), vec3_i32);
EXPECT_TYPE(Type::Common({vec3_ai, vec3_f32, vec3_ai}), vec3_f32);
EXPECT_TYPE(Type::Common({vec3_ai, vec3_f16, vec3_ai}), vec3_f16);
EXPECT_TYPE(Type::Common({vec3_ai, vec4_f32, vec3_ai}), nullptr);
EXPECT_TYPE(Type::Common({vec3_ai, vec3_u32, vec3_ai}), vec3_u32);
EXPECT_TYPE(Type::Common({vec3_ai, vec3_i32, vec3_ai}), vec3_i32);
EXPECT_TYPE(Type::Common({vec3_f32, vec3_af, vec3_f32}), vec3_f32);
EXPECT_TYPE(Type::Common({vec3_f16, vec3_af, vec3_f16}), vec3_f16);
EXPECT_TYPE(Type::Common({vec4_f32, vec3_af, vec4_f32}), nullptr);
EXPECT_TYPE(Type::Common({vec3_u32, vec3_af, vec3_u32}), nullptr);
EXPECT_TYPE(Type::Common({vec3_i32, vec3_af, vec3_i32}), nullptr);
EXPECT_TYPE(Type::Common({vec3_af, vec3_f32, vec3_af}), vec3_f32);
EXPECT_TYPE(Type::Common({vec3_af, vec3_f16, vec3_af}), vec3_f16);
EXPECT_TYPE(Type::Common({vec3_af, vec4_f32, vec3_af}), nullptr);
EXPECT_TYPE(Type::Common({vec3_af, vec3_u32, vec3_af}), nullptr);
EXPECT_TYPE(Type::Common({vec3_af, vec3_i32, vec3_af}), nullptr);
EXPECT_TYPE(Type::Common({vec3_ai, vec3_af, vec3_f32}), vec3_f32);
EXPECT_TYPE(Type::Common({vec3_ai, vec3_af, vec3_f16}), vec3_f16);
EXPECT_TYPE(Type::Common({vec3_ai, vec3_af, vec4_f32}), nullptr);
EXPECT_TYPE(Type::Common({vec3_ai, vec3_af, vec3_u32}), nullptr);
EXPECT_TYPE(Type::Common({vec3_ai, vec3_af, vec3_i32}), nullptr);
auto* mat4x3_af = create<Matrix>(vec3_af, 4u);
auto* mat3x4_f32 = create<Matrix>(vec4_f32, 3u);
auto* mat4x3_f32 = create<Matrix>(vec3_f32, 4u);
auto* mat4x3_f16 = create<Matrix>(vec3_f16, 4u);
EXPECT_TYPE(Type::Common({mat4x3_af, mat4x3_af, mat4x3_af}), mat4x3_af);
EXPECT_TYPE(Type::Common({mat3x4_f32, mat3x4_f32, mat3x4_f32}), mat3x4_f32);
EXPECT_TYPE(Type::Common({mat4x3_f32, mat4x3_f32, mat4x3_f32}), mat4x3_f32);
EXPECT_TYPE(Type::Common({mat4x3_f16, mat4x3_f16, mat4x3_f16}), mat4x3_f16);
EXPECT_TYPE(Type::Common({mat3x4_f32, mat4x3_af, mat3x4_f32}), nullptr);
EXPECT_TYPE(Type::Common({mat4x3_f32, mat4x3_af, mat4x3_f32}), mat4x3_f32);
EXPECT_TYPE(Type::Common({mat4x3_f16, mat4x3_af, mat4x3_f16}), mat4x3_f16);
EXPECT_TYPE(Type::Common({mat4x3_af, mat3x4_f32, mat4x3_af}), nullptr);
EXPECT_TYPE(Type::Common({mat4x3_af, mat4x3_f32, mat4x3_af}), mat4x3_f32);
EXPECT_TYPE(Type::Common({mat4x3_af, mat4x3_f16, mat4x3_af}), mat4x3_f16);
}
} // namespace } // namespace
} // namespace tint::sem } // namespace tint::sem