// Copyright 2022 The Tint Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include "src/tint/resolver/const_eval_test.h" using namespace tint::number_suffixes; // NOLINT namespace tint::resolver { namespace { enum class Kind { kScalar, kVector, }; static std::ostream& operator<<(std::ostream& o, const Kind& k) { switch (k) { case Kind::kScalar: return o << "scalar"; case Kind::kVector: return o << "vector"; } return o << ""; } struct Case { Value input; Value expected; builder::CreatePtrs type; bool unrepresentable = false; }; static std::ostream& operator<<(std::ostream& o, const Case& c) { if (c.unrepresentable) { o << "[unrepresentable] input: " << c.input; } else { o << "input: " << c.input << ", expected: " << c.expected; } return o << ", type: " << c.type; } template Case Success(FROM input, TO expected) { return {Val(input), Val(expected), builder::CreatePtrsFor()}; } template Case Unrepresentable(FROM input) { return {builder::Val(input), builder::Val(0_i), builder::CreatePtrsFor(), /* unrepresentable */ true}; } using ResolverConstEvalConvTest = ResolverTestWithParam>; TEST_P(ResolverConstEvalConvTest, Test) { const auto& kind = std::get<0>(GetParam()); const auto& input = std::get<1>(GetParam()).input; const auto& expected = std::get<1>(GetParam()).expected; const auto& type = std::get<1>(GetParam()).type; const auto unrepresentable = std::get<1>(GetParam()).unrepresentable; auto* input_val = input.Expr(*this); auto* expr = Construct(type.ast(*this), input_val); if (kind == Kind::kVector) { expr = Construct(ty.vec(nullptr, 3), expr); } WrapInFunction(expr); auto* target_sem_ty = type.sem(*this); if (kind == Kind::kVector) { target_sem_ty = create(target_sem_ty, 3u); } if (unrepresentable) { ASSERT_FALSE(r()->Resolve()); EXPECT_THAT(r()->error(), testing::HasSubstr("cannot be represented as")); } else { EXPECT_TRUE(r()->Resolve()) << r()->error(); auto* sem = Sem().Get(expr); ASSERT_NE(sem, nullptr); EXPECT_TYPE(sem->Type(), target_sem_ty); ASSERT_NE(sem->ConstantValue(), nullptr); EXPECT_TYPE(sem->ConstantValue()->Type(), target_sem_ty); auto expected_values = expected.args; if (kind == Kind::kVector) { expected_values.Push(expected_values[0]); expected_values.Push(expected_values[0]); } auto got_values = ScalarsFrom(sem->ConstantValue()); EXPECT_EQ(expected_values, got_values); } } INSTANTIATE_TEST_SUITE_P(ScalarAndVector, ResolverConstEvalConvTest, testing::Combine(testing::Values(Kind::kScalar, Kind::kVector), testing::ValuesIn({ // TODO(crbug.com/tint/1502): Add f16 tests // i32 -> u32 Success(0_i, 0_u), Success(1_i, 1_u), Success(-1_i, 0xffffffff_u), Success(2_i, 2_u), Success(-2_i, 0xfffffffe_u), // i32 -> f32 Success(0_i, 0_f), Success(1_i, 1_f), Success(-1_i, -1_f), Success(2_i, 2_f), Success(-2_i, -2_f), // i32 -> bool Success(0_i, false), Success(1_i, true), Success(-1_i, true), Success(2_i, true), Success(-2_i, true), // u32 -> i32 Success(0_u, 0_i), Success(1_u, 1_i), Success(0xffffffff_u, -1_i), Success(2_u, 2_i), Success(0xfffffffe_u, -2_i), // u32 -> f32 Success(0_u, 0_f), Success(1_u, 1_f), Success(2_u, 2_f), Success(0xffffffff_u, 0xffffffff_f), // u32 -> bool Success(0_u, false), Success(1_u, true), Success(2_u, true), Success(0xffffffff_u, true), // f32 -> i32 Success(0_f, 0_i), Success(1_f, 1_i), Success(2_f, 2_i), Success(1e20_f, i32::Highest()), Success(-1e20_f, i32::Lowest()), // f32 -> u32 Success(0_f, 0_i), Success(1_f, 1_i), Success(-1_f, u32::Lowest()), Success(2_f, 2_i), Success(1e20_f, u32::Highest()), Success(-1e20_f, u32::Lowest()), // f32 -> bool Success(0_f, false), Success(1_f, true), Success(-1_f, true), Success(2_f, true), Success(1e20_f, true), Success(-1e20_f, true), // abstract-int -> i32 Success(0_a, 0_i), Success(1_a, 1_i), Success(-1_a, -1_i), Success(0x7fffffff_a, i32::Highest()), Success(-0x80000000_a, i32::Lowest()), Unrepresentable(0x80000000_a), // abstract-int -> u32 Success(0_a, 0_u), Success(1_a, 1_u), Success(0xffffffff_a, 0xffffffff_u), Unrepresentable(0x100000000_a), Unrepresentable(-1_a), // abstract-int -> f32 Success(0_a, 0_f), Success(1_a, 1_f), Success(0xffffffff_a, 0xffffffff_f), Success(0x100000000_a, 0x100000000_f), Success(-0x100000000_a, -0x100000000_f), Success(0x7fffffffffffffff_a, 0x7fffffffffffffff_f), Success(-0x7fffffffffffffff_a, -0x7fffffffffffffff_f), // abstract-int -> bool Success(0_a, false), Success(1_a, true), Success(0xffffffff_a, true), Success(0x100000000_a, true), Success(-0x100000000_a, true), Success(0x7fffffffffffffff_a, true), Success(-0x7fffffffffffffff_a, true), // abstract-float -> i32 Success(0.0_a, 0_i), Success(1.0_a, 1_i), Success(-1.0_a, -1_i), Success(AFloat(0x7fffffff), i32::Highest()), Success(-AFloat(0x80000000), i32::Lowest()), Unrepresentable(0x80000000_a), // abstract-float -> u32 Success(0.0_a, 0_u), Success(1.0_a, 1_u), Success(AFloat(0xffffffff), 0xffffffff_u), Unrepresentable(AFloat(0x100000000)), Unrepresentable(AFloat(-1)), // abstract-float -> f32 Success(0.0_a, 0_f), Success(1.0_a, 1_f), Success(AFloat(0xffffffff), 0xffffffff_f), Success(AFloat(0x100000000), 0x100000000_f), Success(-AFloat(0x100000000), -0x100000000_f), Unrepresentable(1e40_a), Unrepresentable(-1e40_a), // abstract-float -> bool Success(0.0_a, false), Success(1.0_a, true), Success(AFloat(0xffffffff), true), Success(AFloat(0x100000000), true), Success(-AFloat(0x100000000), true), Success(1e40_a, true), Success(-1e40_a, true), }))); TEST_F(ResolverConstEvalTest, Vec3_Convert_f32_to_i32) { auto* expr = vec3(vec3(1.1_f, 2.2_f, 3.3_f)); WrapInFunction(expr); EXPECT_TRUE(r()->Resolve()) << r()->error(); auto* sem = Sem().Get(expr); ASSERT_NE(sem, nullptr); auto* vec = sem->Type()->As(); ASSERT_NE(vec, nullptr); EXPECT_TRUE(vec->type()->Is()); EXPECT_EQ(vec->Width(), 3u); EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type()); EXPECT_FALSE(sem->ConstantValue()->AllEqual()); EXPECT_FALSE(sem->ConstantValue()->AnyZero()); EXPECT_FALSE(sem->ConstantValue()->AllZero()); EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual()); EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero()); EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero()); EXPECT_EQ(sem->ConstantValue()->Index(0)->As(), 1); EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual()); EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero()); EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero()); EXPECT_EQ(sem->ConstantValue()->Index(1)->As(), 2); EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual()); EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero()); EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero()); EXPECT_EQ(sem->ConstantValue()->Index(2)->As(), 3); } TEST_F(ResolverConstEvalTest, Vec3_Convert_u32_to_f32) { auto* expr = vec3(vec3(10_u, 20_u, 30_u)); WrapInFunction(expr); EXPECT_TRUE(r()->Resolve()) << r()->error(); auto* sem = Sem().Get(expr); ASSERT_NE(sem, nullptr); auto* vec = sem->Type()->As(); ASSERT_NE(vec, nullptr); EXPECT_TRUE(vec->type()->Is()); EXPECT_EQ(vec->Width(), 3u); EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type()); EXPECT_FALSE(sem->ConstantValue()->AllEqual()); EXPECT_FALSE(sem->ConstantValue()->AnyZero()); EXPECT_FALSE(sem->ConstantValue()->AllZero()); EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual()); EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero()); EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero()); EXPECT_EQ(sem->ConstantValue()->Index(0)->As(), 10.f); EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual()); EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero()); EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero()); EXPECT_EQ(sem->ConstantValue()->Index(1)->As(), 20.f); EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual()); EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero()); EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero()); EXPECT_EQ(sem->ConstantValue()->Index(2)->As(), 30.f); } TEST_F(ResolverConstEvalTest, Vec3_Convert_f16_to_i32) { Enable(ast::Extension::kF16); auto* expr = vec3(vec3(1.1_h, 2.2_h, 3.3_h)); WrapInFunction(expr); EXPECT_TRUE(r()->Resolve()) << r()->error(); auto* sem = Sem().Get(expr); EXPECT_NE(sem, nullptr); auto* vec = sem->Type()->As(); ASSERT_NE(vec, nullptr); EXPECT_TRUE(vec->type()->Is()); EXPECT_EQ(vec->Width(), 3u); EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type()); EXPECT_FALSE(sem->ConstantValue()->AllEqual()); EXPECT_FALSE(sem->ConstantValue()->AnyZero()); EXPECT_FALSE(sem->ConstantValue()->AllZero()); EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual()); EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero()); EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero()); EXPECT_EQ(sem->ConstantValue()->Index(0)->As(), 1_i); EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual()); EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero()); EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero()); EXPECT_EQ(sem->ConstantValue()->Index(1)->As(), 2_i); EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual()); EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero()); EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero()); EXPECT_EQ(sem->ConstantValue()->Index(2)->As(), 3_i); } TEST_F(ResolverConstEvalTest, Vec3_Convert_u32_to_f16) { Enable(ast::Extension::kF16); auto* expr = vec3(vec3(10_u, 20_u, 30_u)); WrapInFunction(expr); EXPECT_TRUE(r()->Resolve()) << r()->error(); auto* sem = Sem().Get(expr); EXPECT_NE(sem, nullptr); auto* vec = sem->Type()->As(); ASSERT_NE(vec, nullptr); EXPECT_TRUE(vec->type()->Is()); EXPECT_EQ(vec->Width(), 3u); EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type()); EXPECT_FALSE(sem->ConstantValue()->AllEqual()); EXPECT_FALSE(sem->ConstantValue()->AnyZero()); EXPECT_FALSE(sem->ConstantValue()->AllZero()); EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual()); EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero()); EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero()); EXPECT_EQ(sem->ConstantValue()->Index(0)->As(), 10.f); EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual()); EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero()); EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero()); EXPECT_EQ(sem->ConstantValue()->Index(1)->As(), 20.f); EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual()); EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero()); EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero()); EXPECT_EQ(sem->ConstantValue()->Index(2)->As(), 30.f); } TEST_F(ResolverConstEvalTest, Vec3_Convert_Large_f32_to_i32) { auto* expr = vec3(vec3(1e10_f, -1e20_f, 1e30_f)); WrapInFunction(expr); EXPECT_TRUE(r()->Resolve()) << r()->error(); auto* sem = Sem().Get(expr); ASSERT_NE(sem, nullptr); auto* vec = sem->Type()->As(); ASSERT_NE(vec, nullptr); EXPECT_TRUE(vec->type()->Is()); EXPECT_EQ(vec->Width(), 3u); EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type()); EXPECT_FALSE(sem->ConstantValue()->AllEqual()); EXPECT_FALSE(sem->ConstantValue()->AnyZero()); EXPECT_FALSE(sem->ConstantValue()->AllZero()); EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual()); EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero()); EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero()); EXPECT_EQ(sem->ConstantValue()->Index(0)->As(), i32::Highest()); EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual()); EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero()); EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero()); EXPECT_EQ(sem->ConstantValue()->Index(1)->As(), i32::Lowest()); EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual()); EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero()); EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero()); EXPECT_EQ(sem->ConstantValue()->Index(2)->As(), i32::Highest()); } TEST_F(ResolverConstEvalTest, Vec3_Convert_Large_f32_to_u32) { auto* expr = vec3(vec3(1e10_f, -1e20_f, 1e30_f)); WrapInFunction(expr); EXPECT_TRUE(r()->Resolve()) << r()->error(); auto* sem = Sem().Get(expr); ASSERT_NE(sem, nullptr); auto* vec = sem->Type()->As(); ASSERT_NE(vec, nullptr); EXPECT_TRUE(vec->type()->Is()); EXPECT_EQ(vec->Width(), 3u); EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type()); EXPECT_FALSE(sem->ConstantValue()->AllEqual()); EXPECT_TRUE(sem->ConstantValue()->AnyZero()); EXPECT_FALSE(sem->ConstantValue()->AllZero()); EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual()); EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero()); EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero()); EXPECT_EQ(sem->ConstantValue()->Index(0)->As(), u32::Highest()); EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual()); EXPECT_TRUE(sem->ConstantValue()->Index(1)->AnyZero()); EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllZero()); EXPECT_EQ(sem->ConstantValue()->Index(1)->As(), u32::Lowest()); EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual()); EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero()); EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero()); EXPECT_EQ(sem->ConstantValue()->Index(2)->As(), u32::Highest()); } TEST_F(ResolverConstEvalTest, Vec3_Convert_Large_f32_to_f16) { Enable(ast::Extension::kF16); auto* expr = vec3(Source{{12, 34}}, vec3(1e10_f, 0_f, 0_f)); WrapInFunction(expr); EXPECT_FALSE(r()->Resolve()); EXPECT_EQ(r()->error(), "12:34 error: value 10000000000 cannot be represented as 'f16'"); } TEST_F(ResolverConstEvalTest, Vec3_Convert_Small_f32_to_f16) { Enable(ast::Extension::kF16); auto* expr = vec3(vec3(1e-20_f, -2e-30_f, 3e-40_f)); WrapInFunction(expr); EXPECT_TRUE(r()->Resolve()) << r()->error(); auto* sem = Sem().Get(expr); ASSERT_NE(sem, nullptr); auto* vec = sem->Type()->As(); ASSERT_NE(vec, nullptr); EXPECT_TRUE(vec->type()->Is()); EXPECT_EQ(vec->Width(), 3u); EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type()); EXPECT_FALSE(sem->ConstantValue()->AllEqual()); EXPECT_TRUE(sem->ConstantValue()->AnyZero()); EXPECT_FALSE(sem->ConstantValue()->AllZero()); EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual()); EXPECT_TRUE(sem->ConstantValue()->Index(0)->AnyZero()); EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllZero()); EXPECT_EQ(sem->ConstantValue()->Index(0)->As(), 0.0); EXPECT_FALSE(std::signbit(sem->ConstantValue()->Index(0)->As().value)); EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual()); EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero()); EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero()); EXPECT_EQ(sem->ConstantValue()->Index(1)->As(), -0.0); EXPECT_TRUE(std::signbit(sem->ConstantValue()->Index(1)->As().value)); EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual()); EXPECT_TRUE(sem->ConstantValue()->Index(2)->AnyZero()); EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllZero()); EXPECT_EQ(sem->ConstantValue()->Index(2)->As(), 0.0); EXPECT_FALSE(std::signbit(sem->ConstantValue()->Index(2)->As().value)); } } // namespace } // namespace tint::resolver