// Copyright 2021 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 empecific language governing permissions and // limitations under the License. #include "src/tint/resolver/const_eval_test.h" #include "src/tint/utils/result.h" using namespace tint::number_suffixes; // NOLINT using ::testing::HasSubstr; namespace tint::resolver { namespace { struct Case { Case(utils::VectorRef in_args, utils::VectorRef expected_values) : args(std::move(in_args)), expected(Success{std::move(expected_values), CheckConstantFlags{}}) {} Case(utils::VectorRef in_args, std::string expected_err) : args(std::move(in_args)), expected(Failure{std::move(expected_err)}) {} /// Expected value may be positive or negative Case& PosOrNeg() { Success s = expected.Get(); s.flags.pos_or_neg = true; expected = s; return *this; } /// Expected value should be compared using EXPECT_FLOAT_EQ instead of EXPECT_EQ. /// If optional epsilon is passed in, will be compared using EXPECT_NEAR with that epsilon. Case& FloatComp(std::optional epsilon = {}) { Success s = expected.Get(); s.flags.float_compare = true; s.flags.float_compare_epsilon = epsilon; expected = s; return *this; } struct Success { utils::Vector values; CheckConstantFlags flags; }; struct Failure { std::string error; }; utils::Vector args; utils::Result expected; }; static std::ostream& operator<<(std::ostream& o, const Case& c) { o << "args: "; for (auto& a : c.args) { o << a << ", "; } o << "expected: "; if (c.expected) { auto s = c.expected.Get(); if (s.values.Length() == 1) { o << s.values[0]; } else { o << "["; for (auto& v : s.values) { if (&v != &s.values[0]) { o << ", "; } o << v; } o << "]"; } o << ", pos_or_neg: " << s.flags.pos_or_neg; o << ", float_compare: " << s.flags.float_compare; } else { o << "[ERROR: " << c.expected.Failure().error << "]"; } return o; } using ScalarTypes = std::variant; /// Creates a Case with Values for args and result static Case C(std::initializer_list args, Value result) { return Case{utils::Vector{args}, utils::Vector{std::move(result)}}; } /// Creates a Case with Values for args and result static Case C(std::initializer_list args, std::initializer_list results) { return Case{utils::Vector{args}, utils::Vector{results}}; } /// Convenience overload that creates a Case with just scalars static Case C(std::initializer_list sargs, ScalarTypes sresult) { utils::Vector args; for (auto& sa : sargs) { std::visit([&](auto&& v) { return args.Push(Val(v)); }, sa); } Value result = Val(0_a); std::visit([&](auto&& v) { result = Val(v); }, sresult); return Case{std::move(args), utils::Vector{std::move(result)}}; } /// Creates a Case with Values for args and result static Case C(std::initializer_list sargs, std::initializer_list sresults) { utils::Vector args; for (auto& sa : sargs) { std::visit([&](auto&& v) { return args.Push(Val(v)); }, sa); } utils::Vector results; for (auto& sa : sresults) { std::visit([&](auto&& v) { return results.Push(Val(v)); }, sa); } return Case{std::move(args), std::move(results)}; } /// Creates a Case with Values for args and expected error static Case E(std::initializer_list args, std::string err) { return Case{utils::Vector{args}, std::move(err)}; } /// Convenience overload that creates an expected-error Case with just scalars static Case E(std::initializer_list sargs, std::string err) { utils::Vector args; for (auto& sa : sargs) { std::visit([&](auto&& v) { return args.Push(Val(v)); }, sa); } return Case{std::move(args), std::move(err)}; } using ResolverConstEvalBuiltinTest = ResolverTestWithParam>; TEST_P(ResolverConstEvalBuiltinTest, Test) { Enable(ast::Extension::kF16); auto builtin = std::get<0>(GetParam()); auto& c = std::get<1>(GetParam()); utils::Vector args; for (auto& a : c.args) { args.Push(a.Expr(*this)); } auto* expr = Call(Source{{12, 34}}, sem::str(builtin), std::move(args)); GlobalConst("C", expr); if (c.expected) { auto expected_case = c.expected.Get(); ASSERT_TRUE(r()->Resolve()) << r()->error(); auto* sem = Sem().Get(expr); ASSERT_NE(sem, nullptr); const sem::Constant* value = sem->ConstantValue(); ASSERT_NE(value, nullptr); EXPECT_TYPE(value->Type(), sem->Type()); if (value->Type()->Is()) { // The result type of the constant-evaluated expression is a structure. // Compare each of the fields individually. for (size_t i = 0; i < expected_case.values.Length(); i++) { CheckConstant(value->Index(i), expected_case.values[i], expected_case.flags); } } else { // Return type is not a structure. Just compare the single value ASSERT_EQ(expected_case.values.Length(), 1u) << "const-eval returned non-struct, but Case expected multiple values"; CheckConstant(value, expected_case.values[0], expected_case.flags); } } else { EXPECT_FALSE(r()->Resolve()); EXPECT_EQ(r()->error(), c.expected.Failure().error); } } INSTANTIATE_TEST_SUITE_P( // MixedAbstractArgs, ResolverConstEvalBuiltinTest, testing::Combine(testing::Values(sem::BuiltinType::kAtan2), testing::ValuesIn(std::vector{ C({0_a, -0.0_a}, kPi), C({1.0_a, 0_a}, kPiOver2), }))); template std::vector AbsCases() { std::vector cases = { C({T(0)}, T(0)), C({T(2.0)}, T(2.0)), C({T::Highest()}, T::Highest()), // Vector tests C({Vec(T(2.0), T::Highest())}, Vec(T(2.0), T::Highest())), }; ConcatIntoIf>( cases, std::vector{ C({Negate(T(0))}, T(0)), C({Negate(T(2.0))}, T(2.0)), // If e is signed and is the largest negative, the result is e C({T::Lowest()}, T::Lowest()), // 1 more then min i32 C({Negate(T(2147483647))}, T(2147483647)), C({Vec(T(0), Negate(T(0)))}, Vec(T(0), T(0))), C({Vec(Negate(T(2.0)), T(2.0), T::Highest())}, Vec(T(2.0), T(2.0), T::Highest())), }); return cases; } INSTANTIATE_TEST_SUITE_P( // Abs, ResolverConstEvalBuiltinTest, testing::Combine(testing::Values(sem::BuiltinType::kAbs), testing::ValuesIn(Concat(AbsCases(), // AbsCases(), AbsCases(), AbsCases(), AbsCases(), AbsCases())))); static std::vector AllCases() { return { C({Val(true)}, Val(true)), C({Val(false)}, Val(false)), C({Vec(true, true)}, Val(true)), C({Vec(true, false)}, Val(false)), C({Vec(false, true)}, Val(false)), C({Vec(false, false)}, Val(false)), C({Vec(true, true, true)}, Val(true)), C({Vec(false, true, true)}, Val(false)), C({Vec(true, false, true)}, Val(false)), C({Vec(true, true, false)}, Val(false)), C({Vec(false, false, false)}, Val(false)), C({Vec(true, true, true, true)}, Val(true)), C({Vec(false, true, true, true)}, Val(false)), C({Vec(true, false, true, true)}, Val(false)), C({Vec(true, true, false, true)}, Val(false)), C({Vec(true, true, true, false)}, Val(false)), C({Vec(false, false, false, false)}, Val(false)), }; } INSTANTIATE_TEST_SUITE_P( // All, ResolverConstEvalBuiltinTest, testing::Combine(testing::Values(sem::BuiltinType::kAll), testing::ValuesIn(AllCases()))); static std::vector AnyCases() { return { C({Val(true)}, Val(true)), C({Val(false)}, Val(false)), C({Vec(true, true)}, Val(true)), C({Vec(true, false)}, Val(true)), C({Vec(false, true)}, Val(true)), C({Vec(false, false)}, Val(false)), C({Vec(true, true, true)}, Val(true)), C({Vec(false, true, true)}, Val(true)), C({Vec(true, false, true)}, Val(true)), C({Vec(true, true, false)}, Val(true)), C({Vec(false, false, false)}, Val(false)), C({Vec(true, true, true, true)}, Val(true)), C({Vec(false, true, true, true)}, Val(true)), C({Vec(true, false, true, true)}, Val(true)), C({Vec(true, true, false, true)}, Val(true)), C({Vec(true, true, true, false)}, Val(true)), C({Vec(false, false, false, false)}, Val(false)), }; } INSTANTIATE_TEST_SUITE_P( // Any, ResolverConstEvalBuiltinTest, testing::Combine(testing::Values(sem::BuiltinType::kAny), testing::ValuesIn(AnyCases()))); template std::vector Atan2Cases() { return { // If y is +/-0 and x is negative or -0, +/-PI is returned C({T(0.0), -T(0.0)}, kPi).PosOrNeg().FloatComp(), // If y is +/-0 and x is positive or +0, +/-0 is returned C({T(0.0), T(0.0)}, T(0.0)).PosOrNeg(), // If x is +/-0 and y is negative, -PI/2 is returned C({-T(1.0), T(0.0)}, -kPiOver2).FloatComp(), // C({-T(1.0), -T(0.0)}, -kPiOver2).FloatComp(), // If x is +/-0 and y is positive, +PI/2 is returned C({T(1.0), T(0.0)}, kPiOver2).FloatComp(), // C({T(1.0), -T(0.0)}, kPiOver2).FloatComp(), // Vector tests C({Vec(T(0.0), T(0.0)), Vec(-T(0.0), T(0.0))}, Vec(kPi, T(0.0))).PosOrNeg().FloatComp(), C({Vec(-T(1.0), -T(1.0)), Vec(T(0.0), -T(0.0))}, Vec(-kPiOver2, -kPiOver2)) .FloatComp(), C({Vec(T(1.0), T(1.0)), Vec(T(0.0), -T(0.0))}, Vec(kPiOver2, kPiOver2)).FloatComp(), }; } INSTANTIATE_TEST_SUITE_P( // Atan2, ResolverConstEvalBuiltinTest, testing::Combine(testing::Values(sem::BuiltinType::kAtan2), testing::ValuesIn(Concat(Atan2Cases(), // Atan2Cases(), Atan2Cases())))); template std::vector AtanCases() { return { C({T(1.0)}, kPiOver4).FloatComp(), C({-T(1.0)}, -kPiOver4).FloatComp(), // If i is +/-0, +/-0 is returned C({T(0.0)}, T(0.0)).PosOrNeg(), // Vector tests C({Vec(T(0.0), T(1.0), -T(1.0))}, Vec(T(0.0), kPiOver4, -kPiOver4)).FloatComp(), }; } INSTANTIATE_TEST_SUITE_P( // Atan, ResolverConstEvalBuiltinTest, testing::Combine(testing::Values(sem::BuiltinType::kAtan), testing::ValuesIn(Concat(AtanCases(), // AtanCases(), AtanCases())))); template std::vector AtanhCases() { return { // If i is +/-0, +/-0 is returned C({T(0.0)}, T(0.0)).PosOrNeg(), C({T(0.9)}, T(1.4722193)).FloatComp(), // Vector tests C({Vec(T(0.0), T(0.9), -T(0.9))}, Vec(T(0.0), T(1.4722193), -T(1.4722193))).FloatComp(), E({T(1.1)}, "12:34 error: atanh must be called with a value in the range (-1 .. 1) (exclusive)"), E({-T(1.1)}, "12:34 error: atanh must be called with a value in the range (-1 .. 1) (exclusive)"), }; } INSTANTIATE_TEST_SUITE_P( // Atanh, ResolverConstEvalBuiltinTest, testing::Combine(testing::Values(sem::BuiltinType::kAtanh), testing::ValuesIn(Concat(AtanhCases(), // AtanhCases(), AtanhCases())))); template std::vector AcosCases() { return { // If i is +/-0, +/-0 is returned C({T(0.87758256189)}, T(0.5)).FloatComp(), C({T(1.0)}, T(0.0)), C({-T(1.0)}, kPi).FloatComp(), // Vector tests C({Vec(T(1.0), -T(1.0))}, Vec(T(0), kPi)).FloatComp(), E({T(1.1)}, "12:34 error: acos must be called with a value in the range [-1 .. 1] (inclusive)"), E({-T(1.1)}, "12:34 error: acos must be called with a value in the range [-1 .. 1] (inclusive)"), }; } INSTANTIATE_TEST_SUITE_P( // Acos, ResolverConstEvalBuiltinTest, testing::Combine(testing::Values(sem::BuiltinType::kAcos), testing::ValuesIn(Concat(AcosCases(), // AcosCases(), AcosCases())))); template std::vector AcoshCases() { return { C({T(1.0)}, T(0.0)), C({T(11.5919532755)}, kPi).FloatComp(), // Vector tests C({Vec(T(1.0), T(11.5919532755))}, Vec(T(0), kPi)).FloatComp(), E({T::Smallest()}, "12:34 error: acosh must be called with a value >= 1.0"), E({-T(1.1)}, "12:34 error: acosh must be called with a value >= 1.0"), E({T(0)}, "12:34 error: acosh must be called with a value >= 1.0"), }; } INSTANTIATE_TEST_SUITE_P( // Acosh, ResolverConstEvalBuiltinTest, testing::Combine(testing::Values(sem::BuiltinType::kAcosh), testing::ValuesIn(Concat(AcoshCases(), // AcoshCases(), AcoshCases())))); template std::vector AsinCases() { return { // If i is +/-0, +/-0 is returned C({T(0.0)}, T(0.0)), C({-T(0.0)}, -T(0.0)), C({T(1.0)}, kPiOver2).FloatComp(), C({-T(1.0)}, -kPiOver2).FloatComp(), // Vector tests C({Vec(T(0.0), T(1.0), -T(1.0))}, Vec(T(0.0), kPiOver2, -kPiOver2)).FloatComp(), E({T(1.1)}, "12:34 error: asin must be called with a value in the range [-1 .. 1] (inclusive)"), E({-T(1.1)}, "12:34 error: asin must be called with a value in the range [-1 .. 1] (inclusive)"), }; } INSTANTIATE_TEST_SUITE_P( // Asin, ResolverConstEvalBuiltinTest, testing::Combine(testing::Values(sem::BuiltinType::kAsin), testing::ValuesIn(Concat(AsinCases(), // AsinCases(), AsinCases())))); template std::vector AsinhCases() { return { // If i is +/-0, +/-0 is returned C({T(0.0)}, T(0.0)), C({-T(0.0)}, -T(0.0)), C({T(0.9)}, T(0.80886693565278)).FloatComp(), C({-T(2.0)}, -T(1.4436354751788)).FloatComp(), // Vector tests C({Vec(T(0.0), T(0.9), -T(2.0))}, // Vec(T(0.0), T(0.8088669356278), -T(1.4436354751788))) .FloatComp(), }; } INSTANTIATE_TEST_SUITE_P( // Asinh, ResolverConstEvalBuiltinTest, testing::Combine(testing::Values(sem::BuiltinType::kAsinh), testing::ValuesIn(Concat(AsinhCases(), // AsinhCases(), AsinhCases())))); template std::vector CeilCases() { return { C({T(0)}, T(0)), C({-T(0)}, -T(0)), C({-T(1.5)}, -T(1.0)), C({T(1.5)}, T(2.0)), C({T::Lowest()}, T::Lowest()), C({T::Highest()}, T::Highest()), C({Vec(T(0), T(1.5), -T(1.5))}, Vec(T(0), T(2.0), -T(1.0))), }; } INSTANTIATE_TEST_SUITE_P( // Ceil, ResolverConstEvalBuiltinTest, testing::Combine( testing::Values(sem::BuiltinType::kCeil), testing::ValuesIn(Concat(CeilCases(), CeilCases(), CeilCases())))); template std::vector ClampCases() { return { C({T(0), T(0), T(0)}, T(0)), C({T(0), T(42), T::Highest()}, T(42)), C({T::Lowest(), T(0), T(42)}, T(0)), C({T(0), T::Lowest(), T::Highest()}, T(0)), C({T(0), T::Highest(), T::Lowest()}, T::Lowest()), C({T::Highest(), T::Highest(), T::Highest()}, T::Highest()), C({T::Lowest(), T::Lowest(), T::Lowest()}, T::Lowest()), C({T::Highest(), T::Lowest(), T::Highest()}, T::Highest()), C({T::Lowest(), T::Lowest(), T::Highest()}, T::Lowest()), // Vector tests C({Vec(T(0), T(0)), // Vec(T(0), T(42)), // Vec(T(0), T::Highest())}, // Vec(T(0), T(42))), // C({Vec(T::Lowest(), T(0), T(0)), // Vec(T(0), T::Lowest(), T::Highest()), // Vec(T(42), T::Highest(), T::Lowest())}, // Vec(T(0), T(0), T::Lowest())), }; } INSTANTIATE_TEST_SUITE_P( // Clamp, ResolverConstEvalBuiltinTest, testing::Combine(testing::Values(sem::BuiltinType::kClamp), testing::ValuesIn(Concat(ClampCases(), // ClampCases(), ClampCases(), ClampCases(), ClampCases(), ClampCases())))); template std::vector CosCases() { return { C({-T(0)}, T(1)), C({T(0)}, T(1)), C({T(0.75)}, T(0.7316888689)).FloatComp(), // Vector test C({Vec(T(0), -T(0), T(0.75))}, Vec(T(1), T(1), T(0.7316888689))).FloatComp(), }; } INSTANTIATE_TEST_SUITE_P( // Cos, ResolverConstEvalBuiltinTest, testing::Combine(testing::Values(sem::BuiltinType::kCos), testing::ValuesIn(Concat(CosCases(), // CosCases(), CosCases())))); template std::vector CoshCases() { auto error_msg = [](auto a) { return "12:34 error: " + OverflowErrorMessage(a, FriendlyName()); }; return { C({T(0)}, T(1)), C({-T(0)}, T(1)), C({T(1)}, T(1.5430806348)).FloatComp(), C({T(.75)}, T(1.2946832847)).FloatComp(), // Vector tests C({Vec(T(0), -T(0), T(1))}, Vec(T(1), T(1), T(1.5430806348))).FloatComp(), E({T(10000)}, error_msg(T::Inf())), }; } INSTANTIATE_TEST_SUITE_P( // Cosh, ResolverConstEvalBuiltinTest, testing::Combine(testing::Values(sem::BuiltinType::kCosh), testing::ValuesIn(Concat(CoshCases(), // CoshCases(), CoshCases())))); template std::vector CountLeadingZerosCases() { using B = BitValues; return { C({B::Lsh(1, 31)}, T(0)), // C({B::Lsh(1, 30)}, T(1)), // C({B::Lsh(1, 29)}, T(2)), // C({B::Lsh(1, 28)}, T(3)), //... C({B::Lsh(1, 3)}, T(28)), // C({B::Lsh(1, 2)}, T(29)), // C({B::Lsh(1, 1)}, T(30)), // C({B::Lsh(1, 0)}, T(31)), C({T(0b1111'0000'1111'0000'1111'0000'1111'0000)}, T(0)), C({T(0b0111'1000'0111'1000'0111'1000'0111'1000)}, T(1)), C({T(0b0011'1100'0011'1100'0011'1100'0011'1100)}, T(2)), C({T(0b0001'1110'0001'1110'0001'1110'0001'1110)}, T(3)), //... C({T(0b0000'0000'0000'0000'0000'0000'0000'0111)}, T(29)), C({T(0b0000'0000'0000'0000'0000'0000'0000'0011)}, T(30)), C({T(0b0000'0000'0000'0000'0000'0000'0000'0001)}, T(31)), C({T(0b0000'0000'0000'0000'0000'0000'0000'0000)}, T(32)), // Same as above, but remove leading 0 C({T(0b1111'1000'0111'1000'0111'1000'0111'1000)}, T(0)), C({T(0b1011'1100'0011'1100'0011'1100'0011'1100)}, T(0)), C({T(0b1001'1110'0001'1110'0001'1110'0001'1110)}, T(0)), //... C({T(0b1000'0000'0000'0000'0000'0000'0000'0111)}, T(0)), C({T(0b1000'0000'0000'0000'0000'0000'0000'0011)}, T(0)), C({T(0b1000'0000'0000'0000'0000'0000'0000'0001)}, T(0)), C({T(0b1000'0000'0000'0000'0000'0000'0000'0000)}, T(0)), // Vector tests C({Vec(B::Lsh(1, 31), B::Lsh(1, 30), B::Lsh(1, 29))}, Vec(T(0), T(1), T(2))), C({Vec(B::Lsh(1, 2), B::Lsh(1, 1), B::Lsh(1, 0))}, Vec(T(29), T(30), T(31))), }; } INSTANTIATE_TEST_SUITE_P( // CountLeadingZeros, ResolverConstEvalBuiltinTest, testing::Combine(testing::Values(sem::BuiltinType::kCountLeadingZeros), testing::ValuesIn(Concat(CountLeadingZerosCases(), // CountLeadingZerosCases())))); template std::vector CountTrailingZerosCases() { using B = BitValues; return { C({B::Lsh(1, 31)}, T(31)), // C({B::Lsh(1, 30)}, T(30)), // C({B::Lsh(1, 29)}, T(29)), // C({B::Lsh(1, 28)}, T(28)), //... C({B::Lsh(1, 3)}, T(3)), // C({B::Lsh(1, 2)}, T(2)), // C({B::Lsh(1, 1)}, T(1)), // C({B::Lsh(1, 0)}, T(0)), C({T(0b0000'1111'0000'1111'0000'1111'0000'1111)}, T(0)), C({T(0b0001'1110'0001'1110'0001'1110'0001'1110)}, T(1)), C({T(0b0011'1100'0011'1100'0011'1100'0011'1100)}, T(2)), C({T(0b0111'1000'0111'1000'0111'1000'0111'1000)}, T(3)), //... C({T(0b1110'0000'0000'0000'0000'0000'0000'0000)}, T(29)), C({T(0b1100'0000'0000'0000'0000'0000'0000'0000)}, T(30)), C({T(0b1000'0000'0000'0000'0000'0000'0000'0000)}, T(31)), C({T(0b0000'0000'0000'0000'0000'0000'0000'0000)}, T(32)), //// Same as above, but remove trailing 0 C({T(0b0001'1110'0001'1110'0001'1110'0001'1111)}, T(0)), C({T(0b0011'1100'0011'1100'0011'1100'0011'1101)}, T(0)), C({T(0b0111'1000'0111'1000'0111'1000'0111'1001)}, T(0)), //... C({T(0b1110'0000'0000'0000'0000'0000'0000'0001)}, T(0)), C({T(0b1100'0000'0000'0000'0000'0000'0000'0001)}, T(0)), C({T(0b1000'0000'0000'0000'0000'0000'0000'0001)}, T(0)), C({T(0b0000'0000'0000'0000'0000'0000'0000'0001)}, T(0)), // Vector tests C({Vec(B::Lsh(1, 31), B::Lsh(1, 30), B::Lsh(1, 29))}, Vec(T(31), T(30), T(29))), C({Vec(B::Lsh(1, 2), B::Lsh(1, 1), B::Lsh(1, 0))}, Vec(T(2), T(1), T(0))), }; } INSTANTIATE_TEST_SUITE_P( // CountTrailingZeros, ResolverConstEvalBuiltinTest, testing::Combine(testing::Values(sem::BuiltinType::kCountTrailingZeros), testing::ValuesIn(Concat(CountTrailingZerosCases(), // CountTrailingZerosCases())))); template std::vector CountOneBitsCases() { using B = BitValues; return { C({T(0)}, T(0)), // C({B::Lsh(1, 31)}, T(1)), // C({B::Lsh(1, 30)}, T(1)), // C({B::Lsh(1, 29)}, T(1)), // C({B::Lsh(1, 28)}, T(1)), //... C({B::Lsh(1, 3)}, T(1)), // C({B::Lsh(1, 2)}, T(1)), // C({B::Lsh(1, 1)}, T(1)), // C({B::Lsh(1, 0)}, T(1)), C({T(0b1010'1010'1010'1010'1010'1010'1010'1010)}, T(16)), C({T(0b0000'1111'0000'1111'0000'1111'0000'1111)}, T(16)), C({T(0b0101'0000'0000'0000'0000'0000'0000'0101)}, T(4)), // Vector tests C({Vec(B::Lsh(1, 31), B::Lsh(1, 30), B::Lsh(1, 29))}, Vec(T(1), T(1), T(1))), C({Vec(B::Lsh(1, 2), B::Lsh(1, 1), B::Lsh(1, 0))}, Vec(T(1), T(1), T(1))), C({Vec(T(0b1010'1010'1010'1010'1010'1010'1010'1010), T(0b0000'1111'0000'1111'0000'1111'0000'1111), T(0b0101'0000'0000'0000'0000'0000'0000'0101))}, Vec(T(16), T(16), T(4))), }; } INSTANTIATE_TEST_SUITE_P( // CountOneBits, ResolverConstEvalBuiltinTest, testing::Combine(testing::Values(sem::BuiltinType::kCountOneBits), testing::ValuesIn(Concat(CountOneBitsCases(), // CountOneBitsCases())))); template std::vector CrossCases() { constexpr auto vec_x = [](T v) { return Vec(T(v), T(0), T(0)); }; constexpr auto vec_y = [](T v) { return Vec(T(0), T(v), T(0)); }; constexpr auto vec_z = [](T v) { return Vec(T(0), T(0), T(v)); }; const auto zero = Vec(T(0), T(0), T(0)); const auto unit_x = vec_x(T(1)); const auto unit_y = vec_y(T(1)); const auto unit_z = vec_z(T(1)); const auto neg_unit_x = vec_x(-T(1)); const auto neg_unit_y = vec_y(-T(1)); const auto neg_unit_z = vec_z(-T(1)); const auto highest_x = vec_x(T::Highest()); const auto highest_y = vec_y(T::Highest()); const auto highest_z = vec_z(T::Highest()); const auto smallest_x = vec_x(T::Smallest()); const auto smallest_y = vec_y(T::Smallest()); const auto smallest_z = vec_z(T::Smallest()); const auto lowest_x = vec_x(T::Lowest()); const auto lowest_y = vec_y(T::Lowest()); const auto lowest_z = vec_z(T::Lowest()); std::vector r = { C({zero, zero}, zero), C({unit_x, unit_x}, zero), C({unit_y, unit_y}, zero), C({unit_z, unit_z}, zero), C({smallest_x, smallest_x}, zero), C({smallest_y, smallest_y}, zero), C({smallest_z, smallest_z}, zero), C({lowest_x, lowest_x}, zero), C({lowest_y, lowest_y}, zero), C({lowest_z, lowest_z}, zero), C({highest_x, highest_x}, zero), C({highest_y, highest_y}, zero), C({highest_z, highest_z}, zero), C({smallest_x, highest_x}, zero), C({smallest_y, highest_y}, zero), C({smallest_z, highest_z}, zero), C({unit_x, neg_unit_x}, zero).PosOrNeg(), C({unit_y, neg_unit_y}, zero).PosOrNeg(), C({unit_z, neg_unit_z}, zero).PosOrNeg(), C({unit_x, unit_y}, unit_z), C({unit_y, unit_x}, neg_unit_z), C({unit_z, unit_x}, unit_y), C({unit_x, unit_z}, neg_unit_y), C({unit_y, unit_z}, unit_x), C({unit_z, unit_y}, neg_unit_x), C({vec_x(T(1)), vec_y(T(2))}, vec_z(T(2))), C({vec_y(T(1)), vec_x(T(2))}, vec_z(-T(2))), C({vec_x(T(2)), vec_y(T(3))}, vec_z(T(6))), C({vec_y(T(2)), vec_x(T(3))}, vec_z(-T(6))), C({Vec(T(1), T(2), T(3)), Vec(T(1), T(5), T(7))}, Vec(T(-1), T(-4), T(3))), C({Vec(T(33), T(44), T(55)), Vec(T(13), T(42), T(39))}, Vec(T(-594), T(-572), T(814))), C({Vec(T(3.5), T(4), T(5.5)), Vec(T(1), T(4.5), T(3.5))}, Vec(T(-10.75), T(-6.75), T(11.75))), }; std::string pos_error_msg = "12:34 error: " + OverflowErrorMessage(T::Highest(), "*", T::Highest()); std::string neg_error_msg = "12:34 error: " + OverflowErrorMessage(T::Lowest(), "*", T::Lowest()); ConcatInto( // r, std::vector{ E({highest_x, highest_y}, pos_error_msg), E({highest_y, highest_x}, pos_error_msg), E({highest_z, highest_x}, pos_error_msg), E({highest_x, highest_z}, pos_error_msg), E({highest_y, highest_z}, pos_error_msg), E({highest_z, highest_y}, pos_error_msg), E({lowest_x, lowest_y}, neg_error_msg), E({lowest_y, lowest_x}, neg_error_msg), E({lowest_z, lowest_x}, neg_error_msg), E({lowest_x, lowest_z}, neg_error_msg), E({lowest_y, lowest_z}, neg_error_msg), E({lowest_z, lowest_y}, neg_error_msg), }); return r; } INSTANTIATE_TEST_SUITE_P( // Cross, ResolverConstEvalBuiltinTest, testing::Combine(testing::Values(sem::BuiltinType::kCross), testing::ValuesIn(Concat(CrossCases(), // CrossCases(), // CrossCases())))); template std::vector DistanceCases() { auto error_msg = [](auto a, const char* op, auto b) { return "12:34 error: " + OverflowErrorMessage(a, op, b) + R"( 12:34 note: when calculating distance)"; }; return { C({T(0), T(0)}, T(0)), // length(-5) -> 5 C({T(30), T(35)}, T(5)), C({Vec(T(30), T(20)), Vec(T(25), T(15))}, Val(T(7.0710678119))).FloatComp(), E({T::Lowest(), T::Highest()}, error_msg(T::Lowest(), "-", T::Highest())), E({Vec(T::Highest(), T::Highest()), Vec(T(1), T(1))}, error_msg(T(T::Highest() - T(1)), "*", T(T::Highest() - T(1)))), }; } INSTANTIATE_TEST_SUITE_P( // Distance, ResolverConstEvalBuiltinTest, testing::Combine(testing::Values(sem::BuiltinType::kDistance), testing::ValuesIn(Concat(DistanceCases(), // DistanceCases(), // DistanceCases())))); template std::vector DotCases() { auto r = std::vector{ C({Vec(T(0), T(0)), Vec(T(0), T(0))}, Val(T(0))), C({Vec(T(0), T(0), T(0)), Vec(T(0), T(0), T(0))}, Val(T(0))), C({Vec(T(0), T(0), T(0), T(0)), Vec(T(0), T(0), T(0), T(0))}, Val(T(0))), C({Vec(T(1), T(2), T(3), T(4)), Vec(T(5), T(6), T(7), T(8))}, Val(T(70))), C({Vec(T(1), T(1)), Vec(T(1), T(1))}, Val(T(2))), C({Vec(T(1), T(2)), Vec(T(2), T(1))}, Val(T(4))), C({Vec(T(2), T(2)), Vec(T(2), T(2))}, Val(T(8))), C({Vec(T::Highest(), T::Highest()), Vec(T(1), T(0))}, Val(T::Highest())), C({Vec(T::Lowest(), T::Lowest()), Vec(T(1), T(0))}, Val(T::Lowest())), }; if constexpr (IsAbstract || IsFloatingPoint) { auto error_msg = [](auto a, const char* op, auto b) { return "12:34 error: " + OverflowErrorMessage(a, op, b) + R"( 12:34 note: when calculating dot)"; }; ConcatInto( // r, std::vector{ E({Vec(T::Highest(), T::Highest()), Vec(T(1), T(1))}, error_msg(T::Highest(), "+", T::Highest())), E({Vec(T::Lowest(), T::Lowest()), Vec(T(1), T(1))}, error_msg(T::Lowest(), "+", T::Lowest())), }); } else { // Overflow is not an error for concrete integrals ConcatInto( // r, std::vector{ C({Vec(T::Highest(), T::Highest()), Vec(T(1), T(1))}, Val(Add(T::Highest(), T::Highest()))), C({Vec(T::Lowest(), T::Lowest()), Vec(T(1), T(1))}, Val(Add(T::Lowest(), T::Lowest()))), }); } return r; } INSTANTIATE_TEST_SUITE_P( // Dot, ResolverConstEvalBuiltinTest, testing::Combine(testing::Values(sem::BuiltinType::kDot), testing::ValuesIn(Concat(DotCases(), // DotCases(), // DotCases(), // DotCases(), // DotCases(), // DotCases())))); template std::vector DeterminantCases() { auto error_msg = [](auto a, const char* op, auto b) { return "12:34 error: " + OverflowErrorMessage(a, op, b) + R"( 12:34 note: when calculating determinant)"; }; auto r = std::vector{ // All zero == 0 C({Mat({T(0), T(0)}, // {T(0), T(0)})}, // Val(T(0))), C({Mat({T(0), T(0), T(0)}, // {T(0), T(0), T(0)}, // {T(0), T(0), T(0)})}, // Val(T(0))), C({Mat({T(0), T(0), T(0), T(0)}, // {T(0), T(0), T(0), T(0)}, // {T(0), T(0), T(0), T(0)}, // {T(0), T(0), T(0), T(0)})}, // Val(T(0))), // All same == 0 C({Mat({T(42), T(42)}, // {T(42), T(42)})}, // Val(T(0))), C({Mat({T(42), T(42), T(42)}, // {T(42), T(42), T(42)}, // {T(42), T(42), T(42)})}, // Val(T(0))), C({Mat({T(42), T(42), T(42), T(42)}, // {T(42), T(42), T(42), T(42)}, // {T(42), T(42), T(42), T(42)}, // {T(42), T(42), T(42), T(42)})}, // Val(T(0))), // Various values C({Mat({-T(2), T(17)}, // {T(5), T(45)})}, // Val(-T(175))), C({Mat({T(4), T(6), -T(13)}, // {T(12), T(5), T(8)}, // {T(9), T(17), T(16)})}, // Val(-T(3011))), C({Mat({T(2), T(9), T(8), T(1)}, // {-T(4), T(11), -T(3), T(7)}, // {T(6), T(5), T(12), -T(6)}, // {T(3), -T(10), T(4), -T(7)})}, // Val(T(469))), // Overflow during multiply E({Mat({T::Highest(), T(0)}, // {T(0), T(2)})}, // error_msg(T::Highest(), "*", T(2))), // Overflow during subtract E({Mat({T::Highest(), T::Lowest()}, // {T(1), T(1)})}, // error_msg(T::Highest(), "-", T::Lowest())), }; return r; } INSTANTIATE_TEST_SUITE_P( // Determinant, ResolverConstEvalBuiltinTest, testing::Combine(testing::Values(sem::BuiltinType::kDeterminant), testing::ValuesIn(Concat(DeterminantCases(), // DeterminantCases(), // DeterminantCases())))); template std::vector FaceForwardCases() { // Rotate v by degs around Z axis auto rotate = [&](const Value& v, float degs) { auto x = builder::As(v.args[0]); auto y = builder::As(v.args[1]); auto z = builder::As(v.args[2]); auto rads = T(degs) * kPi / T(180); auto x2 = T(x * std::cos(rads) - y * std::sin(rads)); auto y2 = T(x * std::sin(rads) + y * std::cos(rads)); return Vec(x2, y2, z); }; // An arbitrary input vector and its negation, used for e1 args to FaceForward auto pos_vec = Vec(T(1), T(2), T(3)); auto neg_vec = Vec(-T(1), -T(2), -T(3)); // An arbitrary vector in the xy plane, used for e2 and e3 args to FaceForward. auto fwd_xy = Vec(T(1.23), T(4.56), T(0)); std::vector r = { C({pos_vec, fwd_xy, rotate(fwd_xy, 85)}, neg_vec), C({pos_vec, fwd_xy, rotate(fwd_xy, 85)}, neg_vec), C({pos_vec, fwd_xy, rotate(fwd_xy, 95)}, pos_vec), C({pos_vec, fwd_xy, rotate(fwd_xy, -95)}, pos_vec), C({pos_vec, fwd_xy, rotate(fwd_xy, 180)}, pos_vec), C({pos_vec, rotate(fwd_xy, 33), rotate(fwd_xy, 33 + 85)}, neg_vec), C({pos_vec, rotate(fwd_xy, 33), rotate(fwd_xy, 33 - 85)}, neg_vec), C({pos_vec, rotate(fwd_xy, 33), rotate(fwd_xy, 33 + 95)}, pos_vec), C({pos_vec, rotate(fwd_xy, 33), rotate(fwd_xy, 33 - 95)}, pos_vec), C({pos_vec, rotate(fwd_xy, 33), rotate(fwd_xy, 33 + 180)}, pos_vec), C({pos_vec, rotate(fwd_xy, 234), rotate(fwd_xy, 234 + 85)}, neg_vec), C({pos_vec, rotate(fwd_xy, 234), rotate(fwd_xy, 234 - 85)}, neg_vec), C({pos_vec, rotate(fwd_xy, 234), rotate(fwd_xy, 234 + 95)}, pos_vec), C({pos_vec, rotate(fwd_xy, 234), rotate(fwd_xy, 234 - 95)}, pos_vec), C({pos_vec, rotate(fwd_xy, 234), rotate(fwd_xy, 234 + 180)}, pos_vec), // Same, but swap input and result vectors C({neg_vec, fwd_xy, rotate(fwd_xy, 85)}, pos_vec), C({neg_vec, fwd_xy, rotate(fwd_xy, 85)}, pos_vec), C({neg_vec, fwd_xy, rotate(fwd_xy, 95)}, neg_vec), C({neg_vec, fwd_xy, rotate(fwd_xy, -95)}, neg_vec), C({neg_vec, fwd_xy, rotate(fwd_xy, 180)}, neg_vec), C({neg_vec, rotate(fwd_xy, 33), rotate(fwd_xy, 33 + 85)}, pos_vec), C({neg_vec, rotate(fwd_xy, 33), rotate(fwd_xy, 33 - 85)}, pos_vec), C({neg_vec, rotate(fwd_xy, 33), rotate(fwd_xy, 33 + 95)}, neg_vec), C({neg_vec, rotate(fwd_xy, 33), rotate(fwd_xy, 33 - 95)}, neg_vec), C({neg_vec, rotate(fwd_xy, 33), rotate(fwd_xy, 33 + 180)}, neg_vec), C({neg_vec, rotate(fwd_xy, 234), rotate(fwd_xy, 234 + 85)}, pos_vec), C({neg_vec, rotate(fwd_xy, 234), rotate(fwd_xy, 234 - 85)}, pos_vec), C({neg_vec, rotate(fwd_xy, 234), rotate(fwd_xy, 234 + 95)}, neg_vec), C({neg_vec, rotate(fwd_xy, 234), rotate(fwd_xy, 234 - 95)}, neg_vec), C({neg_vec, rotate(fwd_xy, 234), rotate(fwd_xy, 234 + 180)}, neg_vec), }; auto error_msg = [](auto a, const char* op, auto b) { return "12:34 error: " + OverflowErrorMessage(a, op, b) + R"( 12:34 note: when calculating faceForward)"; }; ConcatInto( // r, std::vector{ // Overflow the dot product operation E({pos_vec, Vec(T::Highest(), T::Highest(), T(0)), Vec(T(1), T(1), T(0))}, error_msg(T::Highest(), "+", T::Highest())), E({pos_vec, Vec(T::Lowest(), T::Lowest(), T(0)), Vec(T(1), T(1), T(0))}, error_msg(T::Lowest(), "+", T::Lowest())), }); return r; } INSTANTIATE_TEST_SUITE_P( // FaceForward, ResolverConstEvalBuiltinTest, testing::Combine(testing::Values(sem::BuiltinType::kFaceForward), testing::ValuesIn(Concat(FaceForwardCases(), // FaceForwardCases(), // FaceForwardCases())))); template std::vector FirstLeadingBitCases() { using B = BitValues; auto r = std::vector{ // Both signed and unsigned return T(-1) for input 0 C({T(0)}, T(-1)), C({B::Lsh(1, 30)}, T(30)), // C({B::Lsh(1, 29)}, T(29)), // C({B::Lsh(1, 28)}, T(28)), //... C({B::Lsh(1, 3)}, T(3)), // C({B::Lsh(1, 2)}, T(2)), // C({B::Lsh(1, 1)}, T(1)), // C({B::Lsh(1, 0)}, T(0)), C({T(0b0000'0000'0100'1000'1000'1000'0000'0000)}, T(22)), C({T(0b0000'0000'0000'0100'1000'1000'0000'0000)}, T(18)), // Vector tests C({Vec(B::Lsh(1, 30), B::Lsh(1, 29), B::Lsh(1, 28))}, Vec(T(30), T(29), T(28))), C({Vec(B::Lsh(1, 2), B::Lsh(1, 1), B::Lsh(1, 0))}, Vec(T(2), T(1), T(0))), }; ConcatIntoIf>( // r, std::vector{ C({B::Lsh(1, 31)}, T(31)), C({T(0b1111'1111'1111'1111'1111'1111'1111'1110)}, T(31)), C({T(0b1111'1111'1111'1111'1111'1111'1111'1100)}, T(31)), C({T(0b1111'1111'1111'1111'1111'1111'1111'1000)}, T(31)), //... C({T(0b1110'0000'0000'0000'0000'0000'0000'0000)}, T(31)), C({T(0b1100'0000'0000'0000'0000'0000'0000'0000)}, T(31)), C({T(0b1000'0000'0000'0000'0000'0000'0000'0000)}, T(31)), }); ConcatIntoIf>( // r, std::vector{ // Signed returns -1 for input -1 C({T(-1)}, T(-1)), C({B::Lsh(1, 31)}, T(30)), C({T(0b1111'1111'1111'1111'1111'1111'1111'1110)}, T(0)), C({T(0b1111'1111'1111'1111'1111'1111'1111'1100)}, T(1)), C({T(0b1111'1111'1111'1111'1111'1111'1111'1000)}, T(2)), //... C({T(0b1110'0000'0000'0000'0000'0000'0000'0000)}, T(28)), C({T(0b1100'0000'0000'0000'0000'0000'0000'0000)}, T(29)), C({T(0b1000'0000'0000'0000'0000'0000'0000'0000)}, T(30)), }); return r; } INSTANTIATE_TEST_SUITE_P( // FirstLeadingBit, ResolverConstEvalBuiltinTest, testing::Combine(testing::Values(sem::BuiltinType::kFirstLeadingBit), testing::ValuesIn(Concat(FirstLeadingBitCases(), // FirstLeadingBitCases())))); template std::vector FirstTrailingBitCases() { using B = BitValues; auto r = std::vector{ C({T(0)}, T(-1)), C({B::Lsh(1, 31)}, T(31)), // C({B::Lsh(1, 30)}, T(30)), // C({B::Lsh(1, 29)}, T(29)), // C({B::Lsh(1, 28)}, T(28)), //... C({B::Lsh(1, 3)}, T(3)), // C({B::Lsh(1, 2)}, T(2)), // C({B::Lsh(1, 1)}, T(1)), // C({B::Lsh(1, 0)}, T(0)), C({T(0b0000'0000'0100'1000'1000'1000'0000'0000)}, T(11)), C({T(0b0000'0100'1000'1000'1000'0000'0000'0000)}, T(15)), // Vector tests C({Vec(B::Lsh(1, 31), B::Lsh(1, 30), B::Lsh(1, 29))}, Vec(T(31), T(30), T(29))), C({Vec(B::Lsh(1, 2), B::Lsh(1, 1), B::Lsh(1, 0))}, Vec(T(2), T(1), T(0))), }; return r; } INSTANTIATE_TEST_SUITE_P( // FirstTrailingBit, ResolverConstEvalBuiltinTest, testing::Combine(testing::Values(sem::BuiltinType::kFirstTrailingBit), testing::ValuesIn(Concat(FirstTrailingBitCases(), // FirstTrailingBitCases())))); template std::vector FloorCases() { return { C({T(0)}, T(0)), C({-T(0)}, -T(0)), C({-T(1.5)}, -T(2.0)), C({T(1.5)}, T(1.0)), C({T::Lowest()}, T::Lowest()), C({T::Highest()}, T::Highest()), C({Vec(T(0), T(1.5), -T(1.5))}, Vec(T(0), T(1.0), -T(2.0))), }; } INSTANTIATE_TEST_SUITE_P( // Floor, ResolverConstEvalBuiltinTest, testing::Combine(testing::Values(sem::BuiltinType::kFloor), testing::ValuesIn(Concat(FloorCases(), // FloorCases(), FloorCases())))); template std::vector FmaCases() { auto error_msg = [](auto a, const char* op, auto b) { return "12:34 error: " + OverflowErrorMessage(a, op, b) + R"( 12:34 note: when calculating fma)"; }; return { C({T(0), T(0), T(0)}, T(0)), C({T(1), T(2), T(3)}, T(5)), C({Vec(T(1), T(2.5), -T(1)), Vec(T(2), T(2.5), T(1)), Vec(T(4), T(3.75), -T(2))}, Vec(T(6), T(10), -T(3))), E({T::Highest(), T::Highest(), T(0)}, error_msg(T::Highest(), "*", T::Highest())), E({T::Highest(), T(1), T::Highest()}, error_msg(T::Highest(), "+", T::Highest())), }; } INSTANTIATE_TEST_SUITE_P( // Fma, ResolverConstEvalBuiltinTest, testing::Combine(testing::Values(sem::BuiltinType::kFma), testing::ValuesIn(Concat(FmaCases(), // FmaCases(), FmaCases())))); template std::vector FrexpCases() { using F = T; // fract type using E = std::conditional_t, AInt, i32>; // exp type std::vector cases = { // Scalar tests // in fract exp C({T(-3.5)}, {F(-0.875), E(2)}), // C({T(-3.0)}, {F(-0.750), E(2)}), // C({T(-2.5)}, {F(-0.625), E(2)}), // C({T(-2.0)}, {F(-0.500), E(2)}), // C({T(-1.5)}, {F(-0.750), E(1)}), // C({T(-1.0)}, {F(-0.500), E(1)}), // C({T(+0.0)}, {F(+0.000), E(0)}), // C({T(+1.0)}, {F(+0.500), E(1)}), // C({T(+1.5)}, {F(+0.750), E(1)}), // C({T(+2.0)}, {F(+0.500), E(2)}), // C({T(+2.5)}, {F(+0.625), E(2)}), // C({T(+3.0)}, {F(+0.750), E(2)}), // C({T(+3.5)}, {F(+0.875), E(2)}), // // Vector tests // in fract exp C({Vec(T(-2.5), T(+1.0))}, {Vec(F(-0.625), F(+0.500)), Vec(E(2), E(1))}), C({Vec(T(+3.5), T(-2.5))}, {Vec(F(+0.875), F(-0.625)), Vec(E(2), E(2))}), }; ConcatIntoIf>(cases, std::vector{ C({T::Highest()}, {F(0x0.ffep0), E(16)}), // C({T::Lowest()}, {F(-0x0.ffep0), E(16)}), // C({T::Smallest()}, {F(0.5), E(-13)}), // }); ConcatIntoIf>(cases, std::vector{ C({T::Highest()}, {F(0x0.ffffffp0), E(128)}), // C({T::Lowest()}, {F(-0x0.ffffffp0), E(128)}), // C({T::Smallest()}, {F(0.5), E(-125)}), // }); ConcatIntoIf>( cases, std::vector{ C({T::Highest()}, {F(0x0.fffffffffffff8p0), E(1024)}), // C({T::Lowest()}, {F(-0x0.fffffffffffff8p0), E(1024)}), // C({T::Smallest()}, {F(0.5), E(-1021)}), // }); return cases; } INSTANTIATE_TEST_SUITE_P( // Frexp, ResolverConstEvalBuiltinTest, testing::Combine(testing::Values(sem::BuiltinType::kFrexp), testing::ValuesIn(Concat(FrexpCases(), // FrexpCases(), // FrexpCases())))); template std::vector InsertBitsCases() { using UT = Number>>; auto e = /* */ T(0b0101'1100'0011'1010'0101'1100'0011'1010); auto newbits = T{0b1010'0011'1100'0101'1010'0011'1100'0101}; auto r = std::vector{ // args: e, newbits, offset, count // If count is 0, result is e C({e, newbits, UT(0), UT(0)}, e), // C({e, newbits, UT(1), UT(0)}, e), // C({e, newbits, UT(2), UT(0)}, e), // C({e, newbits, UT(3), UT(0)}, e), // // ... C({e, newbits, UT(29), UT(0)}, e), // C({e, newbits, UT(30), UT(0)}, e), // C({e, newbits, UT(31), UT(0)}, e), // Copy 1 to 32 bits of newbits to e at offset 0 C({e, newbits, UT(0), UT(1)}, T(0b0101'1100'0011'1010'0101'1100'0011'1011)), C({e, newbits, UT(0), UT(2)}, T(0b0101'1100'0011'1010'0101'1100'0011'1001)), C({e, newbits, UT(0), UT(3)}, T(0b0101'1100'0011'1010'0101'1100'0011'1101)), C({e, newbits, UT(0), UT(4)}, T(0b0101'1100'0011'1010'0101'1100'0011'0101)), C({e, newbits, UT(0), UT(5)}, T(0b0101'1100'0011'1010'0101'1100'0010'0101)), C({e, newbits, UT(0), UT(6)}, T(0b0101'1100'0011'1010'0101'1100'0000'0101)), // ... C({e, newbits, UT(0), UT(29)}, T(0b0100'0011'1100'0101'1010'0011'1100'0101)), C({e, newbits, UT(0), UT(30)}, T(0b0110'0011'1100'0101'1010'0011'1100'0101)), C({e, newbits, UT(0), UT(31)}, T(0b0010'0011'1100'0101'1010'0011'1100'0101)), C({e, newbits, UT(0), UT(32)}, T(0b1010'0011'1100'0101'1010'0011'1100'0101)), // Copy at varying offsets and counts C({e, newbits, UT(3), UT(8)}, T(0b0101'1100'0011'1010'0101'1110'0010'1010)), C({e, newbits, UT(8), UT(8)}, T(0b0101'1100'0011'1010'1100'0101'0011'1010)), C({e, newbits, UT(15), UT(1)}, T(0b0101'1100'0011'1010'1101'1100'0011'1010)), C({e, newbits, UT(16), UT(16)}, T(0b1010'0011'1100'0101'0101'1100'0011'1010)), // Vector tests C({Vec(T(0b1111'0000'1111'0000'1111'0000'1111'0000), // T(0b0000'1111'0000'1111'0000'1111'0000'1111), // T(0b1010'0101'1010'0101'1010'0101'1010'0101)), Vec(T(0b1111'1111'1111'1111'1111'1111'1111'1111), // T(0b1111'1111'1111'1111'1111'1111'1111'1111), // T(0b1111'1111'1111'1111'1111'1111'1111'1111)), Val(UT(3)), Val(UT(8))}, Vec(T(0b1111'0000'1111'0000'1111'0111'1111'1000), // T(0b0000'1111'0000'1111'0000'1111'1111'1111), // T(0b1010'0101'1010'0101'1010'0111'1111'1101))), }; const char* error_msg = "12:34 error: 'offset + 'count' must be less than or equal to the bit width of 'e'"; ConcatInto( // r, std::vector{ E({T(1), T(1), UT(33), UT(0)}, error_msg), // E({T(1), T(1), UT(34), UT(0)}, error_msg), // E({T(1), T(1), UT(1000), UT(0)}, error_msg), // E({T(1), T(1), UT::Highest(), UT()}, error_msg), // E({T(1), T(1), UT(0), UT(33)}, error_msg), // E({T(1), T(1), UT(0), UT(34)}, error_msg), // E({T(1), T(1), UT(0), UT(1000)}, error_msg), // E({T(1), T(1), UT(0), UT::Highest()}, error_msg), // E({T(1), T(1), UT(33), UT(33)}, error_msg), // E({T(1), T(1), UT(34), UT(34)}, error_msg), // E({T(1), T(1), UT(1000), UT(1000)}, error_msg), // E({T(1), T(1), UT::Highest(), UT(1)}, error_msg), E({T(1), T(1), UT(1), UT::Highest()}, error_msg), E({T(1), T(1), UT::Highest(), u32::Highest()}, error_msg), }); return r; } INSTANTIATE_TEST_SUITE_P( // InsertBits, ResolverConstEvalBuiltinTest, testing::Combine(testing::Values(sem::BuiltinType::kInsertBits), testing::ValuesIn(Concat(InsertBitsCases(), // InsertBitsCases())))); template std::vector InverseSqrtCases() { return { C({T(25)}, T(.2)), // Vector tests C({Vec(T(25), T(100))}, Vec(T(.2), T(.1))), E({T(0)}, "12:34 error: inverseSqrt must be called with a value > 0"), E({-T(0)}, "12:34 error: inverseSqrt must be called with a value > 0"), E({-T(25)}, "12:34 error: inverseSqrt must be called with a value > 0"), }; } INSTANTIATE_TEST_SUITE_P( // InverseSqrt, ResolverConstEvalBuiltinTest, testing::Combine(testing::Values(sem::BuiltinType::kInverseSqrt), testing::ValuesIn(Concat(InverseSqrtCases(), // InverseSqrtCases(), InverseSqrtCases())))); template std::vector DegreesAFloatCases() { return { C({T(0)}, T(0)), // C({-T(0)}, -T(0)), // C({T(0.698132)}, T(40)).FloatComp(), // C({-T(1.5708)}, -T(90.000214)).FloatComp(), // C({T(1.5708)}, T(90.000214)).FloatComp(), // C({T(6.28319)}, T(360.00027)).FloatComp(), }; } INSTANTIATE_TEST_SUITE_P( // DegreesAFloat, ResolverConstEvalBuiltinTest, testing::Combine(testing::Values(sem::BuiltinType::kDegrees), testing::ValuesIn(DegreesAFloatCases()))); template std::vector DegreesF32Cases() { return { C({T(0)}, T(0)), // C({-T(0)}, -T(0)), // C({T(0.698132)}, T(40)).FloatComp(), // C({-T(1.5708)}, -T(90.000206)).FloatComp(), // C({T(1.5708)}, T(90.000206)).FloatComp(), // C({T(6.28319)}, T(360.00024)).FloatComp(), }; } INSTANTIATE_TEST_SUITE_P( // DegreesF32, ResolverConstEvalBuiltinTest, testing::Combine(testing::Values(sem::BuiltinType::kDegrees), testing::ValuesIn(DegreesF32Cases()))); template std::vector DegreesF16Cases() { return { C({T(0)}, T(0)), // C({-T(0)}, -T(0)), // C({T(0.698132)}, T(39.96875)).FloatComp(), // C({-T(1.5708)}, -T(89.9375)).FloatComp(), // C({T(1.5708)}, T(89.9375)).FloatComp(), // C({T(6.28319)}, T(359.75)).FloatComp(), }; } INSTANTIATE_TEST_SUITE_P( // DegreesF16, ResolverConstEvalBuiltinTest, testing::Combine(testing::Values(sem::BuiltinType::kDegrees), testing::ValuesIn(DegreesF16Cases()))); template std::vector ExpCases() { auto error_msg = [](auto a) { return "12:34 error: " + OverflowExpErrorMessage("e", a); }; return {C({T(0)}, T(1)), // C({-T(0)}, T(1)), // C({T(2)}, T(7.3890562)).FloatComp(), C({-T(2)}, T(0.13533528)).FloatComp(), // C({T::Lowest()}, T(0)), E({T::Highest()}, error_msg(T::Highest()))}; } INSTANTIATE_TEST_SUITE_P( // Exp, ResolverConstEvalBuiltinTest, testing::Combine(testing::Values(sem::BuiltinType::kExp), testing::ValuesIn(Concat(ExpCases(), // ExpCases(), ExpCases())))); template std::vector Exp2Cases() { auto error_msg = [](auto a) { return "12:34 error: " + OverflowExpErrorMessage("2", a); }; return { C({T(0)}, T(1)), // C({-T(0)}, T(1)), // C({T(2)}, T(4.0)), C({-T(2)}, T(0.25)), // C({T::Lowest()}, T(0)), E({T::Highest()}, error_msg(T::Highest())), }; } INSTANTIATE_TEST_SUITE_P( // Exp2, ResolverConstEvalBuiltinTest, testing::Combine(testing::Values(sem::BuiltinType::kExp2), testing::ValuesIn(Concat(Exp2Cases(), // Exp2Cases(), Exp2Cases())))); template std::vector ExtractBitsCases() { using UT = Number>>; // If T is signed, fills most significant bits of `val` with 1s auto set_msbs_if_signed = [](T val) { if constexpr (IsSignedIntegral) { T result = T(~0); for (size_t b = 0; val; ++b) { if ((val & 1) == 0) { result = result & ~(1 << b); // Clear bit b } val = val >> 1; } return result; } else { return val; } }; auto e = T(0b10100011110001011010001111000101); auto f = T(0b01010101010101010101010101010101); auto g = T(0b11111010001111000101101000111100); auto r = std::vector{ // args: e, offset, count // If count is 0, result is 0 C({e, UT(0), UT(0)}, T(0)), // C({e, UT(1), UT(0)}, T(0)), // C({e, UT(2), UT(0)}, T(0)), // C({e, UT(3), UT(0)}, T(0)), // ... C({e, UT(29), UT(0)}, T(0)), // C({e, UT(30), UT(0)}, T(0)), // C({e, UT(31), UT(0)}, T(0)), // Extract at offset 0, varying counts C({e, UT(0), UT(1)}, set_msbs_if_signed(T(0b1))), // C({e, UT(0), UT(2)}, T(0b01)), // C({e, UT(0), UT(3)}, set_msbs_if_signed(T(0b101))), // C({e, UT(0), UT(4)}, T(0b0101)), // C({e, UT(0), UT(5)}, T(0b00101)), // C({e, UT(0), UT(6)}, T(0b000101)), // // ... C({e, UT(0), UT(28)}, T(0b0011110001011010001111000101)), // C({e, UT(0), UT(29)}, T(0b00011110001011010001111000101)), // C({e, UT(0), UT(30)}, set_msbs_if_signed(T(0b100011110001011010001111000101))), // C({e, UT(0), UT(31)}, T(0b0100011110001011010001111000101)), // C({e, UT(0), UT(32)}, T(0b10100011110001011010001111000101)), // // Extract at varying offsets and counts C({e, UT(0), UT(1)}, set_msbs_if_signed(T(0b1))), // C({e, UT(31), UT(1)}, set_msbs_if_signed(T(0b1))), // C({e, UT(3), UT(5)}, set_msbs_if_signed(T(0b11000))), // C({e, UT(4), UT(7)}, T(0b0111100)), // C({e, UT(10), UT(16)}, set_msbs_if_signed(T(0b1111000101101000))), // C({e, UT(10), UT(22)}, set_msbs_if_signed(T(0b1010001111000101101000))), // Vector tests C({Vec(e, f, g), // Val(UT(5)), Val(UT(8))}, // Vec(T(0b00011110), // set_msbs_if_signed(T(0b10101010)), // set_msbs_if_signed(T(0b11010001)))), }; const char* error_msg = "12:34 error: 'offset + 'count' must be less than or equal to the bit width of 'e'"; ConcatInto( // r, std::vector{ E({T(1), UT(33), UT(0)}, error_msg), E({T(1), UT(34), UT(0)}, error_msg), E({T(1), UT(1000), UT(0)}, error_msg), E({T(1), UT::Highest(), UT(0)}, error_msg), E({T(1), UT(0), UT(33)}, error_msg), E({T(1), UT(0), UT(34)}, error_msg), E({T(1), UT(0), UT(1000)}, error_msg), E({T(1), UT(0), UT::Highest()}, error_msg), E({T(1), UT(33), UT(33)}, error_msg), E({T(1), UT(34), UT(34)}, error_msg), E({T(1), UT(1000), UT(1000)}, error_msg), E({T(1), UT::Highest(), UT(1)}, error_msg), E({T(1), UT(1), UT::Highest()}, error_msg), E({T(1), UT::Highest(), UT::Highest()}, error_msg), }); return r; } INSTANTIATE_TEST_SUITE_P( // ExtractBits, ResolverConstEvalBuiltinTest, testing::Combine(testing::Values(sem::BuiltinType::kExtractBits), testing::ValuesIn(Concat(ExtractBitsCases(), // ExtractBitsCases())))); template std::vector LengthCases() { const auto kSqrtOfHighest = T(std::sqrt(T::Highest())); const auto kSqrtOfHighestSquared = T(kSqrtOfHighest * kSqrtOfHighest); auto error_msg = [](auto a, const char* op, auto b) { return "12:34 error: " + OverflowErrorMessage(a, op, b) + R"( 12:34 note: when calculating length)"; }; return { C({T(0)}, T(0)), C({Vec(T(0), T(0))}, Val(T(0))), C({Vec(T(0), T(0), T(0))}, Val(T(0))), C({Vec(T(0), T(0), T(0), T(0))}, Val(T(0))), C({T(1)}, T(1)), C({Vec(T(1), T(1))}, Val(T(std::sqrt(2)))), C({Vec(T(1), T(1), T(1))}, Val(T(std::sqrt(3)))), C({Vec(T(1), T(1), T(1), T(1))}, Val(T(std::sqrt(4)))), C({T(2)}, T(2)), C({Vec(T(2), T(2))}, Val(T(std::sqrt(8)))), C({Vec(T(2), T(2), T(2))}, Val(T(std::sqrt(12)))), C({Vec(T(2), T(2), T(2), T(2))}, Val(T(std::sqrt(16)))), C({Vec(T(2), T(3))}, Val(T(std::sqrt(13)))), C({Vec(T(2), T(3), T(4))}, Val(T(std::sqrt(29)))), C({Vec(T(2), T(3), T(4), T(5))}, Val(T(std::sqrt(54)))), C({T(-5)}, T(5)), C({T::Highest()}, T::Highest()), C({T::Lowest()}, T::Highest()), C({Vec(T(-2), T(-3), T(-4), T(-5))}, Val(T(std::sqrt(54)))), C({Vec(T(2), T(-3), T(4), T(-5))}, Val(T(std::sqrt(54)))), C({Vec(T(-2), T(3), T(-4), T(5))}, Val(T(std::sqrt(54)))), C({Vec(kSqrtOfHighest, T(0))}, Val(kSqrtOfHighest)).FloatComp(0.2), C({Vec(T(0), kSqrtOfHighest)}, Val(kSqrtOfHighest)).FloatComp(0.2), C({Vec(-kSqrtOfHighest, T(0))}, Val(kSqrtOfHighest)).FloatComp(0.2), C({Vec(T(0), -kSqrtOfHighest)}, Val(kSqrtOfHighest)).FloatComp(0.2), // Overflow when squaring a term E({Vec(T::Highest(), T(0))}, error_msg(T::Highest(), "*", T::Highest())), E({Vec(T(0), T::Highest())}, error_msg(T::Highest(), "*", T::Highest())), // Overflow when adding squared terms E({Vec(kSqrtOfHighest, kSqrtOfHighest)}, error_msg(kSqrtOfHighestSquared, "+", kSqrtOfHighestSquared)), }; } INSTANTIATE_TEST_SUITE_P( // Length, ResolverConstEvalBuiltinTest, testing::Combine(testing::Values(sem::BuiltinType::kLength), testing::ValuesIn(Concat(LengthCases(), // LengthCases(), LengthCases())))); template std::vector LogCases() { auto error_msg = [] { return "12:34 error: log must be called with a value > 0"; }; return {C({T(1)}, T(0)), // C({T(54.598150033)}, T(4)).FloatComp(0.002), // E({T::Lowest()}, error_msg()), E({T(0)}, error_msg()), E({-T(0)}, error_msg())}; } INSTANTIATE_TEST_SUITE_P( // Log, ResolverConstEvalBuiltinTest, testing::Combine(testing::Values(sem::BuiltinType::kLog), testing::ValuesIn(Concat(LogCases(), // LogCases(), LogCases())))); template std::vector LogF16Cases() { return { C({T::Highest()}, T(11.085938)).FloatComp(), }; } INSTANTIATE_TEST_SUITE_P( // LogF16, ResolverConstEvalBuiltinTest, testing::Combine(testing::Values(sem::BuiltinType::kLog), testing::ValuesIn(LogF16Cases()))); template std::vector LogF32Cases() { return { C({T::Highest()}, T(88.722839)).FloatComp(), }; } INSTANTIATE_TEST_SUITE_P( // LogF32, ResolverConstEvalBuiltinTest, testing::Combine(testing::Values(sem::BuiltinType::kLog), testing::ValuesIn(LogF32Cases()))); template std::vector LogAbstractCases() { return { C({T::Highest()}, T(709.78271)).FloatComp(), }; } INSTANTIATE_TEST_SUITE_P( // LogAbstract, ResolverConstEvalBuiltinTest, testing::Combine(testing::Values(sem::BuiltinType::kLog), testing::ValuesIn(LogAbstractCases()))); template std::vector Log2Cases() { auto error_msg = [] { return "12:34 error: log2 must be called with a value > 0"; }; return { C({T(1)}, T(0)), // C({T(4)}, T(2)), // E({T::Lowest()}, error_msg()), E({T(0)}, error_msg()), E({-T(0)}, error_msg()), }; } INSTANTIATE_TEST_SUITE_P( // Log2, ResolverConstEvalBuiltinTest, testing::Combine(testing::Values(sem::BuiltinType::kLog2), testing::ValuesIn(Concat(Log2Cases(), // Log2Cases(), Log2Cases())))); template std::vector Log2F16Cases() { return { C({T::Highest()}, T(15.9922)).FloatComp(), }; } INSTANTIATE_TEST_SUITE_P( // Log2F16, ResolverConstEvalBuiltinTest, testing::Combine(testing::Values(sem::BuiltinType::kLog2), testing::ValuesIn(Log2F16Cases()))); template std::vector Log2F32Cases() { return { C({T::Highest()}, T(128)).FloatComp(), }; } INSTANTIATE_TEST_SUITE_P( // Log2F32, ResolverConstEvalBuiltinTest, testing::Combine(testing::Values(sem::BuiltinType::kLog2), testing::ValuesIn(Log2F32Cases()))); template std::vector Log2AbstractCases() { return { C({T::Highest()}, T(1024)).FloatComp(), }; } INSTANTIATE_TEST_SUITE_P( // Log2Abstract, ResolverConstEvalBuiltinTest, testing::Combine(testing::Values(sem::BuiltinType::kLog2), testing::ValuesIn(Log2AbstractCases()))); template std::vector MaxCases() { return { C({T(0), T(0)}, T(0)), C({T(0), T::Highest()}, T::Highest()), C({T::Lowest(), T(0)}, T(0)), C({T::Highest(), T::Lowest()}, T::Highest()), C({T::Highest(), T::Highest()}, T::Highest()), C({T::Lowest(), T::Lowest()}, T::Lowest()), // Vector tests C({Vec(T(0), T(0)), Vec(T(0), T(42))}, Vec(T(0), T(42))), C({Vec(T::Lowest(), T(0)), Vec(T(0), T::Lowest())}, Vec(T(0), T(0))), C({Vec(T::Lowest(), T::Highest()), Vec(T::Highest(), T::Lowest())}, Vec(T::Highest(), T::Highest())), }; } INSTANTIATE_TEST_SUITE_P( // Max, ResolverConstEvalBuiltinTest, testing::Combine(testing::Values(sem::BuiltinType::kMax), testing::ValuesIn(Concat(MaxCases(), // MaxCases(), MaxCases(), MaxCases(), MaxCases(), MaxCases())))); template std::vector MinCases() { return {C({T(0), T(0)}, T(0)), // C({T(0), T(42)}, T(0)), // C({T::Lowest(), T(0)}, T::Lowest()), // C({T(0), T::Highest()}, T(0)), // C({T::Highest(), T::Lowest()}, T::Lowest()), C({T::Highest(), T::Highest()}, T::Highest()), C({T::Lowest(), T::Lowest()}, T::Lowest()), // Vector tests C({Vec(T(0), T(0)), Vec(T(0), T(42))}, Vec(T(0), T(0))), C({Vec(T::Lowest(), T(0), T(1)), Vec(T(0), T(42), T::Highest())}, Vec(T::Lowest(), T(0), T(1)))}; } INSTANTIATE_TEST_SUITE_P( // Min, ResolverConstEvalBuiltinTest, testing::Combine(testing::Values(sem::BuiltinType::kMin), testing::ValuesIn(Concat(MinCases(), // MinCases(), MinCases(), MinCases(), MinCases(), MinCases())))); template std::vector ModfCases() { return { // Scalar tests // in fract whole C({T(0.0)}, {T(0.0), T(0.0)}), // C({T(1.0)}, {T(0.0), T(1.0)}), // C({T(2.0)}, {T(0.0), T(2.0)}), // C({T(1.5)}, {T(0.5), T(1.0)}), // C({T(4.25)}, {T(0.25), T(4.0)}), // C({T(-1.0)}, {T(0.0), T(-1.0)}), // C({T(-2.0)}, {T(0.0), T(-2.0)}), // C({T(-1.5)}, {T(-0.5), T(-1.0)}), // C({T(-4.25)}, {T(-0.25), T(-4.0)}), // C({T::Lowest()}, {T(0.0), T::Lowest()}), // C({T::Highest()}, {T(0.0), T::Highest()}), // // Vector tests // in fract whole C({Vec(T(0.0), T(0.0))}, {Vec(T(0.0), T(0.0)), Vec(T(0.0), T(0.0))}), C({Vec(T(1.0), T(2.0))}, {Vec(T(0.0), T(0.0)), Vec(T(1), T(2))}), C({Vec(T(-2.0), T(1.0))}, {Vec(T(0.0), T(0.0)), Vec(T(-2), T(1))}), C({Vec(T(1.5), T(-2.25))}, {Vec(T(0.5), T(-0.25)), Vec(T(1.0), T(-2.0))}), C({Vec(T::Lowest(), T::Highest())}, {Vec(T(0.0), T(0.0)), Vec(T::Lowest(), T::Highest())}), }; } INSTANTIATE_TEST_SUITE_P( // Modf, ResolverConstEvalBuiltinTest, testing::Combine(testing::Values(sem::BuiltinType::kModf), testing::ValuesIn(Concat(ModfCases(), // ModfCases(), // ModfCases())))); template std::vector NormalizeCases() { auto error_msg = [&](auto a) { return "12:34 error: " + OverflowErrorMessage(a, "*", a) + R"( 12:34 note: when calculating normalize)"; }; return { C({Vec(T(2), T(4), T(2))}, Vec(T(0.4082482905), T(0.8164965809), T(0.4082482905))) .FloatComp(), C({Vec(T(2), T(0), T(0))}, Vec(T(1), T(0), T(0))), C({Vec(T(0), T(2), T(0))}, Vec(T(0), T(1), T(0))), C({Vec(T(0), T(0), T(2))}, Vec(T(0), T(0), T(1))), C({Vec(-T(2), T(0), T(0))}, Vec(-T(1), T(0), T(0))), C({Vec(T(0), -T(2), T(0))}, Vec(T(0), -T(1), T(0))), C({Vec(T(0), T(0), -T(2))}, Vec(T(0), T(0), -T(1))), E({Vec(T(0), T(0), T(0))}, "12:34 error: zero length vector can not be normalized"), E({Vec(T::Highest(), T::Highest(), T::Highest())}, error_msg(T::Highest())), }; } INSTANTIATE_TEST_SUITE_P( // Normalize, ResolverConstEvalBuiltinTest, testing::Combine(testing::Values(sem::BuiltinType::kNormalize), testing::ValuesIn(Concat(NormalizeCases(), // NormalizeCases(), // NormalizeCases())))); std::vector Pack4x8snormCases() { return { C({Vec(f32(0), f32(0), f32(0), f32(0))}, Val(u32(0x0000'0000))), C({Vec(f32(0), f32(0), f32(0), f32(-1))}, Val(u32(0x8100'0000))), C({Vec(f32(0), f32(0), f32(0), f32(1))}, Val(u32(0x7f00'0000))), C({Vec(f32(0), f32(0), f32(-1), f32(0))}, Val(u32(0x0081'0000))), C({Vec(f32(0), f32(1), f32(0), f32(0))}, Val(u32(0x0000'7f00))), C({Vec(f32(-1), f32(0), f32(0), f32(0))}, Val(u32(0x0000'0081))), C({Vec(f32(1), f32(-1), f32(1), f32(-1))}, Val(u32(0x817f'817f))), C({Vec(f32::Highest(), f32(-0.5), f32(0.5), f32::Lowest())}, Val(u32(0x8140'c17f))), }; } INSTANTIATE_TEST_SUITE_P( // Pack4x8snorm, ResolverConstEvalBuiltinTest, testing::Combine(testing::Values(sem::BuiltinType::kPack4X8Snorm), testing::ValuesIn(Pack4x8snormCases()))); std::vector Pack4x8unormCases() { return { C({Vec(f32(0), f32(0), f32(0), f32(0))}, Val(u32(0x0000'0000))), C({Vec(f32(0), f32(0), f32(0), f32(1))}, Val(u32(0xff00'0000))), C({Vec(f32(0), f32(0), f32(1), f32(0))}, Val(u32(0x00ff'0000))), C({Vec(f32(0), f32(1), f32(0), f32(0))}, Val(u32(0x0000'ff00))), C({Vec(f32(1), f32(0), f32(0), f32(0))}, Val(u32(0x0000'00ff))), C({Vec(f32(1), f32(0), f32(1), f32(0))}, Val(u32(0x00ff'00ff))), C({Vec(f32::Highest(), f32(0), f32(0.5), f32::Lowest())}, Val(u32(0x0080'00ff))), }; } INSTANTIATE_TEST_SUITE_P( // Pack4x8unorm, ResolverConstEvalBuiltinTest, testing::Combine(testing::Values(sem::BuiltinType::kPack4X8Unorm), testing::ValuesIn(Pack4x8unormCases()))); std::vector Pack2x16floatCases() { return { C({Vec(f32(f16::Lowest()), f32(f16::Highest()))}, Val(u32(0x7bff'fbff))), C({Vec(f32(1), f32(-1))}, Val(u32(0xbc00'3c00))), C({Vec(f32(0), f32(0))}, Val(u32(0x0000'0000))), C({Vec(f32(10), f32(-10.5))}, Val(u32(0xc940'4900))), E({Vec(f32(0), f32::Highest())}, "12:34 error: value 3.4028234663852885981e+38 cannot be represented as 'f16'"), E({Vec(f32::Lowest(), f32(0))}, "12:34 error: value -3.4028234663852885981e+38 cannot be represented as 'f16'"), }; } INSTANTIATE_TEST_SUITE_P( // Pack2x16float, ResolverConstEvalBuiltinTest, testing::Combine(testing::Values(sem::BuiltinType::kPack2X16Float), testing::ValuesIn(Pack2x16floatCases()))); std::vector Pack2x16snormCases() { return { C({Vec(f32(0), f32(0))}, Val(u32(0x0000'0000))), C({Vec(f32(0), f32(-1))}, Val(u32(0x8001'0000))), C({Vec(f32(0), f32(1))}, Val(u32(0x7fff'0000))), C({Vec(f32(-1), f32(0))}, Val(u32(0x0000'8001))), C({Vec(f32(1), f32(0))}, Val(u32(0x0000'7fff))), C({Vec(f32(1), f32(-1))}, Val(u32(0x8001'7fff))), C({Vec(f32::Highest(), f32::Lowest())}, Val(u32(0x8001'7fff))), C({Vec(f32(-0.5), f32(0.5))}, Val(u32(0x4000'c001))), }; } INSTANTIATE_TEST_SUITE_P( // Pack2x16snorm, ResolverConstEvalBuiltinTest, testing::Combine(testing::Values(sem::BuiltinType::kPack2X16Snorm), testing::ValuesIn(Pack2x16snormCases()))); std::vector Pack2x16unormCases() { return { C({Vec(f32(0), f32(1))}, Val(u32(0xffff'0000))), C({Vec(f32(1), f32(0))}, Val(u32(0x0000'ffff))), C({Vec(f32(0.5), f32(0))}, Val(u32(0x0000'8000))), C({Vec(f32::Highest(), f32::Lowest())}, Val(u32(0x0000'ffff))), }; } INSTANTIATE_TEST_SUITE_P( // Pack2x16unorm, ResolverConstEvalBuiltinTest, testing::Combine(testing::Values(sem::BuiltinType::kPack2X16Unorm), testing::ValuesIn(Pack2x16unormCases()))); template std::vector ReverseBitsCases() { using B = BitValues; return { C({T(0)}, T(0)), C({B::Lsh(1, 0)}, B::Lsh(1, 31)), // C({B::Lsh(1, 1)}, B::Lsh(1, 30)), // C({B::Lsh(1, 2)}, B::Lsh(1, 29)), // C({B::Lsh(1, 3)}, B::Lsh(1, 28)), // C({B::Lsh(1, 4)}, B::Lsh(1, 27)), // //... C({B::Lsh(1, 27)}, B::Lsh(1, 4)), // C({B::Lsh(1, 28)}, B::Lsh(1, 3)), // C({B::Lsh(1, 29)}, B::Lsh(1, 2)), // C({B::Lsh(1, 30)}, B::Lsh(1, 1)), // C({B::Lsh(1, 31)}, B::Lsh(1, 0)), // C({/**/ T(0b00010001000100010000000000000000)}, /* */ T(0b00000000000000001000100010001000)), C({/**/ T(0b00011000000110000000000000000000)}, /* */ T(0b00000000000000000001100000011000)), C({/**/ T(0b00000100000000001111111111111111)}, /* */ T(0b11111111111111110000000000100000)), C({/**/ T(0b10010101111000110000011111101010)}, /* */ T(0b01010111111000001100011110101001)), // Vector tests C({/**/ Vec(T(0b00010001000100010000000000000000), // T(0b00011000000110000000000000000000), // T(0b00000000000000001111111111111111))}, /* */ Vec(T(0b00000000000000001000100010001000), // T(0b00000000000000000001100000011000), // T(0b11111111111111110000000000000000))), }; } INSTANTIATE_TEST_SUITE_P( // ReverseBits, ResolverConstEvalBuiltinTest, testing::Combine(testing::Values(sem::BuiltinType::kReverseBits), testing::ValuesIn(Concat(ReverseBitsCases(), // ReverseBitsCases())))); template std::vector ReflectCases() { auto pos_y = Vec(T(0), T(1), T(0)); auto neg_y = Vec(T(0), -T(1), T(0)); auto pos_large_y = Vec(T(0), T(10000), T(0)); auto neg_large_y = Vec(T(0), -T(10000), T(0)); auto cos_45 = T(0.70710678118654752440084436210485); auto pos_xyz = Vec(cos_45, cos_45, cos_45); auto r = std::vector{ C({Vec(T(1), -T(1), T(0)), pos_y}, Vec(T(1), T(1), T(0))), C({Vec(T(24), -T(42), T(0)), pos_y}, Vec(T(24), T(42), T(0))), // Flipping reflection vector doesn't change the result C({Vec(T(1), -T(1), T(0)), neg_y}, Vec(T(1), T(1), T(0))), C({Vec(T(24), -T(42), T(0)), neg_y}, Vec(T(24), T(42), T(0))), // Parallel input and reflection vectors: result is negation of input C({pos_y, pos_y}, neg_y), C({neg_y, pos_y}, pos_y), C({pos_large_y, pos_y}, neg_large_y), C({neg_large_y, pos_y}, pos_large_y), // Input axis vectors reflected by normalized(vec(1,1,1)) vector. C({Vec(T(1), T(0), T(0)), pos_xyz}, Vec(T(0), -T(1), -T(1))).FloatComp(0.02), C({Vec(T(0), T(1), T(0)), pos_xyz}, Vec(-T(1), T(0), -T(1))).FloatComp(0.02), C({Vec(T(0), T(0), T(1)), pos_xyz}, Vec(-T(1), -T(1), T(0))).FloatComp(0.02), C({Vec(-T(1), T(0), T(0)), pos_xyz}, Vec(T(0), T(1), T(1))).FloatComp(0.02), C({Vec(T(0), -T(1), T(0)), pos_xyz}, Vec(T(1), T(0), T(1))).FloatComp(0.02), C({Vec(T(0), T(0), -T(1)), pos_xyz}, Vec(T(1), T(1), T(0))).FloatComp(0.02), }; auto error_msg = [](auto a, const char* op, auto b) { return "12:34 error: " + OverflowErrorMessage(a, op, b) + R"( 12:34 note: when calculating reflect)"; }; ConcatInto( // r, std::vector{ // Overflow the dot product operation E({Vec(T::Highest(), T::Highest(), T(0)), Vec(T(1), T(1), T(0))}, error_msg(T::Highest(), "+", T::Highest())), E({Vec(T::Lowest(), T::Lowest(), T(0)), Vec(T(1), T(1), T(0))}, error_msg(T::Lowest(), "+", T::Lowest())), }); return r; } INSTANTIATE_TEST_SUITE_P( // Reflect, ResolverConstEvalBuiltinTest, testing::Combine(testing::Values(sem::BuiltinType::kReflect), testing::ValuesIn(Concat(ReflectCases(), // ReflectCases(), // ReflectCases())))); template std::vector RefractCases() { // Returns "eta" (Greek letter) that denotes the ratio of indices of refraction for the input // and output vector angles from the normal vector. auto eta = [](auto angle1, auto angle2) { // Snell's law: sin(angle1) / sin(angle2) == n2 / n1 // We want the ratio of n1 to n2, so sin(angle2) / sin(angle1) auto angle1_rads = T(angle1) * kPi / T(180); auto angle2_rads = T(angle2) * kPi / T(180); return T(std::sin(angle2_rads) / std::sin(angle1_rads)); }; auto zero = Vec(T(0), T(0), T(0)); auto pos_y = Vec(T(0), T(1), T(0)); auto neg_y = Vec(T(0), -T(1), T(0)); auto pos_x = Vec(T(1), T(0), T(0)); auto neg_x = Vec(-T(1), T(0), T(0)); auto cos_45 = T(0.70710678118654752440084436210485); auto cos_30 = T(0.86602540378443864676372317075294); auto down_right = Vec(T(cos_45), -T(cos_45), T(0)); auto up_right = Vec(T(cos_45), T(cos_45), T(0)); auto eps = 0.001; if constexpr (std::is_same_v) { eps = 0.1; } auto r = std::vector{ // e3 (eta) == 1, no refraction, so input is same as output C({down_right, pos_y, Val(T(1))}, down_right), C({neg_y, pos_y, Val(T(1))}, neg_y), // Varying etas C({down_right, pos_y, Val(eta(45, 45))}, down_right).FloatComp(eps), // e3 == 1 C({down_right, pos_y, Val(eta(45, 30))}, Vec(T(0.5), -T(cos_30), T(0))).FloatComp(eps), C({down_right, pos_y, Val(eta(45, 60))}, Vec(T(cos_30), -T(0.5), T(0))).FloatComp(eps), C({down_right, pos_y, Val(eta(45, 90))}, Vec(T(1), T(0), T(0))).FloatComp(eps), // Flip input and normal, same result C({up_right, neg_y, Val(eta(45, 45))}, up_right).FloatComp(eps), // e3 == 1 C({up_right, neg_y, Val(eta(45, 30))}, Vec(T(0.5), T(cos_30), T(0))).FloatComp(eps), C({up_right, neg_y, Val(eta(45, 60))}, Vec(T(cos_30), T(0.5), T(0))).FloatComp(eps), C({up_right, neg_y, Val(eta(45, 90))}, Vec(T(1), T(0), T(0))).FloatComp(eps), // Flip only normal, result is flipped C({down_right, neg_y, Val(eta(45, 45))}, up_right).FloatComp(eps), // e3 == 1 C({down_right, neg_y, Val(eta(45, 30))}, Vec(T(0.5), T(cos_30), T(0))).FloatComp(eps), C({down_right, neg_y, Val(eta(45, 60))}, Vec(T(cos_30), T(0.5), T(0))).FloatComp(eps), C({down_right, neg_y, Val(eta(45, 90))}, Vec(T(1), T(0), T(0))).FloatComp(eps), // If k < 0.0, returns the refraction vector 0.0 C({down_right, pos_y, Val(T(2))}, zero).FloatComp(eps), // A few more with a different normal (e2) C({down_right, neg_x, Val(eta(45, 45))}, down_right).FloatComp(eps), // e3 == 1 C({down_right, neg_x, Val(eta(45, 30))}, Vec(cos_30, -T(0.5), T(0))).FloatComp(eps), C({down_right, neg_x, Val(eta(45, 60))}, Vec(T(0.5), -T(cos_30), T(0))).FloatComp(eps), }; auto error_msg = [](auto a, const char* op, auto b) { return "12:34 error: " + OverflowErrorMessage(a, op, b) + R"( 12:34 note: when calculating refract)"; }; ConcatInto( // r, std::vector{ // Overflow the dot product operation E({Vec(T::Highest(), T::Highest(), T(0)), Vec(T(1), T(1), T(0)), Val(T(1))}, error_msg(T::Highest(), "+", T::Highest())), E({Vec(T::Lowest(), T::Lowest(), T(0)), Vec(T(1), T(1), T(0)), Val(T(1))}, error_msg(T::Lowest(), "+", T::Lowest())), // Overflow the k^2 operation E({down_right, pos_y, Val(T::Highest())}, error_msg(T::Highest(), "*", T::Highest())), }); return r; } INSTANTIATE_TEST_SUITE_P( // Refract, ResolverConstEvalBuiltinTest, testing::Combine(testing::Values(sem::BuiltinType::kRefract), testing::ValuesIn(Concat(RefractCases(), // RefractCases(), // RefractCases())))); template std::vector RadiansCases() { return { C({T(0)}, T(0)), // C({-T(0)}, -T(0)), // C({T(40)}, T(0.69813168)).FloatComp(), // C({-T(90)}, -T(1.5707964)).FloatComp(), // C({T(90)}, T(1.5707964)).FloatComp(), // C({T(360)}, T(6.2831855)).FloatComp(), }; } INSTANTIATE_TEST_SUITE_P( // Radians, ResolverConstEvalBuiltinTest, testing::Combine(testing::Values(sem::BuiltinType::kRadians), testing::ValuesIn(Concat(RadiansCases(), // RadiansCases())))); template std::vector RadiansF16Cases() { return { C({T(0)}, T(0)), // C({-T(0)}, -T(0)), // C({T(40)}, T(0.69726562)).FloatComp(), // C({-T(90)}, -T(1.5693359)).FloatComp(), // C({T(90)}, T(1.5693359)).FloatComp(), // C({T(360)}, T(6.2773438)).FloatComp(), }; } INSTANTIATE_TEST_SUITE_P( // RadiansF16, ResolverConstEvalBuiltinTest, testing::Combine(testing::Values(sem::BuiltinType::kRadians), testing::ValuesIn(RadiansF16Cases()))); template std::vector RoundCases() { return { C({T(0.0)}, T(0.0)), // C({-T(0.0)}, -T(0.0)), // C({T(1.5)}, T(2.0)), // C({T(2.5)}, T(2.0)), // C({T(2.4)}, T(2.0)), // C({T(2.6)}, T(3.0)), // C({T(1.49999)}, T(1.0)), // C({T(1.50001)}, T(2.0)), // C({-T(1.5)}, -T(2.0)), // C({-T(2.5)}, -T(2.0)), // C({-T(2.6)}, -T(3.0)), // C({-T(2.4)}, -T(2.0)), // // Vector tests C({Vec(T(0.0), T(1.5), T(2.5))}, Vec(T(0.0), T(2.0), T(2.0))), }; } INSTANTIATE_TEST_SUITE_P( // Round, ResolverConstEvalBuiltinTest, testing::Combine(testing::Values(sem::BuiltinType::kRound), testing::ValuesIn(Concat(RoundCases(), // RoundCases(), RoundCases())))); template std::vector SaturateCases() { return { C({T(0)}, T(0)), C({T(1)}, T(1)), C({T::Lowest()}, T(0)), C({T::Highest()}, T(1)), // Vector tests C({Vec(T(0), T(0))}, // Vec(T(0), T(0))), // C({Vec(T(1), T(1))}, // Vec(T(1), T(1))), // C({Vec(T::Lowest(), T(0), T::Highest())}, // Vec(T(0), T(0), T(1))), }; } INSTANTIATE_TEST_SUITE_P( // Saturate, ResolverConstEvalBuiltinTest, testing::Combine(testing::Values(sem::BuiltinType::kSaturate), testing::ValuesIn(Concat(SaturateCases(), // SaturateCases(), SaturateCases())))); template std::vector SelectCases() { return { C({Val(T{1}), Val(T{2}), Val(false)}, Val(T{1})), C({Val(T{1}), Val(T{2}), Val(true)}, Val(T{2})), C({Val(T{2}), Val(T{1}), Val(false)}, Val(T{2})), C({Val(T{2}), Val(T{1}), Val(true)}, Val(T{1})), C({Vec(T{1}, T{2}), Vec(T{3}, T{4}), Vec(false, false)}, Vec(T{1}, T{2})), C({Vec(T{1}, T{2}), Vec(T{3}, T{4}), Vec(false, true)}, Vec(T{1}, T{4})), C({Vec(T{1}, T{2}), Vec(T{3}, T{4}), Vec(true, false)}, Vec(T{3}, T{2})), C({Vec(T{1}, T{2}), Vec(T{3}, T{4}), Vec(true, true)}, Vec(T{3}, T{4})), C({Vec(T{1}, T{1}, T{2}, T{2}), // Vec(T{2}, T{2}, T{1}, T{1}), // Vec(false, true, false, true)}, // Vec(T{1}, T{2}, T{2}, T{1})), // }; } static std::vector SelectBoolCases() { return { C({Val(true), Val(false), Val(false)}, Val(true)), C({Val(true), Val(false), Val(true)}, Val(false)), C({Val(false), Val(true), Val(true)}, Val(true)), C({Val(false), Val(true), Val(false)}, Val(false)), C({Vec(true, true, false, false), // Vec(false, false, true, true), // Vec(false, true, true, false)}, // Vec(true, false, true, false)), // }; } INSTANTIATE_TEST_SUITE_P( // Select, ResolverConstEvalBuiltinTest, testing::Combine(testing::Values(sem::BuiltinType::kSelect), testing::ValuesIn(Concat(SelectCases(), // SelectCases(), SelectCases(), SelectCases(), SelectCases(), SelectCases(), SelectBoolCases())))); template std::vector SignCases() { std::vector cases = { C({T(0)}, T(0)), C({-T(0)}, T(0)), C({-T(1)}, -T(1)), C({-T(10)}, -T(1)), C({-T(100)}, -T(1)), C({T(1)}, T(1)), C({T(10)}, T(1)), C({T(100)}, T(1)), C({T::Highest()}, T(1.0)), C({T::Lowest()}, -T(1.0)), // Vector tests C({Vec(T::Highest(), T::Lowest())}, Vec(T(1.0), -T(1.0))), }; ConcatIntoIf>( cases, std::vector{ C({-T(0.5)}, -T(1)), C({T(0.5)}, T(1)), C({Vec(-T(0.5), T(0), T(0.5))}, Vec(-T(1.0), T(0.0), T(1.0))), }); return cases; } INSTANTIATE_TEST_SUITE_P( // Sign, ResolverConstEvalBuiltinTest, testing::Combine(testing::Values(sem::BuiltinType::kSign), testing::ValuesIn(Concat(SignCases(), // SignCases(), SignCases(), SignCases(), SignCases())))); template std::vector SinCases() { return { C({-T(0)}, -T(0)), C({T(0)}, T(0)), C({T(0.75)}, T(0.68163876)).FloatComp(), C({-T(0.75)}, -T(0.68163876)).FloatComp(), // Vector test C({Vec(T(0), -T(0), T(0.75))}, Vec(T(0), -T(0), T(0.68163876))).FloatComp(), }; } INSTANTIATE_TEST_SUITE_P( // Sin, ResolverConstEvalBuiltinTest, testing::Combine(testing::Values(sem::BuiltinType::kSin), testing::ValuesIn(Concat(SinCases(), // SinCases(), SinCases())))); template std::vector SinhCases() { auto error_msg = [](auto a) { return "12:34 error: " + OverflowErrorMessage(a, FriendlyName()); }; return { C({T(0)}, T(0)), C({-T(0)}, -T(0)), C({T(1)}, T(1.1752012)).FloatComp(), C({T(-1)}, -T(1.1752012)).FloatComp(), // Vector tests C({Vec(T(0), -T(0), T(1))}, Vec(T(0), -T(0), T(1.1752012))).FloatComp(), E({T(10000)}, error_msg(T::Inf())), }; } INSTANTIATE_TEST_SUITE_P( // Sinh, ResolverConstEvalBuiltinTest, testing::Combine(testing::Values(sem::BuiltinType::kSinh), testing::ValuesIn(Concat(SinhCases(), // SinhCases(), SinhCases())))); template std::vector SmoothstepCases() { auto error_msg = [](auto a, const char* op, auto b) { return "12:34 error: " + OverflowErrorMessage(a, op, b) + R"( 12:34 note: when calculating smoothstep)"; }; return { // t == 0 C({T(4), T(6), T(2)}, T(0)), // t == 1 C({T(4), T(6), T(8)}, T(1)), // t == .5 C({T(4), T(6), T(5)}, T(.5)), // Vector tests C({Vec(T(4), T(4)), Vec(T(6), T(6)), Vec(T(2), T(8))}, Vec(T(0), T(1))), // `x - low` underflows E({T::Highest(), T(1), T::Lowest()}, error_msg(T::Lowest(), "-", T::Highest())), // `high - low` underflows E({T::Highest(), T::Lowest(), T(0)}, error_msg(T::Lowest(), "-", T::Highest())), // Divide by zero on `(x - low) / (high - low)` E({T(0), T(0), T(0)}, error_msg(T(0), "/", T(0))), }; } INSTANTIATE_TEST_SUITE_P( // Smoothstep, ResolverConstEvalBuiltinTest, testing::Combine(testing::Values(sem::BuiltinType::kSmoothstep), testing::ValuesIn(Concat(SmoothstepCases(), // SmoothstepCases(), SmoothstepCases())))); template std::vector StepCases() { return { C({T(0), T(0)}, T(1.0)), C({T(0), T(0.5)}, T(1.0)), C({T(0.5), T(0)}, T(0.0)), C({T(1), T(0.5)}, T(0.0)), C({T(0.5), T(1)}, T(1.0)), C({T(1.5), T(1)}, T(0.0)), C({T(1), T(1.5)}, T(1.0)), C({T(-1), T(1)}, T(1.0)), C({T(-1), T(1)}, T(1.0)), C({T(1), T(-1)}, T(0.0)), C({T(-1), T(-1.5)}, T(0.0)), C({T(-1.5), T(-1)}, T(1.0)), C({T::Highest(), T::Lowest()}, T(0.0)), C({T::Lowest(), T::Highest()}, T(1.0)), // Vector tests C({Vec(T(0), T(0)), Vec(T(0), T(0))}, Vec(T(1.0), T(1.0))), C({Vec(T(-1), T(1)), Vec(T(0), T(0))}, Vec(T(1.0), T(0.0))), C({Vec(T::Highest(), T::Lowest()), Vec(T::Lowest(), T::Highest())}, Vec(T(0.0), T(1.0))), }; } INSTANTIATE_TEST_SUITE_P( // Step, ResolverConstEvalBuiltinTest, testing::Combine(testing::Values(sem::BuiltinType::kStep), testing::ValuesIn(Concat(StepCases(), // StepCases(), StepCases())))); template std::vector SqrtCases() { return { C({-T(0)}, -T(0)), // C({T(0)}, T(0)), // C({T(25)}, T(5)), // Vector tests C({Vec(T(25), T(100))}, Vec(T(5), T(10))), E({-T(25)}, "12:34 error: sqrt must be called with a value >= 0"), }; } INSTANTIATE_TEST_SUITE_P( // Sqrt, ResolverConstEvalBuiltinTest, testing::Combine(testing::Values(sem::BuiltinType::kSqrt), testing::ValuesIn(Concat(SqrtCases(), // SqrtCases(), SqrtCases())))); template std::vector TanCases() { return { C({-T(0)}, -T(0)), C({T(0)}, T(0)), C({T(.75)}, T(0.9315964599)).FloatComp(), // Vector test C({Vec(T(0), -T(0), T(.75))}, Vec(T(0), -T(0), T(0.9315964599))).FloatComp(), }; } INSTANTIATE_TEST_SUITE_P( // Tan, ResolverConstEvalBuiltinTest, testing::Combine(testing::Values(sem::BuiltinType::kTan), testing::ValuesIn(Concat(TanCases(), // TanCases(), TanCases())))); template std::vector TanhCases() { return { C({T(0)}, T(0)), C({-T(0)}, -T(0)), C({T(1)}, T(0.761594156)).FloatComp(), C({T(-1)}, -T(0.761594156)).FloatComp(), // Vector tests C({Vec(T(0), -T(0), T(1))}, Vec(T(0), -T(0), T(0.761594156))).FloatComp(), }; } INSTANTIATE_TEST_SUITE_P( // Tanh, ResolverConstEvalBuiltinTest, testing::Combine(testing::Values(sem::BuiltinType::kTanh), testing::ValuesIn(Concat(TanhCases(), // TanhCases(), TanhCases())))); template std::vector TransposeCases() { return { // 2x2 C({Mat({T(1), T(2)}, // {T(3), T(4)})}, // Mat({T(1), T(3)}, // {T(2), T(4)})), // 3x3 C({Mat({T(1), T(2), T(3)}, // {T(4), T(5), T(6)}, // {T(7), T(8), T(9)})}, // Mat({T(1), T(4), T(7)}, // {T(2), T(5), T(8)}, // {T(3), T(6), T(9)})), // 4x4 C({Mat({T(1), T(2), T(3), T(4)}, // {T(5), T(6), T(7), T(8)}, // {T(9), T(10), T(11), T(12)}, // {T(13), T(14), T(15), T(16)})}, // Mat({T(1), T(5), T(9), T(13)}, // {T(2), T(6), T(10), T(14)}, // {T(3), T(7), T(11), T(15)}, // {T(4), T(8), T(12), T(16)})), // 4x2 C({Mat({T(1), T(2), T(3), T(4)}, // {T(5), T(6), T(7), T(8)})}, // Mat({T(1), T(5)}, // {T(2), T(6)}, // {T(3), T(7)}, // {T(4), T(8)})), // 2x4 C({Mat({T(1), T(2)}, // {T(3), T(4)}, // {T(5), T(6)}, // {T(7), T(8)})}, // Mat({T(1), T(3), T(5), T(7)}, // {T(2), T(4), T(6), T(8)})), // 3x2 C({Mat({T(1), T(2), T(3)}, // {T(4), T(5), T(6)})}, // Mat({T(1), T(4)}, // {T(2), T(5)}, // {T(3), T(6)})), // 2x3 C({Mat({T(1), T(2)}, // {T(3), T(4)}, // {T(5), T(6)})}, // Mat({T(1), T(3), T(5)}, // {T(2), T(4), T(6)})), }; } INSTANTIATE_TEST_SUITE_P( // Transpose, ResolverConstEvalBuiltinTest, testing::Combine(testing::Values(sem::BuiltinType::kTranspose), testing::ValuesIn(Concat(TransposeCases(), // TransposeCases(), TransposeCases())))); template std::vector TruncCases() { return {C({T(0)}, T(0)), // C({-T(0)}, -T(0)), // C({T(1.5)}, T(1)), // C({-T(1.5)}, -T(1)), // Vector tests C({Vec(T(0.0), T(1.5), -T(2.2))}, Vec(T(0), T(1), -T(2)))}; } INSTANTIATE_TEST_SUITE_P( // Trunc, ResolverConstEvalBuiltinTest, testing::Combine(testing::Values(sem::BuiltinType::kTrunc), testing::ValuesIn(Concat(TruncCases(), // TruncCases(), TruncCases())))); std::vector Unpack4x8snormCases() { return { C({Val(u32(0x0000'0000))}, Vec(f32(0), f32(0), f32(0), f32(0))), C({Val(u32(0x8100'0000))}, Vec(f32(0), f32(0), f32(0), f32(-1))), C({Val(u32(0x7f00'0000))}, Vec(f32(0), f32(0), f32(0), f32(1))), C({Val(u32(0x0081'0000))}, Vec(f32(0), f32(0), f32(-1), f32(0))), C({Val(u32(0x0000'7f00))}, Vec(f32(0), f32(1), f32(0), f32(0))), C({Val(u32(0x0000'0081))}, Vec(f32(-1), f32(0), f32(0), f32(0))), C({Val(u32(0x817f'817f))}, Vec(f32(1), f32(-1), f32(1), f32(-1))), C({Val(u32(0x816d'937f))}, Vec(f32(1), f32(-0.8582677165354), f32(0.8582677165354), f32(-1))), }; } INSTANTIATE_TEST_SUITE_P( // Unpack4x8snorm, ResolverConstEvalBuiltinTest, testing::Combine(testing::Values(sem::BuiltinType::kUnpack4X8Snorm), testing::ValuesIn(Unpack4x8snormCases()))); std::vector Unpack4x8unormCases() { return { C({Val(u32(0x0000'0000))}, Vec(f32(0), f32(0), f32(0), f32(0))), C({Val(u32(0xff00'0000))}, Vec(f32(0), f32(0), f32(0), f32(1))), C({Val(u32(0x00ff'0000))}, Vec(f32(0), f32(0), f32(1), f32(0))), C({Val(u32(0x0000'ff00))}, Vec(f32(0), f32(1), f32(0), f32(0))), C({Val(u32(0x0000'00ff))}, Vec(f32(1), f32(0), f32(0), f32(0))), C({Val(u32(0x00ff'00ff))}, Vec(f32(1), f32(0), f32(1), f32(0))), C({Val(u32(0x0066'00ff))}, Vec(f32(1), f32(0), f32(0.4), f32(0))), }; } INSTANTIATE_TEST_SUITE_P( // Unpack4x8unorm, ResolverConstEvalBuiltinTest, testing::Combine(testing::Values(sem::BuiltinType::kUnpack4X8Unorm), testing::ValuesIn(Unpack4x8unormCases()))); std::vector Unpack2x16floatCases() { return { C({Val(u32(0x7bff'fbff))}, Vec(f32(f16::Lowest()), f32(f16::Highest()))), C({Val(u32(0xbc00'3c00))}, Vec(f32(1), f32(-1))), C({Val(u32(0x0000'0000))}, Vec(f32(0), f32(0))), C({Val(u32(0xc940'4900))}, Vec(f32(10), f32(-10.5))), }; } INSTANTIATE_TEST_SUITE_P( // Unpack2x16float, ResolverConstEvalBuiltinTest, testing::Combine(testing::Values(sem::BuiltinType::kUnpack2X16Float), testing::ValuesIn(Unpack2x16floatCases()))); std::vector Unpack2x16snormCases() { return { C({Val(u32(0x0000'0000))}, Vec(f32(0), f32(0))), C({Val(u32(0x8001'0000))}, Vec(f32(0), f32(-1))), C({Val(u32(0x7fff'0000))}, Vec(f32(0), f32(1))), C({Val(u32(0x0000'8001))}, Vec(f32(-1), f32(0))), C({Val(u32(0x0000'7fff))}, Vec(f32(1), f32(0))), C({Val(u32(0x8001'7fff))}, Vec(f32(1), f32(-1))), C({Val(u32(0x8001'7fff))}, Vec(f32(1), f32(-1))), C({Val(u32(0x4000'999a))}, Vec(f32(-0.80001220740379), f32(0.500015259254737))).FloatComp(), }; } INSTANTIATE_TEST_SUITE_P( // Unpack2x16snorm, ResolverConstEvalBuiltinTest, testing::Combine(testing::Values(sem::BuiltinType::kUnpack2X16Snorm), testing::ValuesIn(Unpack2x16snormCases()))); std::vector Unpack2x16unormCases() { return { C({Val(u32(0xffff'0000))}, Vec(f32(0), f32(1))), C({Val(u32(0x0000'ffff))}, Vec(f32(1), f32(0))), C({Val(u32(0x0000'6666))}, Vec(f32(0.4), f32(0))), C({Val(u32(0x0000'ffff))}, Vec(f32(1), f32(0))), }; } INSTANTIATE_TEST_SUITE_P( // Unpack2x16unorm, ResolverConstEvalBuiltinTest, testing::Combine(testing::Values(sem::BuiltinType::kUnpack2X16Unorm), testing::ValuesIn(Unpack2x16unormCases()))); std::vector QuantizeToF16Cases() { return { C({0_f}, 0_f), // C({-0_f}, -0_f), // C({1_f}, 1_f), // C({-1_f}, -1_f), // // 0.00006106496 quantized to 0.000061035156 = 0x1p-14 C({0.00006106496_f}, 0.000061035156_f), // C({-0.00006106496_f}, -0.000061035156_f), // // 1.0004883 quantized to 1.0 = 0x1p0 C({1.0004883_f}, 1.0_f), // C({-1.0004883_f}, -1.0_f), // // 8196.0 quantized to 8192.0 = 0x1p13 C({8196_f}, 8192_f), // C({-8196_f}, -8192_f), // // Value in subnormal f16 range C({0x0.034p-14_f}, 0x0.034p-14_f), // C({-0x0.034p-14_f}, -0x0.034p-14_f), // C({0x0.068p-14_f}, 0x0.068p-14_f), // C({-0x0.068p-14_f}, -0x0.068p-14_f), // // 0x0.06b7p-14 quantized to 0x0.068p-14 C({0x0.06b7p-14_f}, 0x0.068p-14_f), // C({-0x0.06b7p-14_f}, -0x0.068p-14_f), // // Vector tests C({Vec(0_f, -0_f)}, Vec(0_f, -0_f)), // C({Vec(1_f, -1_f)}, Vec(1_f, -1_f)), // C({Vec(0.00006106496_f, -0.00006106496_f, 1.0004883_f, -1.0004883_f)}, Vec(0.000061035156_f, -0.000061035156_f, 1.0_f, -1.0_f)), C({Vec(8196_f, 8192_f, 0x0.034p-14_f)}, Vec(8192_f, 8192_f, 0x0.034p-14_f)), C({Vec(0x0.034p-14_f, -0x0.034p-14_f, 0x0.068p-14_f, -0x0.068p-14_f)}, Vec(0x0.034p-14_f, -0x0.034p-14_f, 0x0.068p-14_f, -0x0.068p-14_f)), // Value out of f16 range E({65504.003_f}, "12:34 error: value 65504.00390625 cannot be represented as 'f16'"), E({-65504.003_f}, "12:34 error: value -65504.00390625 cannot be represented as 'f16'"), E({0x1.234p56_f}, "12:34 error: value 81979586966978560 cannot be represented as 'f16'"), E({0x4.321p65_f}, "12:34 error: value 1.5478871919272394752e+20 cannot be represented as 'f16'"), E({Vec(65504.003_f, 0_f)}, "12:34 error: value 65504.00390625 cannot be represented as 'f16'"), E({Vec(0_f, -0x4.321p65_f)}, "12:34 error: value -1.5478871919272394752e+20 cannot be represented as 'f16'"), }; } INSTANTIATE_TEST_SUITE_P( // QuantizeToF16, ResolverConstEvalBuiltinTest, testing::Combine(testing::Values(sem::BuiltinType::kQuantizeToF16), testing::ValuesIn(QuantizeToF16Cases()))); } // namespace } // namespace tint::resolver