tint: Remove ConversionFailure::kTooSmall

Nothing errors on this. Simplifies code.
Also: Disable MSVC warnings about constant overflow.

Fixed: tint:1564
Change-Id: I5bb2c2ebb89966f5b3f7cbcd73672e110b1b98cd
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/91622
Reviewed-by: David Neto <dneto@google.com>
Commit-Queue: Ben Clayton <bclayton@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
This commit is contained in:
Ben Clayton 2022-05-27 20:22:26 +00:00 committed by Dawn LUCI CQ
parent 4b04721075
commit 572eaf271e
13 changed files with 141 additions and 97 deletions

View File

@ -26,8 +26,6 @@ std::ostream& operator<<(std::ostream& out, ConversionFailure failure) {
return out << "value exceeds positive limit for type"; return out << "value exceeds positive limit for type";
case ConversionFailure::kExceedsNegativeLimit: case ConversionFailure::kExceedsNegativeLimit:
return out << "value exceeds negative limit for type"; return out << "value exceeds negative limit for type";
case ConversionFailure::kTooSmall:
return out << "value is too small for type";
} }
return out << "<unknown>"; return out << "<unknown>";
} }

View File

@ -188,7 +188,6 @@ std::enable_if_t<IsNumeric<A>, bool> operator!=(A a, Number<B> b) {
enum class ConversionFailure { enum class ConversionFailure {
kExceedsPositiveLimit, // The value was too big (+'ve) to fit in the target type kExceedsPositiveLimit, // The value was too big (+'ve) to fit in the target type
kExceedsNegativeLimit, // The value was too big (-'ve) to fit in the target type kExceedsNegativeLimit, // The value was too big (-'ve) to fit in the target type
kTooSmall, // The value was too small to fit in the target type
}; };
/// Writes the conversion failure message to the ostream. /// Writes the conversion failure message to the ostream.
@ -209,12 +208,6 @@ utils::Result<TO, ConversionFailure> CheckedConvert(Number<FROM> num) {
if (value < static_cast<T>(TO::kLowest)) { if (value < static_cast<T>(TO::kLowest)) {
return ConversionFailure::kExceedsNegativeLimit; return ConversionFailure::kExceedsNegativeLimit;
} }
if constexpr (IsFloatingPoint<UnwrapNumber<TO>>) {
if ((value < T(0) && value > static_cast<T>(-TO::kSmallest)) ||
(value > T(0) && value < static_cast<T>(TO::kSmallest))) {
return ConversionFailure::kTooSmall;
}
}
return TO(value); // Success return TO(value); // Success
} }

View File

