// 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 { // Bring in std::ostream& operator<<(std::ostream& o, const Types& types) using resolver::operator<<; struct Case { Case(utils::VectorRef in_args, Types expected_value) : args(std::move(in_args)), expected(Success{std::move(expected_value), false, false}) {} 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.pos_or_neg = true; expected = s; return *this; } /// Expected value should be compared using FLOAT_EQ instead of EQ Case& FloatComp() { Success s = expected.Get(); s.float_compare = true; expected = s; return *this; } struct Success { Types value; bool pos_or_neg = false; bool float_compare = false; }; struct Failure { std::string error = nullptr; }; 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(); o << s.value << ", pos_or_neg: " << s.pos_or_neg; } 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, Types result) { return Case{utils::Vector{args}, std::move(result)}; } /// 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); } Types result = Val(0_a); std::visit([&](auto&& v) { result = Val(v); }, sresult); return Case{std::move(args), std::move(result)}; } /// 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)}; } /// Returns the overflow error message for binary ops template std::string OverflowErrorMessage(NumberT lhs, const char* op, NumberT rhs) { std::stringstream ss; ss << std::setprecision(20); ss << "'" << lhs.value << " " << op << " " << rhs.value << "' cannot be represented as '" << FriendlyName() << "'"; return ss.str(); } 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) { std::visit([&](auto&& v) { args.Push(v.Expr(*this)); }, a); } auto* expr = Call(Source{{12, 34}}, sem::str(builtin), std::move(args)); GlobalConst("C", expr); if (c.expected) { auto expected = c.expected.Get(); auto* expected_expr = ToValueBase(expected.value)->Expr(*this); GlobalConst("E", expected_expr); 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()); auto* expected_sem = Sem().Get(expected_expr); const sem::Constant* expected_value = expected_sem->ConstantValue(); ASSERT_NE(expected_value, nullptr); EXPECT_TYPE(expected_value->Type(), expected_sem->Type()); // @TODO(amaiorano): Rewrite using ScalarArgsFrom() ForEachElemPair(value, expected_value, [&](const sem::Constant* a, const sem::Constant* b) { std::visit( [&](auto&& ct_expected) { using T = typename std::decay_t::ElementType; auto v = a->As(); auto e = b->As(); if constexpr (std::is_same_v) { EXPECT_EQ(v, e); } else if constexpr (IsFloatingPoint) { if (std::isnan(e)) { EXPECT_TRUE(std::isnan(v)); } else { auto vf = (expected.pos_or_neg ? Abs(v) : v); if (expected.float_compare) { EXPECT_FLOAT_EQ(vf, e); } else { EXPECT_EQ(vf, e); } } } else { EXPECT_EQ((expected.pos_or_neg ? Abs(v) : v), e); // Check that the constant's integer doesn't contain unexpected // data in the MSBs that are outside of the bit-width of T. EXPECT_EQ(a->As(), b->As()); } }, expected.value); return HasFailure() ? Action::kStop : Action::kContinue; }); } 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())), }); ConcatIntoIf(cases, std::vector{ C({Negate(T::Inf())}, T::Inf()), C({T::Inf()}, T::Inf()), C({T::NaN()}, T::NaN()), C({Vec(Negate(T::Inf()), T::Inf(), T::NaN())}, Vec(T::Inf(), T::Inf(), T::NaN())), }); 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() { std::vector cases = { // 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(), }; ConcatIntoIf( // cases, std::vector{ // If y is +/-INF and x is finite, +/-PI/2 is returned C({T::Inf(), T(0.0)}, kPiOver2).PosOrNeg().FloatComp(), C({-T::Inf(), T(0.0)}, kPiOver2).PosOrNeg().FloatComp(), // If y is +/-INF and x is -INF, +/-3PI/4 is returned C({T::Inf(), -T::Inf()}, k3PiOver4).PosOrNeg().FloatComp(), C({-T::Inf(), -T::Inf()}, k3PiOver4).PosOrNeg().FloatComp(), // If y is +/-INF and x is +INF, +/-PI/4 is returned C({T::Inf(), T::Inf()}, kPiOver4).PosOrNeg().FloatComp(), C({-T::Inf(), T::Inf()}, kPiOver4).PosOrNeg().FloatComp(), // If x is -INF and y is finite and positive, +PI is returned C({T(0.0), -T::Inf()}, kPi).FloatComp(), // If x is -INF and y is finite and negative, -PI is returned C({-T(0.0), -T::Inf()}, -kPi).FloatComp(), // If x is +INF and y is finite and positive, +0 is returned C({T(0.0), T::Inf()}, T(0.0)), // If x is +INF and y is finite and negative, -0 is returned C({-T(0.0), T::Inf()}, -T(0.0)), // If either x is NaN or y is NaN, NaN is returned C({T::NaN(), T(0.0)}, T::NaN()), C({T(0.0), T::NaN()}, T::NaN()), C({T::NaN(), T::NaN()}, T::NaN()), // Vector tests C({Vec(T::Inf(), -T::Inf(), T::Inf(), -T::Inf()), // Vec(T(0.0), T(0.0), -T::Inf(), -T::Inf())}, // Vec(kPiOver2, kPiOver2, k3PiOver4, k3PiOver4)) .PosOrNeg() .FloatComp(), }); return cases; } INSTANTIATE_TEST_SUITE_P( // Atan2, ResolverConstEvalBuiltinTest, testing::Combine(testing::Values(sem::BuiltinType::kAtan2), testing::ValuesIn(Concat(Atan2Cases(), // Atan2Cases(), Atan2Cases())))); template std::vector AtanCases() { std::vector cases = { 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(), }; ConcatIntoIf( // cases, std::vector{ // If i is +/-INF, +/-PI/2 is returned C({T::Inf()}, kPiOver2).PosOrNeg().FloatComp(), C({-T::Inf()}, -kPiOver2).FloatComp(), // If i is NaN, NaN is returned C({T::NaN()}, T::NaN()), // Vector tests C({Vec(T::Inf(), -T::Inf(), T::Inf(), -T::Inf())}, // Vec(kPiOver2, -kPiOver2, kPiOver2, -kPiOver2)) .FloatComp(), }); return cases; } INSTANTIATE_TEST_SUITE_P( // Atan, ResolverConstEvalBuiltinTest, testing::Combine(testing::Values(sem::BuiltinType::kAtan), testing::ValuesIn(Concat(AtanCases(), // AtanCases(), AtanCases())))); template std::vector AtanhCases() { std::vector cases = { // 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(), }; ConcatIntoIf( // cases, std::vector{ E({1.1_a}, "12:34 error: atanh must be called with a value in the range (-1 .. 1) (exclusive)"), E({-1.1_a}, "12:34 error: atanh must be called with a value in the range (-1 .. 1) (exclusive)"), E({T::Inf()}, "12:34 error: atanh must be called with a value in the range (-1 .. 1) (exclusive)"), E({-T::Inf()}, "12:34 error: atanh must be called with a value in the range (-1 .. 1) (exclusive)"), }); ConcatIntoIf( // cases, std::vector{ // If i is NaN, NaN is returned C({T::NaN()}, T::NaN()), // Vector tests C({Vec(T::NaN(), T::NaN())}, Vec(T::NaN(), T::NaN())).FloatComp(), }); return cases; } INSTANTIATE_TEST_SUITE_P( // Atanh, ResolverConstEvalBuiltinTest, testing::Combine(testing::Values(sem::BuiltinType::kAtanh), testing::ValuesIn(Concat(AtanhCases(), // AtanhCases(), AtanhCases())))); template std::vector AcosCases() { std::vector cases = { // 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(), }; ConcatIntoIf( // cases, std::vector{ E({1.1_a}, "12:34 error: acos must be called with a value in the range [-1 .. 1] (inclusive)"), E({-1.1_a}, "12:34 error: acos must be called with a value in the range [-1 .. 1] (inclusive)"), E({T::Inf()}, "12:34 error: acos must be called with a value in the range [-1 .. 1] (inclusive)"), E({-T::Inf()}, "12:34 error: acos must be called with a value in the range [-1 .. 1] (inclusive)"), }); ConcatIntoIf( // cases, std::vector{ // If i is NaN, NaN is returned C({T::NaN()}, T::NaN()), // Vector tests C({Vec(T::NaN(), T::NaN())}, Vec(T::NaN(), T::NaN())), }); return cases; } INSTANTIATE_TEST_SUITE_P( // Acos, ResolverConstEvalBuiltinTest, testing::Combine(testing::Values(sem::BuiltinType::kAcos), testing::ValuesIn(Concat(AcosCases(), // AcosCases(), AcosCases())))); template std::vector AcoshCases() { std::vector cases = { 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(), }; ConcatIntoIf( // cases, std::vector{ E({T::Smallest()}, "12:34 error: acosh must be called with a value >= 1.0"), E({-1.1_a}, "12:34 error: acosh must be called with a value >= 1.0"), E({0_a}, "12:34 error: acosh must be called with a value >= 1.0"), }); return cases; } INSTANTIATE_TEST_SUITE_P( // Acosh, ResolverConstEvalBuiltinTest, testing::Combine(testing::Values(sem::BuiltinType::kAcosh), testing::ValuesIn(Concat(AcoshCases(), // AcoshCases(), AcoshCases())))); template std::vector AsinCases() { std::vector cases = { // 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(), }; ConcatIntoIf( // cases, std::vector{ E({1.1_a}, "12:34 error: asin must be called with a value in the range [-1 .. 1] (inclusive)"), E({-1.1_a}, "12:34 error: asin must be called with a value in the range [-1 .. 1] (inclusive)"), E({T::Inf()}, "12:34 error: asin must be called with a value in the range [-1 .. 1] (inclusive)"), E({-T::Inf()}, "12:34 error: asin must be called with a value in the range [-1 .. 1] (inclusive)"), }); ConcatIntoIf( // cases, std::vector{ // If i is NaN, NaN is returned C({T::NaN()}, T::NaN()), // Vector tests C({Vec(T::NaN(), T::NaN())}, Vec(T::NaN(), T::NaN())).FloatComp(), }); return cases; } INSTANTIATE_TEST_SUITE_P( // Asin, ResolverConstEvalBuiltinTest, testing::Combine(testing::Values(sem::BuiltinType::kAsin), testing::ValuesIn(Concat(AsinCases(), // AsinCases(), AsinCases())))); template std::vector AsinhCases() { std::vector cases = { // 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(), }; ConcatIntoIf( // cases, std::vector{ // If i is +/- INF, +/-INF is returned C({T::Inf()}, T::Inf()), C({-T::Inf()}, -T::Inf()), // If i is NaN, NaN is returned C({T::NaN()}, T::NaN()), // Vector tests C({Vec(T::Inf(), T::NaN(), -T::Inf())}, // Vec(T::Inf(), T::NaN(), -T::Inf())), }); return cases; } INSTANTIATE_TEST_SUITE_P( // Asinh, ResolverConstEvalBuiltinTest, testing::Combine(testing::Values(sem::BuiltinType::kAsinh), testing::ValuesIn(Concat(AsinhCases(), // AsinhCases(), AsinhCases())))); template std::vector CeilCases() { std::vector cases = { 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))), }; ConcatIntoIf( cases, std::vector{ C({-T::Inf()}, -T::Inf()), C({T::Inf()}, T::Inf()), C({T::NaN()}, T::NaN()), C({Vec(-T::Inf(), T::Inf(), T::NaN())}, Vec(-T::Inf(), T::Inf(), T::NaN())), }); return cases; } 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() { std::vector cases = { 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(), }; return cases; } INSTANTIATE_TEST_SUITE_P( // Cos, ResolverConstEvalBuiltinTest, testing::Combine(testing::Values(sem::BuiltinType::kCos), testing::ValuesIn(Concat(CosCases(), // CosCases(), CosCases())))); template std::vector CoshCases() { std::vector cases = { 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(), }; return cases; } 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()); const auto inf_x = vec_x(T::Inf()); const auto inf_y = vec_y(T::Inf()); const auto inf_z = vec_z(T::Inf()); const auto neg_inf_x = vec_x(-T::Inf()); const auto neg_inf_y = vec_y(-T::Inf()); const auto neg_inf_z = vec_z(-T::Inf()); 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))), }; ConcatIntoIf( // r, std::vector{ C({highest_x, highest_y}, inf_z).PosOrNeg(), // C({highest_y, highest_x}, inf_z).PosOrNeg(), // C({highest_z, highest_x}, inf_y).PosOrNeg(), // C({highest_x, highest_z}, inf_y).PosOrNeg(), // C({highest_y, highest_z}, inf_x).PosOrNeg(), // C({highest_z, highest_y}, inf_x).PosOrNeg(), // C({lowest_x, lowest_y}, inf_z).PosOrNeg(), // C({lowest_y, lowest_x}, inf_z).PosOrNeg(), // C({lowest_z, lowest_x}, inf_y).PosOrNeg(), // C({lowest_x, lowest_z}, inf_y).PosOrNeg(), // C({lowest_y, lowest_z}, inf_x).PosOrNeg(), // C({lowest_z, lowest_y}, inf_x).PosOrNeg(), }); 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()); ConcatIntoIf( // 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(), // CrossCases())))); 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() { std::vector cases = { 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))), }; ConcatIntoIf( cases, std::vector{ C({-T::Inf()}, -T::Inf()), C({T::Inf()}, T::Inf()), C({T::NaN()}, T::NaN()), C({Vec(-T::Inf(), T::Inf(), T::NaN())}, Vec(-T::Inf(), T::Inf(), T::NaN())), }); return cases; } INSTANTIATE_TEST_SUITE_P( // Floor, ResolverConstEvalBuiltinTest, testing::Combine(testing::Values(sem::BuiltinType::kFloor), testing::ValuesIn(Concat(FloorCases(), FloorCases(), FloorCases())))); 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))), }; return r; } INSTANTIATE_TEST_SUITE_P( // InsertBits, ResolverConstEvalBuiltinTest, testing::Combine(testing::Values(sem::BuiltinType::kInsertBits), testing::ValuesIn(Concat(InsertBitsCases(), // InsertBitsCases())))); using ResolverConstEvalBuiltinTest_InsertBits_InvalidOffsetAndCount = ResolverTestWithParam>; TEST_P(ResolverConstEvalBuiltinTest_InsertBits_InvalidOffsetAndCount, Test) { auto& p = GetParam(); auto* expr = Call(Source{{12, 34}}, sem::str(sem::BuiltinType::kInsertBits), Expr(1_u), Expr(1_u), Expr(u32(std::get<0>(p))), Expr(u32(std::get<1>(p)))); GlobalConst("C", expr); EXPECT_FALSE(r()->Resolve()); EXPECT_EQ(r()->error(), "12:34 error: 'offset + 'count' must be less than or equal to the bit width of 'e'"); } INSTANTIATE_TEST_SUITE_P(InsertBits, ResolverConstEvalBuiltinTest_InsertBits_InvalidOffsetAndCount, testing::Values( // std::make_tuple(33, 0), // std::make_tuple(34, 0), // std::make_tuple(1000, 0), // std::make_tuple(u32::Highest(), 0), // std::make_tuple(0, 33), // std::make_tuple(0, 34), // std::make_tuple(0, 1000), // std::make_tuple(0, u32::Highest()), // std::make_tuple(33, 33), // std::make_tuple(34, 34), // std::make_tuple(1000, 1000), // std::make_tuple(u32::Highest(), u32::Highest()))); 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)))), }; return r; } INSTANTIATE_TEST_SUITE_P( // ExtractBits, ResolverConstEvalBuiltinTest, testing::Combine(testing::Values(sem::BuiltinType::kExtractBits), testing::ValuesIn(Concat(ExtractBitsCases(), // ExtractBitsCases())))); using ResolverConstEvalBuiltinTest_ExtractBits_InvalidOffsetAndCount = ResolverTestWithParam>; TEST_P(ResolverConstEvalBuiltinTest_ExtractBits_InvalidOffsetAndCount, Test) { auto& p = GetParam(); auto* expr = Call(Source{{12, 34}}, sem::str(sem::BuiltinType::kExtractBits), Expr(1_u), Expr(u32(std::get<0>(p))), Expr(u32(std::get<1>(p)))); GlobalConst("C", expr); EXPECT_FALSE(r()->Resolve()); EXPECT_EQ(r()->error(), "12:34 error: 'offset + 'count' must be less than or equal to the bit width of 'e'"); } INSTANTIATE_TEST_SUITE_P(ExtractBits, ResolverConstEvalBuiltinTest_ExtractBits_InvalidOffsetAndCount, testing::Values( // std::make_tuple(33, 0), // std::make_tuple(34, 0), // std::make_tuple(1000, 0), // std::make_tuple(u32::Highest(), 0), // std::make_tuple(0, 33), // std::make_tuple(0, 34), // std::make_tuple(0, 1000), // std::make_tuple(0, u32::Highest()), // std::make_tuple(33, 33), // std::make_tuple(34, 34), // std::make_tuple(1000, 1000), // std::make_tuple(u32::Highest(), 1), // std::make_tuple(1, u32::Highest()), // std::make_tuple(u32::Highest(), u32::Highest()))); 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 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() { return { C({-T(1)}, -T(1)), C({-T(0.5)}, -T(1)), C({T(0)}, T(0)), C({-T(0)}, T(0)), C({T(0.5)}, T(1)), C({T(1)}, T(1)), C({T::Highest()}, T(1.0)), C({T::Lowest()}, -T(1.0)), // Vector tests C({Vec(-T(0.5), T(0), T(0.5))}, Vec(-T(1.0), T(0.0), T(1.0))), C({Vec(T::Highest(), T::Lowest())}, Vec(T(1.0), -T(1.0))), }; } INSTANTIATE_TEST_SUITE_P( // Sign, ResolverConstEvalBuiltinTest, testing::Combine(testing::Values(sem::BuiltinType::kSign), testing::ValuesIn(Concat(SignCases(), // SignCases(), SignCases())))); template std::vector SinCases() { std::vector cases = { 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(), }; return cases; } INSTANTIATE_TEST_SUITE_P( // Sin, ResolverConstEvalBuiltinTest, testing::Combine(testing::Values(sem::BuiltinType::kSin), testing::ValuesIn(Concat(SinCases(), // SinCases(), SinCases())))); template std::vector SinhCases() { std::vector cases = { 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(), }; return cases; } INSTANTIATE_TEST_SUITE_P( // Sinh, ResolverConstEvalBuiltinTest, testing::Combine(testing::Values(sem::BuiltinType::kSinh), testing::ValuesIn(Concat(SinhCases(), // SinhCases(), SinhCases())))); 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 TanCases() { std::vector cases = { 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(), }; return cases; } INSTANTIATE_TEST_SUITE_P( // Tan, ResolverConstEvalBuiltinTest, testing::Combine(testing::Values(sem::BuiltinType::kTan), testing::ValuesIn(Concat(TanCases(), // TanCases(), TanCases())))); template std::vector TanhCases() { std::vector cases = { 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(), }; return cases; } INSTANTIATE_TEST_SUITE_P( // Tanh, ResolverConstEvalBuiltinTest, testing::Combine(testing::Values(sem::BuiltinType::kTanh), testing::ValuesIn(Concat(TanhCases(), // TanhCases(), TanhCases())))); 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() { (void)E({Vec(0_f, 0_f)}, ""); // Currently unused, but will be soon. 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), // // Value out of f16 range C({65504.003_f}, 65504_f), // C({-65504.003_f}, -65504_f), // C({0x1.234p56_f}, 65504_f), // C({-0x4.321p65_f}, -65504_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)), C({Vec(65504.003_f, 0x1.234p56_f)}, Vec(65504_f, 65504_f)), C({Vec(-0x1.234p56_f, -65504.003_f)}, Vec(-65504_f, -65504_f)), }; } INSTANTIATE_TEST_SUITE_P( // QuantizeToF16, ResolverConstEvalBuiltinTest, testing::Combine(testing::Values(sem::BuiltinType::kQuantizeToF16), testing::ValuesIn(QuantizeToF16Cases()))); } // namespace } // namespace tint::resolver