From aa037ac489cde4ae1ec289e7927821c89d6c579d Mon Sep 17 00:00:00 2001 From: Ben Clayton Date: Wed, 29 Jun 2022 19:07:30 +0000 Subject: [PATCH] tint: Refactor sem::Constant to be less memory-hungry Change sem::Constant to be an interface to the constant data. Implement this so that zero-initialized data doesn't need to allocate the full size of the type. This also makes usage a lot cleaner (no more flattened-list of elements!), and gives us a clear path for supporting constant structures if/when we want to support them. Bug: chromium:1339558 Bug: chromium:1339561 Bug: chromium:1339580 Bug: chromium:1339597 Change-Id: Ifcd456f69aee18d5b84befa896d7b0189d68c2dd Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/94942 Reviewed-by: Dan Sinclair Kokoro: Kokoro Commit-Queue: Ben Clayton Commit-Queue: Ben Clayton --- src/tint/BUILD.gn | 1 - src/tint/CMakeLists.txt | 1 - src/tint/program.cc | 3 + src/tint/program.h | 5 + src/tint/program_builder.h | 26 +- src/tint/resolver/const_eval.h | 4 +- src/tint/resolver/materialize_test.cc | 103 +- src/tint/resolver/resolver.cc | 48 +- src/tint/resolver/resolver.h | 32 +- src/tint/resolver/resolver_constants.cc | 676 +++++-- src/tint/resolver/resolver_constants_test.cc | 1743 +++++++++++++---- src/tint/resolver/validator.cc | 35 +- src/tint/resolver/variable_test.cc | 142 +- src/tint/sem/call.cc | 4 +- src/tint/sem/call.h | 2 +- src/tint/sem/constant.cc | 94 +- src/tint/sem/constant.h | 179 +- src/tint/sem/constant_test.cc | 540 ----- src/tint/sem/expression.cc | 2 +- src/tint/sem/expression.h | 6 +- src/tint/sem/expression_test.cc | 21 +- src/tint/sem/index_accessor_expression.cc | 2 +- src/tint/sem/index_accessor_expression.h | 2 +- src/tint/sem/materialize.cc | 13 +- src/tint/sem/materialize.h | 2 +- src/tint/sem/member_accessor_expression.cc | 3 +- src/tint/sem/variable.cc | 13 +- src/tint/sem/variable.h | 10 +- .../localize_struct_array_assignment.cc | 2 +- .../transform/promote_side_effects_to_decl.cc | 2 +- src/tint/transform/robustness.cc | 13 +- .../transform/zero_init_workgroup_memory.cc | 4 +- src/tint/writer/append_vector.cc | 6 +- src/tint/writer/glsl/generator_impl.cc | 50 +- src/tint/writer/glsl/generator_impl.h | 14 +- src/tint/writer/hlsl/generator_impl.cc | 94 +- src/tint/writer/hlsl/generator_impl.h | 17 +- src/tint/writer/msl/generator_impl.cc | 54 +- src/tint/writer/msl/generator_impl.h | 14 +- src/tint/writer/spirv/builder.cc | 46 +- src/tint/writer/spirv/builder.h | 13 +- 41 files changed, 2255 insertions(+), 1786 deletions(-) delete mode 100644 src/tint/sem/constant_test.cc diff --git a/src/tint/BUILD.gn b/src/tint/BUILD.gn index b59fa37d4d..fb513279cb 100644 --- a/src/tint/BUILD.gn +++ b/src/tint/BUILD.gn @@ -1131,7 +1131,6 @@ if (tint_build_unittests) { "sem/atomic_test.cc", "sem/bool_test.cc", "sem/builtin_test.cc", - "sem/constant_test.cc", "sem/depth_multisampled_texture_test.cc", "sem/depth_texture_test.cc", "sem/expression_test.cc", diff --git a/src/tint/CMakeLists.txt b/src/tint/CMakeLists.txt index 755d4a30ef..3e7154c80d 100644 --- a/src/tint/CMakeLists.txt +++ b/src/tint/CMakeLists.txt @@ -810,7 +810,6 @@ if(TINT_BUILD_TESTS) sem/atomic.cc sem/bool_test.cc sem/builtin_test.cc - sem/constant_test.cc sem/depth_multisampled_texture_test.cc sem/depth_texture_test.cc sem/expression_test.cc diff --git a/src/tint/program.cc b/src/tint/program.cc index 6722a09b0e..1afbd460ec 100644 --- a/src/tint/program.cc +++ b/src/tint/program.cc @@ -38,6 +38,7 @@ Program::Program(Program&& program) types_(std::move(program.types_)), ast_nodes_(std::move(program.ast_nodes_)), sem_nodes_(std::move(program.sem_nodes_)), + constant_nodes_(std::move(program.constant_nodes_)), ast_(std::move(program.ast_)), sem_(std::move(program.sem_)), symbols_(std::move(program.symbols_)), @@ -62,6 +63,7 @@ Program::Program(ProgramBuilder&& builder) { types_ = std::move(builder.Types()); ast_nodes_ = std::move(builder.ASTNodes()); sem_nodes_ = std::move(builder.SemNodes()); + constant_nodes_ = std::move(builder.ConstantNodes()); ast_ = &builder.AST(); // ast::Module is actually a heap allocation. sem_ = std::move(builder.Sem()); symbols_ = std::move(builder.Symbols()); @@ -86,6 +88,7 @@ Program& Program::operator=(Program&& program) { types_ = std::move(program.types_); ast_nodes_ = std::move(program.ast_nodes_); sem_nodes_ = std::move(program.sem_nodes_); + constant_nodes_ = std::move(program.constant_nodes_); ast_ = std::move(program.ast_); sem_ = std::move(program.sem_); symbols_ = std::move(program.symbols_); diff --git a/src/tint/program.h b/src/tint/program.h index 3230e7e786..5fd31ddd74 100644 --- a/src/tint/program.h +++ b/src/tint/program.h @@ -20,6 +20,7 @@ #include "src/tint/ast/function.h" #include "src/tint/program_id.h" +#include "src/tint/sem/constant.h" #include "src/tint/sem/info.h" #include "src/tint/sem/type_manager.h" #include "src/tint/symbol_table.h" @@ -43,6 +44,9 @@ class Program { /// SemNodeAllocator is an alias to BlockAllocator using SemNodeAllocator = utils::BlockAllocator; + /// ConstantAllocator is an alias to BlockAllocator + using ConstantAllocator = utils::BlockAllocator; + /// Constructor Program(); @@ -160,6 +164,7 @@ class Program { sem::Manager types_; ASTNodeAllocator ast_nodes_; SemNodeAllocator sem_nodes_; + ConstantAllocator constant_nodes_; ast::Module* ast_ = nullptr; sem::Info sem_; SymbolTable symbols_{id_}; diff --git a/src/tint/program_builder.h b/src/tint/program_builder.h index 5bccb5fb60..e9b0a803fb 100644 --- a/src/tint/program_builder.h +++ b/src/tint/program_builder.h @@ -88,6 +88,7 @@ #include "src/tint/program_id.h" #include "src/tint/sem/array.h" #include "src/tint/sem/bool.h" +#include "src/tint/sem/constant.h" #include "src/tint/sem/depth_texture.h" #include "src/tint/sem/external_texture.h" #include "src/tint/sem/f16.h" @@ -163,6 +164,9 @@ class ProgramBuilder { /// SemNodeAllocator is an alias to BlockAllocator using SemNodeAllocator = utils::BlockAllocator; + /// ConstantAllocator is an alias to BlockAllocator + using ConstantAllocator = utils::BlockAllocator; + /// Constructor ProgramBuilder(); @@ -229,6 +233,12 @@ class ProgramBuilder { return sem_nodes_; } + /// @returns a reference to the program's semantic constant storage + ConstantAllocator& ConstantNodes() { + AssertNotMoved(); + return constant_nodes_; + } + /// @returns a reference to the program's AST root Module ast::Module& AST() { AssertNotMoved(); @@ -332,9 +342,8 @@ class ProgramBuilder { } /// Creates a new sem::Node owned by the ProgramBuilder. - /// When the ProgramBuilder is destructed, the sem::Node will also be - /// destructed. - /// @param args the arguments to pass to the type constructor + /// When the ProgramBuilder is destructed, the sem::Node will also be destructed. + /// @param args the arguments to pass to the constructor /// @returns the node pointer template traits::EnableIf && @@ -345,6 +354,16 @@ class ProgramBuilder { return sem_nodes_.Create(std::forward(args)...); } + /// Creates a new sem::Constant owned by the ProgramBuilder. + /// When the ProgramBuilder is destructed, the sem::Node will also be destructed. + /// @param args the arguments to pass to the constructor + /// @returns the node pointer + template + traits::EnableIf, T>* create(ARGS&&... args) { + AssertNotMoved(); + return constant_nodes_.Create(std::forward(args)...); + } + /// Creates a new sem::Type owned by the ProgramBuilder. /// When the ProgramBuilder is destructed, owned ProgramBuilder and the /// returned`Type` will also be destructed. @@ -2747,6 +2766,7 @@ class ProgramBuilder { sem::Manager types_; ASTNodeAllocator ast_nodes_; SemNodeAllocator sem_nodes_; + ConstantAllocator constant_nodes_; ast::Module* ast_; sem::Info sem_; SymbolTable symbols_{id_}; diff --git a/src/tint/resolver/const_eval.h b/src/tint/resolver/const_eval.h index 89bb3da8de..3792e353a3 100644 --- a/src/tint/resolver/const_eval.h +++ b/src/tint/resolver/const_eval.h @@ -30,7 +30,9 @@ class Constant; namespace tint::resolver::const_eval { /// Typedef for a constant evaluation function -using Function = sem::Constant(ProgramBuilder& builder, const sem::Constant* args, size_t num_args); +using Function = const sem::Constant*(ProgramBuilder& builder, + sem::Constant const* const* args, + size_t num_args); } // namespace tint::resolver::const_eval diff --git a/src/tint/resolver/materialize_test.cc b/src/tint/resolver/materialize_test.cc index 0ace1ca25d..1b935eef0e 100644 --- a/src/tint/resolver/materialize_test.cc +++ b/src/tint/resolver/materialize_test.cc @@ -73,12 +73,61 @@ static std::ostream& operator<<(std::ostream& o, Expectation m) { return o << ""; } +template +class MaterializeTest : public resolver::ResolverTestWithParam { + protected: + using ProgramBuilder::FriendlyName; + + void CheckTypesAndValues(const sem::Expression* expr, + const tint::sem::Type* expected_sem_ty, + const std::variant& expected_value) { + std::visit([&](auto v) { CheckTypesAndValuesImpl(expr, expected_sem_ty, v); }, + expected_value); + } + + private: + template + void CheckTypesAndValuesImpl(const sem::Expression* expr, + const tint::sem::Type* expected_sem_ty, + T expected_value) { + EXPECT_TYPE(expr->Type(), expected_sem_ty); + + auto* value = expr->ConstantValue(); + ASSERT_NE(value, nullptr); + EXPECT_TYPE(expr->Type(), value->Type()); + + tint::Switch( + expected_sem_ty, // + [&](const sem::Vector* v) { + for (uint32_t i = 0; i < v->Width(); i++) { + auto* el = value->Index(i); + ASSERT_NE(el, nullptr); + EXPECT_TYPE(el->Type(), v->type()); + EXPECT_EQ(std::get(el->Value()), expected_value); + } + }, + [&](const sem::Matrix* m) { + for (uint32_t c = 0; c < m->columns(); c++) { + auto* column = value->Index(c); + ASSERT_NE(column, nullptr); + EXPECT_TYPE(column->Type(), m->ColumnType()); + for (uint32_t r = 0; r < m->rows(); r++) { + auto* el = column->Index(r); + ASSERT_NE(el, nullptr); + EXPECT_TYPE(el->Type(), m->type()); + EXPECT_EQ(std::get(el->Value()), expected_value); + } + } + }, + [&](Default) { EXPECT_EQ(std::get(value->Value()), expected_value); }); + } +}; + //////////////////////////////////////////////////////////////////////////////////////////////////// // MaterializeAbstractNumericToConcreteType // Tests that an abstract-numeric will materialize to the expected concrete type //////////////////////////////////////////////////////////////////////////////////////////////////// namespace materialize_abstract_numeric_to_concrete_type { - // How should the materialization occur? enum class Method { // var a : target_type = abstract_expr; @@ -247,7 +296,7 @@ static std::ostream& operator<<(std::ostream& o, const Data& c) { } using MaterializeAbstractNumericToConcreteType = - resolver::ResolverTestWithParam>; + MaterializeTest>; TEST_P(MaterializeAbstractNumericToConcreteType, Test) { // Once built-in and ops using f16 is properly supported, we'll need to enable this: @@ -323,30 +372,12 @@ TEST_P(MaterializeAbstractNumericToConcreteType, Test) { break; } - auto check_types_and_values = [&](const sem::Expression* expr) { - auto* target_sem_ty = data.target_sem_ty(*this); - - EXPECT_TYPE(expr->Type(), target_sem_ty); - EXPECT_TYPE(expr->ConstantValue().Type(), target_sem_ty); - - uint32_t num_elems = 0; - const sem::Type* target_sem_el_ty = sem::Type::DeepestElementOf(target_sem_ty, &num_elems); - EXPECT_TYPE(expr->ConstantValue().ElementType(), target_sem_el_ty); - expr->ConstantValue().WithElements([&](auto&& vec) { - using VEC_TY = std::decay_t; - using EL_TY = typename VEC_TY::value_type; - ASSERT_TRUE(std::holds_alternative(data.materialized_value)); - VEC_TY expected(num_elems, std::get(data.materialized_value)); - EXPECT_EQ(vec, expected); - }); - }; - switch (expectation) { case Expectation::kMaterialize: { ASSERT_TRUE(r()->Resolve()) << r()->error(); auto* materialize = Sem().Get(abstract_expr); ASSERT_NE(materialize, nullptr); - check_types_and_values(materialize); + CheckTypesAndValues(materialize, data.target_sem_ty(*this), data.materialized_value); break; } case Expectation::kNoMaterialize: { @@ -354,7 +385,7 @@ TEST_P(MaterializeAbstractNumericToConcreteType, Test) { auto* sem = Sem().Get(abstract_expr); ASSERT_NE(sem, nullptr); EXPECT_FALSE(sem->Is()); - check_types_and_values(sem); + CheckTypesAndValues(sem, data.target_sem_ty(*this), data.materialized_value); break; } case Expectation::kInvalidConversion: { @@ -414,8 +445,8 @@ constexpr Method kSwitchMethods[] = { /// Methods that do not materialize constexpr Method kNoMaterializeMethods[] = { Method::kPhonyAssign, - // TODO(crbug.com/tint/1504): Enable once we have abstract overloads of builtins / binary ops: - // Method::kBuiltinArg, Method::kBinaryOp, + // TODO(crbug.com/tint/1504): Enable once we have abstract overloads of builtins / binary + // ops: Method::kBuiltinArg, Method::kBinaryOp, }; INSTANTIATE_TEST_SUITE_P( MaterializeScalar, @@ -703,7 +734,7 @@ static std::ostream& operator<<(std::ostream& o, const Data& c) { } using MaterializeAbstractNumericToDefaultType = - resolver::ResolverTestWithParam>; + MaterializeTest>; TEST_P(MaterializeAbstractNumericToDefaultType, Test) { const auto& param = GetParam(); @@ -751,32 +782,14 @@ TEST_P(MaterializeAbstractNumericToDefaultType, Test) { break; } - auto check_types_and_values = [&](const sem::Expression* expr) { - auto* expected_sem_ty = data.expected_sem_ty(*this); - - EXPECT_TYPE(expr->Type(), expected_sem_ty); - EXPECT_TYPE(expr->ConstantValue().Type(), expected_sem_ty); - - uint32_t num_elems = 0; - const sem::Type* expected_sem_el_ty = - sem::Type::DeepestElementOf(expected_sem_ty, &num_elems); - EXPECT_TYPE(expr->ConstantValue().ElementType(), expected_sem_el_ty); - expr->ConstantValue().WithElements([&](auto&& vec) { - using VEC_TY = std::decay_t; - using EL_TY = typename VEC_TY::value_type; - ASSERT_TRUE(std::holds_alternative(data.materialized_value)); - VEC_TY expected(num_elems, std::get(data.materialized_value)); - EXPECT_EQ(vec, expected); - }); - }; - switch (expectation) { case Expectation::kMaterialize: { ASSERT_TRUE(r()->Resolve()) << r()->error(); for (auto* expr : abstract_exprs) { auto* materialize = Sem().Get(expr); ASSERT_NE(materialize, nullptr); - check_types_and_values(materialize); + CheckTypesAndValues(materialize, data.expected_sem_ty(*this), + data.materialized_value); } break; } diff --git a/src/tint/resolver/resolver.cc b/src/tint/resolver/resolver.cc index e6cad77247..9ef9a303f5 100644 --- a/src/tint/resolver/resolver.cc +++ b/src/tint/resolver/resolver.cc @@ -365,13 +365,13 @@ sem::Variable* Resolver::Let(const ast::Let* v, bool is_global) { sem::Variable* sem = nullptr; if (is_global) { - sem = builder_->create(v, ty, ast::StorageClass::kNone, - ast::Access::kUndefined, sem::Constant{}, - sem::BindingPoint{}); + sem = builder_->create( + v, ty, ast::StorageClass::kNone, ast::Access::kUndefined, /* constant_value */ nullptr, + sem::BindingPoint{}); } else { sem = builder_->create(v, ty, ast::StorageClass::kNone, ast::Access::kUndefined, current_statement_, - sem::Constant{}); + /* constant_value */ nullptr); } sem->SetConstructor(rhs); @@ -419,9 +419,9 @@ sem::Variable* Resolver::Override(const ast::Override* v) { return nullptr; } - auto* sem = builder_->create(v, ty, ast::StorageClass::kNone, - ast::Access::kUndefined, sem::Constant{}, - sem::BindingPoint{}); + auto* sem = builder_->create( + v, ty, ast::StorageClass::kNone, ast::Access::kUndefined, /* constant_value */ nullptr, + sem::BindingPoint{}); if (auto* id = ast::GetAttribute(v->attributes)) { sem->SetConstantId(static_cast(id->value)); @@ -564,11 +564,11 @@ sem::Variable* Resolver::Var(const ast::Var* var, bool is_global) { binding_point = {bp.group->value, bp.binding->value}; } sem = builder_->create(var, var_ty, storage_class, access, - sem::Constant{}, binding_point); + /* constant_value */ nullptr, binding_point); } else { - sem = builder_->create(var, var_ty, storage_class, access, - current_statement_, sem::Constant{}); + sem = builder_->create( + var, var_ty, storage_class, access, current_statement_, /* constant_value */ nullptr); } sem->SetConstructor(rhs); @@ -916,7 +916,7 @@ bool Resolver::WorkgroupSize(const ast::Function* func) { return false; } - sem::Constant value; + const sem::Constant* value = nullptr; if (auto* user = args[i]->As()) { // We have an variable of a module-scope constant. @@ -950,12 +950,12 @@ bool Resolver::WorkgroupSize(const ast::Function* func) { continue; } // validator_.Validate and set the default value for this dimension. - if (value.Element(0).value < 1) { + if (value->As() < 1) { AddError("workgroup_size argument must be at least 1", values[i]->source); return false; } - ws[i].value = value.Element(0); + ws[i].value = value->As(); } current_function_->SetWorkgroupSize(std::move(ws)); @@ -1266,7 +1266,8 @@ sem::Expression* Resolver::Expression(const ast::Expression* root) { [&](const ast::UnaryOpExpression* unary) -> sem::Expression* { return UnaryOp(unary); }, [&](const ast::PhonyExpression*) -> sem::Expression* { return builder_->create(expr, builder_->create(), - current_statement_, sem::Constant{}, + current_statement_, + /* constant_value */ nullptr, /* has_side_effects */ false); }, [&](Default) { @@ -1309,13 +1310,14 @@ const sem::Expression* Resolver::Materialize(const sem::Expression* expr, << ") returned invalid value"; return nullptr; } - auto materialized_val = ConvertValue(std::move(expr_val), target_ty, decl->source); + auto materialized_val = ConvertValue(expr_val, target_ty, decl->source); if (!materialized_val) { + // ConvertValue() has already failed and raised an diagnostic error. return nullptr; } - if (!materialized_val->IsValid()) { + if (!materialized_val.Get()) { TINT_ICE(Resolver, builder_->Diagnostics()) - << decl->source << "ConvertValue(" << builder_->FriendlyName(expr_val.Type()) + << decl->source << "ConvertValue(" << builder_->FriendlyName(expr_val->Type()) << " -> " << builder_->FriendlyName(target_ty) << ") returned invalid value"; return nullptr; } @@ -1678,9 +1680,9 @@ sem::Call* Resolver::BuiltinCall(const ast::CallExpression* expr, } // If the builtin is @const, and all arguments have constant values, evaluate the builtin now. - sem::Constant constant; + const sem::Constant* constant = nullptr; if (builtin.const_eval_fn) { - std::vector values(args.size()); + std::vector values(args.size()); bool is_const = true; // all arguments have constant values for (size_t i = 0; i < values.size(); i++) { if (auto v = args[i]->ConstantValue()) { @@ -1757,7 +1759,7 @@ sem::Call* Resolver::FunctionCall(const ast::CallExpression* expr, // effects. bool has_side_effects = true; auto* call = builder_->create(expr, target, std::move(args), current_statement_, - sem::Constant{}, has_side_effects); + /* constant_value */ nullptr, has_side_effects); target->AddCallSite(call); @@ -2226,21 +2228,21 @@ sem::Array* Resolver::Array(const ast::Array* arr) { return nullptr; } - auto count_val = count_sem->ConstantValue(); + auto* count_val = count_sem->ConstantValue(); if (!count_val) { AddError("array size must evaluate to a constant integer expression", count_expr->source); return nullptr; } - if (auto* ty = count_val.Type(); !ty->is_integer_scalar()) { + if (auto* ty = count_val->Type(); !ty->is_integer_scalar()) { AddError("array size must evaluate to a constant integer expression, but is type '" + builder_->FriendlyName(ty) + "'", count_expr->source); return nullptr; } - count = count_val.Element(0).value; + count = count_val->As(); if (count < 1) { AddError("array size (" + std::to_string(count) + ") must be greater than 0", count_expr->source); diff --git a/src/tint/resolver/resolver.h b/src/tint/resolver/resolver.h index aab772e930..1a45bc4e34 100644 --- a/src/tint/resolver/resolver.h +++ b/src/tint/resolver/resolver.h @@ -209,22 +209,30 @@ class Resolver { /// These methods are called from the expression resolving methods, and so child-expression /// nodes are guaranteed to have been already resolved and any constant values calculated. //////////////////////////////////////////////////////////////////////////////////////////////// - sem::Constant EvaluateConstantValue(const ast::Expression* expr, const sem::Type* type); - sem::Constant EvaluateConstantValue(const ast::IdentifierExpression* ident, - const sem::Type* type); - sem::Constant EvaluateConstantValue(const ast::LiteralExpression* literal, - const sem::Type* type); - sem::Constant EvaluateConstantValue(const ast::CallExpression* call, const sem::Type* type); - sem::Constant EvaluateConstantValue(const ast::IndexAccessorExpression* call, - const sem::Type* type); + const sem::Constant* EvaluateConstantValue(const ast::Expression* expr, const sem::Type* type); + const sem::Constant* EvaluateConstantValue(const ast::IdentifierExpression* ident, + const sem::Type* type); + const sem::Constant* EvaluateConstantValue(const ast::LiteralExpression* literal, + const sem::Type* type); + const sem::Constant* EvaluateConstantValue(const ast::CallExpression* call, + const sem::Type* type); + const sem::Constant* EvaluateConstantValue(const ast::IndexAccessorExpression* call, + const sem::Type* type); - /// The result type of a ConstantEvaluation method. Holds the constant value and a boolean, - /// which is true on success, false on an error. - using ConstantResult = utils::Result; + /// The result type of a ConstantEvaluation method. + /// Can be one of three distinct values: + /// * A non-null sem::Constant pointer. Returned when a expression resolves to a creation time + /// value. + /// * A null sem::Constant pointer. Returned when a expression cannot resolve to a creation time + /// value, but is otherwise legal. + /// * `utils::Failure`. Returned when there was a resolver error. In this situation the method + /// will have already reported a diagnostic error message, and the caller should abort + /// resolving. + using ConstantResult = utils::Result; /// Convert the `value` to `target_type` /// @return the converted value - ConstantResult ConvertValue(const sem::Constant& value, + ConstantResult ConvertValue(const sem::Constant* value, const sem::Type* target_type, const Source& source); diff --git a/src/tint/resolver/resolver_constants.cc b/src/tint/resolver/resolver_constants.cc index 74e432793c..f6ffb28645 100644 --- a/src/tint/resolver/resolver_constants.cc +++ b/src/tint/resolver/resolver_constants.cc @@ -14,7 +14,6 @@ #include "src/tint/resolver/resolver.h" -#include #include #include "src/tint/sem/abstract_float.h" @@ -22,8 +21,6 @@ #include "src/tint/sem/constant.h" #include "src/tint/sem/type_constructor.h" #include "src/tint/utils/compiler_macros.h" -#include "src/tint/utils/map.h" -#include "src/tint/utils/transform.h" using namespace tint::number_suffixes; // NOLINT @@ -31,127 +28,334 @@ namespace tint::resolver { namespace { -/// Converts and returns all the element values of `in` to the type `T`, using the converter -/// function `CONVERTER`. -/// @param elements_in the vector of elements to be converted -/// @param converter a function-like with the signature `void(TO&, FROM)` -/// @returns the elements converted to type T. -template -sem::Constant::Elements Transform(const ELEMENTS_IN& elements_in, CONVERTER&& converter) { - TINT_BEGIN_DISABLE_WARNING(UNREACHABLE_CODE); +/// TypeDispatch is a helper for calling the function `f`, passing a single zero-value argument of +/// the C++ type that corresponds to the sem::Type `type`. For example, calling `TypeDispatch()` +/// with a type of `sem::I32*` will call the function f with a single argument of `i32(0)`. +/// @returns the value returned by calling `f`. +/// @note `type` must be a scalar or abstract numeric type. Other types will not call `f`, and will +/// return the zero-initialized value of the return type for `f`. +template +auto TypeDispatch(const sem::Type* type, F&& f) { + return Switch( + type, // + [&](const sem::AbstractInt*) { return f(AInt(0)); }, // + [&](const sem::AbstractFloat*) { return f(AFloat(0)); }, // + [&](const sem::I32*) { return f(i32(0)); }, // + [&](const sem::U32*) { return f(u32(0)); }, // + [&](const sem::F32*) { return f(f32(0)); }, // + [&](const sem::F16*) { return f(f16(0)); }, // + [&](const sem::Bool*) { return f(static_cast(0)); }); +} - return utils::Transform(elements_in, [&](auto value_in) { - if constexpr (std::is_same_v, bool>) { - return AInt(value_in != 0); +/// @returns `value` if `T` is not a Number, otherwise ValueOf returns the inner value of the +/// Number. +template +inline auto ValueOf(T value) { + if constexpr (std::is_same_v, T>) { + return value; + } else { + return value.value; + } +} + +/// @returns true if `value` is a positive zero. +template +inline bool IsPositiveZero(T value) { + using N = UnwrapNumber; + return Number(value) == Number(0); // Considers sign bit +} + +/// Constant inherits from sem::Constant to add an private implementation method for conversion. +struct Constant : public sem::Constant { + /// Convert attempts to convert the constant value to the given type. On error, Convert() + /// creates a new diagnostic message and returns a Failure. + virtual utils::Result Convert(ProgramBuilder& builder, + const sem::Type* target_ty, + const Source& source) const = 0; +}; + +// Forward declaration +const Constant* CreateComposite(ProgramBuilder& builder, + const sem::Type* type, + std::vector elements); + +/// Element holds a single scalar or abstract-numeric value. +/// Element implements the Constant interface. +template +struct Element : Constant { + Element(const sem::Type* t, T v) : type(t), value(v) {} + ~Element() override = default; + const sem::Type* Type() const override { return type; } + std::variant Value() const override { + if constexpr (IsFloatingPoint>) { + return static_cast(value); } else { - T converted{}; - converter(converted, value_in); - if constexpr (IsFloatingPoint>) { - return AFloat(converted); + return static_cast(value); + } + } + const Constant* Index(size_t) const override { return nullptr; } + bool AllZero() const override { return IsPositiveZero(value); } + bool AnyZero() const override { return IsPositiveZero(value); } + bool AllEqual() const override { return true; } + size_t Hash() const override { return utils::Hash(type, ValueOf(value)); } + + utils::Result Convert(ProgramBuilder& builder, + const sem::Type* target_ty, + const Source& source) const override { + TINT_BEGIN_DISABLE_WARNING(UNREACHABLE_CODE); + if (target_ty == type) { + // If the types are identical, then no conversion is needed. + return this; + } + bool failed = false; + auto* res = TypeDispatch(target_ty, [&](auto zero_to) -> const Constant* { + // `T` is the source type, `value` is the source value. + // `TO` is the target type. + using TO = std::decay_t; + if constexpr (std::is_same_v) { + // [x -> bool] + return builder.create>(target_ty, !IsPositiveZero(value)); + } else if constexpr (std::is_same_v) { + // [bool -> x] + return builder.create>(target_ty, TO(value ? 1 : 0)); + } else if (auto conv = CheckedConvert(value)) { + // Conversion success + return builder.create>(target_ty, conv.Get()); + // --- Below this point are the failure cases --- + } else if constexpr (std::is_same_v || std::is_same_v) { + // [abstract-numeric -> x] - materialization failure + std::stringstream ss; + ss << "value " << value << " cannot be represented as "; + ss << "'" << builder.FriendlyName(target_ty) << "'"; + builder.Diagnostics().add_error(tint::diag::System::Resolver, ss.str(), source); + failed = true; + } else if constexpr (IsFloatingPoint>) { + // [x -> floating-point] - number not exactly representable + // https://www.w3.org/TR/WGSL/#floating-point-conversion + constexpr auto kInf = std::numeric_limits::infinity(); + switch (conv.Failure()) { + case ConversionFailure::kExceedsNegativeLimit: + return builder.create>(target_ty, TO(-kInf)); + case ConversionFailure::kExceedsPositiveLimit: + return builder.create>(target_ty, TO(kInf)); + } } else { - return AInt(converted); + // [x -> integer] - number not exactly representable + // https://www.w3.org/TR/WGSL/#floating-point-conversion + switch (conv.Failure()) { + case ConversionFailure::kExceedsNegativeLimit: + return builder.create>(target_ty, TO(TO::kLowest)); + case ConversionFailure::kExceedsPositiveLimit: + return builder.create>(target_ty, TO(TO::kHighest)); + } } + return nullptr; // Expression is not constant. + }); + if (failed) { + // A diagnostic error has been raised, and resolving should abort. + return utils::Failure; } - }); - - TINT_END_DISABLE_WARNING(UNREACHABLE_CODE); -} - -/// Converts and returns all the element values of `in` to the semantic type `el_ty`, using the -/// converter function `CONVERTER`. -/// @param in the constant to convert -/// @param el_ty the target element type -/// @param converter a function-like with the signature `void(TO&, FROM)` -/// @returns the elements converted to `el_ty` -template -sem::Constant::Elements Transform(const sem::Constant::Elements& in, - const sem::Type* el_ty, - CONVERTER&& converter) { - return std::visit( - [&](auto&& v) { - return Switch( - el_ty, // - [&](const sem::AbstractInt*) { return Transform(v, converter); }, - [&](const sem::AbstractFloat*) { return Transform(v, converter); }, - [&](const sem::I32*) { return Transform(v, converter); }, - [&](const sem::U32*) { return Transform(v, converter); }, - [&](const sem::F32*) { return Transform(v, converter); }, - [&](const sem::F16*) { return Transform(v, converter); }, - [&](const sem::Bool*) { return Transform(v, converter); }, - [&](Default) -> sem::Constant::Elements { - diag::List diags; - TINT_UNREACHABLE(Semantic, diags) - << "invalid element type " << el_ty->TypeInfo().name; - return {}; - }); - }, - in); -} - -/// Converts and returns all the elements in `in` to the type `el_ty`. -/// If the value does not fit in the target type, and: -/// * the target type is an integer type, then the resulting value will be clamped to the integer's -/// highest or lowest value. -/// * the target type is an float type, then the resulting value will be either positive or -/// negative infinity, based on the sign of the input value. -/// @param in the input elements -/// @param el_ty the target element type -/// @returns the elements converted to `el_ty` -sem::Constant::Elements ConvertElements(const sem::Constant::Elements& in, const sem::Type* el_ty) { - return Transform(in, el_ty, [](auto& el_out, auto el_in) { - using OUT = std::decay_t; - if (auto conv = CheckedConvert(el_in)) { - el_out = conv.Get(); - } else { - constexpr auto kInf = std::numeric_limits::infinity(); - switch (conv.Failure()) { - case ConversionFailure::kExceedsNegativeLimit: - el_out = IsFloatingPoint> ? OUT(-kInf) : OUT::kLowest; - break; - case ConversionFailure::kExceedsPositiveLimit: - el_out = IsFloatingPoint> ? OUT(kInf) : OUT::kHighest; - break; - } - } - }); -} - -/// Converts and returns all the elements in `in` to the type `el_ty`, by performing a -/// `CheckedConvert` on each element value. A single error diagnostic will be raised if an element -/// value cannot be represented by the target type. -/// @param in the input elements -/// @param el_ty the target element type -/// @returns the elements converted to `el_ty`, or a Failure if some elements could not be -/// represented by the target type. -utils::Result MaterializeElements(const sem::Constant::Elements& in, - const sem::Type* el_ty, - ProgramBuilder& builder, - Source source) { - std::optional failure; - - auto out = Transform(in, el_ty, [&](auto& el_out, auto el_in) { - using OUT = std::decay_t; - if (auto conv = CheckedConvert(el_in)) { - el_out = conv.Get(); - } else if (!failure.has_value()) { - std::stringstream ss; - ss << "value " << el_in << " cannot be represented as "; - ss << "'" << builder.FriendlyName(el_ty) << "'"; - failure = ss.str(); - } - }); - - if (failure.has_value()) { - builder.Diagnostics().add_error(diag::System::Resolver, std::move(failure.value()), source); - return utils::Failure; + return res; + TINT_END_DISABLE_WARNING(UNREACHABLE_CODE); } - return out; + sem::Type const* const type; + const T value; +}; + +/// Splat holds a single Constant value, duplicated as all children. +/// Splat is used for zero-initializers, 'splat' constructors, or constructors where each element is +/// identical. Splat may be of a vector, matrix or array type. +/// Splat implements the Constant interface. +struct Splat : Constant { + Splat(const sem::Type* t, const Constant* e, size_t n) : type(t), el(e), count(n) {} + ~Splat() override = default; + const sem::Type* Type() const override { return type; } + std::variant Value() const override { return {}; } + const Constant* Index(size_t i) const override { return i < count ? el : nullptr; } + bool AllZero() const override { return el->AllZero(); } + bool AnyZero() const override { return el->AnyZero(); } + bool AllEqual() const override { return true; } + size_t Hash() const override { return utils::Hash(type, el->Hash(), count); } + + utils::Result Convert(ProgramBuilder& builder, + const sem::Type* target_ty, + const Source& source) const override { + // Convert the single splatted element type. + auto conv_el = el->Convert(builder, sem::Type::ElementOf(target_ty), source); + if (!conv_el) { + return utils::Failure; + } + if (!conv_el.Get()) { + return nullptr; + } + return builder.create(target_ty, conv_el.Get(), count); + } + + sem::Type const* const type; + const Constant* el; + const size_t count; +}; + +/// Composite holds a number of mixed child Constant values. +/// Composite may be of a vector, matrix or array type. +/// If each element is the same type and value, then a Splat would be a more efficient constant +/// implementation. Use CreateComposite() to create the appropriate Constant type. +/// Composite implements the Constant interface. +struct Composite : Constant { + Composite(const sem::Type* t, std::vector els, bool all_0, bool any_0) + : type(t), elements(std::move(els)), all_zero(all_0), any_zero(any_0), hash(CalcHash()) {} + ~Composite() override = default; + const sem::Type* Type() const override { return type; } + std::variant Value() const override { return {}; } + const Constant* Index(size_t i) const override { + return i < elements.size() ? elements[i] : nullptr; + } + bool AllZero() const override { return all_zero; } + bool AnyZero() const override { return any_zero; } + bool AllEqual() const override { return false; /* otherwise this should be a Splat */ } + size_t Hash() const override { return hash; } + + utils::Result Convert(ProgramBuilder& builder, + const sem::Type* target_ty, + const Source& source) const override { + // Convert each of the composite element types. + auto* el_ty = sem::Type::ElementOf(target_ty); + std::vector conv_els; + conv_els.reserve(elements.size()); + for (auto* el : elements) { + auto conv_el = el->Convert(builder, el_ty, source); + if (!conv_el) { + return utils::Failure; + } + if (!conv_el.Get()) { + return nullptr; + } + conv_els.emplace_back(conv_el.Get()); + } + return CreateComposite(builder, target_ty, std::move(conv_els)); + } + + size_t CalcHash() { + auto h = utils::Hash(type, all_zero, any_zero); + for (auto* el : elements) { + utils::HashCombine(&h, el->Hash()); + } + return h; + } + + sem::Type const* const type; + const std::vector elements; + const bool all_zero; + const bool any_zero; + const size_t hash; +}; + +/// CreateElement constructs and returns an Element. +template +const Constant* CreateElement(ProgramBuilder& builder, const sem::Type* t, T v) { + return builder.create>(t, v); +} + +/// ZeroValue returns a Constant for the zero-value of the type `type`. +const Constant* ZeroValue(ProgramBuilder& builder, const sem::Type* type) { + return Switch( + type, // + [&](const sem::Vector* v) -> const Constant* { + auto* zero_el = ZeroValue(builder, v->type()); + return builder.create(type, zero_el, v->Width()); + }, + [&](const sem::Matrix* m) -> const Constant* { + auto* zero_el = ZeroValue(builder, m->ColumnType()); + return builder.create(type, zero_el, m->columns()); + }, + [&](const sem::Array* a) -> const Constant* { + if (auto* zero_el = ZeroValue(builder, a->ElemType())) { + return builder.create(type, zero_el, a->Count()); + } + return nullptr; + }, + [&](Default) -> const Constant* { + return TypeDispatch(type, [&](auto zero) -> const Constant* { + return CreateElement(builder, type, zero); + }); + }); +} + +/// Equal returns true if the constants `a` and `b` are of the same type and value. +bool Equal(const sem::Constant* a, const sem::Constant* b) { + if (a->Hash() != b->Hash()) { + return false; + } + if (a->Type() != b->Type()) { + return false; + } + return Switch( + a->Type(), // + [&](const sem::Vector* vec) { + for (size_t i = 0; i < vec->Width(); i++) { + if (!Equal(a->Index(i), b->Index(i))) { + return false; + } + } + return true; + }, + [&](const sem::Matrix* mat) { + for (size_t i = 0; i < mat->columns(); i++) { + if (!Equal(a->Index(i), b->Index(i))) { + return false; + } + } + return true; + }, + [&](const sem::Array* arr) { + for (size_t i = 0; i < arr->Count(); i++) { + if (!Equal(a->Index(i), b->Index(i))) { + return false; + } + } + return true; + }, + [&](Default) { return a->Value() == b->Value(); }); +} + +/// CreateComposite is used to construct a constant of a vector, matrix or array type. +/// CreateComposite examines the element values and will return either a Composite or a Splat, +/// depending on the element types and values. +const Constant* CreateComposite(ProgramBuilder& builder, + const sem::Type* type, + std::vector elements) { + if (elements.size() == 0) { + return nullptr; + } + bool any_zero = false; + bool all_zero = true; + bool all_equal = true; + auto* first = elements.front(); + for (auto* el : elements) { + if (!any_zero && el->AnyZero()) { + any_zero = true; + } + if (all_zero && !el->AllZero()) { + all_zero = false; + } + if (all_equal && el != first) { + if (!Equal(el, first)) { + all_equal = false; + } + } + } + if (all_equal) { + return builder.create(type, elements[0], elements.size()); + } else { + return builder.create(type, std::move(elements), all_zero, any_zero); + } } } // namespace -sem::Constant Resolver::EvaluateConstantValue(const ast::Expression* expr, const sem::Type* type) { +const sem::Constant* Resolver::EvaluateConstantValue(const ast::Expression* expr, + const sem::Type* type) { return Switch( expr, // [&](const ast::IdentifierExpression* e) { return EvaluateConstantValue(e, type); }, @@ -160,112 +364,176 @@ sem::Constant Resolver::EvaluateConstantValue(const ast::Expression* expr, const [&](const ast::IndexAccessorExpression* e) { return EvaluateConstantValue(e, type); }); } -sem::Constant Resolver::EvaluateConstantValue(const ast::IdentifierExpression* ident, - const sem::Type*) { +const sem::Constant* Resolver::EvaluateConstantValue(const ast::IdentifierExpression* ident, + const sem::Type*) { if (auto* sem = builder_->Sem().Get(ident)) { return sem->ConstantValue(); } return {}; } -sem::Constant Resolver::EvaluateConstantValue(const ast::LiteralExpression* literal, - const sem::Type* type) { +const sem::Constant* Resolver::EvaluateConstantValue(const ast::LiteralExpression* literal, + const sem::Type* type) { return Switch( literal, [&](const ast::BoolLiteralExpression* lit) { - return sem::Constant{type, {AInt(lit->value ? 1 : 0)}}; + return CreateElement(*builder_, type, lit->value); }, - [&](const ast::IntLiteralExpression* lit) { - return sem::Constant{type, {AInt(lit->value)}}; + [&](const ast::IntLiteralExpression* lit) -> const Constant* { + switch (lit->suffix) { + case ast::IntLiteralExpression::Suffix::kNone: + return CreateElement(*builder_, type, AInt(lit->value)); + case ast::IntLiteralExpression::Suffix::kI: + return CreateElement(*builder_, type, i32(lit->value)); + case ast::IntLiteralExpression::Suffix::kU: + return CreateElement(*builder_, type, u32(lit->value)); + } + return nullptr; }, - [&](const ast::FloatLiteralExpression* lit) { - return sem::Constant{type, {AFloat(lit->value)}}; + [&](const ast::FloatLiteralExpression* lit) -> const Constant* { + switch (lit->suffix) { + case ast::FloatLiteralExpression::Suffix::kNone: + return CreateElement(*builder_, type, AFloat(lit->value)); + case ast::FloatLiteralExpression::Suffix::kF: + return CreateElement(*builder_, type, f32(lit->value)); + case ast::FloatLiteralExpression::Suffix::kH: + return CreateElement(*builder_, type, f16(lit->value)); + } + return nullptr; }); } -sem::Constant Resolver::EvaluateConstantValue(const ast::CallExpression* call, - const sem::Type* ty) { - uint32_t num_elems = 0; - auto* el_ty = sem::Type::DeepestElementOf(ty, &num_elems); - if (!el_ty || num_elems == 0) { - return {}; - } - +const sem::Constant* Resolver::EvaluateConstantValue(const ast::CallExpression* call, + const sem::Type* ty) { // Note: we are building constant values for array types. The working group as verbally agreed // to support constant expression arrays, but this is not (yet) part of the spec. // See: https://github.com/gpuweb/gpuweb/issues/3056 // For zero value init, return 0s if (call->args.empty()) { - return Switch( - el_ty, - [&](const sem::AbstractInt*) { - return sem::Constant(ty, std::vector(num_elems, AInt(0))); - }, - [&](const sem::AbstractFloat*) { - return sem::Constant(ty, std::vector(num_elems, AFloat(0))); - }, - [&](const sem::I32*) { return sem::Constant(ty, std::vector(num_elems, AInt(0))); }, - [&](const sem::U32*) { return sem::Constant(ty, std::vector(num_elems, AInt(0))); }, - [&](const sem::F32*) { return sem::Constant(ty, std::vector(num_elems, AFloat(0))); }, - [&](const sem::F16*) { return sem::Constant(ty, std::vector(num_elems, AFloat(0))); }, - [&](const sem::Bool*) { return sem::Constant(ty, std::vector(num_elems, AInt(0))); }); + return ZeroValue(*builder_, ty); } - // Build value for type_ctor from each child value by converting to type_ctor's type. - std::optional elements; - for (auto* expr : call->args) { - auto* arg = builder_->Sem().Get(expr); + uint32_t el_count = 0; + auto* el_ty = sem::Type::ElementOf(ty, &el_count); + if (!el_ty) { + return nullptr; // Target type does not support constant values + } + + // value_of returns a `const Constant*` for the expression `expr`, or nullptr if the expression + // does not have a constant value. + auto value_of = [&](const ast::Expression* expr) { + return static_cast(builder_->Sem().Get(expr)->ConstantValue()); + }; + + if (call->args.size() == 1) { + // Type constructor or conversion that takes a single argument. + auto& src = call->args[0]->source; + auto* arg = value_of(call->args[0]); if (!arg) { - return {}; - } - auto value = arg->ConstantValue(); - if (!value) { - return {}; + return nullptr; // Single argument is not constant. } - // Convert the elements to the desired type. - auto converted = ConvertElements(value.GetElements(), el_ty); - - if (elements.has_value()) { - // Append the converted vector to elements - std::visit( - [&](auto&& dst) { - using VEC_TY = std::decay_t; - const auto& src = std::get(converted); - dst.insert(dst.end(), src.begin(), src.end()); - }, - elements.value()); - } else { - elements = std::move(converted); + if (ty->is_scalar()) { // Scalar type conversion: i32(x), u32(x), bool(x), etc + return ConvertValue(arg, el_ty, src).Get(); } + + if (arg->Type() == el_ty) { + // Argument type matches function type. This is a splat. + auto splat = [&](size_t n) { return builder_->create(ty, arg, n); }; + return Switch( + ty, // + [&](const sem::Vector* v) { return splat(v->Width()); }, + [&](const sem::Matrix* m) { return splat(m->columns()); }, + [&](const sem::Array* a) { return splat(a->Count()); }); + } + + // Argument type and function type mismatch. This is a type conversion. + if (auto conv = ConvertValue(arg, ty, src)) { + return conv.Get(); + } + + return nullptr; } - if (!elements) { - return {}; - } + // Multiple arguments. Must be a type constructor. - return std::visit( - [&](auto&& v) { - if (num_elems != v.size()) { - if (v.size() == 1) { - // Splat single-value initializers - for (uint32_t i = 0; i < num_elems - 1; ++i) { - v.emplace_back(v[0]); + std::vector els; // The constant elements for the composite constant. + els.reserve(el_count); + + // Helper for pushing all the argument constants to `els`. + auto push_all_args = [&] { + for (auto* expr : call->args) { + auto* arg = value_of(expr); + if (!arg) { + return; + } + els.emplace_back(arg); + } + }; + + Switch( + ty, // What's the target type being constructed? + [&](const sem::Vector*) { + // Vector can be constructed with a mix of scalars / abstract numerics and smaller + // vectors. + for (auto* expr : call->args) { + auto* arg = value_of(expr); + if (!arg) { + return; + } + auto* arg_ty = arg->Type(); + if (auto* arg_vec = arg_ty->As()) { + // Extract out vector elements. + for (uint32_t i = 0; i < arg_vec->Width(); i++) { + auto* el = static_cast(arg->Index(i)); + if (!el) { + return; + } + els.emplace_back(el); } } else { - // Provided number of arguments does not match the required number of elements. - // Validation should error here. - return sem::Constant{}; + els.emplace_back(arg); } } - return sem::Constant(ty, std::move(elements.value())); }, - elements.value()); + [&](const sem::Matrix* m) { + // Matrix can be constructed with a set of scalars / abstract numerics, or column + // vectors. + if (call->args.size() == m->columns() * m->rows()) { + // Matrix built from scalars / abstract numerics + for (uint32_t c = 0; c < m->columns(); c++) { + std::vector column; + column.reserve(m->rows()); + for (uint32_t r = 0; r < m->rows(); r++) { + auto* arg = value_of(call->args[r + c * m->rows()]); + if (!arg) { + return; + } + column.emplace_back(arg); + } + els.push_back(CreateComposite(*builder_, m->ColumnType(), std::move(column))); + } + } else if (call->args.size() == m->columns()) { + // Matrix built from column vectors + push_all_args(); + } + }, + [&](const sem::Array*) { + // Arrays must be constructed using a list of elements + push_all_args(); + }); + + if (els.size() != el_count) { + // If the number of constant elements doesn't match the type, then something went wrong. + return nullptr; + } + // Construct and return either a Composite or Splat. + return CreateComposite(*builder_, ty, std::move(els)); } -sem::Constant Resolver::EvaluateConstantValue(const ast::IndexAccessorExpression* accessor, - const sem::Type* el_ty) { +const sem::Constant* Resolver::EvaluateConstantValue(const ast::IndexAccessorExpression* accessor, + const sem::Type*) { auto* obj_sem = builder_->Sem().Get(accessor->object); if (!obj_sem) { return {}; @@ -282,20 +550,14 @@ sem::Constant Resolver::EvaluateConstantValue(const ast::IndexAccessorExpression } auto idx_val = idx_sem->ConstantValue(); - if (!idx_val || idx_val.ElementCount() != 1) { + if (!idx_val) { return {}; } - AInt idx = idx_val.Element(0); - - // The immediate child element count. uint32_t el_count = 0; - sem::Type::ElementOf(obj_val.Type(), &el_count); - - // The total number of most-nested elements per child element type. - uint32_t step = 0; - sem::Type::DeepestElementOf(el_ty, &step); + sem::Type::ElementOf(obj_val->Type(), &el_count); + AInt idx = idx_val->As(); if (idx < 0 || idx >= el_count) { auto clamped = std::min(std::max(idx, 0), el_count - 1); AddWarning("index " + std::to_string(idx) + " out of bounds [0.." + @@ -305,32 +567,20 @@ sem::Constant Resolver::EvaluateConstantValue(const ast::IndexAccessorExpression idx = clamped; } - return sem::Constant{el_ty, obj_val.WithElements([&](auto&& v) { - using VEC = std::decay_t; - return sem::Constant::Elements( - VEC(v.begin() + (idx * step), v.begin() + (idx + 1) * step)); - })}; + return obj_val->Index(static_cast(idx)); } -utils::Result Resolver::ConvertValue(const sem::Constant& value, - const sem::Type* ty, - const Source& source) { - if (value.Type() == ty) { +utils::Result Resolver::ConvertValue(const sem::Constant* value, + const sem::Type* target_ty, + const Source& source) { + if (value->Type() == target_ty) { return value; } - - auto* el_ty = sem::Type::DeepestElementOf(ty); - if (el_ty == nullptr) { - return sem::Constant{}; + auto conv = static_cast(value)->Convert(*builder_, target_ty, source); + if (!conv) { + return utils::Failure; } - if (value.ElementType() == el_ty) { - return sem::Constant(ty, value.GetElements()); - } - - if (auto res = MaterializeElements(value.GetElements(), el_ty, *builder_, source)) { - return sem::Constant(ty, std::move(res.Get())); - } - return utils::Failure; + return conv.Get(); } } // namespace tint::resolver diff --git a/src/tint/resolver/resolver_constants_test.cc b/src/tint/resolver/resolver_constants_test.cc index 00b886dcf2..ff8a18994d 100644 --- a/src/tint/resolver/resolver_constants_test.cc +++ b/src/tint/resolver/resolver_constants_test.cc @@ -14,10 +14,13 @@ #include "src/tint/resolver/resolver.h" +#include + #include "gtest/gtest.h" #include "src/tint/resolver/resolver_test_helper.h" #include "src/tint/sem/expression.h" #include "src/tint/sem/index_accessor_expression.h" +#include "src/tint/sem/test_helper.h" using namespace tint::number_suffixes; // NOLINT @@ -39,10 +42,11 @@ TEST_F(ResolverConstantsTest, Scalar_i32) { auto* sem = Sem().Get(expr); ASSERT_NE(sem, nullptr); EXPECT_TRUE(sem->Type()->Is()); - EXPECT_EQ(sem->ConstantValue().Type(), sem->Type()); - EXPECT_EQ(sem->ConstantValue().ElementType(), sem->Type()); - ASSERT_EQ(sem->ConstantValue().ElementCount(), 1u); - EXPECT_EQ(sem->ConstantValue().Element(0).value, 99); + EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type()); + EXPECT_TRUE(sem->ConstantValue()->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->AllZero()); + EXPECT_EQ(sem->ConstantValue()->As(), 99); } TEST_F(ResolverConstantsTest, Scalar_u32) { @@ -54,10 +58,11 @@ TEST_F(ResolverConstantsTest, Scalar_u32) { auto* sem = Sem().Get(expr); ASSERT_NE(sem, nullptr); EXPECT_TRUE(sem->Type()->Is()); - EXPECT_EQ(sem->ConstantValue().Type(), sem->Type()); - EXPECT_EQ(sem->ConstantValue().ElementType(), sem->Type()); - ASSERT_EQ(sem->ConstantValue().ElementCount(), 1u); - EXPECT_EQ(sem->ConstantValue().Element(0).value, 99u); + EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type()); + EXPECT_TRUE(sem->ConstantValue()->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->AllZero()); + EXPECT_EQ(sem->ConstantValue()->As(), 99u); } TEST_F(ResolverConstantsTest, Scalar_f32) { @@ -69,10 +74,11 @@ TEST_F(ResolverConstantsTest, Scalar_f32) { auto* sem = Sem().Get(expr); ASSERT_NE(sem, nullptr); EXPECT_TRUE(sem->Type()->Is()); - EXPECT_EQ(sem->ConstantValue().Type(), sem->Type()); - EXPECT_EQ(sem->ConstantValue().ElementType(), sem->Type()); - ASSERT_EQ(sem->ConstantValue().ElementCount(), 1u); - EXPECT_EQ(sem->ConstantValue().Element(0).value, 9.9f); + EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type()); + EXPECT_TRUE(sem->ConstantValue()->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->AllZero()); + EXPECT_EQ(sem->ConstantValue()->As().value, 9.9f); } TEST_F(ResolverConstantsTest, Scalar_f16) { @@ -85,11 +91,12 @@ TEST_F(ResolverConstantsTest, Scalar_f16) { auto* sem = Sem().Get(expr); EXPECT_NE(sem, nullptr); EXPECT_TRUE(sem->Type()->Is()); - EXPECT_EQ(sem->ConstantValue().Type(), sem->Type()); - EXPECT_EQ(sem->ConstantValue().ElementType(), sem->Type()); - ASSERT_EQ(sem->ConstantValue().ElementCount(), 1u); + EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type()); + EXPECT_TRUE(sem->ConstantValue()->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->AllZero()); // 9.9 is not exactly representable by f16, and should be quantized to 9.8984375 - EXPECT_EQ(sem->ConstantValue().Element(0).value, 9.8984375f); + EXPECT_EQ(sem->ConstantValue()->As(), 9.8984375f); } TEST_F(ResolverConstantsTest, Scalar_bool) { @@ -101,10 +108,11 @@ TEST_F(ResolverConstantsTest, Scalar_bool) { auto* sem = Sem().Get(expr); ASSERT_NE(sem, nullptr); EXPECT_TRUE(sem->Type()->Is()); - EXPECT_EQ(sem->ConstantValue().Type(), sem->Type()); - EXPECT_EQ(sem->ConstantValue().ElementType(), sem->Type()); - ASSERT_EQ(sem->ConstantValue().ElementCount(), 1u); - EXPECT_EQ(sem->ConstantValue().Element(0), true); + EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type()); + EXPECT_TRUE(sem->ConstantValue()->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->AllZero()); + EXPECT_EQ(sem->ConstantValue()->As(), true); } TEST_F(ResolverConstantsTest, Vec3_ZeroInit_i32) { @@ -119,12 +127,25 @@ TEST_F(ResolverConstantsTest, Vec3_ZeroInit_i32) { ASSERT_NE(vec, nullptr); EXPECT_TRUE(vec->type()->Is()); EXPECT_EQ(vec->Width(), 3u); - EXPECT_EQ(sem->ConstantValue().Type(), sem->Type()); - EXPECT_TRUE(sem->ConstantValue().ElementType()->Is()); - ASSERT_EQ(sem->ConstantValue().ElementCount(), 3u); - EXPECT_EQ(sem->ConstantValue().Element(0).value, 0); - EXPECT_EQ(sem->ConstantValue().Element(1).value, 0); - EXPECT_EQ(sem->ConstantValue().Element(2).value, 0); + EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type()); + EXPECT_TRUE(sem->ConstantValue()->AllEqual()); + EXPECT_TRUE(sem->ConstantValue()->AnyZero()); + EXPECT_TRUE(sem->ConstantValue()->AllZero()); + + EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual()); + EXPECT_TRUE(sem->ConstantValue()->Index(0)->AnyZero()); + EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(0)->As(), 0); + + EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual()); + EXPECT_TRUE(sem->ConstantValue()->Index(1)->AnyZero()); + EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(1)->As(), 0); + + EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual()); + EXPECT_TRUE(sem->ConstantValue()->Index(2)->AnyZero()); + EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(2)->As(), 0); } TEST_F(ResolverConstantsTest, Vec3_ZeroInit_u32) { @@ -139,12 +160,25 @@ TEST_F(ResolverConstantsTest, Vec3_ZeroInit_u32) { ASSERT_NE(vec, nullptr); EXPECT_TRUE(vec->type()->Is()); EXPECT_EQ(vec->Width(), 3u); - EXPECT_EQ(sem->ConstantValue().Type(), sem->Type()); - EXPECT_TRUE(sem->ConstantValue().ElementType()->Is()); - ASSERT_EQ(sem->ConstantValue().ElementCount(), 3u); - EXPECT_EQ(sem->ConstantValue().Element(0).value, 0u); - EXPECT_EQ(sem->ConstantValue().Element(1).value, 0u); - EXPECT_EQ(sem->ConstantValue().Element(2).value, 0u); + EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type()); + EXPECT_TRUE(sem->ConstantValue()->AllEqual()); + EXPECT_TRUE(sem->ConstantValue()->AnyZero()); + EXPECT_TRUE(sem->ConstantValue()->AllZero()); + + EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual()); + EXPECT_TRUE(sem->ConstantValue()->Index(0)->AnyZero()); + EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(0)->As(), 0u); + + EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual()); + EXPECT_TRUE(sem->ConstantValue()->Index(1)->AnyZero()); + EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(1)->As(), 0u); + + EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual()); + EXPECT_TRUE(sem->ConstantValue()->Index(2)->AnyZero()); + EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(2)->As(), 0u); } TEST_F(ResolverConstantsTest, Vec3_ZeroInit_f32) { @@ -159,12 +193,25 @@ TEST_F(ResolverConstantsTest, Vec3_ZeroInit_f32) { ASSERT_NE(vec, nullptr); EXPECT_TRUE(vec->type()->Is()); EXPECT_EQ(vec->Width(), 3u); - EXPECT_EQ(sem->ConstantValue().Type(), sem->Type()); - EXPECT_TRUE(sem->ConstantValue().ElementType()->Is()); - ASSERT_EQ(sem->ConstantValue().ElementCount(), 3u); - EXPECT_EQ(sem->ConstantValue().Element(0).value, 0.0); - EXPECT_EQ(sem->ConstantValue().Element(1).value, 0.0); - EXPECT_EQ(sem->ConstantValue().Element(2).value, 0.0); + EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type()); + EXPECT_TRUE(sem->ConstantValue()->AllEqual()); + EXPECT_TRUE(sem->ConstantValue()->AnyZero()); + EXPECT_TRUE(sem->ConstantValue()->AllZero()); + + EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual()); + EXPECT_TRUE(sem->ConstantValue()->Index(0)->AnyZero()); + EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(0)->As(), 0._a); + + EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual()); + EXPECT_TRUE(sem->ConstantValue()->Index(1)->AnyZero()); + EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(1)->As(), 0._a); + + EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual()); + EXPECT_TRUE(sem->ConstantValue()->Index(2)->AnyZero()); + EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(2)->As(), 0._a); } TEST_F(ResolverConstantsTest, Vec3_ZeroInit_f16) { @@ -180,12 +227,25 @@ TEST_F(ResolverConstantsTest, Vec3_ZeroInit_f16) { ASSERT_NE(vec, nullptr); EXPECT_TRUE(vec->type()->Is()); EXPECT_EQ(vec->Width(), 3u); - EXPECT_EQ(sem->ConstantValue().Type(), sem->Type()); - EXPECT_TRUE(sem->ConstantValue().ElementType()->Is()); - ASSERT_EQ(sem->ConstantValue().ElementCount(), 3u); - EXPECT_EQ(sem->ConstantValue().Element(0).value, 0.0); - EXPECT_EQ(sem->ConstantValue().Element(1).value, 0.0); - EXPECT_EQ(sem->ConstantValue().Element(2).value, 0.0); + EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type()); + EXPECT_TRUE(sem->ConstantValue()->AllEqual()); + EXPECT_TRUE(sem->ConstantValue()->AnyZero()); + EXPECT_TRUE(sem->ConstantValue()->AllZero()); + + EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual()); + EXPECT_TRUE(sem->ConstantValue()->Index(0)->AnyZero()); + EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(0)->As(), 0._a); + + EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual()); + EXPECT_TRUE(sem->ConstantValue()->Index(1)->AnyZero()); + EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(1)->As(), 0._a); + + EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual()); + EXPECT_TRUE(sem->ConstantValue()->Index(2)->AnyZero()); + EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(2)->As(), 0._a); } TEST_F(ResolverConstantsTest, Vec3_ZeroInit_bool) { @@ -200,12 +260,25 @@ TEST_F(ResolverConstantsTest, Vec3_ZeroInit_bool) { ASSERT_NE(vec, nullptr); EXPECT_TRUE(vec->type()->Is()); EXPECT_EQ(vec->Width(), 3u); - EXPECT_EQ(sem->ConstantValue().Type(), sem->Type()); - EXPECT_TRUE(sem->ConstantValue().ElementType()->Is()); - ASSERT_EQ(sem->ConstantValue().ElementCount(), 3u); - EXPECT_EQ(sem->ConstantValue().Element(0), false); - EXPECT_EQ(sem->ConstantValue().Element(1), false); - EXPECT_EQ(sem->ConstantValue().Element(2), false); + EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type()); + EXPECT_TRUE(sem->ConstantValue()->AllEqual()); + EXPECT_TRUE(sem->ConstantValue()->AnyZero()); + EXPECT_TRUE(sem->ConstantValue()->AllZero()); + + EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual()); + EXPECT_TRUE(sem->ConstantValue()->Index(0)->AnyZero()); + EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(0)->As(), false); + + EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual()); + EXPECT_TRUE(sem->ConstantValue()->Index(1)->AnyZero()); + EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(1)->As(), false); + + EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual()); + EXPECT_TRUE(sem->ConstantValue()->Index(2)->AnyZero()); + EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(2)->As(), false); } TEST_F(ResolverConstantsTest, Vec3_Splat_i32) { @@ -220,12 +293,25 @@ TEST_F(ResolverConstantsTest, Vec3_Splat_i32) { ASSERT_NE(vec, nullptr); EXPECT_TRUE(vec->type()->Is()); EXPECT_EQ(vec->Width(), 3u); - EXPECT_EQ(sem->ConstantValue().Type(), sem->Type()); - EXPECT_TRUE(sem->ConstantValue().ElementType()->Is()); - ASSERT_EQ(sem->ConstantValue().ElementCount(), 3u); - EXPECT_EQ(sem->ConstantValue().Element(0).value, 99); - EXPECT_EQ(sem->ConstantValue().Element(1).value, 99); - EXPECT_EQ(sem->ConstantValue().Element(2).value, 99); + EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type()); + EXPECT_TRUE(sem->ConstantValue()->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->AllZero()); + + EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(0)->As(), 99); + + EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(1)->As(), 99); + + EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(2)->As(), 99); } TEST_F(ResolverConstantsTest, Vec3_Splat_u32) { @@ -240,12 +326,25 @@ TEST_F(ResolverConstantsTest, Vec3_Splat_u32) { ASSERT_NE(vec, nullptr); EXPECT_TRUE(vec->type()->Is()); EXPECT_EQ(vec->Width(), 3u); - EXPECT_EQ(sem->ConstantValue().Type(), sem->Type()); - EXPECT_TRUE(sem->ConstantValue().ElementType()->Is()); - ASSERT_EQ(sem->ConstantValue().ElementCount(), 3u); - EXPECT_EQ(sem->ConstantValue().Element(0).value, 99u); - EXPECT_EQ(sem->ConstantValue().Element(1).value, 99u); - EXPECT_EQ(sem->ConstantValue().Element(2).value, 99u); + EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type()); + EXPECT_TRUE(sem->ConstantValue()->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->AllZero()); + + EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(0)->As(), 99u); + + EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(1)->As(), 99u); + + EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(2)->As(), 99u); } TEST_F(ResolverConstantsTest, Vec3_Splat_f32) { @@ -260,12 +359,25 @@ TEST_F(ResolverConstantsTest, Vec3_Splat_f32) { ASSERT_NE(vec, nullptr); EXPECT_TRUE(vec->type()->Is()); EXPECT_EQ(vec->Width(), 3u); - EXPECT_EQ(sem->ConstantValue().Type(), sem->Type()); - EXPECT_TRUE(sem->ConstantValue().ElementType()->Is()); - ASSERT_EQ(sem->ConstantValue().ElementCount(), 3u); - EXPECT_EQ(sem->ConstantValue().Element(0).value, 9.9f); - EXPECT_EQ(sem->ConstantValue().Element(1).value, 9.9f); - EXPECT_EQ(sem->ConstantValue().Element(2).value, 9.9f); + EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type()); + EXPECT_TRUE(sem->ConstantValue()->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->AllZero()); + + EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(0)->As(), 9.9f); + + EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(1)->As(), 9.9f); + + EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(2)->As(), 9.9f); } TEST_F(ResolverConstantsTest, Vec3_Splat_f16) { @@ -281,13 +393,26 @@ TEST_F(ResolverConstantsTest, Vec3_Splat_f16) { ASSERT_NE(vec, nullptr); EXPECT_TRUE(vec->type()->Is()); EXPECT_EQ(vec->Width(), 3u); - EXPECT_EQ(sem->ConstantValue().Type(), sem->Type()); - EXPECT_TRUE(sem->ConstantValue().ElementType()->Is()); - ASSERT_EQ(sem->ConstantValue().ElementCount(), 3u); + EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type()); + EXPECT_TRUE(sem->ConstantValue()->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->AllZero()); // 9.9 is not exactly representable by f16, and should be quantized to 9.8984375 - EXPECT_EQ(sem->ConstantValue().Element(0).value, 9.8984375f); - EXPECT_EQ(sem->ConstantValue().Element(1).value, 9.8984375f); - EXPECT_EQ(sem->ConstantValue().Element(2).value, 9.8984375f); + + EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(0)->As(), 9.8984375f); + + EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(1)->As(), 9.8984375f); + + EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(2)->As(), 9.8984375f); } TEST_F(ResolverConstantsTest, Vec3_Splat_bool) { @@ -302,12 +427,25 @@ TEST_F(ResolverConstantsTest, Vec3_Splat_bool) { ASSERT_NE(vec, nullptr); EXPECT_TRUE(vec->type()->Is()); EXPECT_EQ(vec->Width(), 3u); - EXPECT_EQ(sem->ConstantValue().Type(), sem->Type()); - EXPECT_TRUE(sem->ConstantValue().ElementType()->Is()); - ASSERT_EQ(sem->ConstantValue().ElementCount(), 3u); - EXPECT_EQ(sem->ConstantValue().Element(0), true); - EXPECT_EQ(sem->ConstantValue().Element(1), true); - EXPECT_EQ(sem->ConstantValue().Element(2), true); + EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type()); + EXPECT_TRUE(sem->ConstantValue()->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->AllZero()); + + EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(0)->As(), true); + + EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(1)->As(), true); + + EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(2)->As(), true); } TEST_F(ResolverConstantsTest, Vec3_FullConstruct_i32) { @@ -322,12 +460,25 @@ TEST_F(ResolverConstantsTest, Vec3_FullConstruct_i32) { ASSERT_NE(vec, nullptr); EXPECT_TRUE(vec->type()->Is()); EXPECT_EQ(vec->Width(), 3u); - EXPECT_EQ(sem->ConstantValue().Type(), sem->Type()); - EXPECT_TRUE(sem->ConstantValue().ElementType()->Is()); - ASSERT_EQ(sem->ConstantValue().ElementCount(), 3u); - EXPECT_EQ(sem->ConstantValue().Element(0).value, 1); - EXPECT_EQ(sem->ConstantValue().Element(1).value, 2); - EXPECT_EQ(sem->ConstantValue().Element(2).value, 3); + EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type()); + EXPECT_FALSE(sem->ConstantValue()->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->AllZero()); + + EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(0)->As(), 1); + + EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(1)->As(), 2); + + EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(2)->As(), 3); } TEST_F(ResolverConstantsTest, Vec3_FullConstruct_u32) { @@ -342,12 +493,25 @@ TEST_F(ResolverConstantsTest, Vec3_FullConstruct_u32) { ASSERT_NE(vec, nullptr); EXPECT_TRUE(vec->type()->Is()); EXPECT_EQ(vec->Width(), 3u); - EXPECT_EQ(sem->ConstantValue().Type(), sem->Type()); - EXPECT_TRUE(sem->ConstantValue().ElementType()->Is()); - ASSERT_EQ(sem->ConstantValue().ElementCount(), 3u); - EXPECT_EQ(sem->ConstantValue().Element(0).value, 1); - EXPECT_EQ(sem->ConstantValue().Element(1).value, 2); - EXPECT_EQ(sem->ConstantValue().Element(2).value, 3); + EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type()); + EXPECT_FALSE(sem->ConstantValue()->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->AllZero()); + + EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(0)->As(), 1); + + EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(1)->As(), 2); + + EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(2)->As(), 3); } TEST_F(ResolverConstantsTest, Vec3_FullConstruct_f32) { @@ -362,12 +526,25 @@ TEST_F(ResolverConstantsTest, Vec3_FullConstruct_f32) { ASSERT_NE(vec, nullptr); EXPECT_TRUE(vec->type()->Is()); EXPECT_EQ(vec->Width(), 3u); - EXPECT_EQ(sem->ConstantValue().Type(), sem->Type()); - EXPECT_TRUE(sem->ConstantValue().ElementType()->Is()); - ASSERT_EQ(sem->ConstantValue().ElementCount(), 3u); - EXPECT_EQ(sem->ConstantValue().Element(0).value, 1.f); - EXPECT_EQ(sem->ConstantValue().Element(1).value, 2.f); - EXPECT_EQ(sem->ConstantValue().Element(2).value, 3.f); + EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type()); + EXPECT_FALSE(sem->ConstantValue()->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->AllZero()); + + EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(0)->As(), 1.f); + + EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(1)->As(), 2.f); + + EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(2)->As(), 3.f); } TEST_F(ResolverConstantsTest, Vec3_FullConstruct_f16) { @@ -383,12 +560,25 @@ TEST_F(ResolverConstantsTest, Vec3_FullConstruct_f16) { ASSERT_NE(vec, nullptr); EXPECT_TRUE(vec->type()->Is()); EXPECT_EQ(vec->Width(), 3u); - EXPECT_EQ(sem->ConstantValue().Type(), sem->Type()); - EXPECT_TRUE(sem->ConstantValue().ElementType()->Is()); - ASSERT_EQ(sem->ConstantValue().ElementCount(), 3u); - EXPECT_EQ(sem->ConstantValue().Element(0).value, 1.f); - EXPECT_EQ(sem->ConstantValue().Element(1).value, 2.f); - EXPECT_EQ(sem->ConstantValue().Element(2).value, 3.f); + EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type()); + EXPECT_FALSE(sem->ConstantValue()->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->AllZero()); + + EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(0)->As(), 1.f); + + EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(1)->As(), 2.f); + + EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(2)->As(), 3.f); } TEST_F(ResolverConstantsTest, Vec3_FullConstruct_bool) { @@ -403,12 +593,25 @@ TEST_F(ResolverConstantsTest, Vec3_FullConstruct_bool) { ASSERT_NE(vec, nullptr); EXPECT_TRUE(vec->type()->Is()); EXPECT_EQ(vec->Width(), 3u); - EXPECT_EQ(sem->ConstantValue().Type(), sem->Type()); - EXPECT_TRUE(sem->ConstantValue().ElementType()->Is()); - ASSERT_EQ(sem->ConstantValue().ElementCount(), 3u); - EXPECT_EQ(sem->ConstantValue().Element(0), true); - EXPECT_EQ(sem->ConstantValue().Element(1), false); - EXPECT_EQ(sem->ConstantValue().Element(2), true); + EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type()); + EXPECT_FALSE(sem->ConstantValue()->AllEqual()); + EXPECT_TRUE(sem->ConstantValue()->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->AllZero()); + + EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(0)->As(), true); + + EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual()); + EXPECT_TRUE(sem->ConstantValue()->Index(1)->AnyZero()); + EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(1)->As(), false); + + EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(2)->As(), true); } TEST_F(ResolverConstantsTest, Vec3_MixConstruct_i32) { @@ -423,12 +626,25 @@ TEST_F(ResolverConstantsTest, Vec3_MixConstruct_i32) { ASSERT_NE(vec, nullptr); EXPECT_TRUE(vec->type()->Is()); EXPECT_EQ(vec->Width(), 3u); - EXPECT_EQ(sem->ConstantValue().Type(), sem->Type()); - EXPECT_TRUE(sem->ConstantValue().ElementType()->Is()); - ASSERT_EQ(sem->ConstantValue().ElementCount(), 3u); - EXPECT_EQ(sem->ConstantValue().Element(0).value, 1); - EXPECT_EQ(sem->ConstantValue().Element(1).value, 2); - EXPECT_EQ(sem->ConstantValue().Element(2).value, 3); + EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type()); + EXPECT_FALSE(sem->ConstantValue()->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->AllZero()); + + EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(0)->As(), 1); + + EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(1)->As(), 2); + + EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(2)->As(), 3); } TEST_F(ResolverConstantsTest, Vec3_MixConstruct_u32) { @@ -443,12 +659,25 @@ TEST_F(ResolverConstantsTest, Vec3_MixConstruct_u32) { ASSERT_NE(vec, nullptr); EXPECT_TRUE(vec->type()->Is()); EXPECT_EQ(vec->Width(), 3u); - EXPECT_EQ(sem->ConstantValue().Type(), sem->Type()); - EXPECT_TRUE(sem->ConstantValue().ElementType()->Is()); - ASSERT_EQ(sem->ConstantValue().ElementCount(), 3u); - EXPECT_EQ(sem->ConstantValue().Element(0).value, 1); - EXPECT_EQ(sem->ConstantValue().Element(1).value, 2); - EXPECT_EQ(sem->ConstantValue().Element(2).value, 3); + EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type()); + EXPECT_FALSE(sem->ConstantValue()->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->AllZero()); + + EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(0)->As(), 1); + + EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(1)->As(), 2); + + EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(2)->As(), 3); } TEST_F(ResolverConstantsTest, Vec3_MixConstruct_f32) { @@ -463,12 +692,157 @@ TEST_F(ResolverConstantsTest, Vec3_MixConstruct_f32) { ASSERT_NE(vec, nullptr); EXPECT_TRUE(vec->type()->Is()); EXPECT_EQ(vec->Width(), 3u); - EXPECT_EQ(sem->ConstantValue().Type(), sem->Type()); - EXPECT_TRUE(sem->ConstantValue().ElementType()->Is()); - ASSERT_EQ(sem->ConstantValue().ElementCount(), 3u); - EXPECT_EQ(sem->ConstantValue().Element(0).value, 1.f); - EXPECT_EQ(sem->ConstantValue().Element(1).value, 2.f); - EXPECT_EQ(sem->ConstantValue().Element(2).value, 3.f); + EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type()); + EXPECT_FALSE(sem->ConstantValue()->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->AllZero()); + + EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(0)->As(), 1.f); + + EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(1)->As(), 2.f); + + EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(2)->As(), 3.f); +} + +TEST_F(ResolverConstantsTest, Vec3_MixConstruct_f32_all_10) { + auto* expr = vec3(10_f, vec2(10_f, 10_f)); + WrapInFunction(expr); + + EXPECT_TRUE(r()->Resolve()) << r()->error(); + + auto* sem = Sem().Get(expr); + EXPECT_NE(sem, nullptr); + auto* vec = sem->Type()->As(); + ASSERT_NE(vec, nullptr); + EXPECT_TRUE(vec->type()->Is()); + EXPECT_EQ(vec->Width(), 3u); + EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type()); + EXPECT_TRUE(sem->ConstantValue()->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->AllZero()); + + EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(0)->As(), 10_f); + + EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(1)->As(), 10_f); + + EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(2)->As(), 10_f); +} + +TEST_F(ResolverConstantsTest, Vec3_MixConstruct_f32_all_positive_0) { + auto* expr = vec3(0_f, vec2(0_f, 0_f)); + WrapInFunction(expr); + + EXPECT_TRUE(r()->Resolve()) << r()->error(); + + auto* sem = Sem().Get(expr); + EXPECT_NE(sem, nullptr); + auto* vec = sem->Type()->As(); + ASSERT_NE(vec, nullptr); + EXPECT_TRUE(vec->type()->Is()); + EXPECT_EQ(vec->Width(), 3u); + EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type()); + EXPECT_TRUE(sem->ConstantValue()->AllEqual()); + EXPECT_TRUE(sem->ConstantValue()->AnyZero()); + EXPECT_TRUE(sem->ConstantValue()->AllZero()); + + EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual()); + EXPECT_TRUE(sem->ConstantValue()->Index(0)->AnyZero()); + EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(0)->As(), 0_f); + + EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual()); + EXPECT_TRUE(sem->ConstantValue()->Index(1)->AnyZero()); + EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(1)->As(), 0_f); + + EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual()); + EXPECT_TRUE(sem->ConstantValue()->Index(2)->AnyZero()); + EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(2)->As(), 0_f); +} + +TEST_F(ResolverConstantsTest, Vec3_MixConstruct_f32_all_negative_0) { + auto* expr = vec3(vec2(-0_f, -0_f), -0_f); + WrapInFunction(expr); + + EXPECT_TRUE(r()->Resolve()) << r()->error(); + + auto* sem = Sem().Get(expr); + EXPECT_NE(sem, nullptr); + auto* vec = sem->Type()->As(); + ASSERT_NE(vec, nullptr); + EXPECT_TRUE(vec->type()->Is()); + EXPECT_EQ(vec->Width(), 3u); + EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type()); + EXPECT_TRUE(sem->ConstantValue()->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->AllZero()); + + EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(0)->As(), -0_f); + + EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(1)->As(), -0_f); + + EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(2)->As(), -0_f); +} + +TEST_F(ResolverConstantsTest, Vec3_MixConstruct_f32_mixed_sign_0) { + auto* expr = vec3(0_f, vec2(-0_f, 0_f)); + WrapInFunction(expr); + + EXPECT_TRUE(r()->Resolve()) << r()->error(); + + auto* sem = Sem().Get(expr); + EXPECT_NE(sem, nullptr); + auto* vec = sem->Type()->As(); + ASSERT_NE(vec, nullptr); + EXPECT_TRUE(vec->type()->Is()); + EXPECT_EQ(vec->Width(), 3u); + EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type()); + EXPECT_FALSE(sem->ConstantValue()->AllEqual()); + EXPECT_TRUE(sem->ConstantValue()->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->AllZero()); + + EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual()); + EXPECT_TRUE(sem->ConstantValue()->Index(0)->AnyZero()); + EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(0)->As(), 0_f); + + EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(1)->As(), -0_f); + + EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual()); + EXPECT_TRUE(sem->ConstantValue()->Index(2)->AnyZero()); + EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(2)->As(), 0_f); } TEST_F(ResolverConstantsTest, Vec3_MixConstruct_f16) { @@ -484,12 +858,161 @@ TEST_F(ResolverConstantsTest, Vec3_MixConstruct_f16) { ASSERT_NE(vec, nullptr); EXPECT_TRUE(vec->type()->Is()); EXPECT_EQ(vec->Width(), 3u); - EXPECT_EQ(sem->ConstantValue().Type(), sem->Type()); - EXPECT_TRUE(sem->ConstantValue().ElementType()->Is()); - ASSERT_EQ(sem->ConstantValue().ElementCount(), 3u); - EXPECT_EQ(sem->ConstantValue().Element(0).value, 1.f); - EXPECT_EQ(sem->ConstantValue().Element(1).value, 2.f); - EXPECT_EQ(sem->ConstantValue().Element(2).value, 3.f); + EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type()); + EXPECT_FALSE(sem->ConstantValue()->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->AllZero()); + + EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(0)->As(), 1.f); + + EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(1)->As(), 2.f); + + EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(2)->As(), 3.f); +} + +TEST_F(ResolverConstantsTest, Vec3_MixConstruct_f16_all_10) { + Enable(ast::Extension::kF16); + auto* expr = vec3(10_h, vec2(10_h, 10_h)); + WrapInFunction(expr); + + EXPECT_TRUE(r()->Resolve()) << r()->error(); + + auto* sem = Sem().Get(expr); + EXPECT_NE(sem, nullptr); + auto* vec = sem->Type()->As(); + ASSERT_NE(vec, nullptr); + EXPECT_TRUE(vec->type()->Is()); + EXPECT_EQ(vec->Width(), 3u); + EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type()); + EXPECT_TRUE(sem->ConstantValue()->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->AllZero()); + + EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(0)->As(), 10_h); + + EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(1)->As(), 10_h); + + EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(2)->As(), 10_h); +} + +TEST_F(ResolverConstantsTest, Vec3_MixConstruct_f16_all_positive_0) { + Enable(ast::Extension::kF16); + auto* expr = vec3(0_h, vec2(0_h, 0_h)); + WrapInFunction(expr); + + EXPECT_TRUE(r()->Resolve()) << r()->error(); + + auto* sem = Sem().Get(expr); + EXPECT_NE(sem, nullptr); + auto* vec = sem->Type()->As(); + ASSERT_NE(vec, nullptr); + EXPECT_TRUE(vec->type()->Is()); + EXPECT_EQ(vec->Width(), 3u); + EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type()); + EXPECT_TRUE(sem->ConstantValue()->AllEqual()); + EXPECT_TRUE(sem->ConstantValue()->AnyZero()); + EXPECT_TRUE(sem->ConstantValue()->AllZero()); + + EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual()); + EXPECT_TRUE(sem->ConstantValue()->Index(0)->AnyZero()); + EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(0)->As(), 0_h); + + EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual()); + EXPECT_TRUE(sem->ConstantValue()->Index(1)->AnyZero()); + EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(1)->As(), 0_h); + + EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual()); + EXPECT_TRUE(sem->ConstantValue()->Index(2)->AnyZero()); + EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(2)->As(), 0_h); +} + +TEST_F(ResolverConstantsTest, Vec3_MixConstruct_f16_all_negative_0) { + Enable(ast::Extension::kF16); + auto* expr = vec3(vec2(-0_h, -0_h), -0_h); + WrapInFunction(expr); + + EXPECT_TRUE(r()->Resolve()) << r()->error(); + + auto* sem = Sem().Get(expr); + EXPECT_NE(sem, nullptr); + auto* vec = sem->Type()->As(); + ASSERT_NE(vec, nullptr); + EXPECT_TRUE(vec->type()->Is()); + EXPECT_EQ(vec->Width(), 3u); + EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type()); + EXPECT_TRUE(sem->ConstantValue()->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->AllZero()); + + EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(0)->As(), -0_h); + + EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(1)->As(), -0_h); + + EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(2)->As(), -0_h); +} + +TEST_F(ResolverConstantsTest, Vec3_MixConstruct_f16_mixed_sign_0) { + Enable(ast::Extension::kF16); + auto* expr = vec3(0_h, vec2(-0_h, 0_h)); + WrapInFunction(expr); + + EXPECT_TRUE(r()->Resolve()) << r()->error(); + + auto* sem = Sem().Get(expr); + EXPECT_NE(sem, nullptr); + auto* vec = sem->Type()->As(); + ASSERT_NE(vec, nullptr); + EXPECT_TRUE(vec->type()->Is()); + EXPECT_EQ(vec->Width(), 3u); + EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type()); + EXPECT_FALSE(sem->ConstantValue()->AllEqual()); + EXPECT_TRUE(sem->ConstantValue()->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->AllZero()); + + EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual()); + EXPECT_TRUE(sem->ConstantValue()->Index(0)->AnyZero()); + EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(0)->As(), 0_h); + + EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(1)->As(), -0_h); + + EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual()); + EXPECT_TRUE(sem->ConstantValue()->Index(2)->AnyZero()); + EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(2)->As(), 0_h); } TEST_F(ResolverConstantsTest, Vec3_MixConstruct_bool) { @@ -504,12 +1027,91 @@ TEST_F(ResolverConstantsTest, Vec3_MixConstruct_bool) { ASSERT_NE(vec, nullptr); EXPECT_TRUE(vec->type()->Is()); EXPECT_EQ(vec->Width(), 3u); - EXPECT_EQ(sem->ConstantValue().Type(), sem->Type()); - EXPECT_TRUE(sem->ConstantValue().ElementType()->Is()); - ASSERT_EQ(sem->ConstantValue().ElementCount(), 3u); - EXPECT_EQ(sem->ConstantValue().Element(0), true); - EXPECT_EQ(sem->ConstantValue().Element(1), false); - EXPECT_EQ(sem->ConstantValue().Element(2), true); + EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type()); + EXPECT_FALSE(sem->ConstantValue()->AllEqual()); + EXPECT_TRUE(sem->ConstantValue()->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->AllZero()); + + EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(0)->As(), true); + + EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual()); + EXPECT_TRUE(sem->ConstantValue()->Index(1)->AnyZero()); + EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(1)->As(), false); + + EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(2)->As(), true); +} + +TEST_F(ResolverConstantsTest, Vec3_MixConstruct_all_true) { + auto* expr = vec3(true, vec2(true, true)); + WrapInFunction(expr); + + EXPECT_TRUE(r()->Resolve()) << r()->error(); + + auto* sem = Sem().Get(expr); + ASSERT_NE(sem, nullptr); + auto* vec = sem->Type()->As(); + ASSERT_NE(vec, nullptr); + EXPECT_TRUE(vec->type()->Is()); + EXPECT_EQ(vec->Width(), 3u); + EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type()); + EXPECT_TRUE(sem->ConstantValue()->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->AllZero()); + + EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(0)->As(), true); + + EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(1)->As(), true); + + EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(2)->As(), true); +} + +TEST_F(ResolverConstantsTest, Vec3_MixConstruct_all_false) { + auto* expr = vec3(false, vec2(false, false)); + WrapInFunction(expr); + + EXPECT_TRUE(r()->Resolve()) << r()->error(); + + auto* sem = Sem().Get(expr); + ASSERT_NE(sem, nullptr); + auto* vec = sem->Type()->As(); + ASSERT_NE(vec, nullptr); + EXPECT_TRUE(vec->type()->Is()); + EXPECT_EQ(vec->Width(), 3u); + EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type()); + EXPECT_TRUE(sem->ConstantValue()->AllEqual()); + EXPECT_TRUE(sem->ConstantValue()->AnyZero()); + EXPECT_TRUE(sem->ConstantValue()->AllZero()); + + EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual()); + EXPECT_TRUE(sem->ConstantValue()->Index(0)->AnyZero()); + EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(0)->As(), false); + + EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual()); + EXPECT_TRUE(sem->ConstantValue()->Index(1)->AnyZero()); + EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(1)->As(), false); + + EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual()); + EXPECT_TRUE(sem->ConstantValue()->Index(2)->AnyZero()); + EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(2)->As(), false); } TEST_F(ResolverConstantsTest, Vec3_Convert_f32_to_i32) { @@ -524,12 +1126,25 @@ TEST_F(ResolverConstantsTest, Vec3_Convert_f32_to_i32) { ASSERT_NE(vec, nullptr); EXPECT_TRUE(vec->type()->Is()); EXPECT_EQ(vec->Width(), 3u); - EXPECT_EQ(sem->ConstantValue().Type(), sem->Type()); - EXPECT_TRUE(sem->ConstantValue().ElementType()->Is()); - ASSERT_EQ(sem->ConstantValue().ElementCount(), 3u); - EXPECT_EQ(sem->ConstantValue().Element(0).value, 1); - EXPECT_EQ(sem->ConstantValue().Element(1).value, 2); - EXPECT_EQ(sem->ConstantValue().Element(2).value, 3); + EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type()); + EXPECT_FALSE(sem->ConstantValue()->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->AllZero()); + + EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(0)->As(), 1); + + EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(1)->As(), 2); + + EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(2)->As(), 3); } TEST_F(ResolverConstantsTest, Vec3_Convert_u32_to_f32) { @@ -544,12 +1159,25 @@ TEST_F(ResolverConstantsTest, Vec3_Convert_u32_to_f32) { ASSERT_NE(vec, nullptr); EXPECT_TRUE(vec->type()->Is()); EXPECT_EQ(vec->Width(), 3u); - EXPECT_EQ(sem->ConstantValue().Type(), sem->Type()); - EXPECT_TRUE(sem->ConstantValue().ElementType()->Is()); - ASSERT_EQ(sem->ConstantValue().ElementCount(), 3u); - EXPECT_EQ(sem->ConstantValue().Element(0).value, 10.f); - EXPECT_EQ(sem->ConstantValue().Element(1).value, 20.f); - EXPECT_EQ(sem->ConstantValue().Element(2).value, 30.f); + EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type()); + EXPECT_FALSE(sem->ConstantValue()->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->AllZero()); + + EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(0)->As(), 10.f); + + EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(1)->As(), 20.f); + + EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(2)->As(), 30.f); } TEST_F(ResolverConstantsTest, Vec3_Convert_f16_to_i32) { @@ -565,12 +1193,25 @@ TEST_F(ResolverConstantsTest, Vec3_Convert_f16_to_i32) { ASSERT_NE(vec, nullptr); EXPECT_TRUE(vec->type()->Is()); EXPECT_EQ(vec->Width(), 3u); - EXPECT_EQ(sem->ConstantValue().Type(), sem->Type()); - EXPECT_TRUE(sem->ConstantValue().ElementType()->Is()); - ASSERT_EQ(sem->ConstantValue().ElementCount(), 3u); - EXPECT_EQ(sem->ConstantValue().Element(0).value, 1); - EXPECT_EQ(sem->ConstantValue().Element(1).value, 2); - EXPECT_EQ(sem->ConstantValue().Element(2).value, 3); + EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type()); + EXPECT_FALSE(sem->ConstantValue()->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->AllZero()); + + EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(0)->As(), 1_i); + + EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(1)->As(), 2_i); + + EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(2)->As(), 3_i); } TEST_F(ResolverConstantsTest, Vec3_Convert_u32_to_f16) { @@ -586,12 +1227,25 @@ TEST_F(ResolverConstantsTest, Vec3_Convert_u32_to_f16) { ASSERT_NE(vec, nullptr); EXPECT_TRUE(vec->type()->Is()); EXPECT_EQ(vec->Width(), 3u); - EXPECT_EQ(sem->ConstantValue().Type(), sem->Type()); - EXPECT_TRUE(sem->ConstantValue().ElementType()->Is()); - ASSERT_EQ(sem->ConstantValue().ElementCount(), 3u); - EXPECT_EQ(sem->ConstantValue().Element(0).value, 10.f); - EXPECT_EQ(sem->ConstantValue().Element(1).value, 20.f); - EXPECT_EQ(sem->ConstantValue().Element(2).value, 30.f); + EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type()); + EXPECT_FALSE(sem->ConstantValue()->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->AllZero()); + + EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(0)->As(), 10.f); + + EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(1)->As(), 20.f); + + EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(2)->As(), 30.f); } TEST_F(ResolverConstantsTest, Vec3_Convert_Large_f32_to_i32) { @@ -606,12 +1260,25 @@ TEST_F(ResolverConstantsTest, Vec3_Convert_Large_f32_to_i32) { ASSERT_NE(vec, nullptr); EXPECT_TRUE(vec->type()->Is()); EXPECT_EQ(vec->Width(), 3u); - EXPECT_EQ(sem->ConstantValue().Type(), sem->Type()); - EXPECT_TRUE(sem->ConstantValue().ElementType()->Is()); - ASSERT_EQ(sem->ConstantValue().ElementCount(), 3u); - EXPECT_EQ(sem->ConstantValue().Element(0).value, i32::kHighest); - EXPECT_EQ(sem->ConstantValue().Element(1).value, i32::kLowest); - EXPECT_EQ(sem->ConstantValue().Element(2).value, i32::kHighest); + EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type()); + EXPECT_FALSE(sem->ConstantValue()->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->AllZero()); + + EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(0)->As(), i32::kHighest); + + EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(1)->As(), i32::kLowest); + + EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(2)->As(), i32::kHighest); } TEST_F(ResolverConstantsTest, Vec3_Convert_Large_f32_to_u32) { @@ -626,12 +1293,25 @@ TEST_F(ResolverConstantsTest, Vec3_Convert_Large_f32_to_u32) { ASSERT_NE(vec, nullptr); EXPECT_TRUE(vec->type()->Is()); EXPECT_EQ(vec->Width(), 3u); - EXPECT_EQ(sem->ConstantValue().Type(), sem->Type()); - EXPECT_TRUE(sem->ConstantValue().ElementType()->Is()); - ASSERT_EQ(sem->ConstantValue().ElementCount(), 3u); - EXPECT_EQ(sem->ConstantValue().Element(0).value, u32::kHighest); - EXPECT_EQ(sem->ConstantValue().Element(1).value, u32::kLowest); - EXPECT_EQ(sem->ConstantValue().Element(2).value, u32::kHighest); + EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type()); + EXPECT_FALSE(sem->ConstantValue()->AllEqual()); + EXPECT_TRUE(sem->ConstantValue()->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->AllZero()); + + EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(0)->As(), u32::kHighest); + + EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual()); + EXPECT_TRUE(sem->ConstantValue()->Index(1)->AnyZero()); + EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(1)->As(), u32::kLowest); + + EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(2)->As(), u32::kHighest); } TEST_F(ResolverConstantsTest, Vec3_Convert_Large_f32_to_f16) { @@ -650,12 +1330,25 @@ TEST_F(ResolverConstantsTest, Vec3_Convert_Large_f32_to_f16) { ASSERT_NE(vec, nullptr); EXPECT_TRUE(vec->type()->Is()); EXPECT_EQ(vec->Width(), 3u); - EXPECT_EQ(sem->ConstantValue().Type(), sem->Type()); - EXPECT_TRUE(sem->ConstantValue().ElementType()->Is()); - ASSERT_EQ(sem->ConstantValue().ElementCount(), 3u); - EXPECT_EQ(sem->ConstantValue().Element(0).value, kInf); - EXPECT_EQ(sem->ConstantValue().Element(1).value, -kInf); - EXPECT_EQ(sem->ConstantValue().Element(2).value, kInf); + EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type()); + EXPECT_FALSE(sem->ConstantValue()->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->AllZero()); + + EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(0)->As(), kInf); + + EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(1)->As(), -kInf); + + EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(2)->As(), kInf); } TEST_F(ResolverConstantsTest, Vec3_Convert_Small_f32_to_f16) { @@ -672,12 +1365,28 @@ TEST_F(ResolverConstantsTest, Vec3_Convert_Small_f32_to_f16) { ASSERT_NE(vec, nullptr); EXPECT_TRUE(vec->type()->Is()); EXPECT_EQ(vec->Width(), 3u); - EXPECT_EQ(sem->ConstantValue().Type(), sem->Type()); - EXPECT_TRUE(sem->ConstantValue().ElementType()->Is()); - ASSERT_EQ(sem->ConstantValue().ElementCount(), 3u); - EXPECT_EQ(sem->ConstantValue().Element(0).value, 0.0); - EXPECT_EQ(sem->ConstantValue().Element(1).value, -0.0); - EXPECT_EQ(sem->ConstantValue().Element(2).value, 0.0); + EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type()); + EXPECT_FALSE(sem->ConstantValue()->AllEqual()); + EXPECT_TRUE(sem->ConstantValue()->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->AllZero()); + + EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual()); + EXPECT_TRUE(sem->ConstantValue()->Index(0)->AnyZero()); + EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(0)->As(), 0.0); + EXPECT_FALSE(std::signbit(sem->ConstantValue()->Index(0)->As().value)); + + EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(1)->As(), -0.0); + EXPECT_TRUE(std::signbit(sem->ConstantValue()->Index(1)->As().value)); + + EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual()); + EXPECT_TRUE(sem->ConstantValue()->Index(2)->AnyZero()); + EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(2)->As(), 0.0); + EXPECT_FALSE(std::signbit(sem->ConstantValue()->Index(2)->As().value)); } TEST_F(ResolverConstantsTest, Mat2x3_ZeroInit_f32) { @@ -693,15 +1402,40 @@ TEST_F(ResolverConstantsTest, Mat2x3_ZeroInit_f32) { EXPECT_TRUE(mat->type()->Is()); EXPECT_EQ(mat->columns(), 2u); EXPECT_EQ(mat->rows(), 3u); - EXPECT_EQ(sem->ConstantValue().Type(), sem->Type()); - EXPECT_TRUE(sem->ConstantValue().ElementType()->Is()); - ASSERT_EQ(sem->ConstantValue().ElementCount(), 6u); - EXPECT_EQ(sem->ConstantValue().Element(0).value, 0._f); - EXPECT_EQ(sem->ConstantValue().Element(1).value, 0._f); - EXPECT_EQ(sem->ConstantValue().Element(2).value, 0._f); - EXPECT_EQ(sem->ConstantValue().Element(3).value, 0._f); - EXPECT_EQ(sem->ConstantValue().Element(4).value, 0._f); - EXPECT_EQ(sem->ConstantValue().Element(5).value, 0._f); + EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type()); + EXPECT_TRUE(sem->ConstantValue()->AllEqual()); + EXPECT_TRUE(sem->ConstantValue()->AnyZero()); + EXPECT_TRUE(sem->ConstantValue()->AllZero()); + + EXPECT_TRUE(sem->ConstantValue()->Index(0)->Index(0)->AllEqual()); + EXPECT_TRUE(sem->ConstantValue()->Index(0)->Index(0)->AnyZero()); + EXPECT_TRUE(sem->ConstantValue()->Index(0)->Index(0)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(0)->Index(0)->As(), 0._f); + + EXPECT_TRUE(sem->ConstantValue()->Index(0)->Index(1)->AllEqual()); + EXPECT_TRUE(sem->ConstantValue()->Index(0)->Index(1)->AnyZero()); + EXPECT_TRUE(sem->ConstantValue()->Index(0)->Index(1)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(0)->Index(1)->As(), 0._f); + + EXPECT_TRUE(sem->ConstantValue()->Index(0)->Index(2)->AllEqual()); + EXPECT_TRUE(sem->ConstantValue()->Index(0)->Index(2)->AnyZero()); + EXPECT_TRUE(sem->ConstantValue()->Index(0)->Index(2)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(0)->Index(2)->As(), 0._f); + + EXPECT_TRUE(sem->ConstantValue()->Index(1)->Index(0)->AllEqual()); + EXPECT_TRUE(sem->ConstantValue()->Index(1)->Index(0)->AnyZero()); + EXPECT_TRUE(sem->ConstantValue()->Index(1)->Index(0)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(1)->Index(0)->As(), 0._f); + + EXPECT_TRUE(sem->ConstantValue()->Index(1)->Index(1)->AllEqual()); + EXPECT_TRUE(sem->ConstantValue()->Index(1)->Index(1)->AnyZero()); + EXPECT_TRUE(sem->ConstantValue()->Index(1)->Index(1)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(1)->Index(1)->As(), 0._f); + + EXPECT_TRUE(sem->ConstantValue()->Index(1)->Index(2)->AllEqual()); + EXPECT_TRUE(sem->ConstantValue()->Index(1)->Index(2)->AnyZero()); + EXPECT_TRUE(sem->ConstantValue()->Index(1)->Index(2)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(1)->Index(2)->As(), 0._f); } TEST_F(ResolverConstantsTest, Mat2x3_ZeroInit_f16) { @@ -719,15 +1453,40 @@ TEST_F(ResolverConstantsTest, Mat2x3_ZeroInit_f16) { EXPECT_TRUE(mat->type()->Is()); EXPECT_EQ(mat->columns(), 2u); EXPECT_EQ(mat->rows(), 3u); - EXPECT_EQ(sem->ConstantValue().Type(), sem->Type()); - EXPECT_TRUE(sem->ConstantValue().ElementType()->Is()); - ASSERT_EQ(sem->ConstantValue().ElementCount(), 6u); - EXPECT_EQ(sem->ConstantValue().Element(0).value, 0._h); - EXPECT_EQ(sem->ConstantValue().Element(1).value, 0._h); - EXPECT_EQ(sem->ConstantValue().Element(2).value, 0._h); - EXPECT_EQ(sem->ConstantValue().Element(3).value, 0._h); - EXPECT_EQ(sem->ConstantValue().Element(4).value, 0._h); - EXPECT_EQ(sem->ConstantValue().Element(5).value, 0._h); + EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type()); + EXPECT_TRUE(sem->ConstantValue()->AllEqual()); + EXPECT_TRUE(sem->ConstantValue()->AnyZero()); + EXPECT_TRUE(sem->ConstantValue()->AllZero()); + + EXPECT_TRUE(sem->ConstantValue()->Index(0)->Index(0)->AllEqual()); + EXPECT_TRUE(sem->ConstantValue()->Index(0)->Index(0)->AnyZero()); + EXPECT_TRUE(sem->ConstantValue()->Index(0)->Index(0)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(0)->Index(0)->As(), 0._h); + + EXPECT_TRUE(sem->ConstantValue()->Index(0)->Index(1)->AllEqual()); + EXPECT_TRUE(sem->ConstantValue()->Index(0)->Index(1)->AnyZero()); + EXPECT_TRUE(sem->ConstantValue()->Index(0)->Index(1)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(0)->Index(1)->As(), 0._h); + + EXPECT_TRUE(sem->ConstantValue()->Index(0)->Index(2)->AllEqual()); + EXPECT_TRUE(sem->ConstantValue()->Index(0)->Index(2)->AnyZero()); + EXPECT_TRUE(sem->ConstantValue()->Index(0)->Index(2)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(0)->Index(2)->As(), 0._h); + + EXPECT_TRUE(sem->ConstantValue()->Index(1)->Index(0)->AllEqual()); + EXPECT_TRUE(sem->ConstantValue()->Index(1)->Index(0)->AnyZero()); + EXPECT_TRUE(sem->ConstantValue()->Index(1)->Index(0)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(1)->Index(0)->As(), 0._h); + + EXPECT_TRUE(sem->ConstantValue()->Index(1)->Index(1)->AllEqual()); + EXPECT_TRUE(sem->ConstantValue()->Index(1)->Index(1)->AnyZero()); + EXPECT_TRUE(sem->ConstantValue()->Index(1)->Index(1)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(1)->Index(1)->As(), 0._h); + + EXPECT_TRUE(sem->ConstantValue()->Index(1)->Index(2)->AllEqual()); + EXPECT_TRUE(sem->ConstantValue()->Index(1)->Index(2)->AnyZero()); + EXPECT_TRUE(sem->ConstantValue()->Index(1)->Index(2)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(1)->Index(2)->As(), 0._h); } TEST_F(ResolverConstantsTest, Mat3x2_Construct_Scalars_af) { @@ -743,15 +1502,40 @@ TEST_F(ResolverConstantsTest, Mat3x2_Construct_Scalars_af) { EXPECT_TRUE(mat->type()->Is()); EXPECT_EQ(mat->columns(), 3u); EXPECT_EQ(mat->rows(), 2u); - EXPECT_EQ(sem->ConstantValue().Type(), sem->Type()); - EXPECT_TRUE(sem->ConstantValue().ElementType()->Is()); - ASSERT_EQ(sem->ConstantValue().ElementCount(), 6u); - EXPECT_EQ(sem->ConstantValue().Element(0).value, 1._a); - EXPECT_EQ(sem->ConstantValue().Element(1).value, 2._a); - EXPECT_EQ(sem->ConstantValue().Element(2).value, 3._a); - EXPECT_EQ(sem->ConstantValue().Element(3).value, 4._a); - EXPECT_EQ(sem->ConstantValue().Element(4).value, 5._a); - EXPECT_EQ(sem->ConstantValue().Element(5).value, 6._a); + EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type()); + EXPECT_FALSE(sem->ConstantValue()->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->AllZero()); + + EXPECT_TRUE(sem->ConstantValue()->Index(0)->Index(0)->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->Index(0)->Index(0)->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->Index(0)->Index(0)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(0)->Index(0)->As(), 1._a); + + EXPECT_TRUE(sem->ConstantValue()->Index(0)->Index(1)->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->Index(0)->Index(1)->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->Index(0)->Index(1)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(0)->Index(1)->As(), 2._a); + + EXPECT_TRUE(sem->ConstantValue()->Index(1)->Index(0)->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->Index(1)->Index(0)->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->Index(1)->Index(0)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(1)->Index(0)->As(), 3._a); + + EXPECT_TRUE(sem->ConstantValue()->Index(1)->Index(1)->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->Index(1)->Index(1)->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->Index(1)->Index(1)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(1)->Index(1)->As(), 4._a); + + EXPECT_TRUE(sem->ConstantValue()->Index(2)->Index(0)->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->Index(2)->Index(0)->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->Index(2)->Index(0)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(2)->Index(0)->As(), 5._a); + + EXPECT_TRUE(sem->ConstantValue()->Index(2)->Index(1)->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->Index(2)->Index(1)->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->Index(2)->Index(1)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(2)->Index(1)->As(), 6._a); } TEST_F(ResolverConstantsTest, Mat3x2_Construct_Columns_af) { @@ -770,15 +1554,40 @@ TEST_F(ResolverConstantsTest, Mat3x2_Construct_Columns_af) { EXPECT_TRUE(mat->type()->Is()); EXPECT_EQ(mat->columns(), 3u); EXPECT_EQ(mat->rows(), 2u); - EXPECT_EQ(sem->ConstantValue().Type(), sem->Type()); - EXPECT_TRUE(sem->ConstantValue().ElementType()->Is()); - ASSERT_EQ(sem->ConstantValue().ElementCount(), 6u); - EXPECT_EQ(sem->ConstantValue().Element(0).value, 1._a); - EXPECT_EQ(sem->ConstantValue().Element(1).value, 2._a); - EXPECT_EQ(sem->ConstantValue().Element(2).value, 3._a); - EXPECT_EQ(sem->ConstantValue().Element(3).value, 4._a); - EXPECT_EQ(sem->ConstantValue().Element(4).value, 5._a); - EXPECT_EQ(sem->ConstantValue().Element(5).value, 6._a); + EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type()); + EXPECT_FALSE(sem->ConstantValue()->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->AllZero()); + + EXPECT_TRUE(sem->ConstantValue()->Index(0)->Index(0)->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->Index(0)->Index(0)->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->Index(0)->Index(0)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(0)->Index(0)->As(), 1._a); + + EXPECT_TRUE(sem->ConstantValue()->Index(0)->Index(1)->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->Index(0)->Index(1)->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->Index(0)->Index(1)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(0)->Index(1)->As(), 2._a); + + EXPECT_TRUE(sem->ConstantValue()->Index(1)->Index(0)->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->Index(1)->Index(0)->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->Index(1)->Index(0)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(1)->Index(0)->As(), 3._a); + + EXPECT_TRUE(sem->ConstantValue()->Index(1)->Index(1)->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->Index(1)->Index(1)->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->Index(1)->Index(1)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(1)->Index(1)->As(), 4._a); + + EXPECT_TRUE(sem->ConstantValue()->Index(2)->Index(0)->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->Index(2)->Index(0)->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->Index(2)->Index(0)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(2)->Index(0)->As(), 5._a); + + EXPECT_TRUE(sem->ConstantValue()->Index(2)->Index(1)->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->Index(2)->Index(1)->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->Index(2)->Index(1)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(2)->Index(1)->As(), 6._a); } TEST_F(ResolverConstantsTest, Array_i32_Zero) { @@ -793,13 +1602,30 @@ TEST_F(ResolverConstantsTest, Array_i32_Zero) { ASSERT_NE(arr, nullptr); EXPECT_TRUE(arr->ElemType()->Is()); EXPECT_EQ(arr->Count(), 4u); - EXPECT_EQ(sem->ConstantValue().Type(), sem->Type()); - EXPECT_TRUE(sem->ConstantValue().ElementType()->Is()); - ASSERT_EQ(sem->ConstantValue().ElementCount(), 4u); - EXPECT_EQ(sem->ConstantValue().Element(0).value, 0_i); - EXPECT_EQ(sem->ConstantValue().Element(1).value, 0_i); - EXPECT_EQ(sem->ConstantValue().Element(2).value, 0_i); - EXPECT_EQ(sem->ConstantValue().Element(3).value, 0_i); + EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type()); + EXPECT_TRUE(sem->ConstantValue()->AllEqual()); + EXPECT_TRUE(sem->ConstantValue()->AnyZero()); + EXPECT_TRUE(sem->ConstantValue()->AllZero()); + + EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual()); + EXPECT_TRUE(sem->ConstantValue()->Index(0)->AnyZero()); + EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(0)->As(), 0_i); + + EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual()); + EXPECT_TRUE(sem->ConstantValue()->Index(1)->AnyZero()); + EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(1)->As(), 0_i); + + EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual()); + EXPECT_TRUE(sem->ConstantValue()->Index(2)->AnyZero()); + EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(2)->As(), 0_i); + + EXPECT_TRUE(sem->ConstantValue()->Index(3)->AllEqual()); + EXPECT_TRUE(sem->ConstantValue()->Index(3)->AnyZero()); + EXPECT_TRUE(sem->ConstantValue()->Index(3)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(3)->As(), 0_i); } TEST_F(ResolverConstantsTest, Array_f32_Zero) { @@ -814,13 +1640,30 @@ TEST_F(ResolverConstantsTest, Array_f32_Zero) { ASSERT_NE(arr, nullptr); EXPECT_TRUE(arr->ElemType()->Is()); EXPECT_EQ(arr->Count(), 4u); - EXPECT_EQ(sem->ConstantValue().Type(), sem->Type()); - EXPECT_TRUE(sem->ConstantValue().ElementType()->Is()); - ASSERT_EQ(sem->ConstantValue().ElementCount(), 4u); - EXPECT_EQ(sem->ConstantValue().Element(0).value, 0_f); - EXPECT_EQ(sem->ConstantValue().Element(1).value, 0_f); - EXPECT_EQ(sem->ConstantValue().Element(2).value, 0_f); - EXPECT_EQ(sem->ConstantValue().Element(3).value, 0_f); + EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type()); + EXPECT_TRUE(sem->ConstantValue()->AllEqual()); + EXPECT_TRUE(sem->ConstantValue()->AnyZero()); + EXPECT_TRUE(sem->ConstantValue()->AllZero()); + + EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual()); + EXPECT_TRUE(sem->ConstantValue()->Index(0)->AnyZero()); + EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(0)->As(), 0_f); + + EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual()); + EXPECT_TRUE(sem->ConstantValue()->Index(1)->AnyZero()); + EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(1)->As(), 0_f); + + EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual()); + EXPECT_TRUE(sem->ConstantValue()->Index(2)->AnyZero()); + EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(2)->As(), 0_f); + + EXPECT_TRUE(sem->ConstantValue()->Index(3)->AllEqual()); + EXPECT_TRUE(sem->ConstantValue()->Index(3)->AnyZero()); + EXPECT_TRUE(sem->ConstantValue()->Index(3)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(3)->As(), 0_f); } TEST_F(ResolverConstantsTest, Array_vec3_f32_Zero) { @@ -835,15 +1678,40 @@ TEST_F(ResolverConstantsTest, Array_vec3_f32_Zero) { ASSERT_NE(arr, nullptr); EXPECT_TRUE(arr->ElemType()->Is()); EXPECT_EQ(arr->Count(), 2u); - EXPECT_EQ(sem->ConstantValue().Type(), sem->Type()); - EXPECT_TRUE(sem->ConstantValue().ElementType()->Is()); - ASSERT_EQ(sem->ConstantValue().ElementCount(), 6u); - EXPECT_EQ(sem->ConstantValue().Element(0).value, 0_f); - EXPECT_EQ(sem->ConstantValue().Element(1).value, 0_f); - EXPECT_EQ(sem->ConstantValue().Element(2).value, 0_f); - EXPECT_EQ(sem->ConstantValue().Element(3).value, 0_f); - EXPECT_EQ(sem->ConstantValue().Element(4).value, 0_f); - EXPECT_EQ(sem->ConstantValue().Element(5).value, 0_f); + EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type()); + EXPECT_TRUE(sem->ConstantValue()->AllEqual()); + EXPECT_TRUE(sem->ConstantValue()->AnyZero()); + EXPECT_TRUE(sem->ConstantValue()->AllZero()); + + EXPECT_TRUE(sem->ConstantValue()->Index(0)->Index(0)->AllEqual()); + EXPECT_TRUE(sem->ConstantValue()->Index(0)->Index(0)->AnyZero()); + EXPECT_TRUE(sem->ConstantValue()->Index(0)->Index(0)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(0)->Index(0)->As(), 0_f); + + EXPECT_TRUE(sem->ConstantValue()->Index(0)->Index(1)->AllEqual()); + EXPECT_TRUE(sem->ConstantValue()->Index(0)->Index(1)->AnyZero()); + EXPECT_TRUE(sem->ConstantValue()->Index(0)->Index(1)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(0)->Index(1)->As(), 0_f); + + EXPECT_TRUE(sem->ConstantValue()->Index(0)->Index(2)->AllEqual()); + EXPECT_TRUE(sem->ConstantValue()->Index(0)->Index(2)->AnyZero()); + EXPECT_TRUE(sem->ConstantValue()->Index(0)->Index(2)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(0)->Index(2)->As(), 0_f); + + EXPECT_TRUE(sem->ConstantValue()->Index(1)->Index(0)->AllEqual()); + EXPECT_TRUE(sem->ConstantValue()->Index(1)->Index(0)->AnyZero()); + EXPECT_TRUE(sem->ConstantValue()->Index(1)->Index(0)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(1)->Index(0)->As(), 0_f); + + EXPECT_TRUE(sem->ConstantValue()->Index(1)->Index(1)->AllEqual()); + EXPECT_TRUE(sem->ConstantValue()->Index(1)->Index(1)->AnyZero()); + EXPECT_TRUE(sem->ConstantValue()->Index(1)->Index(1)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(1)->Index(1)->As(), 0_f); + + EXPECT_TRUE(sem->ConstantValue()->Index(1)->Index(2)->AllEqual()); + EXPECT_TRUE(sem->ConstantValue()->Index(1)->Index(2)->AnyZero()); + EXPECT_TRUE(sem->ConstantValue()->Index(1)->Index(2)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(1)->Index(2)->As(), 0_f); } TEST_F(ResolverConstantsTest, Array_i32_Elements) { @@ -858,13 +1726,30 @@ TEST_F(ResolverConstantsTest, Array_i32_Elements) { ASSERT_NE(arr, nullptr); EXPECT_TRUE(arr->ElemType()->Is()); EXPECT_EQ(arr->Count(), 4u); - EXPECT_EQ(sem->ConstantValue().Type(), sem->Type()); - EXPECT_TRUE(sem->ConstantValue().ElementType()->Is()); - ASSERT_EQ(sem->ConstantValue().ElementCount(), 4u); - EXPECT_EQ(sem->ConstantValue().Element(0).value, 10_i); - EXPECT_EQ(sem->ConstantValue().Element(1).value, 20_i); - EXPECT_EQ(sem->ConstantValue().Element(2).value, 30_i); - EXPECT_EQ(sem->ConstantValue().Element(3).value, 40_i); + EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type()); + EXPECT_FALSE(sem->ConstantValue()->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->AllZero()); + + EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(0)->As(), 10_i); + + EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(1)->As(), 20_i); + + EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(2)->As(), 30_i); + + EXPECT_TRUE(sem->ConstantValue()->Index(3)->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->Index(3)->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->Index(3)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(3)->As(), 40_i); } TEST_F(ResolverConstantsTest, Array_f32_Elements) { @@ -879,13 +1764,30 @@ TEST_F(ResolverConstantsTest, Array_f32_Elements) { ASSERT_NE(arr, nullptr); EXPECT_TRUE(arr->ElemType()->Is()); EXPECT_EQ(arr->Count(), 4u); - EXPECT_EQ(sem->ConstantValue().Type(), sem->Type()); - EXPECT_TRUE(sem->ConstantValue().ElementType()->Is()); - ASSERT_EQ(sem->ConstantValue().ElementCount(), 4u); - EXPECT_EQ(sem->ConstantValue().Element(0).value, 10_f); - EXPECT_EQ(sem->ConstantValue().Element(1).value, 20_f); - EXPECT_EQ(sem->ConstantValue().Element(2).value, 30_f); - EXPECT_EQ(sem->ConstantValue().Element(3).value, 40_f); + EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type()); + EXPECT_FALSE(sem->ConstantValue()->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->AllZero()); + + EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(0)->As(), 10_f); + + EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(1)->As(), 20_f); + + EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(2)->As(), 30_f); + + EXPECT_TRUE(sem->ConstantValue()->Index(3)->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->Index(3)->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->Index(3)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(3)->As(), 40_f); } TEST_F(ResolverConstantsTest, Array_vec3_f32_Elements) { @@ -901,15 +1803,16 @@ TEST_F(ResolverConstantsTest, Array_vec3_f32_Elements) { ASSERT_NE(arr, nullptr); EXPECT_TRUE(arr->ElemType()->Is()); EXPECT_EQ(arr->Count(), 2u); - EXPECT_EQ(sem->ConstantValue().Type(), sem->Type()); - EXPECT_TRUE(sem->ConstantValue().ElementType()->Is()); - ASSERT_EQ(sem->ConstantValue().ElementCount(), 6u); - EXPECT_EQ(sem->ConstantValue().Element(0).value, 1_f); - EXPECT_EQ(sem->ConstantValue().Element(1).value, 2_f); - EXPECT_EQ(sem->ConstantValue().Element(2).value, 3_f); - EXPECT_EQ(sem->ConstantValue().Element(3).value, 4_f); - EXPECT_EQ(sem->ConstantValue().Element(4).value, 5_f); - EXPECT_EQ(sem->ConstantValue().Element(5).value, 6_f); + EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type()); + EXPECT_FALSE(sem->ConstantValue()->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(0)->Index(0)->As(), 1_f); + EXPECT_EQ(sem->ConstantValue()->Index(0)->Index(1)->As(), 2_f); + EXPECT_EQ(sem->ConstantValue()->Index(0)->Index(2)->As(), 3_f); + EXPECT_EQ(sem->ConstantValue()->Index(1)->Index(0)->As(), 4_f); + EXPECT_EQ(sem->ConstantValue()->Index(1)->Index(1)->As(), 5_f); + EXPECT_EQ(sem->ConstantValue()->Index(1)->Index(2)->As(), 6_f); } //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -925,10 +1828,11 @@ TEST_F(ResolverConstantsTest, Vec3_Index) { auto* sem = Sem().Get(expr); ASSERT_NE(sem, nullptr); ASSERT_TRUE(sem->Type()->Is()); - EXPECT_EQ(sem->ConstantValue().Type(), sem->Type()); - EXPECT_TRUE(sem->ConstantValue().ElementType()->Is()); - ASSERT_EQ(sem->ConstantValue().ElementCount(), 1u); - EXPECT_EQ(sem->ConstantValue().Element(0).value, 3_i); + EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type()); + EXPECT_TRUE(sem->ConstantValue()->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->AllZero()); + EXPECT_EQ(sem->ConstantValue()->As(), 3_i); } TEST_F(ResolverConstantsTest, Vec3_Index_OOB_High) { @@ -941,10 +1845,11 @@ TEST_F(ResolverConstantsTest, Vec3_Index_OOB_High) { auto* sem = Sem().Get(expr); ASSERT_NE(sem, nullptr); ASSERT_TRUE(sem->Type()->Is()); - EXPECT_EQ(sem->ConstantValue().Type(), sem->Type()); - EXPECT_TRUE(sem->ConstantValue().ElementType()->Is()); - ASSERT_EQ(sem->ConstantValue().ElementCount(), 1u); - EXPECT_EQ(sem->ConstantValue().Element(0).value, 3_i); + EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type()); + EXPECT_TRUE(sem->ConstantValue()->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->AllZero()); + EXPECT_EQ(sem->ConstantValue()->As(), 3_i); } TEST_F(ResolverConstantsTest, Vec3_Index_OOB_Low) { @@ -957,10 +1862,11 @@ TEST_F(ResolverConstantsTest, Vec3_Index_OOB_Low) { auto* sem = Sem().Get(expr); ASSERT_NE(sem, nullptr); ASSERT_TRUE(sem->Type()->Is()); - EXPECT_EQ(sem->ConstantValue().Type(), sem->Type()); - EXPECT_TRUE(sem->ConstantValue().ElementType()->Is()); - ASSERT_EQ(sem->ConstantValue().ElementCount(), 1u); - EXPECT_EQ(sem->ConstantValue().Element(0).value, 1_i); + EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type()); + EXPECT_TRUE(sem->ConstantValue()->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->AllZero()); + EXPECT_EQ(sem->ConstantValue()->As(), 1_i); } TEST_F(ResolverConstantsTest, Mat3x2_Index) { @@ -975,11 +1881,17 @@ TEST_F(ResolverConstantsTest, Mat3x2_Index) { auto* vec = sem->Type()->As(); ASSERT_NE(vec, nullptr); EXPECT_EQ(vec->Width(), 2u); - EXPECT_EQ(sem->ConstantValue().Type(), sem->Type()); - EXPECT_TRUE(sem->ConstantValue().ElementType()->Is()); - ASSERT_EQ(sem->ConstantValue().ElementCount(), 2u); - EXPECT_EQ(sem->ConstantValue().Element(0).value, 5._a); - EXPECT_EQ(sem->ConstantValue().Element(1).value, 6._a); + EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type()); + + EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(0)->As(), 5._a); + + EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(1)->As(), 6._a); } TEST_F(ResolverConstantsTest, Mat3x2_Index_OOB_High) { @@ -996,11 +1908,17 @@ TEST_F(ResolverConstantsTest, Mat3x2_Index_OOB_High) { auto* vec = sem->Type()->As(); ASSERT_NE(vec, nullptr); EXPECT_EQ(vec->Width(), 2u); - EXPECT_EQ(sem->ConstantValue().Type(), sem->Type()); - EXPECT_TRUE(sem->ConstantValue().ElementType()->Is()); - ASSERT_EQ(sem->ConstantValue().ElementCount(), 2u); - EXPECT_EQ(sem->ConstantValue().Element(0).value, 5._a); - EXPECT_EQ(sem->ConstantValue().Element(1).value, 6._a); + EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type()); + + EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(0)->As(), 5._a); + + EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(1)->As(), 6._a); } TEST_F(ResolverConstantsTest, Mat3x2_Index_OOB_Low) { @@ -1017,11 +1935,17 @@ TEST_F(ResolverConstantsTest, Mat3x2_Index_OOB_Low) { auto* vec = sem->Type()->As(); ASSERT_NE(vec, nullptr); EXPECT_EQ(vec->Width(), 2u); - EXPECT_EQ(sem->ConstantValue().Type(), sem->Type()); - EXPECT_TRUE(sem->ConstantValue().ElementType()->Is()); - ASSERT_EQ(sem->ConstantValue().ElementCount(), 2u); - EXPECT_EQ(sem->ConstantValue().Element(0).value, 1._a); - EXPECT_EQ(sem->ConstantValue().Element(1).value, 2._a); + EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type()); + + EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(0)->As(), 1._a); + + EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(1)->As(), 2._a); } TEST_F(ResolverConstantsTest, Array_vec3_f32_Index) { @@ -1038,12 +1962,22 @@ TEST_F(ResolverConstantsTest, Array_vec3_f32_Index) { ASSERT_NE(vec, nullptr); EXPECT_TRUE(vec->type()->Is()); EXPECT_EQ(vec->Width(), 3u); - EXPECT_EQ(sem->ConstantValue().Type(), sem->Type()); - EXPECT_TRUE(sem->ConstantValue().ElementType()->Is()); - ASSERT_EQ(sem->ConstantValue().ElementCount(), 3u); - EXPECT_EQ(sem->ConstantValue().Element(0).value, 4_f); - EXPECT_EQ(sem->ConstantValue().Element(1).value, 5_f); - EXPECT_EQ(sem->ConstantValue().Element(2).value, 6_f); + EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type()); + + EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(0)->As(), 4_f); + + EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(1)->As(), 5_f); + + EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(2)->As(), 6_f); } TEST_F(ResolverConstantsTest, Array_vec3_f32_Index_OOB_High) { @@ -1061,12 +1995,22 @@ TEST_F(ResolverConstantsTest, Array_vec3_f32_Index_OOB_High) { ASSERT_NE(vec, nullptr); EXPECT_TRUE(vec->type()->Is()); EXPECT_EQ(vec->Width(), 3u); - EXPECT_EQ(sem->ConstantValue().Type(), sem->Type()); - EXPECT_TRUE(sem->ConstantValue().ElementType()->Is()); - ASSERT_EQ(sem->ConstantValue().ElementCount(), 3u); - EXPECT_EQ(sem->ConstantValue().Element(0).value, 4_f); - EXPECT_EQ(sem->ConstantValue().Element(1).value, 5_f); - EXPECT_EQ(sem->ConstantValue().Element(2).value, 6_f); + EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type()); + + EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(0)->As(), 4_f); + + EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(1)->As(), 5_f); + + EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(2)->As(), 6_f); } TEST_F(ResolverConstantsTest, Array_vec3_f32_Index_OOB_Low) { @@ -1084,19 +2028,32 @@ TEST_F(ResolverConstantsTest, Array_vec3_f32_Index_OOB_Low) { ASSERT_NE(vec, nullptr); EXPECT_TRUE(vec->type()->Is()); EXPECT_EQ(vec->Width(), 3u); - EXPECT_EQ(sem->ConstantValue().Type(), sem->Type()); - EXPECT_TRUE(sem->ConstantValue().ElementType()->Is()); - ASSERT_EQ(sem->ConstantValue().ElementCount(), 3u); - EXPECT_EQ(sem->ConstantValue().Element(0).value, 1_f); - EXPECT_EQ(sem->ConstantValue().Element(1).value, 2_f); - EXPECT_EQ(sem->ConstantValue().Element(2).value, 3_f); + EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type()); + EXPECT_FALSE(sem->ConstantValue()->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->AllZero()); + + EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(0)->As(), 1_f); + + EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(1)->As(), 2_f); + + EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual()); + EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero()); + EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero()); + EXPECT_EQ(sem->ConstantValue()->Index(2)->As(), 3_f); } TEST_F(ResolverConstantsTest, ChainedIndex) { auto* arr_expr = Construct(ty.array(ty.mat2x3(), 2_u), // array, 2u> mat2x3(vec3(1_f, 2_f, 3_f), // vec3(4_f, 5_f, 6_f)), // - mat2x3(vec3(7_f, 8_f, 9_f), // + mat2x3(vec3(7_f, 0_f, 9_f), // vec3(10_f, 11_f, 12_f))); auto* mat_expr = IndexAccessor(arr_expr, 1_i); // arr[1] @@ -1114,15 +2071,40 @@ TEST_F(ResolverConstantsTest, ChainedIndex) { EXPECT_TRUE(ty->ColumnType()->Is()); EXPECT_EQ(ty->columns(), 2u); EXPECT_EQ(ty->rows(), 3u); - EXPECT_EQ(mat->ConstantValue().Type(), mat->Type()); - EXPECT_TRUE(mat->ConstantValue().ElementType()->Is()); - ASSERT_EQ(mat->ConstantValue().ElementCount(), 6u); - EXPECT_EQ(mat->ConstantValue().Element(0).value, 7_f); - EXPECT_EQ(mat->ConstantValue().Element(1).value, 8_f); - EXPECT_EQ(mat->ConstantValue().Element(2).value, 9_f); - EXPECT_EQ(mat->ConstantValue().Element(3).value, 10_f); - EXPECT_EQ(mat->ConstantValue().Element(4).value, 11_f); - EXPECT_EQ(mat->ConstantValue().Element(5).value, 12_f); + EXPECT_EQ(mat->ConstantValue()->Type(), mat->Type()); + EXPECT_FALSE(mat->ConstantValue()->AllEqual()); + EXPECT_TRUE(mat->ConstantValue()->AnyZero()); + EXPECT_FALSE(mat->ConstantValue()->AllZero()); + + EXPECT_TRUE(mat->ConstantValue()->Index(0)->Index(0)->AllEqual()); + EXPECT_FALSE(mat->ConstantValue()->Index(0)->Index(0)->AnyZero()); + EXPECT_FALSE(mat->ConstantValue()->Index(0)->Index(0)->AllZero()); + EXPECT_EQ(mat->ConstantValue()->Index(0)->Index(0)->As(), 7_f); + + EXPECT_TRUE(mat->ConstantValue()->Index(0)->Index(1)->AllEqual()); + EXPECT_TRUE(mat->ConstantValue()->Index(0)->Index(1)->AnyZero()); + EXPECT_TRUE(mat->ConstantValue()->Index(0)->Index(1)->AllZero()); + EXPECT_EQ(mat->ConstantValue()->Index(0)->Index(1)->As(), 0_f); + + EXPECT_TRUE(mat->ConstantValue()->Index(0)->Index(2)->AllEqual()); + EXPECT_FALSE(mat->ConstantValue()->Index(0)->Index(2)->AnyZero()); + EXPECT_FALSE(mat->ConstantValue()->Index(0)->Index(2)->AllZero()); + EXPECT_EQ(mat->ConstantValue()->Index(0)->Index(2)->As(), 9_f); + + EXPECT_TRUE(mat->ConstantValue()->Index(1)->Index(0)->AllEqual()); + EXPECT_FALSE(mat->ConstantValue()->Index(1)->Index(0)->AnyZero()); + EXPECT_FALSE(mat->ConstantValue()->Index(1)->Index(0)->AllZero()); + EXPECT_EQ(mat->ConstantValue()->Index(1)->Index(0)->As(), 10_f); + + EXPECT_TRUE(mat->ConstantValue()->Index(1)->Index(1)->AllEqual()); + EXPECT_FALSE(mat->ConstantValue()->Index(1)->Index(1)->AnyZero()); + EXPECT_FALSE(mat->ConstantValue()->Index(1)->Index(1)->AllZero()); + EXPECT_EQ(mat->ConstantValue()->Index(1)->Index(1)->As(), 11_f); + + EXPECT_TRUE(mat->ConstantValue()->Index(1)->Index(2)->AllEqual()); + EXPECT_FALSE(mat->ConstantValue()->Index(1)->Index(2)->AnyZero()); + EXPECT_FALSE(mat->ConstantValue()->Index(1)->Index(2)->AllZero()); + EXPECT_EQ(mat->ConstantValue()->Index(1)->Index(2)->As(), 12_f); } { auto* vec = Sem().Get(vec_expr); @@ -1131,21 +2113,35 @@ TEST_F(ResolverConstantsTest, ChainedIndex) { ASSERT_NE(vec->Type(), nullptr); EXPECT_TRUE(ty->type()->Is()); EXPECT_EQ(ty->Width(), 3u); - EXPECT_EQ(vec->ConstantValue().Type(), vec->Type()); - EXPECT_TRUE(vec->ConstantValue().ElementType()->Is()); - ASSERT_EQ(vec->ConstantValue().ElementCount(), 3u); - EXPECT_EQ(vec->ConstantValue().Element(0).value, 7_f); - EXPECT_EQ(vec->ConstantValue().Element(1).value, 8_f); - EXPECT_EQ(vec->ConstantValue().Element(2).value, 9_f); + EXPECT_EQ(vec->ConstantValue()->Type(), vec->Type()); + EXPECT_FALSE(vec->ConstantValue()->AllEqual()); + EXPECT_TRUE(vec->ConstantValue()->AnyZero()); + EXPECT_FALSE(vec->ConstantValue()->AllZero()); + + EXPECT_TRUE(vec->ConstantValue()->Index(0)->AllEqual()); + EXPECT_FALSE(vec->ConstantValue()->Index(0)->AnyZero()); + EXPECT_FALSE(vec->ConstantValue()->Index(0)->AllZero()); + EXPECT_EQ(vec->ConstantValue()->Index(0)->As(), 7_f); + + EXPECT_TRUE(vec->ConstantValue()->Index(1)->AllEqual()); + EXPECT_TRUE(vec->ConstantValue()->Index(1)->AnyZero()); + EXPECT_TRUE(vec->ConstantValue()->Index(1)->AllZero()); + EXPECT_EQ(vec->ConstantValue()->Index(1)->As(), 0_f); + + EXPECT_TRUE(vec->ConstantValue()->Index(2)->AllEqual()); + EXPECT_FALSE(vec->ConstantValue()->Index(2)->AnyZero()); + EXPECT_FALSE(vec->ConstantValue()->Index(2)->AllZero()); + EXPECT_EQ(vec->ConstantValue()->Index(2)->As(), 9_f); } { auto* f = Sem().Get(f32_expr); EXPECT_NE(f, nullptr); EXPECT_TRUE(f->Type()->Is()); - EXPECT_EQ(f->ConstantValue().Type(), f->Type()); - EXPECT_TRUE(f->ConstantValue().ElementType()->Is()); - ASSERT_EQ(f->ConstantValue().ElementCount(), 1u); - EXPECT_EQ(f->ConstantValue().Element(0).value, 9_f); + EXPECT_EQ(f->ConstantValue()->Type(), f->Type()); + EXPECT_TRUE(f->ConstantValue()->AllEqual()); + EXPECT_FALSE(f->ConstantValue()->AnyZero()); + EXPECT_FALSE(f->ConstantValue()->AllZero()); + EXPECT_EQ(f->ConstantValue()->As(), 9_f); } } @@ -1174,15 +2170,40 @@ TEST_F(ResolverConstantsTest, ChainedIndex_OOB) { EXPECT_TRUE(ty->ColumnType()->Is()); EXPECT_EQ(ty->columns(), 2u); EXPECT_EQ(ty->rows(), 3u); - EXPECT_EQ(mat->ConstantValue().Type(), mat->Type()); - EXPECT_TRUE(mat->ConstantValue().ElementType()->Is()); - ASSERT_EQ(mat->ConstantValue().ElementCount(), 6u); - EXPECT_EQ(mat->ConstantValue().Element(0).value, 1_f); - EXPECT_EQ(mat->ConstantValue().Element(1).value, 2_f); - EXPECT_EQ(mat->ConstantValue().Element(2).value, 3_f); - EXPECT_EQ(mat->ConstantValue().Element(3).value, 4_f); - EXPECT_EQ(mat->ConstantValue().Element(4).value, 5_f); - EXPECT_EQ(mat->ConstantValue().Element(5).value, 6_f); + EXPECT_EQ(mat->ConstantValue()->Type(), mat->Type()); + EXPECT_FALSE(mat->ConstantValue()->AllEqual()); + EXPECT_FALSE(mat->ConstantValue()->AnyZero()); + EXPECT_FALSE(mat->ConstantValue()->AllZero()); + + EXPECT_TRUE(mat->ConstantValue()->Index(0)->Index(0)->AllEqual()); + EXPECT_FALSE(mat->ConstantValue()->Index(0)->Index(0)->AnyZero()); + EXPECT_FALSE(mat->ConstantValue()->Index(0)->Index(0)->AllZero()); + EXPECT_EQ(mat->ConstantValue()->Index(0)->Index(0)->As(), 1_f); + + EXPECT_TRUE(mat->ConstantValue()->Index(0)->Index(1)->AllEqual()); + EXPECT_FALSE(mat->ConstantValue()->Index(0)->Index(1)->AnyZero()); + EXPECT_FALSE(mat->ConstantValue()->Index(0)->Index(1)->AllZero()); + EXPECT_EQ(mat->ConstantValue()->Index(0)->Index(1)->As(), 2_f); + + EXPECT_TRUE(mat->ConstantValue()->Index(0)->Index(2)->AllEqual()); + EXPECT_FALSE(mat->ConstantValue()->Index(0)->Index(2)->AnyZero()); + EXPECT_FALSE(mat->ConstantValue()->Index(0)->Index(2)->AllZero()); + EXPECT_EQ(mat->ConstantValue()->Index(0)->Index(2)->As(), 3_f); + + EXPECT_TRUE(mat->ConstantValue()->Index(1)->Index(0)->AllEqual()); + EXPECT_FALSE(mat->ConstantValue()->Index(1)->Index(0)->AnyZero()); + EXPECT_FALSE(mat->ConstantValue()->Index(1)->Index(0)->AllZero()); + EXPECT_EQ(mat->ConstantValue()->Index(1)->Index(0)->As(), 4_f); + + EXPECT_TRUE(mat->ConstantValue()->Index(1)->Index(1)->AllEqual()); + EXPECT_FALSE(mat->ConstantValue()->Index(1)->Index(1)->AnyZero()); + EXPECT_FALSE(mat->ConstantValue()->Index(1)->Index(1)->AllZero()); + EXPECT_EQ(mat->ConstantValue()->Index(1)->Index(1)->As(), 5_f); + + EXPECT_TRUE(mat->ConstantValue()->Index(1)->Index(2)->AllEqual()); + EXPECT_FALSE(mat->ConstantValue()->Index(1)->Index(2)->AnyZero()); + EXPECT_FALSE(mat->ConstantValue()->Index(1)->Index(2)->AllZero()); + EXPECT_EQ(mat->ConstantValue()->Index(1)->Index(2)->As(), 6_f); } { auto* vec = Sem().Get(vec_expr); @@ -1191,21 +2212,35 @@ TEST_F(ResolverConstantsTest, ChainedIndex_OOB) { ASSERT_NE(vec->Type(), nullptr); EXPECT_TRUE(ty->type()->Is()); EXPECT_EQ(ty->Width(), 3u); - EXPECT_EQ(vec->ConstantValue().Type(), vec->Type()); - EXPECT_TRUE(vec->ConstantValue().ElementType()->Is()); - ASSERT_EQ(vec->ConstantValue().ElementCount(), 3u); - EXPECT_EQ(vec->ConstantValue().Element(0).value, 1_f); - EXPECT_EQ(vec->ConstantValue().Element(1).value, 2_f); - EXPECT_EQ(vec->ConstantValue().Element(2).value, 3_f); + EXPECT_EQ(vec->ConstantValue()->Type(), vec->Type()); + EXPECT_FALSE(vec->ConstantValue()->AllEqual()); + EXPECT_FALSE(vec->ConstantValue()->AnyZero()); + EXPECT_FALSE(vec->ConstantValue()->AllZero()); + + EXPECT_TRUE(vec->ConstantValue()->Index(0)->AllEqual()); + EXPECT_FALSE(vec->ConstantValue()->Index(0)->AnyZero()); + EXPECT_FALSE(vec->ConstantValue()->Index(0)->AllZero()); + EXPECT_EQ(vec->ConstantValue()->Index(0)->As(), 1_f); + + EXPECT_TRUE(vec->ConstantValue()->Index(1)->AllEqual()); + EXPECT_FALSE(vec->ConstantValue()->Index(1)->AnyZero()); + EXPECT_FALSE(vec->ConstantValue()->Index(1)->AllZero()); + EXPECT_EQ(vec->ConstantValue()->Index(1)->As(), 2_f); + + EXPECT_TRUE(vec->ConstantValue()->Index(2)->AllEqual()); + EXPECT_FALSE(vec->ConstantValue()->Index(2)->AnyZero()); + EXPECT_FALSE(vec->ConstantValue()->Index(2)->AllZero()); + EXPECT_EQ(vec->ConstantValue()->Index(2)->As(), 3_f); } { auto* f = Sem().Get(f32_expr); EXPECT_NE(f, nullptr); EXPECT_TRUE(f->Type()->Is()); - EXPECT_EQ(f->ConstantValue().Type(), f->Type()); - EXPECT_TRUE(f->ConstantValue().ElementType()->Is()); - ASSERT_EQ(f->ConstantValue().ElementCount(), 1u); - EXPECT_EQ(f->ConstantValue().Element(0).value, 3_f); + EXPECT_EQ(f->ConstantValue()->Type(), f->Type()); + EXPECT_TRUE(f->ConstantValue()->AllEqual()); + EXPECT_FALSE(f->ConstantValue()->AnyZero()); + EXPECT_FALSE(f->ConstantValue()->AllZero()); + EXPECT_EQ(f->ConstantValue()->As(), 3_f); } } diff --git a/src/tint/resolver/validator.cc b/src/tint/resolver/validator.cc index 0048d741b0..558cb1253d 100644 --- a/src/tint/resolver/validator.cc +++ b/src/tint/resolver/validator.cc @@ -1604,16 +1604,16 @@ bool Validator::TextureBuiltinFunction(const sem::Call* call) const { auto& signature = builtin->Signature(); auto check_arg_is_constexpr = [&](sem::ParameterUsage usage, int min, int max) { - auto index = signature.IndexOf(usage); - if (index < 0) { + auto signed_index = signature.IndexOf(usage); + if (signed_index < 0) { return true; } + auto index = static_cast(signed_index); std::string name = sem::str(usage); - auto* arg = call->Arguments()[static_cast(index)]; + auto* arg = call->Arguments()[index]; if (auto values = arg->ConstantValue()) { // Assert that the constant values are of the expected type. - if (!values.Type()->IsAnyOf() || - !values.ElementType()->Is()) { + if (!values->Type()->is_integer_scalar_or_vector()) { TINT_ICE(Resolver, diagnostics_) << "failed to resolve '" + func_name + "' " << name << " parameter type"; return false; @@ -1631,25 +1631,26 @@ bool Validator::TextureBuiltinFunction(const sem::Call* call) const { return ast::TraverseAction::Stop; }); if (is_const_expr) { - auto vector = - builtin->Parameters()[static_cast(index)]->Type()->Is(); - for (size_t i = 0, n = values.ElementCount(); i < n; i++) { - auto value = values.Element(i).value; - if (value < min || value > max) { - if (vector) { + if (auto* vector = builtin->Parameters()[index]->Type()->As()) { + for (size_t i = 0; i < vector->Width(); i++) { + auto value = values->Index(i)->As(); + if (value < min || value > max) { AddError("each component of the " + name + " argument must be at least " + std::to_string(min) + " and at most " + std::to_string(max) + ". " + name + " component " + std::to_string(i) + " is " + std::to_string(value), arg->Declaration()->source); - } else { - AddError("the " + name + " argument must be at least " + - std::to_string(min) + " and at most " + - std::to_string(max) + ". " + name + " is " + - std::to_string(value), - arg->Declaration()->source); + return false; } + } + } else { + auto value = values->As(); + if (value < min || value > max) { + AddError("the " + name + " argument must be at least " + + std::to_string(min) + " and at most " + std::to_string(max) + + ". " + name + " is " + std::to_string(value), + arg->Declaration()->source); return false; } } diff --git a/src/tint/resolver/variable_test.cc b/src/tint/resolver/variable_test.cc index 2ee597944a..7eb0deac03 100644 --- a/src/tint/resolver/variable_test.cc +++ b/src/tint/resolver/variable_test.cc @@ -920,21 +920,13 @@ TEST_F(ResolverVariableTest, LocalConst_ExplicitType_Decls) { ASSERT_TRUE(TypeOf(c_vf32)->Is()); ASSERT_TRUE(TypeOf(c_mf32)->Is()); - EXPECT_TRUE(Sem().Get(c_i32)->ConstantValue().AllZero()); - EXPECT_TRUE(Sem().Get(c_u32)->ConstantValue().AllZero()); - EXPECT_TRUE(Sem().Get(c_f32)->ConstantValue().AllZero()); - EXPECT_TRUE(Sem().Get(c_vi32)->ConstantValue().AllZero()); - EXPECT_TRUE(Sem().Get(c_vu32)->ConstantValue().AllZero()); - EXPECT_TRUE(Sem().Get(c_vf32)->ConstantValue().AllZero()); - EXPECT_TRUE(Sem().Get(c_mf32)->ConstantValue().AllZero()); - - EXPECT_EQ(Sem().Get(c_i32)->ConstantValue().ElementCount(), 1u); - EXPECT_EQ(Sem().Get(c_u32)->ConstantValue().ElementCount(), 1u); - EXPECT_EQ(Sem().Get(c_f32)->ConstantValue().ElementCount(), 1u); - EXPECT_EQ(Sem().Get(c_vi32)->ConstantValue().ElementCount(), 3u); - EXPECT_EQ(Sem().Get(c_vu32)->ConstantValue().ElementCount(), 3u); - EXPECT_EQ(Sem().Get(c_vf32)->ConstantValue().ElementCount(), 3u); - EXPECT_EQ(Sem().Get(c_mf32)->ConstantValue().ElementCount(), 9u); + EXPECT_TRUE(Sem().Get(c_i32)->ConstantValue()->AllZero()); + EXPECT_TRUE(Sem().Get(c_u32)->ConstantValue()->AllZero()); + EXPECT_TRUE(Sem().Get(c_f32)->ConstantValue()->AllZero()); + EXPECT_TRUE(Sem().Get(c_vi32)->ConstantValue()->AllZero()); + EXPECT_TRUE(Sem().Get(c_vu32)->ConstantValue()->AllZero()); + EXPECT_TRUE(Sem().Get(c_vf32)->ConstantValue()->AllZero()); + EXPECT_TRUE(Sem().Get(c_mf32)->ConstantValue()->AllZero()); } TEST_F(ResolverVariableTest, LocalConst_ImplicitType_Decls) { @@ -949,7 +941,11 @@ TEST_F(ResolverVariableTest, LocalConst_ImplicitType_Decls) { auto* c_vai = Const("i", nullptr, Construct(ty.vec(nullptr, 3), Expr(0_a))); auto* c_vaf = Const("j", nullptr, Construct(ty.vec(nullptr, 3), Expr(0._a))); auto* c_mf32 = Const("k", nullptr, mat3x3()); - auto* c_maf32 = Const("l", nullptr, Construct(ty.mat(nullptr, 3, 3), Expr(0._a))); + auto* c_maf32 = Const("l", nullptr, + Construct(ty.mat(nullptr, 3, 3), // + Construct(ty.vec(nullptr, 3), Expr(0._a)), + Construct(ty.vec(nullptr, 3), Expr(0._a)), + Construct(ty.vec(nullptr, 3), Expr(0._a)))); WrapInFunction(c_i32, c_u32, c_f32, c_ai, c_af, c_vi32, c_vu32, c_vf32, c_vai, c_vaf, c_mf32, c_maf32); @@ -982,31 +978,18 @@ TEST_F(ResolverVariableTest, LocalConst_ImplicitType_Decls) { ASSERT_TRUE(TypeOf(c_mf32)->Is()); ASSERT_TRUE(TypeOf(c_maf32)->Is()); - EXPECT_TRUE(Sem().Get(c_i32)->ConstantValue().AllZero()); - EXPECT_TRUE(Sem().Get(c_u32)->ConstantValue().AllZero()); - EXPECT_TRUE(Sem().Get(c_f32)->ConstantValue().AllZero()); - EXPECT_TRUE(Sem().Get(c_ai)->ConstantValue().AllZero()); - EXPECT_TRUE(Sem().Get(c_af)->ConstantValue().AllZero()); - EXPECT_TRUE(Sem().Get(c_vi32)->ConstantValue().AllZero()); - EXPECT_TRUE(Sem().Get(c_vu32)->ConstantValue().AllZero()); - EXPECT_TRUE(Sem().Get(c_vf32)->ConstantValue().AllZero()); - EXPECT_TRUE(Sem().Get(c_vai)->ConstantValue().AllZero()); - EXPECT_TRUE(Sem().Get(c_vaf)->ConstantValue().AllZero()); - EXPECT_TRUE(Sem().Get(c_mf32)->ConstantValue().AllZero()); - EXPECT_TRUE(Sem().Get(c_maf32)->ConstantValue().AllZero()); - - EXPECT_EQ(Sem().Get(c_i32)->ConstantValue().ElementCount(), 1u); - EXPECT_EQ(Sem().Get(c_u32)->ConstantValue().ElementCount(), 1u); - EXPECT_EQ(Sem().Get(c_f32)->ConstantValue().ElementCount(), 1u); - EXPECT_EQ(Sem().Get(c_ai)->ConstantValue().ElementCount(), 1u); - EXPECT_EQ(Sem().Get(c_af)->ConstantValue().ElementCount(), 1u); - EXPECT_EQ(Sem().Get(c_vi32)->ConstantValue().ElementCount(), 3u); - EXPECT_EQ(Sem().Get(c_vu32)->ConstantValue().ElementCount(), 3u); - EXPECT_EQ(Sem().Get(c_vf32)->ConstantValue().ElementCount(), 3u); - EXPECT_EQ(Sem().Get(c_vai)->ConstantValue().ElementCount(), 3u); - EXPECT_EQ(Sem().Get(c_vaf)->ConstantValue().ElementCount(), 3u); - EXPECT_EQ(Sem().Get(c_mf32)->ConstantValue().ElementCount(), 9u); - EXPECT_EQ(Sem().Get(c_maf32)->ConstantValue().ElementCount(), 9u); + EXPECT_TRUE(Sem().Get(c_i32)->ConstantValue()->AllZero()); + EXPECT_TRUE(Sem().Get(c_u32)->ConstantValue()->AllZero()); + EXPECT_TRUE(Sem().Get(c_f32)->ConstantValue()->AllZero()); + EXPECT_TRUE(Sem().Get(c_ai)->ConstantValue()->AllZero()); + EXPECT_TRUE(Sem().Get(c_af)->ConstantValue()->AllZero()); + EXPECT_TRUE(Sem().Get(c_vi32)->ConstantValue()->AllZero()); + EXPECT_TRUE(Sem().Get(c_vu32)->ConstantValue()->AllZero()); + EXPECT_TRUE(Sem().Get(c_vf32)->ConstantValue()->AllZero()); + EXPECT_TRUE(Sem().Get(c_vai)->ConstantValue()->AllZero()); + EXPECT_TRUE(Sem().Get(c_vaf)->ConstantValue()->AllZero()); + EXPECT_TRUE(Sem().Get(c_mf32)->ConstantValue()->AllZero()); + EXPECT_TRUE(Sem().Get(c_maf32)->ConstantValue()->AllZero()); } TEST_F(ResolverVariableTest, LocalConst_PropagateConstValue) { @@ -1020,8 +1003,7 @@ TEST_F(ResolverVariableTest, LocalConst_PropagateConstValue) { ASSERT_TRUE(TypeOf(c)->Is()); - ASSERT_EQ(Sem().Get(c)->ConstantValue().ElementCount(), 1u); - EXPECT_EQ(Sem().Get(c)->ConstantValue().Element(0), 42_i); + EXPECT_EQ(Sem().Get(c)->ConstantValue()->As(), 42_i); } // Enable when we have @const operators implemented @@ -1034,8 +1016,7 @@ TEST_F(ResolverVariableTest, DISABLED_LocalConst_ConstEval) { ASSERT_TRUE(TypeOf(c)->Is()); - ASSERT_EQ(Sem().Get(c)->ConstantValue().ElementCount(), 1u); - EXPECT_EQ(Sem().Get(c)->ConstantValue().Element(0), 3_i); + EXPECT_EQ(Sem().Get(c)->ConstantValue()->As(), 3_i); } //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -1126,21 +1107,13 @@ TEST_F(ResolverVariableTest, GlobalConst_ExplicitType_Decls) { ASSERT_TRUE(TypeOf(c_vf32)->Is()); ASSERT_TRUE(TypeOf(c_mf32)->Is()); - EXPECT_TRUE(Sem().Get(c_i32)->ConstantValue().AllZero()); - EXPECT_TRUE(Sem().Get(c_u32)->ConstantValue().AllZero()); - EXPECT_TRUE(Sem().Get(c_f32)->ConstantValue().AllZero()); - EXPECT_TRUE(Sem().Get(c_vi32)->ConstantValue().AllZero()); - EXPECT_TRUE(Sem().Get(c_vu32)->ConstantValue().AllZero()); - EXPECT_TRUE(Sem().Get(c_vf32)->ConstantValue().AllZero()); - EXPECT_TRUE(Sem().Get(c_mf32)->ConstantValue().AllZero()); - - EXPECT_EQ(Sem().Get(c_i32)->ConstantValue().ElementCount(), 1u); - EXPECT_EQ(Sem().Get(c_u32)->ConstantValue().ElementCount(), 1u); - EXPECT_EQ(Sem().Get(c_f32)->ConstantValue().ElementCount(), 1u); - EXPECT_EQ(Sem().Get(c_vi32)->ConstantValue().ElementCount(), 3u); - EXPECT_EQ(Sem().Get(c_vu32)->ConstantValue().ElementCount(), 3u); - EXPECT_EQ(Sem().Get(c_vf32)->ConstantValue().ElementCount(), 3u); - EXPECT_EQ(Sem().Get(c_mf32)->ConstantValue().ElementCount(), 9u); + EXPECT_TRUE(Sem().Get(c_i32)->ConstantValue()->AllZero()); + EXPECT_TRUE(Sem().Get(c_u32)->ConstantValue()->AllZero()); + EXPECT_TRUE(Sem().Get(c_f32)->ConstantValue()->AllZero()); + EXPECT_TRUE(Sem().Get(c_vi32)->ConstantValue()->AllZero()); + EXPECT_TRUE(Sem().Get(c_vu32)->ConstantValue()->AllZero()); + EXPECT_TRUE(Sem().Get(c_vf32)->ConstantValue()->AllZero()); + EXPECT_TRUE(Sem().Get(c_mf32)->ConstantValue()->AllZero()); } TEST_F(ResolverVariableTest, GlobalConst_ImplicitType_Decls) { @@ -1155,7 +1128,11 @@ TEST_F(ResolverVariableTest, GlobalConst_ImplicitType_Decls) { auto* c_vai = GlobalConst("i", nullptr, Construct(ty.vec(nullptr, 3), Expr(0_a))); auto* c_vaf = GlobalConst("j", nullptr, Construct(ty.vec(nullptr, 3), Expr(0._a))); auto* c_mf32 = GlobalConst("k", nullptr, mat3x3()); - auto* c_maf32 = GlobalConst("l", nullptr, Construct(ty.mat(nullptr, 3, 3), Expr(0._a))); + auto* c_maf32 = GlobalConst("l", nullptr, + Construct(ty.mat(nullptr, 3, 3), // + Construct(ty.vec(nullptr, 3), Expr(0._a)), + Construct(ty.vec(nullptr, 3), Expr(0._a)), + Construct(ty.vec(nullptr, 3), Expr(0._a)))); ASSERT_TRUE(r()->Resolve()) << r()->error(); @@ -1185,31 +1162,18 @@ TEST_F(ResolverVariableTest, GlobalConst_ImplicitType_Decls) { ASSERT_TRUE(TypeOf(c_mf32)->Is()); ASSERT_TRUE(TypeOf(c_maf32)->Is()); - EXPECT_TRUE(Sem().Get(c_i32)->ConstantValue().AllZero()); - EXPECT_TRUE(Sem().Get(c_u32)->ConstantValue().AllZero()); - EXPECT_TRUE(Sem().Get(c_f32)->ConstantValue().AllZero()); - EXPECT_TRUE(Sem().Get(c_ai)->ConstantValue().AllZero()); - EXPECT_TRUE(Sem().Get(c_af)->ConstantValue().AllZero()); - EXPECT_TRUE(Sem().Get(c_vi32)->ConstantValue().AllZero()); - EXPECT_TRUE(Sem().Get(c_vu32)->ConstantValue().AllZero()); - EXPECT_TRUE(Sem().Get(c_vf32)->ConstantValue().AllZero()); - EXPECT_TRUE(Sem().Get(c_vai)->ConstantValue().AllZero()); - EXPECT_TRUE(Sem().Get(c_vaf)->ConstantValue().AllZero()); - EXPECT_TRUE(Sem().Get(c_mf32)->ConstantValue().AllZero()); - EXPECT_TRUE(Sem().Get(c_maf32)->ConstantValue().AllZero()); - - EXPECT_EQ(Sem().Get(c_i32)->ConstantValue().ElementCount(), 1u); - EXPECT_EQ(Sem().Get(c_u32)->ConstantValue().ElementCount(), 1u); - EXPECT_EQ(Sem().Get(c_f32)->ConstantValue().ElementCount(), 1u); - EXPECT_EQ(Sem().Get(c_ai)->ConstantValue().ElementCount(), 1u); - EXPECT_EQ(Sem().Get(c_af)->ConstantValue().ElementCount(), 1u); - EXPECT_EQ(Sem().Get(c_vi32)->ConstantValue().ElementCount(), 3u); - EXPECT_EQ(Sem().Get(c_vu32)->ConstantValue().ElementCount(), 3u); - EXPECT_EQ(Sem().Get(c_vf32)->ConstantValue().ElementCount(), 3u); - EXPECT_EQ(Sem().Get(c_vai)->ConstantValue().ElementCount(), 3u); - EXPECT_EQ(Sem().Get(c_vaf)->ConstantValue().ElementCount(), 3u); - EXPECT_EQ(Sem().Get(c_mf32)->ConstantValue().ElementCount(), 9u); - EXPECT_EQ(Sem().Get(c_maf32)->ConstantValue().ElementCount(), 9u); + EXPECT_TRUE(Sem().Get(c_i32)->ConstantValue()->AllZero()); + EXPECT_TRUE(Sem().Get(c_u32)->ConstantValue()->AllZero()); + EXPECT_TRUE(Sem().Get(c_f32)->ConstantValue()->AllZero()); + EXPECT_TRUE(Sem().Get(c_ai)->ConstantValue()->AllZero()); + EXPECT_TRUE(Sem().Get(c_af)->ConstantValue()->AllZero()); + EXPECT_TRUE(Sem().Get(c_vi32)->ConstantValue()->AllZero()); + EXPECT_TRUE(Sem().Get(c_vu32)->ConstantValue()->AllZero()); + EXPECT_TRUE(Sem().Get(c_vf32)->ConstantValue()->AllZero()); + EXPECT_TRUE(Sem().Get(c_vai)->ConstantValue()->AllZero()); + EXPECT_TRUE(Sem().Get(c_vaf)->ConstantValue()->AllZero()); + EXPECT_TRUE(Sem().Get(c_mf32)->ConstantValue()->AllZero()); + EXPECT_TRUE(Sem().Get(c_maf32)->ConstantValue()->AllZero()); } TEST_F(ResolverVariableTest, GlobalConst_PropagateConstValue) { @@ -1221,8 +1185,7 @@ TEST_F(ResolverVariableTest, GlobalConst_PropagateConstValue) { ASSERT_TRUE(TypeOf(c)->Is()); - ASSERT_EQ(Sem().Get(c)->ConstantValue().ElementCount(), 1u); - EXPECT_EQ(Sem().Get(c)->ConstantValue().Element(0), 42_i); + EXPECT_EQ(Sem().Get(c)->ConstantValue()->As(), 42_i); } // Enable when we have @const operators implemented @@ -1233,8 +1196,7 @@ TEST_F(ResolverVariableTest, DISABLED_GlobalConst_ConstEval) { ASSERT_TRUE(TypeOf(c)->Is()); - ASSERT_EQ(Sem().Get(c)->ConstantValue().ElementCount(), 1u); - EXPECT_EQ(Sem().Get(c)->ConstantValue().Element(0), 3_i); + EXPECT_EQ(Sem().Get(c)->ConstantValue()->As(), 3_i); } //////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/tint/sem/call.cc b/src/tint/sem/call.cc index f688cce22f..bfce3b1c53 100644 --- a/src/tint/sem/call.cc +++ b/src/tint/sem/call.cc @@ -25,9 +25,9 @@ Call::Call(const ast::CallExpression* declaration, const CallTarget* target, std::vector arguments, const Statement* statement, - Constant constant, + const Constant* constant, bool has_side_effects) - : Base(declaration, target->ReturnType(), statement, std::move(constant), has_side_effects), + : Base(declaration, target->ReturnType(), statement, constant, has_side_effects), target_(target), arguments_(std::move(arguments)) {} diff --git a/src/tint/sem/call.h b/src/tint/sem/call.h index e27f388888..1955bf9640 100644 --- a/src/tint/sem/call.h +++ b/src/tint/sem/call.h @@ -38,7 +38,7 @@ class Call final : public Castable { const CallTarget* target, std::vector arguments, const Statement* statement, - Constant constant, + const Constant* constant, bool has_side_effects); /// Destructor diff --git a/src/tint/sem/constant.cc b/src/tint/sem/constant.cc index ed38686e31..70bb08c5b5 100644 --- a/src/tint/sem/constant.cc +++ b/src/tint/sem/constant.cc @@ -14,102 +14,10 @@ #include "src/tint/sem/constant.h" -#include -#include - -#include "src/tint/debug.h" -#include "src/tint/program_builder.h" -#include "src/tint/sem/type.h" - namespace tint::sem { -namespace { -size_t CountElements(const Constant::Elements& elements) { - return std::visit([](auto&& vec) { return vec.size(); }, elements); -} - -template -bool IsNegativeFloat(T value) { - (void)value; - if constexpr (IsFloatingPoint) { - return std::signbit(value); - } else { - return false; - } -} - -} // namespace - -Constant::Constant() {} - -Constant::Constant(const sem::Type* ty, Elements els) - : type_(ty), elem_type_(CheckElemType(ty, CountElements(els))), elems_(std::move(els)) {} - -Constant::Constant(const sem::Type* ty, AInts vec) : Constant(ty, Elements{std::move(vec)}) {} - -Constant::Constant(const sem::Type* ty, AFloats vec) : Constant(ty, Elements{std::move(vec)}) {} - -Constant::Constant(const Constant&) = default; +Constant::Constant() = default; Constant::~Constant() = default; -Constant& Constant::operator=(const Constant& rhs) = default; - -bool Constant::AnyZero() const { - return WithElements([&](auto&& vec) { - using T = typename std::decay_t::value_type; - for (auto el : vec) { - if (el == T(0) && !IsNegativeFloat(el.value)) { - return true; - } - } - return false; - }); -} - -bool Constant::AllZero(size_t start, size_t end) const { - return WithElements([&](auto&& vec) { - using T = typename std::decay_t::value_type; - for (size_t i = start; i < end; i++) { - auto el = vec[i]; - if (el != T(0) || IsNegativeFloat(el.value)) { - return false; - } - } - return true; - }); -} - -bool Constant::AllEqual(size_t start, size_t end) const { - return WithElements([&](auto&& vec) { - if (!vec.empty()) { - auto value = vec[start]; - bool float_sign = IsNegativeFloat(vec[start].value); - for (size_t i = start + 1; i < end; i++) { - if (vec[i] != value || float_sign != IsNegativeFloat(vec[i].value)) { - return false; - } - } - } - return true; - }); -} - -const Type* Constant::CheckElemType(const sem::Type* ty, size_t num_elements) { - diag::List diag; - uint32_t count = 0; - auto* el_ty = Type::DeepestElementOf(ty, &count); - if (!el_ty) { - TINT_ICE(Semantic, diag) << "Unsupported sem::Constant type: " << ty->TypeInfo().name; - return nullptr; - } - if (num_elements != count) { - TINT_ICE(Semantic, diag) << "sem::Constant() type <-> element mismatch. type: '" - << ty->TypeInfo().name << "' provided: " << num_elements - << " require: " << count; - } - TINT_ASSERT(Semantic, el_ty->is_abstract_or_scalar()); - return el_ty; -} - } // namespace tint::sem diff --git a/src/tint/sem/constant.h b/src/tint/sem/constant.h index cd73a79347..b46127c833 100644 --- a/src/tint/sem/constant.h +++ b/src/tint/sem/constant.h @@ -15,10 +15,7 @@ #ifndef SRC_TINT_SEM_CONSTANT_H_ #define SRC_TINT_SEM_CONSTANT_H_ -#include -#include #include -#include #include "src/tint/number.h" @@ -29,167 +26,53 @@ class Type; namespace tint::sem { -/// A Constant holds a compile-time evaluated expression value, expressed as a flattened list of -/// element values. The expression type may be of an abstract-numeric, scalar, vector or matrix -/// type. Constant holds the element values in either a vector of abstract-integer (AInt) or -/// abstract-float (AFloat), depending on the element type. +/// Constant is the interface to a compile-time evaluated expression value. class Constant { public: - /// AInts is a vector of AInt, used to hold elements of the WGSL types: - /// * abstract-integer - /// * i32 - /// * u32 - /// * bool (0 or 1) - using AInts = std::vector; - - /// AFloats is a vector of AFloat, used to hold elements of the WGSL types: - /// * abstract-float - /// * f32 - /// * f16 - using AFloats = std::vector; - - /// Elements is either a vector of AInts or AFloats - using Elements = std::variant; - - /// Helper that resolves to either AInt or AFloat based on the element type T. - template - using ElementFor = std::conditional_t>, AFloat, AInt>; - - /// Helper that resolves to either AInts or AFloats based on the element type T. - template - using ElementVectorFor = std::conditional_t>, AFloats, AInts>; - - /// Constructs an invalid Constant + /// Constructor Constant(); - /// Constructs a Constant of the given type and element values - /// @param ty the Constant type - /// @param els the Constant element values - Constant(const sem::Type* ty, Elements els); - - /// Constructs a Constant of the given type and element values - /// @param ty the Constant type - /// @param vec the Constant element values - Constant(const sem::Type* ty, AInts vec); - - /// Constructs a Constant of the given type and element values - /// @param ty the Constant type - /// @param vec the Constant element values - Constant(const sem::Type* ty, AFloats vec); - - /// Constructs a Constant of the given type and element values - /// @param ty the Constant type - /// @param els the Constant element values - template - Constant(const sem::Type* ty, std::initializer_list els); - - /// Copy constructor - Constant(const Constant&); - /// Destructor - ~Constant(); + virtual ~Constant(); - /// Copy assignment - /// @param other the Constant to copy - /// @returns this Constant - Constant& operator=(const Constant& other); + /// @returns the type of the constant + virtual const sem::Type* Type() const = 0; - /// @returns true if the Constant has been initialized - bool IsValid() const { return type_ != nullptr; } + /// @returns the value of this Constant, if this constant is of a scalar value or abstract + /// numeric, otherwsie std::monostate. + virtual std::variant Value() const = 0; - /// @return true if the Constant has been initialized - operator bool() const { return IsValid(); } + /// @returns the child constant element with the given index, or nullptr if the constant has no + /// children, or the index is out of bounds. + virtual const Constant* Index(size_t) const = 0; - /// @returns the type of the Constant - const sem::Type* Type() const { return type_; } + /// @returns true if child elements of this constant are positive-zero valued. + virtual bool AllZero() const = 0; - /// @returns the number of elements - size_t ElementCount() const { - return std::visit([](auto&& v) { return v.size(); }, elems_); - } + /// @returns true if any child elements of this constant are positive-zero valued. + virtual bool AnyZero() const = 0; - /// @returns the flattened element type of the Constant - const sem::Type* ElementType() const { return elem_type_; } + /// @returns true if all child elements of this constant have the same value and type. + virtual bool AllEqual() const = 0; - /// @returns the constant's flattened elements - const Elements& GetElements() const { return elems_; } + /// @returns a hash of the constant. + virtual size_t Hash() const = 0; - /// WithElements calls the function `f` with the vector of elements as either AFloats or AInts - /// @param f a function-like with the signature `R(auto&&)`. - /// @returns the result of calling `f`. - template - auto WithElements(F&& f) const { - return std::visit(std::forward(f), elems_); - } - - /// WithElements calls the function `f` with the element vector as either AFloats or AInts - /// @param f a function-like with the signature `R(auto&&)`. - /// @returns the result of calling `f`. - template - auto WithElements(F&& f) { - return std::visit(std::forward(f), elems_); - } - - /// @returns the elements as a vector of AInt - inline const AInts& IElements() const { return std::get(elems_); } - - /// @returns the elements as a vector of AFloat - inline const AFloats& FElements() const { return std::get(elems_); } - - /// @returns true if any element is positive zero - bool AnyZero() const; - - /// @returns true if all elements are positive zero - bool AllZero() const { return AllZero(0, ElementCount()); } - - /// @returns true if all elements are the same value, with the same sign-bit. - bool AllEqual() const { return AllEqual(0, ElementCount()); } - - /// @param start the first element index - /// @param end one past the last element index - /// @returns true if all elements between `[start, end)` are zero - bool AllZero(size_t start, size_t end) const; - - /// @param start the first element index - /// @param end one past the last element index - /// @returns true if all elements between `[start, end)` are the same value - bool AllEqual(size_t start, size_t end) const; - - /// @param index the index of the element - /// @return the element at `index`, which must be of type `T`. + /// @returns the value of the constant as the given scalar or abstract value. template - T Element(size_t index) const; - - private: - /// Checks that the provided type matches the number of expected elements. - /// @returns the element type of `ty`. - const sem::Type* CheckElemType(const sem::Type* ty, size_t num_elements); - - const sem::Type* type_ = nullptr; - const sem::Type* elem_type_ = nullptr; - Elements elems_; + T As() const { + return std::visit( + [](auto v) { + if constexpr (std::is_same_v) { + return T(0); + } else { + return static_cast(v); + } + }, + Value()); + } }; -template -Constant::Constant(const sem::Type* ty, std::initializer_list els) - : type_(ty), elem_type_(CheckElemType(type_, els.size())) { - ElementVectorFor elements; - elements.reserve(els.size()); - for (auto el : els) { - elements.emplace_back(ElementFor(el)); - } - elems_ = Elements{std::move(elements)}; -} - -template -T Constant::Element(size_t index) const { - if constexpr (std::is_same_v, AFloats>) { - return static_cast(FElements()[index].value); - } else { - return static_cast(IElements()[index].value); - } -} - } // namespace tint::sem #endif // SRC_TINT_SEM_CONSTANT_H_ diff --git a/src/tint/sem/constant_test.cc b/src/tint/sem/constant_test.cc deleted file mode 100644 index e47e9a153a..0000000000 --- a/src/tint/sem/constant_test.cc +++ /dev/null @@ -1,540 +0,0 @@ -// Copyright 2022 The Tint Authors. -// -// 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 "src/tint/sem/constant.h" - -#include - -#include "src/tint/sem/abstract_float.h" -#include "src/tint/sem/abstract_int.h" -#include "src/tint/sem/test_helper.h" - -using namespace tint::number_suffixes; // NOLINT - -namespace tint::sem { -namespace { - -struct ConstantTest : public TestHelper { - const sem::Array* Array(uint32_t n, const sem::Type* el_ty) { - return create(el_ty, - /* count */ n, - /* align */ 16u, - /* size */ 4u * n, - /* stride */ 16u * n, - /* implicit_stride */ 16u * n); - } -}; - -TEST_F(ConstantTest, ConstructorInitializerList) { - { - auto i = AInt(AInt::kHighest); - Constant c(create(), {i}); - c.WithElements([&](auto&& vec) { EXPECT_THAT(vec, testing::ElementsAre(i)); }); - } - { - auto i = i32(i32::kHighest); - Constant c(create(), {i}); - c.WithElements([&](auto&& vec) { EXPECT_THAT(vec, testing::ElementsAre(i)); }); - } - { - auto i = u32(u32::kHighest); - Constant c(create(), {i}); - c.WithElements([&](auto&& vec) { EXPECT_THAT(vec, testing::ElementsAre(i)); }); - } - { - Constant c(create(), {false}); - c.WithElements([&](auto&& vec) { EXPECT_THAT(vec, testing::ElementsAre(0_a)); }); - } - { - Constant c(create(), {true}); - c.WithElements([&](auto&& vec) { EXPECT_THAT(vec, testing::ElementsAre(1_a)); }); - } - { - auto f = AFloat(AFloat::kHighest); - Constant c(create(), {f}); - c.WithElements([&](auto&& vec) { EXPECT_THAT(vec, testing::ElementsAre(f)); }); - } - { - auto f = f32(f32::kHighest); - Constant c(create(), {f}); - c.WithElements([&](auto&& vec) { EXPECT_THAT(vec, testing::ElementsAre(f)); }); - } - { - auto f = f16(f16::kHighest); - Constant c(create(), {f}); - c.WithElements([&](auto&& vec) { EXPECT_THAT(vec, testing::ElementsAre(f)); }); - } -} - -TEST_F(ConstantTest, Element_ai) { - auto* ty = create(); - Constant c(ty, {1_a}); - EXPECT_EQ(c.Element(0), 1_a); - EXPECT_EQ(c.ElementCount(), 1u); - EXPECT_TYPE(c.Type(), ty); - EXPECT_TYPE(c.ElementType(), ty); -} - -TEST_F(ConstantTest, Element_i32) { - auto* ty = create(); - Constant c(ty, {1_a}); - EXPECT_EQ(c.Element(0), 1_i); - EXPECT_EQ(c.ElementCount(), 1u); - EXPECT_TYPE(c.Type(), ty); - EXPECT_TYPE(c.ElementType(), ty); -} - -TEST_F(ConstantTest, Element_u32) { - auto* ty = create(); - Constant c(ty, {1_a}); - EXPECT_EQ(c.Element(0), 1_u); - EXPECT_EQ(c.ElementCount(), 1u); - EXPECT_TYPE(c.Type(), ty); - EXPECT_TYPE(c.ElementType(), ty); -} - -TEST_F(ConstantTest, Element_bool) { - auto* ty = create(); - Constant c(ty, {true}); - EXPECT_EQ(c.Element(0), true); - EXPECT_EQ(c.ElementCount(), 1u); - EXPECT_TYPE(c.Type(), ty); - EXPECT_TYPE(c.ElementType(), ty); -} - -TEST_F(ConstantTest, Element_af) { - auto* ty = create(); - Constant c(ty, {1.0_a}); - EXPECT_EQ(c.Element(0), 1.0_a); - EXPECT_EQ(c.ElementCount(), 1u); - EXPECT_TYPE(c.Type(), ty); - EXPECT_TYPE(c.ElementType(), ty); -} - -TEST_F(ConstantTest, Element_f32) { - auto* ty = create(); - Constant c(ty, {1.0_a}); - EXPECT_EQ(c.Element(0), 1.0_f); - EXPECT_EQ(c.ElementCount(), 1u); - EXPECT_TYPE(c.Type(), ty); - EXPECT_TYPE(c.ElementType(), ty); -} - -TEST_F(ConstantTest, Element_f16) { - auto* ty = create(); - Constant c(ty, {1.0_a}); - EXPECT_EQ(c.Element(0), 1.0_h); - EXPECT_EQ(c.ElementCount(), 1u); - EXPECT_TYPE(c.Type(), ty); - EXPECT_TYPE(c.ElementType(), ty); -} - -TEST_F(ConstantTest, Element_vec3_ai) { - auto* el_ty = create(); - auto* ty = create(el_ty, 3u); - Constant c(ty, {1_a, 2_a, 3_a}); - EXPECT_EQ(c.Element(0), 1_a); - EXPECT_EQ(c.Element(1), 2_a); - EXPECT_EQ(c.Element(2), 3_a); - EXPECT_EQ(c.ElementCount(), 3u); - EXPECT_TYPE(c.Type(), ty); - EXPECT_TYPE(c.ElementType(), el_ty); -} - -TEST_F(ConstantTest, Element_vec3_i32) { - auto* el_ty = create(); - auto* ty = create(el_ty, 3u); - Constant c(ty, {1_a, 2_a, 3_a}); - EXPECT_EQ(c.Element(0), 1_i); - EXPECT_EQ(c.Element(1), 2_i); - EXPECT_EQ(c.Element(2), 3_i); - EXPECT_EQ(c.ElementCount(), 3u); - EXPECT_TYPE(c.Type(), ty); - EXPECT_TYPE(c.ElementType(), el_ty); -} - -TEST_F(ConstantTest, Element_vec3_u32) { - auto* el_ty = create(); - auto* ty = create(el_ty, 3u); - Constant c(ty, {1_a, 2_a, 3_a}); - EXPECT_EQ(c.Element(0), 1_u); - EXPECT_EQ(c.Element(1), 2_u); - EXPECT_EQ(c.Element(2), 3_u); - EXPECT_EQ(c.ElementCount(), 3u); - EXPECT_TYPE(c.Type(), ty); - EXPECT_TYPE(c.ElementType(), el_ty); -} - -TEST_F(ConstantTest, Element_vec3_bool) { - auto* el_ty = create(); - auto* ty = create(el_ty, 2u); - Constant c(ty, {true, false}); - EXPECT_EQ(c.Element(0), true); - EXPECT_EQ(c.Element(1), false); - EXPECT_EQ(c.ElementCount(), 2u); - EXPECT_TYPE(c.Type(), ty); - EXPECT_TYPE(c.ElementType(), el_ty); -} - -TEST_F(ConstantTest, Element_vec3_af) { - auto* el_ty = create(); - auto* ty = create(el_ty, 3u); - Constant c(ty, {1.0_a, 2.0_a, 3.0_a}); - EXPECT_EQ(c.Element(0), 1.0_a); - EXPECT_EQ(c.Element(1), 2.0_a); - EXPECT_EQ(c.Element(2), 3.0_a); - EXPECT_EQ(c.ElementCount(), 3u); - EXPECT_TYPE(c.Type(), ty); - EXPECT_TYPE(c.ElementType(), el_ty); -} - -TEST_F(ConstantTest, Element_vec3_f32) { - auto* el_ty = create(); - auto* ty = create(el_ty, 3u); - Constant c(ty, {1.0_a, 2.0_a, 3.0_a}); - EXPECT_EQ(c.Element(0), 1.0_f); - EXPECT_EQ(c.Element(1), 2.0_f); - EXPECT_EQ(c.Element(2), 3.0_f); - EXPECT_EQ(c.ElementCount(), 3u); - EXPECT_TYPE(c.Type(), ty); - EXPECT_TYPE(c.ElementType(), el_ty); -} - -TEST_F(ConstantTest, Element_vec3_f16) { - auto* el_ty = create(); - auto* ty = create(el_ty, 3u); - Constant c(ty, {1.0_a, 2.0_a, 3.0_a}); - EXPECT_EQ(c.Element(0), 1.0_h); - EXPECT_EQ(c.Element(1), 2.0_h); - EXPECT_EQ(c.Element(2), 3.0_h); - EXPECT_EQ(c.ElementCount(), 3u); - EXPECT_TYPE(c.Type(), ty); - EXPECT_TYPE(c.ElementType(), el_ty); -} - -TEST_F(ConstantTest, Element_mat2x3_af) { - auto* el_ty = create(); - auto* ty = create(create(el_ty, 3u), 2u); - Constant c(ty, {1.0_a, 2.0_a, 3.0_a, 4.0_a, 5.0_a, 6.0_a}); - EXPECT_EQ(c.Element(0), 1.0_a); - EXPECT_EQ(c.Element(1), 2.0_a); - EXPECT_EQ(c.Element(2), 3.0_a); - EXPECT_EQ(c.Element(3), 4.0_a); - EXPECT_EQ(c.Element(4), 5.0_a); - EXPECT_EQ(c.Element(5), 6.0_a); - EXPECT_EQ(c.ElementCount(), 6u); - EXPECT_TYPE(c.Type(), ty); - EXPECT_TYPE(c.ElementType(), el_ty); -} - -TEST_F(ConstantTest, Element_mat2x3_f32) { - auto* el_ty = create(); - auto* ty = create(create(el_ty, 3u), 2u); - Constant c(ty, {1.0_a, 2.0_a, 3.0_a, 4.0_a, 5.0_a, 6.0_a}); - EXPECT_EQ(c.Element(0), 1.0_f); - EXPECT_EQ(c.Element(1), 2.0_f); - EXPECT_EQ(c.Element(2), 3.0_f); - EXPECT_EQ(c.Element(3), 4.0_f); - EXPECT_EQ(c.Element(4), 5.0_f); - EXPECT_EQ(c.Element(5), 6.0_f); - EXPECT_EQ(c.ElementCount(), 6u); - EXPECT_TYPE(c.Type(), ty); - EXPECT_TYPE(c.ElementType(), el_ty); -} - -TEST_F(ConstantTest, Element_mat2x3_f16) { - auto* el_ty = create(); - auto* ty = create(create(el_ty, 3u), 2u); - Constant c(ty, {1.0_a, 2.0_a, 3.0_a, 4.0_a, 5.0_a, 6.0_a}); - EXPECT_EQ(c.Element(0), 1.0_h); - EXPECT_EQ(c.Element(1), 2.0_h); - EXPECT_EQ(c.Element(2), 3.0_h); - EXPECT_EQ(c.Element(3), 4.0_h); - EXPECT_EQ(c.Element(4), 5.0_h); - EXPECT_EQ(c.Element(5), 6.0_h); - EXPECT_EQ(c.ElementCount(), 6u); - EXPECT_TYPE(c.Type(), ty); - EXPECT_TYPE(c.ElementType(), el_ty); -} - -TEST_F(ConstantTest, Element_arr_vec3_ai) { - auto* el_ty = create(); - auto* ty = Array(2, create(el_ty, 3u)); - Constant c(ty, {1_a, 2_a, 3_a, 4_a, 5_a, 6_a}); - EXPECT_EQ(c.Element(0), 1_a); - EXPECT_EQ(c.Element(1), 2_a); - EXPECT_EQ(c.Element(2), 3_a); - EXPECT_EQ(c.Element(3), 4_a); - EXPECT_EQ(c.Element(4), 5_a); - EXPECT_EQ(c.Element(5), 6_a); - EXPECT_EQ(c.ElementCount(), 6u); - EXPECT_TYPE(c.Type(), ty); - EXPECT_TYPE(c.ElementType(), el_ty); -} - -TEST_F(ConstantTest, Element_arr_vec3_i32) { - auto* el_ty = create(); - auto* ty = Array(2, create(el_ty, 3u)); - Constant c(ty, {1_a, 2_a, 3_a, 4_a, 5_a, 6_a}); - EXPECT_EQ(c.Element(0), 1_i); - EXPECT_EQ(c.Element(1), 2_i); - EXPECT_EQ(c.Element(2), 3_i); - EXPECT_EQ(c.Element(3), 4_i); - EXPECT_EQ(c.Element(4), 5_i); - EXPECT_EQ(c.Element(5), 6_i); - EXPECT_EQ(c.ElementCount(), 6u); - EXPECT_TYPE(c.Type(), ty); - EXPECT_TYPE(c.ElementType(), el_ty); -} - -TEST_F(ConstantTest, Element_arr_vec3_u32) { - auto* el_ty = create(); - auto* ty = Array(2, create(el_ty, 3u)); - Constant c(ty, {1_a, 2_a, 3_a, 4_a, 5_a, 6_a}); - EXPECT_EQ(c.Element(0), 1_u); - EXPECT_EQ(c.Element(1), 2_u); - EXPECT_EQ(c.Element(2), 3_u); - EXPECT_EQ(c.Element(3), 4_u); - EXPECT_EQ(c.Element(4), 5_u); - EXPECT_EQ(c.Element(5), 6_u); - EXPECT_EQ(c.ElementCount(), 6u); - EXPECT_TYPE(c.Type(), ty); - EXPECT_TYPE(c.ElementType(), el_ty); -} - -TEST_F(ConstantTest, Element_arr_vec3_bool) { - auto* el_ty = create(); - auto* ty = Array(2, create(el_ty, 2u)); - Constant c(ty, {true, false, false, true}); - EXPECT_EQ(c.Element(0), true); - EXPECT_EQ(c.Element(1), false); - EXPECT_EQ(c.Element(2), false); - EXPECT_EQ(c.Element(3), true); - EXPECT_EQ(c.ElementCount(), 4u); - EXPECT_TYPE(c.Type(), ty); - EXPECT_TYPE(c.ElementType(), el_ty); -} - -TEST_F(ConstantTest, Element_arr_vec3_af) { - auto* el_ty = create(); - auto* ty = Array(2, create(el_ty, 3u)); - Constant c(ty, {1.0_a, 2.0_a, 3.0_a, 4.0_a, 5.0_a, 6.0_a}); - EXPECT_EQ(c.Element(0), 1.0_a); - EXPECT_EQ(c.Element(1), 2.0_a); - EXPECT_EQ(c.Element(2), 3.0_a); - EXPECT_EQ(c.Element(3), 4.0_a); - EXPECT_EQ(c.Element(4), 5.0_a); - EXPECT_EQ(c.Element(5), 6.0_a); - EXPECT_EQ(c.ElementCount(), 6u); - EXPECT_TYPE(c.Type(), ty); - EXPECT_TYPE(c.ElementType(), el_ty); -} - -TEST_F(ConstantTest, Element_arr_vec3_f32) { - auto* el_ty = create(); - auto* ty = Array(2, create(el_ty, 3u)); - Constant c(ty, {1.0_a, 2.0_a, 3.0_a, 4.0_a, 5.0_a, 6.0_a}); - EXPECT_EQ(c.Element(0), 1.0_f); - EXPECT_EQ(c.Element(1), 2.0_f); - EXPECT_EQ(c.Element(2), 3.0_f); - EXPECT_EQ(c.Element(3), 4.0_f); - EXPECT_EQ(c.Element(4), 5.0_f); - EXPECT_EQ(c.Element(5), 6.0_f); - EXPECT_EQ(c.ElementCount(), 6u); - EXPECT_TYPE(c.Type(), ty); - EXPECT_TYPE(c.ElementType(), el_ty); -} - -TEST_F(ConstantTest, Element_arr_vec3_f16) { - auto* el_ty = create(); - auto* ty = Array(2, create(el_ty, 3u)); - Constant c(ty, {1.0_a, 2.0_a, 3.0_a, 4.0_a, 5.0_a, 6.0_a}); - EXPECT_EQ(c.Element(0), 1.0_h); - EXPECT_EQ(c.Element(1), 2.0_h); - EXPECT_EQ(c.Element(2), 3.0_h); - EXPECT_EQ(c.Element(3), 4.0_h); - EXPECT_EQ(c.Element(4), 5.0_h); - EXPECT_EQ(c.Element(5), 6.0_h); - EXPECT_EQ(c.ElementCount(), 6u); - EXPECT_TYPE(c.Type(), ty); - EXPECT_TYPE(c.ElementType(), el_ty); -} - -TEST_F(ConstantTest, Element_arr_arr_mat2x3_f32) { - auto* el_ty = create(); - auto* ty = Array(2, Array(2, create(create(el_ty, 3u), 2u))); - Constant c(ty, { - 1.0_a, 2.0_a, 3.0_a, // - 4.0_a, 5.0_a, 6.0_a, // - - 7.0_a, 8.0_a, 9.0_a, // - 10.0_a, 11.0_a, 12.0_a, // - - 13.0_a, 14.0_a, 15.0_a, // - 16.0_a, 17.0_a, 18.0_a, // - - 19.0_a, 20.0_a, 21.0_a, // - 22.0_a, 23.0_a, 24.0_a, // - }); - for (size_t i = 0; i < 24; i++) { - EXPECT_EQ(c.Element(i), f32(i + 1)); - } - EXPECT_EQ(c.ElementCount(), 24u); - EXPECT_TYPE(c.Type(), ty); - EXPECT_TYPE(c.ElementType(), el_ty); -} - -TEST_F(ConstantTest, AnyZero) { - auto* vec3_ai = create(create(), 3u); - EXPECT_EQ(Constant(vec3_ai, {1_a, 2_a, 3_a}).AnyZero(), false); - EXPECT_EQ(Constant(vec3_ai, {0_a, 2_a, 3_a}).AnyZero(), true); - EXPECT_EQ(Constant(vec3_ai, {1_a, 0_a, 3_a}).AnyZero(), true); - EXPECT_EQ(Constant(vec3_ai, {1_a, 2_a, 0_a}).AnyZero(), true); - EXPECT_EQ(Constant(vec3_ai, {0_a, 0_a, 0_a}).AnyZero(), true); - - auto* vec3_af = create(create(), 3u); - EXPECT_EQ(Constant(vec3_af, {1._a, 2._a, 3._a}).AnyZero(), false); - EXPECT_EQ(Constant(vec3_af, {0._a, 2._a, 3._a}).AnyZero(), true); - EXPECT_EQ(Constant(vec3_af, {1._a, 0._a, 3._a}).AnyZero(), true); - EXPECT_EQ(Constant(vec3_af, {1._a, 2._a, 0._a}).AnyZero(), true); - EXPECT_EQ(Constant(vec3_af, {0._a, 0._a, 0._a}).AnyZero(), true); - - EXPECT_EQ(Constant(vec3_af, {1._a, -2._a, 3._a}).AnyZero(), false); - EXPECT_EQ(Constant(vec3_af, {0._a, -2._a, 3._a}).AnyZero(), true); - EXPECT_EQ(Constant(vec3_af, {1._a, -0._a, 3._a}).AnyZero(), false); - EXPECT_EQ(Constant(vec3_af, {1._a, -2._a, 0._a}).AnyZero(), true); - EXPECT_EQ(Constant(vec3_af, {0._a, -0._a, 0._a}).AnyZero(), true); - EXPECT_EQ(Constant(vec3_af, {-0._a, -0._a, -0._a}).AnyZero(), false); -} - -TEST_F(ConstantTest, AllZero) { - auto* vec3_ai = create(create(), 3u); - EXPECT_EQ(Constant(vec3_ai, {1_a, 2_a, 3_a}).AllZero(), false); - EXPECT_EQ(Constant(vec3_ai, {0_a, 2_a, 3_a}).AllZero(), false); - EXPECT_EQ(Constant(vec3_ai, {1_a, 0_a, 3_a}).AllZero(), false); - EXPECT_EQ(Constant(vec3_ai, {1_a, 2_a, 0_a}).AllZero(), false); - EXPECT_EQ(Constant(vec3_ai, {0_a, 0_a, 0_a}).AllZero(), true); - - auto* vec3_af = create(create(), 3u); - EXPECT_EQ(Constant(vec3_af, {1._a, 2._a, 3._a}).AllZero(), false); - EXPECT_EQ(Constant(vec3_af, {0._a, 2._a, 3._a}).AllZero(), false); - EXPECT_EQ(Constant(vec3_af, {1._a, 0._a, 3._a}).AllZero(), false); - EXPECT_EQ(Constant(vec3_af, {1._a, 2._a, 0._a}).AllZero(), false); - EXPECT_EQ(Constant(vec3_af, {0._a, 0._a, 0._a}).AllZero(), true); - - EXPECT_EQ(Constant(vec3_af, {1._a, -2._a, 3._a}).AllZero(), false); - EXPECT_EQ(Constant(vec3_af, {0._a, -2._a, 3._a}).AllZero(), false); - EXPECT_EQ(Constant(vec3_af, {1._a, -0._a, 3._a}).AllZero(), false); - EXPECT_EQ(Constant(vec3_af, {1._a, -2._a, 0._a}).AllZero(), false); - EXPECT_EQ(Constant(vec3_af, {0._a, -0._a, 0._a}).AllZero(), false); - EXPECT_EQ(Constant(vec3_af, {-0._a, -0._a, -0._a}).AllZero(), false); -} - -TEST_F(ConstantTest, AllEqual) { - auto* vec3_ai = create(create(), 3u); - EXPECT_EQ(Constant(vec3_ai, {1_a, 2_a, 3_a}).AllEqual(), false); - EXPECT_EQ(Constant(vec3_ai, {1_a, 1_a, 3_a}).AllEqual(), false); - EXPECT_EQ(Constant(vec3_ai, {1_a, 3_a, 3_a}).AllEqual(), false); - EXPECT_EQ(Constant(vec3_ai, {1_a, 1_a, 1_a}).AllEqual(), true); - EXPECT_EQ(Constant(vec3_ai, {2_a, 2_a, 2_a}).AllEqual(), true); - EXPECT_EQ(Constant(vec3_ai, {3_a, 3_a, 3_a}).AllEqual(), true); - EXPECT_EQ(Constant(vec3_ai, {0_a, 0_a, 0_a}).AllEqual(), true); - - auto* vec3_af = create(create(), 3u); - EXPECT_EQ(Constant(vec3_af, {1._a, 2._a, 3._a}).AllEqual(), false); - EXPECT_EQ(Constant(vec3_af, {1._a, 1._a, 3._a}).AllEqual(), false); - EXPECT_EQ(Constant(vec3_af, {1._a, 3._a, 3._a}).AllEqual(), false); - EXPECT_EQ(Constant(vec3_af, {1._a, 1._a, 1._a}).AllEqual(), true); - EXPECT_EQ(Constant(vec3_af, {2._a, 2._a, 2._a}).AllEqual(), true); - EXPECT_EQ(Constant(vec3_af, {3._a, 3._a, 3._a}).AllEqual(), true); - EXPECT_EQ(Constant(vec3_af, {0._a, 0._a, 0._a}).AllEqual(), true); - EXPECT_EQ(Constant(vec3_af, {0._a, -0._a, 0._a}).AllEqual(), false); -} - -TEST_F(ConstantTest, AllZeroRange) { - auto* vec3_ai = create(create(), 3u); - EXPECT_EQ(Constant(vec3_ai, {1_a, 2_a, 3_a}).AllZero(1, 3), false); - EXPECT_EQ(Constant(vec3_ai, {0_a, 2_a, 3_a}).AllZero(1, 3), false); - EXPECT_EQ(Constant(vec3_ai, {1_a, 2_a, 3_a}).AllZero(1, 3), false); - EXPECT_EQ(Constant(vec3_ai, {1_a, 2_a, 0_a}).AllZero(1, 3), false); - EXPECT_EQ(Constant(vec3_ai, {0_a, 0_a, 3_a}).AllZero(1, 3), false); - EXPECT_EQ(Constant(vec3_ai, {0_a, 2_a, 0_a}).AllZero(1, 3), false); - EXPECT_EQ(Constant(vec3_ai, {1_a, 0_a, 0_a}).AllZero(1, 3), true); - - EXPECT_EQ(Constant(vec3_ai, {1_a, 2_a, 3_a}).AllZero(0, 2), false); - EXPECT_EQ(Constant(vec3_ai, {0_a, 2_a, 3_a}).AllZero(0, 2), false); - EXPECT_EQ(Constant(vec3_ai, {1_a, 2_a, 3_a}).AllZero(0, 2), false); - EXPECT_EQ(Constant(vec3_ai, {1_a, 2_a, 0_a}).AllZero(0, 2), false); - EXPECT_EQ(Constant(vec3_ai, {0_a, 0_a, 3_a}).AllZero(0, 2), true); - EXPECT_EQ(Constant(vec3_ai, {0_a, 2_a, 0_a}).AllZero(0, 2), false); - EXPECT_EQ(Constant(vec3_ai, {1_a, 0_a, 0_a}).AllZero(0, 2), false); - - auto* vec3_af = create(create(), 3u); - EXPECT_EQ(Constant(vec3_af, {1._a, 2._a, 3._a}).AllZero(1, 3), false); - EXPECT_EQ(Constant(vec3_af, {0._a, 2._a, 3._a}).AllZero(1, 3), false); - EXPECT_EQ(Constant(vec3_af, {1._a, 2._a, 3._a}).AllZero(1, 3), false); - EXPECT_EQ(Constant(vec3_af, {1._a, 2._a, 0._a}).AllZero(1, 3), false); - EXPECT_EQ(Constant(vec3_af, {0._a, 0._a, 3._a}).AllZero(1, 3), false); - EXPECT_EQ(Constant(vec3_af, {0._a, 2._a, 0._a}).AllZero(1, 3), false); - EXPECT_EQ(Constant(vec3_af, {1._a, 0._a, 0._a}).AllZero(1, 3), true); - EXPECT_EQ(Constant(vec3_af, {1._a, -0._a, 0._a}).AllZero(1, 3), false); - EXPECT_EQ(Constant(vec3_af, {1._a, 0._a, -0._a}).AllZero(1, 3), false); - EXPECT_EQ(Constant(vec3_af, {1._a, -0._a, -0._a}).AllZero(1, 3), false); - - EXPECT_EQ(Constant(vec3_af, {1._a, 2._a, 3._a}).AllZero(0, 2), false); - EXPECT_EQ(Constant(vec3_af, {0._a, 2._a, 3._a}).AllZero(0, 2), false); - EXPECT_EQ(Constant(vec3_af, {1._a, 2._a, 3._a}).AllZero(0, 2), false); - EXPECT_EQ(Constant(vec3_af, {1._a, 2._a, 0._a}).AllZero(0, 2), false); - EXPECT_EQ(Constant(vec3_af, {0._a, 0._a, 3._a}).AllZero(0, 2), true); - EXPECT_EQ(Constant(vec3_af, {-0._a, 0._a, 1._a}).AllZero(0, 2), false); - EXPECT_EQ(Constant(vec3_af, {0._a, -0._a, 1._a}).AllZero(0, 2), false); - EXPECT_EQ(Constant(vec3_af, {-0._a, -0._a, 1._a}).AllZero(0, 2), false); - EXPECT_EQ(Constant(vec3_af, {0._a, 2._a, 0._a}).AllZero(0, 2), false); - EXPECT_EQ(Constant(vec3_af, {1._a, 0._a, 0._a}).AllZero(0, 2), false); -} - -TEST_F(ConstantTest, AllEqualRange) { - auto* vec3_ai = create(create(), 3u); - EXPECT_EQ(Constant(vec3_ai, {1_a, 2_a, 3_a}).AllEqual(1, 3), false); - EXPECT_EQ(Constant(vec3_ai, {1_a, 1_a, 3_a}).AllEqual(1, 3), false); - EXPECT_EQ(Constant(vec3_ai, {1_a, 3_a, 3_a}).AllEqual(1, 3), true); - EXPECT_EQ(Constant(vec3_ai, {1_a, 1_a, 1_a}).AllEqual(1, 3), true); - EXPECT_EQ(Constant(vec3_ai, {2_a, 2_a, 2_a}).AllEqual(1, 3), true); - EXPECT_EQ(Constant(vec3_ai, {2_a, 2_a, 3_a}).AllEqual(1, 3), false); - EXPECT_EQ(Constant(vec3_ai, {1_a, 0_a, 0_a}).AllEqual(1, 3), true); - EXPECT_EQ(Constant(vec3_ai, {0_a, 1_a, 0_a}).AllEqual(1, 3), false); - EXPECT_EQ(Constant(vec3_ai, {0_a, 0_a, 1_a}).AllEqual(1, 3), false); - EXPECT_EQ(Constant(vec3_ai, {0_a, 0_a, 0_a}).AllEqual(1, 3), true); - - auto* vec3_af = create(create(), 3u); - EXPECT_EQ(Constant(vec3_af, {1._a, 2._a, 3._a}).AllEqual(1, 3), false); - EXPECT_EQ(Constant(vec3_af, {1._a, 1._a, 3._a}).AllEqual(1, 3), false); - EXPECT_EQ(Constant(vec3_af, {1._a, 3._a, 3._a}).AllEqual(1, 3), true); - EXPECT_EQ(Constant(vec3_af, {1._a, 1._a, 1._a}).AllEqual(1, 3), true); - EXPECT_EQ(Constant(vec3_af, {2._a, 2._a, 2._a}).AllEqual(1, 3), true); - EXPECT_EQ(Constant(vec3_af, {2._a, 2._a, 3._a}).AllEqual(1, 3), false); - EXPECT_EQ(Constant(vec3_af, {1._a, 0._a, 0._a}).AllEqual(1, 3), true); - EXPECT_EQ(Constant(vec3_af, {0._a, 1._a, 0._a}).AllEqual(1, 3), false); - EXPECT_EQ(Constant(vec3_af, {0._a, 0._a, 1._a}).AllEqual(1, 3), false); - EXPECT_EQ(Constant(vec3_af, {0._a, 0._a, 0._a}).AllEqual(1, 3), true); - EXPECT_EQ(Constant(vec3_af, {1._a, -0._a, 0._a}).AllEqual(1, 3), false); - EXPECT_EQ(Constant(vec3_af, {0._a, -1._a, 0._a}).AllEqual(1, 3), false); - EXPECT_EQ(Constant(vec3_af, {0._a, -0._a, 1._a}).AllEqual(1, 3), false); - EXPECT_EQ(Constant(vec3_af, {0._a, -0._a, 0._a}).AllEqual(1, 3), false); - EXPECT_EQ(Constant(vec3_af, {0._a, -0._a, -0._a}).AllEqual(1, 3), true); - EXPECT_EQ(Constant(vec3_af, {-0._a, -0._a, -0._a}).AllEqual(1, 3), true); -} - -} // namespace -} // namespace tint::sem diff --git a/src/tint/sem/expression.cc b/src/tint/sem/expression.cc index 57ec68bf21..4415db5c48 100644 --- a/src/tint/sem/expression.cc +++ b/src/tint/sem/expression.cc @@ -25,7 +25,7 @@ namespace tint::sem { Expression::Expression(const ast::Expression* declaration, const sem::Type* type, const Statement* statement, - Constant constant, + const Constant* constant, bool has_side_effects, const Variable* source_var /* = nullptr */) : declaration_(declaration), diff --git a/src/tint/sem/expression.h b/src/tint/sem/expression.h index a7838516bb..05e5dacf30 100644 --- a/src/tint/sem/expression.h +++ b/src/tint/sem/expression.h @@ -41,7 +41,7 @@ class Expression : public Castable { Expression(const ast::Expression* declaration, const sem::Type* type, const Statement* statement, - Constant constant, + const Constant* constant, bool has_side_effects, const Variable* source_var = nullptr); @@ -58,7 +58,7 @@ class Expression : public Castable { const Statement* Stmt() const { return statement_; } /// @return the constant value of this expression - const Constant& ConstantValue() const { return constant_; } + const Constant* ConstantValue() const { return constant_; } /// Returns the variable or parameter that this expression derives from. /// For reference and pointer expressions, this will either be the originating @@ -88,7 +88,7 @@ class Expression : public Castable { private: const sem::Type* const type_; const Statement* const statement_; - const Constant constant_; + const Constant* const constant_; sem::Behaviors behaviors_{sem::Behavior::kNext}; const bool has_side_effects_; }; diff --git a/src/tint/sem/expression_test.cc b/src/tint/sem/expression_test.cc index fc1adeb88d..cc4bf0e3a8 100644 --- a/src/tint/sem/expression_test.cc +++ b/src/tint/sem/expression_test.cc @@ -23,13 +23,30 @@ using namespace tint::number_suffixes; // NOLINT namespace tint::sem { namespace { +class MockConstant : public sem::Constant { + public: + explicit MockConstant(const sem::Type* ty) : type(ty) {} + ~MockConstant() override {} + const sem::Type* Type() const override { return type; } + std::variant Value() const override { return {}; } + const Constant* Index(size_t) const override { return {}; } + bool AllZero() const override { return {}; } + bool AnyZero() const override { return {}; } + bool AllEqual() const override { return {}; } + size_t Hash() const override { return 0; } + + private: + const sem::Type* type; +}; + using ExpressionTest = TestHelper; TEST_F(ExpressionTest, UnwrapMaterialize) { + MockConstant c(create()); auto* a = create(/* declaration */ nullptr, create(), /* statement */ nullptr, - Constant{}, + /* constant_value */ nullptr, /* has_side_effects */ false, /* source_var */ nullptr); - auto* b = create(a, /* statement */ nullptr, Constant{create(), {1_a}}); + auto* b = create(a, /* statement */ nullptr, &c); EXPECT_EQ(a, a->UnwrapMaterialize()); EXPECT_EQ(a, b->UnwrapMaterialize()); diff --git a/src/tint/sem/index_accessor_expression.cc b/src/tint/sem/index_accessor_expression.cc index 06f0990217..cd74201cef 100644 --- a/src/tint/sem/index_accessor_expression.cc +++ b/src/tint/sem/index_accessor_expression.cc @@ -27,7 +27,7 @@ IndexAccessorExpression::IndexAccessorExpression(const ast::IndexAccessorExpress const Expression* object, const Expression* index, const Statement* statement, - Constant constant, + const Constant* constant, bool has_side_effects, const Variable* source_var /* = nullptr */) : Base(declaration, type, statement, constant, has_side_effects, source_var), diff --git a/src/tint/sem/index_accessor_expression.h b/src/tint/sem/index_accessor_expression.h index c77f55db02..233b0fa4ef 100644 --- a/src/tint/sem/index_accessor_expression.h +++ b/src/tint/sem/index_accessor_expression.h @@ -43,7 +43,7 @@ class IndexAccessorExpression final : public CastableDeclaration(), - /* type */ constant.Type(), + /* type */ constant->Type(), /* statement */ statement, /* constant */ constant, /* has_side_effects */ false, /* source_var */ expr->SourceVariable()), - expr_(expr) { - // Materialize nodes only wrap compile-time expressions, and so the Materialize expression must - // have a constant value. - TINT_ASSERT(Semantic, constant.IsValid()); -} + expr_(expr) {} Materialize::~Materialize() = default; diff --git a/src/tint/sem/materialize.h b/src/tint/sem/materialize.h index a7c0e3af5e..0cbac29ef6 100644 --- a/src/tint/sem/materialize.h +++ b/src/tint/sem/materialize.h @@ -31,7 +31,7 @@ class Materialize final : public Castable { /// @param expr the inner expression, being materialized /// @param statement the statement that owns this expression /// @param constant the constant value of this expression - Materialize(const Expression* expr, const Statement* statement, Constant constant); + Materialize(const Expression* expr, const Statement* statement, const Constant* constant); /// Destructor ~Materialize() override; diff --git a/src/tint/sem/member_accessor_expression.cc b/src/tint/sem/member_accessor_expression.cc index bd706a510e..f9929c74c2 100644 --- a/src/tint/sem/member_accessor_expression.cc +++ b/src/tint/sem/member_accessor_expression.cc @@ -29,8 +29,7 @@ MemberAccessorExpression::MemberAccessorExpression(const ast::MemberAccessorExpr const Expression* object, bool has_side_effects, const Variable* source_var /* = nullptr */) - : Base(declaration, type, statement, Constant{}, has_side_effects, source_var), - object_(object) {} + : Base(declaration, type, statement, nullptr, has_side_effects, source_var), object_(object) {} MemberAccessorExpression::~MemberAccessorExpression() = default; diff --git a/src/tint/sem/variable.cc b/src/tint/sem/variable.cc index 5439600448..38498073e7 100644 --- a/src/tint/sem/variable.cc +++ b/src/tint/sem/variable.cc @@ -33,7 +33,7 @@ Variable::Variable(const ast::Variable* declaration, const sem::Type* type, ast::StorageClass storage_class, ast::Access access, - Constant constant_value) + const Constant* constant_value) : declaration_(declaration), type_(type), storage_class_(storage_class), @@ -47,9 +47,8 @@ LocalVariable::LocalVariable(const ast::Variable* declaration, ast::StorageClass storage_class, ast::Access access, const sem::Statement* statement, - Constant constant_value) - : Base(declaration, type, storage_class, access, std::move(constant_value)), - statement_(statement) {} + const Constant* constant_value) + : Base(declaration, type, storage_class, access, constant_value), statement_(statement) {} LocalVariable::~LocalVariable() = default; @@ -57,9 +56,9 @@ GlobalVariable::GlobalVariable(const ast::Variable* declaration, const sem::Type* type, ast::StorageClass storage_class, ast::Access access, - Constant constant_value, + const Constant* constant_value, sem::BindingPoint binding_point) - : Base(declaration, type, storage_class, access, std::move(constant_value)), + : Base(declaration, type, storage_class, access, constant_value), binding_point_(binding_point) {} GlobalVariable::~GlobalVariable() = default; @@ -70,7 +69,7 @@ Parameter::Parameter(const ast::Parameter* declaration, ast::StorageClass storage_class, ast::Access access, const ParameterUsage usage /* = ParameterUsage::kNone */) - : Base(declaration, type, storage_class, access, Constant{}), index_(index), usage_(usage) {} + : Base(declaration, type, storage_class, access, nullptr), index_(index), usage_(usage) {} Parameter::~Parameter() = default; diff --git a/src/tint/sem/variable.h b/src/tint/sem/variable.h index 3b5bd0cc09..28e8f975a6 100644 --- a/src/tint/sem/variable.h +++ b/src/tint/sem/variable.h @@ -52,7 +52,7 @@ class Variable : public Castable { const sem::Type* type, ast::StorageClass storage_class, ast::Access access, - Constant constant_value); + const Constant* constant_value); /// Destructor ~Variable() override; @@ -70,7 +70,7 @@ class Variable : public Castable { ast::Access Access() const { return access_; } /// @return the constant value of this expression - const Constant& ConstantValue() const { return constant_value_; } + const Constant* ConstantValue() const { return constant_value_; } /// @returns the variable constructor expression, or nullptr if the variable /// does not have one. @@ -91,7 +91,7 @@ class Variable : public Castable { const sem::Type* const type_; const ast::StorageClass storage_class_; const ast::Access access_; - const Constant constant_value_; + const Constant* constant_value_; const Expression* constructor_ = nullptr; std::vector users_; }; @@ -111,7 +111,7 @@ class LocalVariable final : public Castable { ast::StorageClass storage_class, ast::Access access, const sem::Statement* statement, - Constant constant_value); + const Constant* constant_value); /// Destructor ~LocalVariable() override; @@ -145,7 +145,7 @@ class GlobalVariable final : public Castable { const sem::Type* type, ast::StorageClass storage_class, ast::Access access, - Constant constant_value, + const Constant* constant_value, sem::BindingPoint binding_point = {}); /// Destructor diff --git a/src/tint/transform/localize_struct_array_assignment.cc b/src/tint/transform/localize_struct_array_assignment.cc index d6cdded9a0..7c3d695f09 100644 --- a/src/tint/transform/localize_struct_array_assignment.cc +++ b/src/tint/transform/localize_struct_array_assignment.cc @@ -46,7 +46,7 @@ class LocalizeStructArrayAssignment::State { expr, b.Diagnostics(), [&](const ast::IndexAccessorExpression* ia) { // Indexing using a runtime value? auto* idx_sem = ctx.src->Sem().Get(ia->index); - if (!idx_sem->ConstantValue().IsValid()) { + if (!idx_sem->ConstantValue()) { // Indexing a member access expr? if (auto* ma = ia->object->As()) { // That accesses an array? diff --git a/src/tint/transform/promote_side_effects_to_decl.cc b/src/tint/transform/promote_side_effects_to_decl.cc index d527a4c690..7551c23778 100644 --- a/src/tint/transform/promote_side_effects_to_decl.cc +++ b/src/tint/transform/promote_side_effects_to_decl.cc @@ -275,7 +275,7 @@ class DecomposeSideEffects::CollectHoistsState : public StateBase { if (auto* sem_e = sem.Get(e)) { if (auto* var_user = sem_e->As()) { // Don't hoist constants. - if (var_user->ConstantValue().IsValid()) { + if (var_user->ConstantValue()) { return false; } // Don't hoist read-only variables as they cannot receive diff --git a/src/tint/transform/robustness.cc b/src/tint/transform/robustness.cc index 2e1cc40710..beb11082f1 100644 --- a/src/tint/transform/robustness.cc +++ b/src/tint/transform/robustness.cc @@ -120,17 +120,18 @@ struct Robustness::State { return nullptr; } - if (auto idx_constant = idx_sem->ConstantValue()) { + if (auto* idx_constant = idx_sem->ConstantValue()) { // Constant value index - if (idx_constant.Type()->Is()) { - idx.i32 = static_cast(idx_constant.Element(0).value); + auto val = std::get(idx_constant->Value()); + if (idx_constant->Type()->Is()) { + idx.i32 = static_cast(val); idx.is_signed = true; - } else if (idx_constant.Type()->Is()) { - idx.u32 = static_cast(idx_constant.Element(0).value); + } else if (idx_constant->Type()->Is()) { + idx.u32 = static_cast(val); idx.is_signed = false; } else { TINT_ICE(Transform, b.Diagnostics()) << "unsupported constant value for accessor " - << idx_constant.Type()->TypeInfo().name; + << idx_constant->Type()->TypeInfo().name; return nullptr; } } else { diff --git a/src/tint/transform/zero_init_workgroup_memory.cc b/src/tint/transform/zero_init_workgroup_memory.cc index f56dc617fd..96395f1c74 100644 --- a/src/tint/transform/zero_init_workgroup_memory.cc +++ b/src/tint/transform/zero_init_workgroup_memory.cc @@ -358,8 +358,8 @@ struct ZeroInitWorkgroupMemory::State { continue; } auto* sem = ctx.src->Sem().Get(expr); - if (auto c = sem->ConstantValue()) { - workgroup_size_const *= c.Element(0).value; + if (auto* c = sem->ConstantValue()) { + workgroup_size_const *= c->As(); continue; } // Constant value could not be found. Build expression instead. diff --git a/src/tint/writer/append_vector.cc b/src/tint/writer/append_vector.cc index 7755ae3465..c5f184d4b5 100644 --- a/src/tint/writer/append_vector.cc +++ b/src/tint/writer/append_vector.cc @@ -59,7 +59,7 @@ const sem::Expression* Zero(ProgramBuilder& b, const sem::Type* ty, const sem::S << "unsupported vector element type: " << ty->TypeInfo().name; return nullptr; } - auto* sem = b.create(expr, ty, stmt, sem::Constant{}, + auto* sem = b.create(expr, ty, stmt, /* constant_value */ nullptr, /* has_side_effects */ false); b.Sem().Add(expr, sem); return sem; @@ -139,7 +139,7 @@ const sem::Call* AppendVector(ProgramBuilder* b, ast::StorageClass::kNone, ast::Access::kUndefined)); auto* scalar_cast_sem = b->create( scalar_cast_ast, scalar_cast_target, std::vector{scalar_sem}, - statement, sem::Constant{}, /* has_side_effects */ false); + statement, /* constant_value */ nullptr, /* has_side_effects */ false); b->Sem().Add(scalar_cast_ast, scalar_cast_sem); packed.emplace_back(scalar_cast_sem); } else { @@ -158,7 +158,7 @@ const sem::Call* AppendVector(ProgramBuilder* b, ast::Access::kUndefined); })); auto* constructor_sem = b->create(constructor_ast, constructor_target, packed, - statement, sem::Constant{}, + statement, /* constant_value */ nullptr, /* has_side_effects */ false); b->Sem().Add(constructor_ast, constructor_sem); return constructor_sem; diff --git a/src/tint/writer/glsl/generator_impl.cc b/src/tint/writer/glsl/generator_impl.cc index 022db4b6d4..fdf4557361 100644 --- a/src/tint/writer/glsl/generator_impl.cc +++ b/src/tint/writer/glsl/generator_impl.cc @@ -1332,7 +1332,7 @@ bool GeneratorImpl::EmitBarrierCall(std::ostream& out, const sem::Builtin* built const ast::Expression* GeneratorImpl::CreateF32Zero(const sem::Statement* stmt) { auto* zero = builder_.Expr(0_f); auto* f32 = builder_.create(); - auto* sem_zero = builder_.create(zero, f32, stmt, sem::Constant{}, + auto* sem_zero = builder_.create(zero, f32, stmt, /* constant_value */ nullptr, /* has_side_effects */ false); builder_.Sem().Add(zero, sem_zero); return zero; @@ -1771,7 +1771,7 @@ bool GeneratorImpl::EmitDiscard(const ast::DiscardStatement*) { bool GeneratorImpl::EmitExpression(std::ostream& out, const ast::Expression* expr) { if (auto* sem = builder_.Sem().Get(expr)) { - if (auto constant = sem->ConstantValue()) { + if (auto* constant = sem->ConstantValue()) { return EmitConstant(out, constant); } } @@ -2214,31 +2214,23 @@ bool GeneratorImpl::EmitEntryPointFunction(const ast::Function* func) { return true; } -bool GeneratorImpl::EmitConstant(std::ostream& out, const sem::Constant& constant) { - return EmitConstantRange(out, constant, constant.Type(), 0, constant.ElementCount()); -} - -bool GeneratorImpl::EmitConstantRange(std::ostream& out, - const sem::Constant& constant, - const sem::Type* range_ty, - size_t start, - size_t end) { +bool GeneratorImpl::EmitConstant(std::ostream& out, const sem::Constant* constant) { return Switch( - range_ty, // + constant->Type(), // [&](const sem::Bool*) { - out << (constant.Element(start) ? "true" : "false"); + out << (constant->As() ? "true" : "false"); return true; }, [&](const sem::F32*) { - PrintF32(out, static_cast(constant.Element(start))); + PrintF32(out, constant->As()); return true; }, [&](const sem::I32*) { - out << constant.Element(start).value; + out << constant->As(); return true; }, [&](const sem::U32*) { - out << constant.Element(start).value << "u"; + out << constant->As() << "u"; return true; }, [&](const sem::Vector* v) { @@ -2248,15 +2240,15 @@ bool GeneratorImpl::EmitConstantRange(std::ostream& out, ScopedParen sp(out); - if (constant.AllEqual(start, end)) { - return EmitConstantRange(out, constant, v->type(), start, start + 1); + if (constant->AllEqual()) { + return EmitConstant(out, constant->Index(0)); } - for (size_t i = start; i < end; i++) { - if (i > start) { + for (size_t i = 0; i < v->Width(); i++) { + if (i > 0) { out << ", "; } - if (!EmitConstantRange(out, constant, v->type(), i, i + 1u)) { + if (!EmitConstant(out, constant->Index(i))) { return false; } } @@ -2273,9 +2265,7 @@ bool GeneratorImpl::EmitConstantRange(std::ostream& out, if (column_idx > 0) { out << ", "; } - size_t col_start = m->rows() * column_idx; - size_t col_end = col_start + m->rows(); - if (!EmitConstantRange(out, constant, m->ColumnType(), col_start, col_end)) { + if (!EmitConstant(out, constant->Index(column_idx))) { return false; } } @@ -2288,15 +2278,11 @@ bool GeneratorImpl::EmitConstantRange(std::ostream& out, ScopedParen sp(out); - auto* el_ty = a->ElemType(); - - uint32_t step = 0; - sem::Type::DeepestElementOf(el_ty, &step); - for (size_t i = start; i < end; i += step) { - if (i > start) { + for (size_t i = 0; i < a->Count(); i++) { + if (i > 0) { out << ", "; } - if (!EmitConstantRange(out, constant, el_ty, i, i + step)) { + if (!EmitConstant(out, constant->Index(i))) { return false; } } @@ -2306,7 +2292,7 @@ bool GeneratorImpl::EmitConstantRange(std::ostream& out, [&](Default) { diagnostics_.add_error( diag::System::Writer, - "unhandled constant type: " + builder_.FriendlyName(constant.Type())); + "unhandled constant type: " + builder_.FriendlyName(constant->Type())); return false; }); } diff --git a/src/tint/writer/glsl/generator_impl.h b/src/tint/writer/glsl/generator_impl.h index f925bba41d..15b7ee1520 100644 --- a/src/tint/writer/glsl/generator_impl.h +++ b/src/tint/writer/glsl/generator_impl.h @@ -346,19 +346,7 @@ class GeneratorImpl : public TextGenerator { /// @param out the output stream /// @param constant the constant value to emit /// @returns true if the constant value was successfully emitted - bool EmitConstant(std::ostream& out, const sem::Constant& constant); - /// Handles emitting a sub-range of a constant value - /// @param out the output stream - /// @param constant the constant value to emit - /// @param range_ty the sub-range type - /// @param start the element index for the first element - /// @param end the element index for one past the last element - /// @returns true if the constant value was successfully emitted - bool EmitConstantRange(std::ostream& out, - const sem::Constant& constant, - const sem::Type* range_ty, - size_t start, - size_t end); + bool EmitConstant(std::ostream& out, const sem::Constant* constant); /// Handles a literal /// @param out the output stream /// @param lit the literal to emit diff --git a/src/tint/writer/hlsl/generator_impl.cc b/src/tint/writer/hlsl/generator_impl.cc index 3e4352b634..b307c8d887 100644 --- a/src/tint/writer/hlsl/generator_impl.cc +++ b/src/tint/writer/hlsl/generator_impl.cc @@ -615,8 +615,7 @@ bool GeneratorImpl::EmitAssign(const ast::AssignmentStatement* stmt) { if (auto* mat = TypeOf(lhs_sub_access->object)->UnwrapRef()->As()) { auto* rhs_col_idx_sem = builder_.Sem().Get(lhs_access->index); auto* rhs_row_idx_sem = builder_.Sem().Get(lhs_sub_access->index); - if (!rhs_col_idx_sem->ConstantValue().IsValid() || - !rhs_row_idx_sem->ConstantValue().IsValid()) { + if (!rhs_col_idx_sem->ConstantValue() || !rhs_row_idx_sem->ConstantValue()) { return EmitDynamicMatrixScalarAssignment(stmt, mat); } } @@ -626,7 +625,7 @@ bool GeneratorImpl::EmitAssign(const ast::AssignmentStatement* stmt) { const auto* lhs_access_type = TypeOf(lhs_access->object)->UnwrapRef(); if (auto* mat = lhs_access_type->As()) { auto* lhs_index_sem = builder_.Sem().Get(lhs_access->index); - if (!lhs_index_sem->ConstantValue().IsValid()) { + if (!lhs_index_sem->ConstantValue()) { return EmitDynamicMatrixVectorAssignment(stmt, mat); } } @@ -634,7 +633,7 @@ bool GeneratorImpl::EmitAssign(const ast::AssignmentStatement* stmt) { // indices if (auto* vec = lhs_access_type->As()) { auto* rhs_sem = builder_.Sem().Get(lhs_access->index); - if (!rhs_sem->ConstantValue().IsValid()) { + if (!rhs_sem->ConstantValue()) { return EmitDynamicVectorAssignment(stmt, vec); } } @@ -654,28 +653,30 @@ bool GeneratorImpl::EmitAssign(const ast::AssignmentStatement* stmt) { bool GeneratorImpl::EmitExpressionOrOneIfZero(std::ostream& out, const ast::Expression* expr) { // For constants, replace literal 0 with 1. - if (const auto& val = builder_.Sem().Get(expr)->ConstantValue()) { - if (!val.AnyZero()) { + if (const auto* val = builder_.Sem().Get(expr)->ConstantValue()) { + if (!val->AnyZero()) { return EmitExpression(out, expr); } - if (val.Type()->IsAnyOf()) { - return EmitValue(out, val.Type(), 1); + auto* ty = val->Type(); + + if (ty->IsAnyOf()) { + return EmitValue(out, ty, 1); } - if (auto* vec = val.Type()->As()) { + if (auto* vec = ty->As()) { auto* elem_ty = vec->type(); - if (!EmitType(out, val.Type(), ast::StorageClass::kNone, ast::Access::kUndefined, "")) { + if (!EmitType(out, ty, ast::StorageClass::kNone, ast::Access::kUndefined, "")) { return false; } out << "("; - for (size_t i = 0; i < val.ElementCount(); ++i) { + for (size_t i = 0; i < vec->Width(); ++i) { if (i != 0) { out << ", "; } - auto s = val.Element(i).value; + auto s = val->Index(i)->As(); if (!EmitValue(out, elem_ty, (s == 0) ? 1 : static_cast(s))) { return false; } @@ -1181,9 +1182,9 @@ bool GeneratorImpl::EmitUniformBufferAccess( // If true, use scalar_offset_value, otherwise use scalar_offset_expr bool scalar_offset_constant = false; - if (auto val = offset_arg->ConstantValue()) { - TINT_ASSERT(Writer, val.Type()->Is()); - scalar_offset_value = static_cast(val.Element(0).value); + if (auto* val = offset_arg->ConstantValue()) { + TINT_ASSERT(Writer, val->Type()->Is()); + scalar_offset_value = static_cast(std::get(val->Value())); scalar_offset_value /= 4; // bytes -> scalar index scalar_offset_constant = true; } @@ -2337,7 +2338,7 @@ bool GeneratorImpl::EmitTextureCall(std::ostream& out, case sem::BuiltinType::kTextureGather: out << ".Gather"; if (builtin->Parameters()[0]->Usage() == sem::ParameterUsage::kComponent) { - switch (call->Arguments()[0]->ConstantValue().Element(0).value) { + switch (call->Arguments()[0]->ConstantValue()->As()) { case 0: out << "Red"; break; @@ -2384,8 +2385,9 @@ bool GeneratorImpl::EmitTextureCall(std::ostream& out, auto* i32 = builder_.create(); auto* zero = builder_.Expr(0_i); auto* stmt = builder_.Sem().Get(vector)->Stmt(); - builder_.Sem().Add(zero, builder_.create(zero, i32, stmt, sem::Constant{}, - /* has_side_effects */ false)); + builder_.Sem().Add( + zero, builder_.create(zero, i32, stmt, /* constant_value */ nullptr, + /* has_side_effects */ false)); auto* packed = AppendVector(&builder_, vector, zero); return EmitExpression(out, packed->Declaration()); }; @@ -2614,7 +2616,7 @@ bool GeneratorImpl::EmitDiscard(const ast::DiscardStatement*) { bool GeneratorImpl::EmitExpression(std::ostream& out, const ast::Expression* expr) { if (auto* sem = builder_.Sem().Get(expr)) { - if (auto constant = sem->ConstantValue()) { + if (auto* constant = sem->ConstantValue()) { return EmitConstant(out, constant); } } @@ -3109,43 +3111,35 @@ bool GeneratorImpl::EmitEntryPointFunction(const ast::Function* func) { return true; } -bool GeneratorImpl::EmitConstant(std::ostream& out, const sem::Constant& constant) { - return EmitConstantRange(out, constant, constant.Type(), 0, constant.ElementCount()); -} - -bool GeneratorImpl::EmitConstantRange(std::ostream& out, - const sem::Constant& constant, - const sem::Type* range_ty, - size_t start, - size_t end) { +bool GeneratorImpl::EmitConstant(std::ostream& out, const sem::Constant* constant) { return Switch( - range_ty, // + constant->Type(), // [&](const sem::Bool*) { - out << (constant.Element(start) ? "true" : "false"); + out << (constant->As() ? "true" : "false"); return true; }, [&](const sem::F32*) { - PrintF32(out, static_cast(constant.Element(start))); + PrintF32(out, constant->As()); return true; }, [&](const sem::I32*) { - out << constant.Element(start).value; + out << constant->As(); return true; }, [&](const sem::U32*) { - out << constant.Element(start).value << "u"; + out << constant->As() << "u"; return true; }, [&](const sem::Vector* v) { - if (constant.AllEqual(start, end)) { + if (constant->AllEqual()) { { ScopedParen sp(out); - if (!EmitConstantRange(out, constant, v->type(), start, start + 1)) { + if (!EmitConstant(out, constant->Index(0))) { return false; } } out << "."; - for (size_t i = start; i < end; i++) { + for (size_t i = 0; i < v->Width(); i++) { out << "x"; } return true; @@ -3157,11 +3151,11 @@ bool GeneratorImpl::EmitConstantRange(std::ostream& out, ScopedParen sp(out); - for (size_t i = start; i < end; i++) { - if (i > start) { + for (size_t i = 0; i < v->Width(); i++) { + if (i > 0) { out << ", "; } - if (!EmitConstantRange(out, constant, v->type(), i, i + 1u)) { + if (!EmitConstant(out, constant->Index(i))) { return false; } } @@ -3174,20 +3168,18 @@ bool GeneratorImpl::EmitConstantRange(std::ostream& out, ScopedParen sp(out); - for (size_t column_idx = 0; column_idx < m->columns(); column_idx++) { - if (column_idx > 0) { + for (size_t i = 0; i < m->columns(); i++) { + if (i > 0) { out << ", "; } - size_t col_start = m->rows() * column_idx; - size_t col_end = col_start + m->rows(); - if (!EmitConstantRange(out, constant, m->ColumnType(), col_start, col_end)) { + if (!EmitConstant(out, constant->Index(i))) { return false; } } return true; }, [&](const sem::Array* a) { - if (constant.AllZero(start, end)) { + if (constant->AllZero()) { out << "("; if (!EmitType(out, a, ast::StorageClass::kNone, ast::Access::kUndefined, "")) { return false; @@ -3199,15 +3191,11 @@ bool GeneratorImpl::EmitConstantRange(std::ostream& out, out << "{"; TINT_DEFER(out << "}"); - auto* el_ty = a->ElemType(); - - uint32_t step = 0; - sem::Type::DeepestElementOf(el_ty, &step); - for (size_t i = start; i < end; i += step) { - if (i > start) { + for (size_t i = 0; i < a->Count(); i++) { + if (i > 0) { out << ", "; } - if (!EmitConstantRange(out, constant, el_ty, i, i + step)) { + if (!EmitConstant(out, constant->Index(i))) { return false; } } @@ -3217,7 +3205,7 @@ bool GeneratorImpl::EmitConstantRange(std::ostream& out, [&](Default) { diagnostics_.add_error( diag::System::Writer, - "unhandled constant type: " + builder_.FriendlyName(constant.Type())); + "unhandled constant type: " + builder_.FriendlyName(constant->Type())); return false; }); } diff --git a/src/tint/writer/hlsl/generator_impl.h b/src/tint/writer/hlsl/generator_impl.h index 89605487cc..bf2debeda1 100644 --- a/src/tint/writer/hlsl/generator_impl.h +++ b/src/tint/writer/hlsl/generator_impl.h @@ -93,7 +93,8 @@ class GeneratorImpl : public TextGenerator { /// @param stmt the statement to emit /// @returns true if the statement was emitted successfully bool EmitAssign(const ast::AssignmentStatement* stmt); - /// Emits code such that if `expr` is zero, it emits one, else `expr` + /// Emits code such that if `expr` is zero, it emits one, else `expr`. + /// Used to avoid divide-by-zeros by substituting constant zeros with ones. /// @param out the output of the expression stream /// @param expr the expression /// @returns true if the expression was emitted, false otherwise @@ -342,19 +343,7 @@ class GeneratorImpl : public TextGenerator { /// @param out the output stream /// @param constant the constant value to emit /// @returns true if the constant value was successfully emitted - bool EmitConstant(std::ostream& out, const sem::Constant& constant); - /// Handles emitting a sub-range of a constant value - /// @param out the output stream - /// @param constant the constant value to emit - /// @param range_ty the sub-range type - /// @param start the element index for the first element - /// @param end the element index for one past the last element - /// @returns true if the constant value was successfully emitted - bool EmitConstantRange(std::ostream& out, - const sem::Constant& constant, - const sem::Type* range_ty, - size_t start, - size_t end); + bool EmitConstant(std::ostream& out, const sem::Constant* constant); /// Handles a literal /// @param out the output stream /// @param lit the literal to emit diff --git a/src/tint/writer/msl/generator_impl.cc b/src/tint/writer/msl/generator_impl.cc index f5ffa6c615..43324bb7a3 100644 --- a/src/tint/writer/msl/generator_impl.cc +++ b/src/tint/writer/msl/generator_impl.cc @@ -1201,7 +1201,7 @@ bool GeneratorImpl::EmitTextureCall(std::ostream& out, break; // Other texture dimensions don't have an offset } } - auto c = component->ConstantValue().Element(0); + auto c = component->ConstantValue()->As(); switch (c.value) { case 0: out << "component::x"; @@ -1594,31 +1594,23 @@ bool GeneratorImpl::EmitZeroValue(std::ostream& out, const sem::Type* type) { }); } -bool GeneratorImpl::EmitConstant(std::ostream& out, const sem::Constant& constant) { - return EmitConstantRange(out, constant, constant.Type(), 0, constant.ElementCount()); -} - -bool GeneratorImpl::EmitConstantRange(std::ostream& out, - const sem::Constant& constant, - const sem::Type* range_ty, - size_t start, - size_t end) { +bool GeneratorImpl::EmitConstant(std::ostream& out, const sem::Constant* constant) { return Switch( - range_ty, // + constant->Type(), // [&](const sem::Bool*) { - out << (constant.Element(start) ? "true" : "false"); + out << (constant->As() ? "true" : "false"); return true; }, [&](const sem::F32*) { - PrintF32(out, static_cast(constant.Element(start))); + PrintF32(out, constant->As()); return true; }, [&](const sem::I32*) { - PrintI32(out, static_cast(constant.Element(start).value)); + PrintI32(out, constant->As()); return true; }, [&](const sem::U32*) { - out << constant.Element(start).value << "u"; + out << constant->As() << "u"; return true; }, [&](const sem::Vector* v) { @@ -1628,18 +1620,18 @@ bool GeneratorImpl::EmitConstantRange(std::ostream& out, ScopedParen sp(out); - if (constant.AllEqual(start, end)) { - if (!EmitConstantRange(out, constant, v->type(), start, start + 1)) { + if (constant->AllEqual()) { + if (!EmitConstant(out, constant->Index(0))) { return false; } return true; } - for (size_t i = start; i < end; i++) { - if (i > start) { + for (size_t i = 0; i < v->Width(); i++) { + if (i > 0) { out << ", "; } - if (!EmitConstantRange(out, constant, v->type(), i, i + 1u)) { + if (!EmitConstant(out, constant->Index(i))) { return false; } } @@ -1652,13 +1644,11 @@ bool GeneratorImpl::EmitConstantRange(std::ostream& out, ScopedParen sp(out); - for (size_t column_idx = 0; column_idx < m->columns(); column_idx++) { - if (column_idx > 0) { + for (size_t i = 0; i < m->columns(); i++) { + if (i > 0) { out << ", "; } - size_t col_start = m->rows() * column_idx; - size_t col_end = col_start + m->rows(); - if (!EmitConstantRange(out, constant, m->ColumnType(), col_start, col_end)) { + if (!EmitConstant(out, constant->Index(i))) { return false; } } @@ -1669,7 +1659,7 @@ bool GeneratorImpl::EmitConstantRange(std::ostream& out, return false; } - if (constant.AllZero(start, end)) { + if (constant->AllZero()) { out << "{}"; return true; } @@ -1677,15 +1667,11 @@ bool GeneratorImpl::EmitConstantRange(std::ostream& out, out << "{"; TINT_DEFER(out << "}"); - auto* el_ty = a->ElemType(); - - uint32_t step = 0; - sem::Type::DeepestElementOf(el_ty, &step); - for (size_t i = start; i < end; i += step) { - if (i > start) { + for (size_t i = 0; i < a->Count(); i++) { + if (i > 0) { out << ", "; } - if (!EmitConstantRange(out, constant, el_ty, i, i + step)) { + if (!EmitConstant(out, constant->Index(i))) { return false; } } @@ -1695,7 +1681,7 @@ bool GeneratorImpl::EmitConstantRange(std::ostream& out, [&](Default) { diagnostics_.add_error( diag::System::Writer, - "unhandled constant type: " + builder_.FriendlyName(constant.Type())); + "unhandled constant type: " + builder_.FriendlyName(constant->Type())); return false; }); } diff --git a/src/tint/writer/msl/generator_impl.h b/src/tint/writer/msl/generator_impl.h index ed0c35b470..f27d2585df 100644 --- a/src/tint/writer/msl/generator_impl.h +++ b/src/tint/writer/msl/generator_impl.h @@ -256,19 +256,7 @@ class GeneratorImpl : public TextGenerator { /// @param out the output stream /// @param constant the constant value to emit /// @returns true if the constant value was successfully emitted - bool EmitConstant(std::ostream& out, const sem::Constant& constant); - /// Handles emitting a sub-range of a constant value - /// @param out the output stream - /// @param constant the constant value to emit - /// @param range_ty the sub-range type - /// @param start the element index for the first element - /// @param end the element index for one past the last element - /// @returns true if the constant value was successfully emitted - bool EmitConstantRange(std::ostream& out, - const sem::Constant& constant, - const sem::Type* range_ty, - size_t start, - size_t end); + bool EmitConstant(std::ostream& out, const sem::Constant* constant); /// Handles a literal /// @param out the output of the expression stream /// @param lit the literal to emit diff --git a/src/tint/writer/spirv/builder.cc b/src/tint/writer/spirv/builder.cc index fea7a14506..833324a338 100644 --- a/src/tint/writer/spirv/builder.cc +++ b/src/tint/writer/spirv/builder.cc @@ -959,7 +959,7 @@ bool Builder::GenerateIndexAccessor(const ast::IndexAccessorExpression* expr, Ac Operand(result_type_id), extract, Operand(info->source_id), - Operand(idx_constval.Element(0)), + Operand(idx_constval->As()), })) { return false; } @@ -1703,20 +1703,14 @@ uint32_t Builder::GenerateLiteralIfNeeded(const ast::Variable* var, return GenerateConstantIfNeeded(constant); } -uint32_t Builder::GenerateConstantIfNeeded(const sem::Constant& constant) { - return GenerateConstantRangeIfNeeded(constant, constant.Type(), 0, constant.ElementCount()); -} - -uint32_t Builder::GenerateConstantRangeIfNeeded(const sem::Constant& constant, - const sem::Type* range_ty, - size_t start, - size_t end) { - if (constant.AllZero(start, end)) { - return GenerateConstantNullIfNeeded(range_ty); +uint32_t Builder::GenerateConstantIfNeeded(const sem::Constant* constant) { + if (constant->AllZero()) { + return GenerateConstantNullIfNeeded(constant->Type()); } + auto* ty = constant->Type(); - auto composite = [&](const sem::Type* el_ty) -> uint32_t { - auto type_id = GenerateTypeIfNeeded(range_ty); + auto composite = [&](size_t el_count) -> uint32_t { + auto type_id = GenerateTypeIfNeeded(ty); if (!type_id) { return 0; } @@ -1724,14 +1718,12 @@ uint32_t Builder::GenerateConstantRangeIfNeeded(const sem::Constant& constant, static constexpr size_t kOpsResultIdx = 1; // operand index of the result std::vector ops; - ops.reserve(end - start + 2); + ops.reserve(el_count + 2); ops.emplace_back(type_id); ops.push_back(Operand(0u)); // Placeholder for the result ID - uint32_t step = 0; - sem::Type::DeepestElementOf(el_ty, &step); - for (size_t i = start; i < end; i += step) { - auto id = GenerateConstantRangeIfNeeded(constant, el_ty, i, i + step); + for (size_t i = 0; i < el_count; i++) { + auto id = GenerateConstantIfNeeded(constant->Index(i)); if (!id) { return 0; } @@ -1749,28 +1741,28 @@ uint32_t Builder::GenerateConstantRangeIfNeeded(const sem::Constant& constant, }; return Switch( - range_ty, // + ty, // [&](const sem::Bool*) { - bool val = constant.Element(start); + bool val = constant->As(); return GenerateConstantIfNeeded(ScalarConstant::Bool(val)); }, [&](const sem::F32*) { - auto val = f32(constant.Element(start)); + auto val = constant->As(); return GenerateConstantIfNeeded(ScalarConstant::F32(val.value)); }, [&](const sem::I32*) { - auto val = i32(constant.Element(start)); + auto val = constant->As(); return GenerateConstantIfNeeded(ScalarConstant::I32(val.value)); }, [&](const sem::U32*) { - auto val = u32(constant.Element(start)); + auto val = constant->As(); return GenerateConstantIfNeeded(ScalarConstant::U32(val.value)); }, - [&](const sem::Vector* v) { return composite(v->type()); }, - [&](const sem::Matrix* m) { return composite(m->ColumnType()); }, - [&](const sem::Array* a) { return composite(a->ElemType()); }, + [&](const sem::Vector* v) { return composite(v->Width()); }, + [&](const sem::Matrix* m) { return composite(m->columns()); }, + [&](const sem::Array* a) { return composite(a->Count()); }, [&](Default) { - error_ = "unhandled constant type: " + builder_.FriendlyName(constant.Type()); + error_ = "unhandled constant type: " + builder_.FriendlyName(ty); return false; }); } diff --git a/src/tint/writer/spirv/builder.h b/src/tint/writer/spirv/builder.h index 8465089888..f6bb93fad3 100644 --- a/src/tint/writer/spirv/builder.h +++ b/src/tint/writer/spirv/builder.h @@ -554,18 +554,7 @@ class Builder { /// Generates a constant value if needed /// @param constant the constant to generate. /// @returns the ID on success or 0 on failure - uint32_t GenerateConstantIfNeeded(const sem::Constant& constant); - - /// Handles emitting a sub-range of a constant value - /// @param constant the constant value to emit - /// @param range_ty the sub-range type - /// @param start the element index for the first element - /// @param end the element index for one past the last element - /// @returns true if the constant value was successfully emitted - uint32_t GenerateConstantRangeIfNeeded(const sem::Constant& constant, - const sem::Type* range_ty, - size_t start, - size_t end); + uint32_t GenerateConstantIfNeeded(const sem::Constant* constant); /// Generates a scalar constant if needed /// @param constant the constant to generate.