@ -15,6 +15,7 @@
#include <cmath> #include <cmath>
#include "src/tint/program_builder.h" #include "src/tint/program_builder.h"
#include "src/tint/utils/compiler_macros.h"
#include "gtest/gtest.h" #include "gtest/gtest.h"
@ -28,43 +29,40 @@ constexpr int64_t kHighestU32 = static_cast<int64_t>(std::numeric_limits<uint32_
constexpr int64_t kLowestI32 = static_cast<int64_t>(std::numeric_limits<int32_t>::min()); constexpr int64_t kLowestI32 = static_cast<int64_t>(std::numeric_limits<int32_t>::min());
constexpr int64_t kLowestU32 = static_cast<int64_t>(std::numeric_limits<uint32_t>::min()); constexpr int64_t kLowestU32 = static_cast<int64_t>(std::numeric_limits<uint32_t>::min());
// Highest float32 value. Calculated as: // Highest float32 value.
// (2^127)×(1+(0x7fffff÷0x800000)) constexpr double kHighestF32 = 0x1.fffffep+127;
constexpr double kHighestF32 = 340282346638528859811704183484516925440.0;
// Next ULP up from kHighestF32 for a float64. Calculated as: // Next ULP up from kHighestF32 for a float64.
// (2^127)×(1+(0xfffffe0000001÷0x10000000000000)) constexpr double kHighestF32NextULP = 0x1.fffffe0000001p+127;
constexpr double kHighestF32NextULP = 340282346638528897590636046441678635008.0;
// Smallest positive normal float32 value. Calculated as: // Smallest positive normal float32 value.
// 2^-126 constexpr double kSmallestF32 = 0x1p-126;
constexpr double kSmallestF32 = 1.1754943508222875e-38;
// Next ULP down from kSmallestF32 for a float64. Calculated as: // Highest subnormal value for a float32.
// (2^-127)×(1+(0xfffffffffffff÷0x10000000000000)) constexpr double kHighestF32Subnormal = 0x0.fffffep-126;
constexpr double kSmallestF32PrevULP = 1.1754943508222874e-38;
// Highest float16 value. Calculated as: // Highest float16 value.
// (2^15)×(1+(0x3ff÷0x400)) constexpr double kHighestF16 = 0x1.ffcp+15;
constexpr double kHighestF16 = 65504.0;
// Next ULP up from kHighestF16 for a float64. Calculated as: // Next ULP up from kHighestF16 for a float64.
// (2^15)×(1+(0xffc0000000001÷0x10000000000000)) constexpr double kHighestF16NextULP = 0x1.ffc0000000001p+15;
constexpr double kHighestF16NextULP = 65504.00000000001;
// Smallest positive normal float16 value. Calculated as: // Smallest positive normal float16 value.
// 2^-14 constexpr double kSmallestF16 = 0x1p-14;
constexpr double kSmallestF16 = 0.00006103515625;
// Next ULP down from kSmallestF16 for a float64. Calculated as: // Highest subnormal value for a float32.
// (2^-15)×(1+(0xfffffffffffff÷0x10000000000000)) constexpr double kHighestF16Subnormal = 0x0.ffcp-14;
constexpr double kSmallestF16PrevULP = 0.00006103515624999999;
constexpr double kLowestF32 = -kHighestF32; constexpr double kLowestF32 = -kHighestF32;
constexpr double kLowestF32NextULP = -kHighestF32NextULP; constexpr double kLowestF32NextULP = -kHighestF32NextULP;
constexpr double kLowestF16 = -kHighestF16; constexpr double kLowestF16 = -kHighestF16;
constexpr double kLowestF16NextULP = -kHighestF16NextULP; constexpr double kLowestF16NextULP = -kHighestF16NextULP;
// MSVC (only in release builds) can grumble about some of the inlined numerical overflow /
// underflow that's done in this file. We like to think we know what we're doing, so silence the
// warning.
TINT_BEGIN_DISABLE_WARNING(CONSTANT_OVERFLOW);
TEST(NumberTest, CheckedConvertIdentity) { TEST(NumberTest, CheckedConvertIdentity) {
EXPECT_EQ(CheckedConvert<AInt>(0_a), 0_a); EXPECT_EQ(CheckedConvert<AInt>(0_a), 0_a);
EXPECT_EQ(CheckedConvert<AFloat>(0_a), 0.0_a); EXPECT_EQ(CheckedConvert<AFloat>(0_a), 0.0_a);
@ -120,9 +118,11 @@ TEST(NumberTest, CheckedConvertExceedsNegativeLimit) {
ConversionFailure::kExceedsNegativeLimit); ConversionFailure::kExceedsNegativeLimit);
} }
TEST(NumberTest, CheckedConvertTooSmall) { TEST(NumberTest, CheckedConvertSubnormals) {
EXPECT_EQ(CheckedConvert<f32>(AFloat(kSmallestF32PrevULP)), ConversionFailure::kTooSmall); EXPECT_EQ(CheckedConvert<f32>(AFloat(kHighestF32Subnormal)), f32(kHighestF32Subnormal));
EXPECT_EQ(CheckedConvert<f16>(AFloat(kSmallestF16PrevULP)), ConversionFailure::kTooSmall); EXPECT_EQ(CheckedConvert<f16>(AFloat(kHighestF16Subnormal)), f16(kHighestF16Subnormal));
EXPECT_EQ(CheckedConvert<f32>(AFloat(-kHighestF32Subnormal)), f32(-kHighestF32Subnormal));
EXPECT_EQ(CheckedConvert<f16>(AFloat(-kHighestF16Subnormal)), f16(-kHighestF16Subnormal));
} }
TEST(NumberTest, QuantizeF16) { TEST(NumberTest, QuantizeF16) {
@ -141,5 +141,7 @@ TEST(NumberTest, QuantizeF16) {
EXPECT_TRUE(std::isnan(f16(nan))); EXPECT_TRUE(std::isnan(f16(nan)));
} }
TINT_END_DISABLE_WARNING(CONSTANT_OVERFLOW);
} // namespace } // namespace
} // namespace tint } // namespace tint

View File

