tint: Clamp constants to type's limits when number is unrepresentable
When converting values between two concrete types, handle the case that the value is unrepresentable by the target type. For integers, the converted value will be either the maximum or minimum value for the integer type. For floats, the converted value will be positive or negative infinity. Bug: tint:1504 Change-Id: Ia56fb8170c0ea994632194f166062823d9507249 Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/91621 Reviewed-by: David Neto <dneto@google.com> Commit-Queue: Ben Clayton <bclayton@chromium.org>
This commit is contained in:
parent
d42a809e8c
commit
09373989ec
|
@ -89,14 +89,34 @@ sem::Constant::Elements Transform(const sem::Constant::Elements& in,
|
|||
in);
|
||||
}
|
||||
|
||||
/// Converts and returns all the elements in `in` to the type `el_ty`, by performing a `static_cast`
|
||||
/// on each element value. No checks will be performed that the value fits in the target type.
|
||||
/// Converts and returns all the elements in `in` to the type `el_ty`.
|
||||
/// If the value does not fit in the target type, and:
|
||||
/// * the target type is an integer type, then the resulting value will be clamped to the integer's
|
||||
/// highest or lowest value.
|
||||
/// * the target type is an float type, then the resulting value will be either positive or
|
||||
/// negative infinity, based on the sign of the input value.
|
||||
/// @param in the input elements
|
||||
/// @param el_ty the target element type
|
||||
/// @returns the elements converted to `el_ty`
|
||||
sem::Constant::Elements ConvertElements(const sem::Constant::Elements& in, const sem::Type* el_ty) {
|
||||
return Transform(in, el_ty, [](auto& el_out, auto el_in) {
|
||||
el_out = std::decay_t<decltype(el_out)>(el_in);
|
||||
using OUT = std::decay_t<decltype(el_out)>;
|
||||
if (auto conv = CheckedConvert<OUT>(el_in)) {
|
||||
el_out = conv.Get();
|
||||
} else {
|
||||
constexpr auto kInf = std::numeric_limits<double>::infinity();
|
||||
switch (conv.Failure()) {
|
||||
case ConversionFailure::kExceedsNegativeLimit:
|
||||
el_out = IsFloatingPoint<UnwrapNumber<OUT>> ? OUT(-kInf) : OUT::kLowest;
|
||||
break;
|
||||
case ConversionFailure::kExceedsPositiveLimit:
|
||||
el_out = IsFloatingPoint<UnwrapNumber<OUT>> ? OUT(kInf) : OUT::kHighest;
|
||||
break;
|
||||
case ConversionFailure::kTooSmall:
|
||||
el_out = OUT(el_in < 0 ? -0.0 : 0.0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -389,7 +389,7 @@ TEST_F(ResolverConstantsTest, Vec3_MixConstruct_bool) {
|
|||
EXPECT_EQ(sem->ConstantValue().Element<bool>(2), true);
|
||||
}
|
||||
|
||||
TEST_F(ResolverConstantsTest, Vec3_Cast_f32_to_i32) {
|
||||
TEST_F(ResolverConstantsTest, Vec3_Convert_f32_to_i32) {
|
||||
auto* expr = vec3<i32>(vec3<f32>(1.1_f, 2.2_f, 3.3_f));
|
||||
WrapInFunction(expr);
|
||||
|
||||
|
@ -408,7 +408,7 @@ TEST_F(ResolverConstantsTest, Vec3_Cast_f32_to_i32) {
|
|||
EXPECT_EQ(sem->ConstantValue().Element<AInt>(2).value, 3);
|
||||
}
|
||||
|
||||
TEST_F(ResolverConstantsTest, Vec3_Cast_u32_to_f32) {
|
||||
TEST_F(ResolverConstantsTest, Vec3_Convert_u32_to_f32) {
|
||||
auto* expr = vec3<f32>(vec3<u32>(10_u, 20_u, 30_u));
|
||||
WrapInFunction(expr);
|
||||
|
||||
|
@ -427,5 +427,89 @@ TEST_F(ResolverConstantsTest, Vec3_Cast_u32_to_f32) {
|
|||
EXPECT_EQ(sem->ConstantValue().Element<AFloat>(2).value, 30.f);
|
||||
}
|
||||
|
||||
TEST_F(ResolverConstantsTest, Vec3_Convert_Large_f32_to_i32) {
|
||||
auto* expr = vec3<i32>(vec3<f32>(1e10_f, -1e20_f, 1e30_f));
|
||||
WrapInFunction(expr);
|
||||
|
||||
EXPECT_TRUE(r()->Resolve()) << r()->error();
|
||||
|
||||
auto* sem = Sem().Get(expr);
|
||||
EXPECT_NE(sem, nullptr);
|
||||
ASSERT_TRUE(sem->Type()->Is<sem::Vector>());
|
||||
EXPECT_TRUE(sem->Type()->As<sem::Vector>()->type()->Is<sem::I32>());
|
||||
EXPECT_EQ(sem->Type()->As<sem::Vector>()->Width(), 3u);
|
||||
EXPECT_EQ(sem->ConstantValue().Type(), sem->Type());
|
||||
EXPECT_TRUE(sem->ConstantValue().ElementType()->Is<sem::I32>());
|
||||
ASSERT_EQ(sem->ConstantValue().ElementCount(), 3u);
|
||||
EXPECT_EQ(sem->ConstantValue().Element<AInt>(0).value, i32::kHighest);
|
||||
EXPECT_EQ(sem->ConstantValue().Element<AInt>(1).value, i32::kLowest);
|
||||
EXPECT_EQ(sem->ConstantValue().Element<AInt>(2).value, i32::kHighest);
|
||||
}
|
||||
|
||||
TEST_F(ResolverConstantsTest, Vec3_Convert_Large_f32_to_u32) {
|
||||
auto* expr = vec3<u32>(vec3<f32>(1e10_f, -1e20_f, 1e30_f));
|
||||
WrapInFunction(expr);
|
||||
|
||||
EXPECT_TRUE(r()->Resolve()) << r()->error();
|
||||
|
||||
auto* sem = Sem().Get(expr);
|
||||
EXPECT_NE(sem, nullptr);
|
||||
ASSERT_TRUE(sem->Type()->Is<sem::Vector>());
|
||||
EXPECT_TRUE(sem->Type()->As<sem::Vector>()->type()->Is<sem::U32>());
|
||||
EXPECT_EQ(sem->Type()->As<sem::Vector>()->Width(), 3u);
|
||||
EXPECT_EQ(sem->ConstantValue().Type(), sem->Type());
|
||||
EXPECT_TRUE(sem->ConstantValue().ElementType()->Is<sem::U32>());
|
||||
ASSERT_EQ(sem->ConstantValue().ElementCount(), 3u);
|
||||
EXPECT_EQ(sem->ConstantValue().Element<AInt>(0).value, u32::kHighest);
|
||||
EXPECT_EQ(sem->ConstantValue().Element<AInt>(1).value, u32::kLowest);
|
||||
EXPECT_EQ(sem->ConstantValue().Element<AInt>(2).value, u32::kHighest);
|
||||
}
|
||||
|
||||
// TODO(crbug.com/tint/1502): Enable when f16 overloads are implemented
|
||||
TEST_F(ResolverConstantsTest, DISABLED_Vec3_Convert_Large_f32_to_f16) {
|
||||
Enable(ast::Extension::kF16);
|
||||
|
||||
auto* expr = vec3<f16>(vec3<f32>(0.00001_f, -0.00002_f, 0.00003_f));
|
||||
WrapInFunction(expr);
|
||||
|
||||
EXPECT_TRUE(r()->Resolve()) << r()->error();
|
||||
|
||||
constexpr auto kInf = std::numeric_limits<double>::infinity();
|
||||
|
||||
auto* sem = Sem().Get(expr);
|
||||
EXPECT_NE(sem, nullptr);
|
||||
ASSERT_TRUE(sem->Type()->Is<sem::Vector>());
|
||||
EXPECT_TRUE(sem->Type()->As<sem::Vector>()->type()->Is<sem::F16>());
|
||||
EXPECT_EQ(sem->Type()->As<sem::Vector>()->Width(), 3u);
|
||||
EXPECT_EQ(sem->ConstantValue().Type(), sem->Type());
|
||||
EXPECT_TRUE(sem->ConstantValue().ElementType()->Is<sem::F16>());
|
||||
ASSERT_EQ(sem->ConstantValue().ElementCount(), 3u);
|
||||
EXPECT_EQ(sem->ConstantValue().Element<AFloat>(0).value, kInf);
|
||||
EXPECT_EQ(sem->ConstantValue().Element<AFloat>(1).value, -kInf);
|
||||
EXPECT_EQ(sem->ConstantValue().Element<AFloat>(2).value, kInf);
|
||||
}
|
||||
|
||||
// TODO(crbug.com/tint/1502): Enable when f16 overloads are implemented
|
||||
TEST_F(ResolverConstantsTest, DISABLED_Vec3_Convert_Small_f32_to_f16) {
|
||||
Enable(ast::Extension::kF16);
|
||||
|
||||
auto* expr = vec3<f16>(vec3<f32>(1e-10_f, -1e20_f, 1e30_f));
|
||||
WrapInFunction(expr);
|
||||
|
||||
EXPECT_TRUE(r()->Resolve()) << r()->error();
|
||||
|
||||
auto* sem = Sem().Get(expr);
|
||||
EXPECT_NE(sem, nullptr);
|
||||
ASSERT_TRUE(sem->Type()->Is<sem::Vector>());
|
||||
EXPECT_TRUE(sem->Type()->As<sem::Vector>()->type()->Is<sem::F16>());
|
||||
EXPECT_EQ(sem->Type()->As<sem::Vector>()->Width(), 3u);
|
||||
EXPECT_EQ(sem->ConstantValue().Type(), sem->Type());
|
||||
EXPECT_TRUE(sem->ConstantValue().ElementType()->Is<sem::F16>());
|
||||
ASSERT_EQ(sem->ConstantValue().ElementCount(), 3u);
|
||||
EXPECT_EQ(sem->ConstantValue().Element<AFloat>(0).value, 0.0);
|
||||
EXPECT_EQ(sem->ConstantValue().Element<AFloat>(1).value, -0.0);
|
||||
EXPECT_EQ(sem->ConstantValue().Element<AFloat>(2).value, 0.0);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace tint::resolver
|
||||
|
|
Loading…
Reference in New Issue