@ -364,8 +364,6 @@ Token Lexer::try_float() {
if (has_f_suffix) { if (has_f_suffix) {
if (auto f = CheckedConvert<f32>(AFloat(value))) { if (auto f = CheckedConvert<f32>(AFloat(value))) {
return {Token::Type::kFloatLiteral_F, source, static_cast<double>(f.Get())}; return {Token::Type::kFloatLiteral_F, source, static_cast<double>(f.Get())};
} else if (f.Failure() == ConversionFailure::kTooSmall) {
return {Token::Type::kFloatLiteral_F, source, 0.0};
} else { } else {
return {Token::Type::kError, source, "value cannot be represented as 'f32'"}; return {Token::Type::kError, source, "value cannot be represented as 'f32'"};
} }

View File

@ -376,59 +376,60 @@ constexpr double kMaxF32 = static_cast<double>(f32::kHighest);
constexpr double kPiF64 = 3.141592653589793; constexpr double kPiF64 = 3.141592653589793;
constexpr double kPiF32 = 3.1415927410125732; // kPiF64 quantized to f32 constexpr double kPiF32 = 3.1415927410125732; // kPiF64 quantized to f32
// (2^-127)×(1+(0xfffffffffffff÷0x10000000000000)) constexpr double kSubnormalF32 = 0x1.0p-128;
constexpr double kTooSmallF32 = 1.1754943508222874e-38;
INSTANTIATE_TEST_SUITE_P( INSTANTIATE_TEST_SUITE_P(
MaterializeScalar, MaterializeScalar,
MaterializeAbstractNumeric, // MaterializeAbstractNumeric, //
testing::Combine(testing::Values(Expectation::kMaterialize), // testing::Combine(testing::Values(Expectation::kMaterialize), //
testing::ValuesIn(kScalarMethods), // testing::ValuesIn(kScalarMethods), //
testing::Values(Types<i32, AInt>(0_a, 0.0), // testing::Values(Types<i32, AInt>(0_a, 0.0), //
Types<i32, AInt>(2147483647_a, 2147483647.0), // Types<i32, AInt>(2147483647_a, 2147483647.0), //
Types<i32, AInt>(-2147483648_a, -2147483648.0), // Types<i32, AInt>(-2147483648_a, -2147483648.0), //
Types<u32, AInt>(0_a, 0.0), // Types<u32, AInt>(0_a, 0.0), //
Types<u32, AInt>(4294967295_a, 4294967295.0), // Types<u32, AInt>(4294967295_a, 4294967295.0), //
Types<f32, AFloat>(0.0_a, 0.0), // Types<f32, AFloat>(0.0_a, 0.0), //
Types<f32, AFloat>(AFloat(kMaxF32), kMaxF32), // Types<f32, AFloat>(AFloat(kMaxF32), kMaxF32), //
Types<f32, AFloat>(AFloat(-kMaxF32), -kMaxF32), // Types<f32, AFloat>(AFloat(-kMaxF32), -kMaxF32), //
Types<f32, AFloat>(AFloat(kPiF32), kPiF64), // Types<f32, AFloat>(AFloat(kPiF32), kPiF64), //
Types<f32, AFloat>(0.0_a, kTooSmallF32), // Types<f32, AFloat>(AFloat(kSubnormalF32), kSubnormalF32), //
Types<f32, AFloat>(-0.0_a, -kTooSmallF32) // Types<f32, AFloat>(AFloat(-kSubnormalF32), -kSubnormalF32) //
/* Types<f16, AFloat>(1.0_a), */ // /* Types<f16, AFloat>(1.0_a), */ //
/* Types<f16, AFloat>(1.0_a), */))); /* Types<f16, AFloat>(1.0_a), */)));
INSTANTIATE_TEST_SUITE_P( INSTANTIATE_TEST_SUITE_P(
MaterializeVector, MaterializeVector,
MaterializeAbstractNumeric, // MaterializeAbstractNumeric, //
testing::Combine(testing::Values(Expectation::kMaterialize), // testing::Combine(testing::Values(Expectation::kMaterialize), //
testing::ValuesIn(kVectorMethods), // testing::ValuesIn(kVectorMethods), //
testing::Values(Types<i32V, AIntV>(0_a, 0.0), // testing::Values(Types<i32V, AIntV>(0_a, 0.0), //
Types<i32V, AIntV>(2147483647_a, 2147483647.0), // Types<i32V, AIntV>(2147483647_a, 2147483647.0), //
Types<i32V, AIntV>(-2147483648_a, -2147483648.0), // Types<i32V, AIntV>(-2147483648_a, -2147483648.0), //
Types<u32V, AIntV>(0_a, 0.0), // Types<u32V, AIntV>(0_a, 0.0), //
Types<u32V, AIntV>(4294967295_a, 4294967295.0), // Types<u32V, AIntV>(4294967295_a, 4294967295.0), //
Types<f32V, AFloatV>(0.0_a, 0.0), // Types<f32V, AFloatV>(0.0_a, 0.0), //
Types<f32V, AFloatV>(AFloat(kMaxF32), kMaxF32), // Types<f32V, AFloatV>(AFloat(kMaxF32), kMaxF32), //
Types<f32V, AFloatV>(AFloat(-kMaxF32), -kMaxF32), // Types<f32V, AFloatV>(AFloat(-kMaxF32), -kMaxF32), //
Types<f32V, AFloatV>(AFloat(kPiF32), kPiF64), // Types<f32V, AFloatV>(AFloat(kPiF32), kPiF64), //
Types<f32V, AFloatV>(0.0_a, kTooSmallF32), // Types<f32V, AFloatV>(AFloat(kSubnormalF32), kSubnormalF32), //
Types<f32V, AFloatV>(-0.0_a, -kTooSmallF32) // Types<f32V, AFloatV>(AFloat(-kSubnormalF32),
/* Types<f16V, AFloatV>(1.0_a), */ // -kSubnormalF32) //
/* Types<f16V, AFloatV>(1.0_a), */ //
/* Types<f16V, AFloatV>(1.0_a), */))); /* Types<f16V, AFloatV>(1.0_a), */)));
INSTANTIATE_TEST_SUITE_P( INSTANTIATE_TEST_SUITE_P(
MaterializeMatrix, MaterializeMatrix,
MaterializeAbstractNumeric, // MaterializeAbstractNumeric, //
testing::Combine(testing::Values(Expectation::kMaterialize), // testing::Combine(testing::Values(Expectation::kMaterialize), //
testing::ValuesIn(kMatrixMethods), // testing::ValuesIn(kMatrixMethods), //
testing::Values(Types<f32M, AFloatM>(0.0_a, 0.0), // testing::Values(Types<f32M, AFloatM>(0.0_a, 0.0), //
Types<f32M, AFloatM>(AFloat(kMaxF32), kMaxF32), // Types<f32M, AFloatM>(AFloat(kMaxF32), kMaxF32), //
Types<f32M, AFloatM>(AFloat(-kMaxF32), -kMaxF32), // Types<f32M, AFloatM>(AFloat(-kMaxF32), -kMaxF32), //
Types<f32M, AFloatM>(AFloat(kPiF32), kPiF64), // Types<f32M, AFloatM>(AFloat(kPiF32), kPiF64), //
Types<f32M, AFloatM>(0.0_a, kTooSmallF32), // Types<f32M, AFloatM>(AFloat(kSubnormalF32), kSubnormalF32), //
Types<f32M, AFloatM>(-0.0_a, -kTooSmallF32) // Types<f32M, AFloatM>(AFloat(-kSubnormalF32),
/* Types<f16V, AFloatM>(1.0_a), */ // -kSubnormalF32) //
/* Types<f16V, AFloatM>(1.0_a), */ //
))); )));
INSTANTIATE_TEST_SUITE_P( INSTANTIATE_TEST_SUITE_P(

View File

@ -39,7 +39,7 @@ namespace {
/// @returns the elements converted to type T. /// @returns the elements converted to type T.
template <typename T, typename ELEMENTS_IN, typename CONVERTER> template <typename T, typename ELEMENTS_IN, typename CONVERTER>
sem::Constant::Elements Transform(const ELEMENTS_IN& elements_in, CONVERTER&& converter) { sem::Constant::Elements Transform(const ELEMENTS_IN& elements_in, CONVERTER&& converter) {
TINT_BEGIN_DISABLE_WARNING_UNREACHABLE_CODE(); TINT_BEGIN_DISABLE_WARNING(UNREACHABLE_CODE);
return utils::Transform(elements_in, [&](auto value_in) { return utils::Transform(elements_in, [&](auto value_in) {
if constexpr (std::is_same_v<UnwrapNumber<T>, bool>) { if constexpr (std::is_same_v<UnwrapNumber<T>, bool>) {
@ -55,7 +55,7 @@ sem::Constant::Elements Transform(const ELEMENTS_IN& elements_in, CONVERTER&& co
} }
}); });
TINT_END_DISABLE_WARNING_UNREACHABLE_CODE(); TINT_END_DISABLE_WARNING(UNREACHABLE_CODE);
} }
/// Converts and returns all the element values of `in` to the semantic type `el_ty`, using the /// Converts and returns all the element values of `in` to the semantic type `el_ty`, using the
@ -112,9 +112,6 @@ sem::Constant::Elements ConvertElements(const sem::Constant::Elements& in, const
case ConversionFailure::kExceedsPositiveLimit: case ConversionFailure::kExceedsPositiveLimit:
el_out = IsFloatingPoint<UnwrapNumber<OUT>> ? OUT(kInf) : OUT::kHighest; el_out = IsFloatingPoint<UnwrapNumber<OUT>> ? OUT(kInf) : OUT::kHighest;
break; break;
case ConversionFailure::kTooSmall:
el_out = OUT(el_in < 0 ? -0.0 : 0.0);
break;
} }
} }
}); });
@ -137,8 +134,6 @@ utils::Result<sem::Constant::Elements> MaterializeElements(const sem::Constant::
using OUT = std::decay_t<decltype(el_out)>; using OUT = std::decay_t<decltype(el_out)>;
if (auto conv = CheckedConvert<OUT>(el_in)) { if (auto conv = CheckedConvert<OUT>(el_in)) {
el_out = conv.Get(); el_out = conv.Get();
} else if (conv.Failure() == ConversionFailure::kTooSmall) {
el_out = OUT(el_in < 0 ? -0.0 : 0.0);
} else if (!failure.has_value()) { } else if (!failure.has_value()) {
std::stringstream ss; std::stringstream ss;
ss << "value " << el_in << " cannot be represented as "; ss << "value " << el_in << " cannot be represented as ";

View File

@ -12,27 +12,30 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
#include "src/tint/utils/concat.h"
#ifndef SRC_TINT_UTILS_COMPILER_MACROS_H_ #ifndef SRC_TINT_UTILS_COMPILER_MACROS_H_
#define SRC_TINT_UTILS_COMPILER_MACROS_H_ #define SRC_TINT_UTILS_COMPILER_MACROS_H_
#define TINT_REQUIRE_SEMICOLON \ #define TINT_REQUIRE_SEMICOLON static_assert(true)
do { \
} while (false)
#if defined(_MSC_VER) #if defined(_MSC_VER)
#define TINT_WARNING_UNREACHABLE_CODE 4702
#define TINT_WARNING_CONSTANT_OVERFLOW 4756
// clang-format off // clang-format off
#define TINT_BEGIN_DISABLE_WARNING_UNREACHABLE_CODE() \ #define TINT_BEGIN_DISABLE_WARNING(name) \
__pragma(warning(push)) \ __pragma(warning(push)) \
__pragma(warning(disable:4702)) \ __pragma(warning(disable:TINT_CONCAT(TINT_WARNING_, name))) \
TINT_REQUIRE_SEMICOLON TINT_REQUIRE_SEMICOLON
#define TINT_END_DISABLE_WARNING_UNREACHABLE_CODE() \ #define TINT_END_DISABLE_WARNING(name) \
__pragma(warning(pop)) \ __pragma(warning(pop)) \
TINT_REQUIRE_SEMICOLON TINT_REQUIRE_SEMICOLON
// clang-format on // clang-format on
#else #else
// clang-format off // clang-format off
#define TINT_BEGIN_DISABLE_WARNING_UNREACHABLE_CODE() TINT_REQUIRE_SEMICOLON #define TINT_BEGIN_DISABLE_WARNING(name) TINT_REQUIRE_SEMICOLON
#define TINT_END_DISABLE_WARNING_UNREACHABLE_CODE() TINT_REQUIRE_SEMICOLON #define TINT_END_DISABLE_WARNING(name) TINT_REQUIRE_SEMICOLON
// clang-format on // clang-format on
#endif // defined(_MSC_VER) #endif // defined(_MSC_VER)

View File

@ -0,0 +1,3 @@
fn foo() {
let b = 1e-40f;
}

View File

@ -0,0 +1,10 @@
#version 310 es
layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
void unused_entry_point() {
return;
}
void foo() {
float b = 9.9999461e-41f;
}

View File

@ -0,0 +1,8 @@
[numthreads(1, 1, 1)]
void unused_entry_point() {
return;
}
void foo() {
const float b = 9.9999461e-41f;
}

View File

@ -0,0 +1,7 @@
#include <metal_stdlib>
using namespace metal;
void foo() {
float const b = 9.9999461e-41f;
}

View File

@ -0,0 +1,23 @@
; SPIR-V
; Version: 1.3
; Generator: Google Tint Compiler; 0
; Bound: 9
; Schema: 0
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %unused_entry_point "unused_entry_point"
OpExecutionMode %unused_entry_point LocalSize 1 1 1
OpName %unused_entry_point "unused_entry_point"
OpName %foo "foo"
%void = OpTypeVoid
%1 = OpTypeFunction %void
%float = OpTypeFloat 32
%float_0x1_16c2pn133 = OpConstant %float 0x1.16c2p-133
%unused_entry_point = OpFunction %void None %1
%4 = OpLabel
OpReturn
OpFunctionEnd
%foo = OpFunction %void None %1
%6 = OpLabel
OpReturn
OpFunctionEnd

View File

@ -0,0 +1,3 @@
fn foo() {
let b = 0x1.16c2p-133;
}