tint/reader/wgsl: Lex abstract hex floats
Scale up the hex float parsing to support doubles. Also make it an error if the hex float resolves to an inf or NaN. See: https://github.com/gpuweb/gpuweb/issues/2953 Bug: tint:1504 Change-Id: Ie923bb5f660285fb7563cfe8dafa07d87b898a7e Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/91426 Kokoro: Kokoro <noreply+kokoro@google.com> Commit-Queue: Ben Clayton <bclayton@chromium.org> Reviewed-by: David Neto <dneto@google.com>
This commit is contained in:
parent
f45180a428
commit
30f01c1790
|
@ -387,17 +387,18 @@ Token Lexer::try_float() {
|
||||||
}
|
}
|
||||||
|
|
||||||
Token Lexer::try_hex_float() {
|
Token Lexer::try_hex_float() {
|
||||||
constexpr uint32_t kExponentBits = 8;
|
constexpr uint64_t kExponentBits = 11;
|
||||||
constexpr uint32_t kMantissaBits = 23;
|
constexpr uint64_t kMantissaBits = 52;
|
||||||
constexpr uint32_t kTotalBits = 1 + kExponentBits + kMantissaBits;
|
constexpr uint64_t kTotalBits = 1 + kExponentBits + kMantissaBits;
|
||||||
constexpr uint32_t kTotalMsb = kTotalBits - 1;
|
constexpr uint64_t kTotalMsb = kTotalBits - 1;
|
||||||
constexpr uint32_t kMantissaMsb = kMantissaBits - 1;
|
constexpr uint64_t kMantissaMsb = kMantissaBits - 1;
|
||||||
constexpr uint32_t kMantissaShiftRight = kTotalBits - kMantissaBits;
|
constexpr uint64_t kMantissaShiftRight = kTotalBits - kMantissaBits;
|
||||||
constexpr int32_t kExponentBias = 127;
|
constexpr int64_t kExponentBias = 1023;
|
||||||
constexpr uint32_t kExponentMask = (1 << kExponentBits) - 1;
|
constexpr uint64_t kExponentMask = (1 << kExponentBits) - 1;
|
||||||
constexpr int32_t kExponentMax = kExponentMask; // Including NaN / inf
|
constexpr int64_t kExponentMax = kExponentMask; // Including NaN / inf
|
||||||
constexpr uint32_t kExponentLeftShift = kMantissaBits;
|
constexpr uint64_t kExponentLeftShift = kMantissaBits;
|
||||||
constexpr uint32_t kSignBit = kTotalBits - 1;
|
constexpr uint64_t kSignBit = kTotalBits - 1;
|
||||||
|
constexpr uint64_t kOne = 1;
|
||||||
|
|
||||||
auto start = pos();
|
auto start = pos();
|
||||||
auto end = pos();
|
auto end = pos();
|
||||||
|
@ -409,7 +410,7 @@ Token Lexer::try_hex_float() {
|
||||||
// clang-format on
|
// clang-format on
|
||||||
|
|
||||||
// -?
|
// -?
|
||||||
int32_t sign_bit = 0;
|
int64_t sign_bit = 0;
|
||||||
if (matches(end, "-")) {
|
if (matches(end, "-")) {
|
||||||
sign_bit = 1;
|
sign_bit = 1;
|
||||||
end++;
|
end++;
|
||||||
|
@ -421,8 +422,8 @@ Token Lexer::try_hex_float() {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t mantissa = 0;
|
uint64_t mantissa = 0;
|
||||||
uint32_t exponent = 0;
|
uint64_t exponent = 0;
|
||||||
|
|
||||||
// TODO(dneto): Values in the normal range for the format do not explicitly
|
// TODO(dneto): Values in the normal range for the format do not explicitly
|
||||||
// store the most significant bit. The algorithm here works hard to eliminate
|
// store the most significant bit. The algorithm here works hard to eliminate
|
||||||
|
@ -435,7 +436,7 @@ Token Lexer::try_hex_float() {
|
||||||
// `set_next_mantissa_bit_to` sets next `mantissa` bit starting from msb to
|
// `set_next_mantissa_bit_to` sets next `mantissa` bit starting from msb to
|
||||||
// lsb to value 1 if `set` is true, 0 otherwise. Returns true on success, i.e.
|
// lsb to value 1 if `set` is true, 0 otherwise. Returns true on success, i.e.
|
||||||
// when the bit can be accommodated in the available space.
|
// when the bit can be accommodated in the available space.
|
||||||
uint32_t mantissa_next_bit = kTotalMsb;
|
uint64_t mantissa_next_bit = kTotalMsb;
|
||||||
auto set_next_mantissa_bit_to = [&](bool set, bool integer_part) -> bool {
|
auto set_next_mantissa_bit_to = [&](bool set, bool integer_part) -> bool {
|
||||||
// If adding bits for the integer part, we can overflow whether we set the
|
// If adding bits for the integer part, we can overflow whether we set the
|
||||||
// bit or not. For the fractional part, we can only overflow when setting
|
// bit or not. For the fractional part, we can only overflow when setting
|
||||||
|
@ -447,7 +448,7 @@ Token Lexer::try_hex_float() {
|
||||||
return false; // Overflowed mantissa
|
return false; // Overflowed mantissa
|
||||||
}
|
}
|
||||||
if (set) {
|
if (set) {
|
||||||
mantissa |= (1 << mantissa_next_bit);
|
mantissa |= (kOne << mantissa_next_bit);
|
||||||
}
|
}
|
||||||
--mantissa_next_bit;
|
--mantissa_next_bit;
|
||||||
return true;
|
return true;
|
||||||
|
@ -503,7 +504,7 @@ Token Lexer::try_hex_float() {
|
||||||
has_zero_integer = false;
|
has_zero_integer = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int32_t bit = 3; bit >= 0; --bit) {
|
for (int bit = 3; bit >= 0; --bit) {
|
||||||
auto v = 1 & (nibble >> bit);
|
auto v = 1 & (nibble >> bit);
|
||||||
|
|
||||||
// Skip leading 0s and the first 1
|
// Skip leading 0s and the first 1
|
||||||
|
@ -524,7 +525,7 @@ Token Lexer::try_hex_float() {
|
||||||
// [0-9a-fA-F]*
|
// [0-9a-fA-F]*
|
||||||
for (auto i = fractional_range.first; i < fractional_range.second; ++i) {
|
for (auto i = fractional_range.first; i < fractional_range.second; ++i) {
|
||||||
auto nibble = hex_value(at(i));
|
auto nibble = hex_value(at(i));
|
||||||
for (int32_t bit = 3; bit >= 0; --bit) {
|
for (int bit = 3; bit >= 0; --bit) {
|
||||||
auto v = 1 & (nibble >> bit);
|
auto v = 1 & (nibble >> bit);
|
||||||
|
|
||||||
if (v == 1) {
|
if (v == 1) {
|
||||||
|
@ -552,8 +553,8 @@ Token Lexer::try_hex_float() {
|
||||||
|
|
||||||
// Parse the optional exponent.
|
// Parse the optional exponent.
|
||||||
// ((p|P)(\+|-)?[0-9]+)?
|
// ((p|P)(\+|-)?[0-9]+)?
|
||||||
uint32_t input_exponent = 0; // Defaults to 0 if not present
|
uint64_t input_exponent = 0; // Defaults to 0 if not present
|
||||||
int32_t exponent_sign = 1;
|
int64_t exponent_sign = 1;
|
||||||
// If the 'p' part is present, the rest of the exponent must exist.
|
// If the 'p' part is present, the rest of the exponent must exist.
|
||||||
bool has_f_suffix = false;
|
bool has_f_suffix = false;
|
||||||
if (has_exponent) {
|
if (has_exponent) {
|
||||||
|
@ -568,7 +569,7 @@ Token Lexer::try_hex_float() {
|
||||||
|
|
||||||
// Parse exponent from input
|
// Parse exponent from input
|
||||||
// [0-9]+
|
// [0-9]+
|
||||||
// Allow overflow (in uint32_t) when the floating point value magnitude is
|
// Allow overflow (in uint64_t) when the floating point value magnitude is
|
||||||
// zero.
|
// zero.
|
||||||
bool has_exponent_digits = false;
|
bool has_exponent_digits = false;
|
||||||
while (end < length() && isdigit(at(end))) {
|
while (end < length() && isdigit(at(end))) {
|
||||||
|
@ -605,14 +606,14 @@ Token Lexer::try_hex_float() {
|
||||||
} else {
|
} else {
|
||||||
// Ensure input exponent is not too large; i.e. that it won't overflow when
|
// Ensure input exponent is not too large; i.e. that it won't overflow when
|
||||||
// adding the exponent bias.
|
// adding the exponent bias.
|
||||||
const uint32_t kIntMax = static_cast<uint32_t>(std::numeric_limits<int32_t>::max());
|
const uint64_t kIntMax = static_cast<uint64_t>(std::numeric_limits<int64_t>::max());
|
||||||
const uint32_t kMaxInputExponent = kIntMax - kExponentBias;
|
const uint64_t kMaxInputExponent = kIntMax - kExponentBias;
|
||||||
if (input_exponent > kMaxInputExponent) {
|
if (input_exponent > kMaxInputExponent) {
|
||||||
return {Token::Type::kError, source, "exponent is too large for hex float"};
|
return {Token::Type::kError, source, "exponent is too large for hex float"};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compute exponent so far
|
// Compute exponent so far
|
||||||
exponent += static_cast<uint32_t>(static_cast<int32_t>(input_exponent) * exponent_sign);
|
exponent += static_cast<uint64_t>(static_cast<int64_t>(input_exponent) * exponent_sign);
|
||||||
|
|
||||||
// Bias exponent if non-zero
|
// Bias exponent if non-zero
|
||||||
// After this, if exponent is <= 0, our value is a denormal
|
// After this, if exponent is <= 0, our value is a denormal
|
||||||
|
@ -631,7 +632,7 @@ Token Lexer::try_hex_float() {
|
||||||
|
|
||||||
// We can now safely work with exponent as a signed quantity, as there's no
|
// We can now safely work with exponent as a signed quantity, as there's no
|
||||||
// chance to overflow
|
// chance to overflow
|
||||||
int32_t signed_exponent = static_cast<int32_t>(exponent);
|
int64_t signed_exponent = static_cast<int64_t>(exponent);
|
||||||
|
|
||||||
// Shift mantissa to occupy the low 23 bits
|
// Shift mantissa to occupy the low 23 bits
|
||||||
mantissa >>= kMantissaShiftRight;
|
mantissa >>= kMantissaShiftRight;
|
||||||
|
@ -642,7 +643,7 @@ Token Lexer::try_hex_float() {
|
||||||
// then shift the mantissa to make exponent zero.
|
// then shift the mantissa to make exponent zero.
|
||||||
if (signed_exponent <= 0) {
|
if (signed_exponent <= 0) {
|
||||||
mantissa >>= 1;
|
mantissa >>= 1;
|
||||||
mantissa |= (1 << kMantissaMsb);
|
mantissa |= (kOne << kMantissaMsb);
|
||||||
}
|
}
|
||||||
|
|
||||||
while (signed_exponent < 0) {
|
while (signed_exponent < 0) {
|
||||||
|
@ -656,24 +657,30 @@ Token Lexer::try_hex_float() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (signed_exponent > kExponentMax) {
|
if (signed_exponent >= kExponentMax || (signed_exponent == kExponentMax && mantissa != 0)) {
|
||||||
// Overflow: set to infinity
|
std::string type = has_f_suffix ? "f32" : "abstract-float";
|
||||||
signed_exponent = kExponentMax;
|
return {Token::Type::kError, source, "value cannot be represented as '" + type + "'"};
|
||||||
mantissa = 0;
|
|
||||||
} else if (signed_exponent == kExponentMax && mantissa != 0) {
|
|
||||||
// NaN: set to infinity
|
|
||||||
mantissa = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Combine sign, mantissa, and exponent
|
// Combine sign, mantissa, and exponent
|
||||||
uint32_t result_u32 = sign_bit << kSignBit;
|
uint64_t result_u64 = sign_bit << kSignBit;
|
||||||
result_u32 |= mantissa;
|
result_u64 |= mantissa;
|
||||||
result_u32 |= (static_cast<uint32_t>(signed_exponent) & kExponentMask) << kExponentLeftShift;
|
result_u64 |= (static_cast<uint64_t>(signed_exponent) & kExponentMask) << kExponentLeftShift;
|
||||||
|
|
||||||
// Reinterpret as float and return
|
// Reinterpret as float and return
|
||||||
float result_f32;
|
double result_f64;
|
||||||
std::memcpy(&result_f32, &result_u32, sizeof(result_f32));
|
std::memcpy(&result_f64, &result_u64, 8);
|
||||||
double result_f64 = static_cast<double>(result_f32);
|
|
||||||
|
if (has_f_suffix) {
|
||||||
|
// Quantize to f32
|
||||||
|
// TODO(crbug.com/tint/1564): If the hex-float value is not exactly representable then we
|
||||||
|
// should be erroring here.
|
||||||
|
result_f64 = static_cast<double>(static_cast<float>(result_f64));
|
||||||
|
if (std::isinf(result_f64)) {
|
||||||
|
return {Token::Type::kError, source, "value cannot be represented as 'f32'"};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return {has_f_suffix ? Token::Type::kFloatLiteral_F : Token::Type::kFloatLiteral, source,
|
return {has_f_suffix ? Token::Type::kFloatLiteral_F : Token::Type::kFloatLiteral, source,
|
||||||
result_f64};
|
result_f64};
|
||||||
}
|
}
|
||||||
|
|
|
@ -224,8 +224,12 @@ TEST_P(ParserImplFloatLiteralTest, Parse) {
|
||||||
EXPECT_FALSE(c.errored);
|
EXPECT_FALSE(c.errored);
|
||||||
EXPECT_FALSE(p->has_error()) << p->error();
|
EXPECT_FALSE(p->has_error()) << p->error();
|
||||||
ASSERT_NE(c.value, nullptr);
|
ASSERT_NE(c.value, nullptr);
|
||||||
ASSERT_TRUE(c->Is<ast::FloatLiteralExpression>());
|
auto* literal = c->As<ast::FloatLiteralExpression>();
|
||||||
EXPECT_DOUBLE_EQ(c->As<ast::FloatLiteralExpression>()->value, params.expected);
|
ASSERT_NE(literal, nullptr);
|
||||||
|
EXPECT_DOUBLE_EQ(literal->value, params.expected)
|
||||||
|
<< "\n"
|
||||||
|
<< "got: " << std::hexfloat << literal->value << "\n"
|
||||||
|
<< "expected: " << std::hexfloat << params.expected;
|
||||||
if (params.input.back() == 'f') {
|
if (params.input.back() == 'f') {
|
||||||
EXPECT_EQ(c->As<ast::FloatLiteralExpression>()->suffix,
|
EXPECT_EQ(c->As<ast::FloatLiteralExpression>()->suffix,
|
||||||
ast::FloatLiteralExpression::Suffix::kF);
|
ast::FloatLiteralExpression::Suffix::kF);
|
||||||
|
@ -290,27 +294,60 @@ FloatLiteralTestCaseList HexFloatCases() {
|
||||||
{"-0x1.02p-3", 1.0 / -1024.0 - 1.0 / 8.0},
|
{"-0x1.02p-3", 1.0 / -1024.0 - 1.0 / 8.0},
|
||||||
|
|
||||||
// Near lowest non-denorm
|
// Near lowest non-denorm
|
||||||
{"0x1p-124", std::ldexp(1.0 * 8.0, -127)},
|
{"0x1p-1020", std::ldexp(1.0 * 8.0, -1023)},
|
||||||
{"0x1p-125", std::ldexp(1.0 * 4.0, -127)},
|
{"0x1p-1021", std::ldexp(1.0 * 4.0, -1023)},
|
||||||
{"-0x1p-124", -std::ldexp(1.0 * 8.0, -127)},
|
{"-0x1p-1020", -std::ldexp(1.0 * 8.0, -1023)},
|
||||||
{"-0x1p-125", -std::ldexp(1.0 * 4.0, -127)},
|
{"-0x1p-1021", -std::ldexp(1.0 * 4.0, -1023)},
|
||||||
|
|
||||||
|
{"0x1p-124f", std::ldexp(1.0 * 8.0, -127)},
|
||||||
|
{"0x1p-125f", std::ldexp(1.0 * 4.0, -127)},
|
||||||
|
{"-0x1p-124f", -std::ldexp(1.0 * 8.0, -127)},
|
||||||
|
{"-0x1p-125f", -std::ldexp(1.0 * 4.0, -127)},
|
||||||
|
|
||||||
// Lowest non-denorm
|
// Lowest non-denorm
|
||||||
{"0x1p-126", std::ldexp(1.0 * 2.0, -127)},
|
{"0x1p-1022", std::ldexp(1.0 * 2.0, -1023)},
|
||||||
{"-0x1p-126", -std::ldexp(1.0 * 2.0, -127)},
|
{"-0x1p-1022", -std::ldexp(1.0 * 2.0, -1023)},
|
||||||
|
|
||||||
|
{"0x1p-126f", std::ldexp(1.0 * 2.0, -127)},
|
||||||
|
{"-0x1p-126f", -std::ldexp(1.0 * 2.0, -127)},
|
||||||
|
|
||||||
// Denormalized values
|
// Denormalized values
|
||||||
{"0x1p-127", std::ldexp(1.0, -127)},
|
{"0x1p-1023", std::ldexp(1.0, -1023)},
|
||||||
{"0x1p-128", std::ldexp(1.0 / 2.0, -127)},
|
{"0x1p-1024", std::ldexp(1.0 / 2.0, -1023)},
|
||||||
{"0x1p-129", std::ldexp(1.0 / 4.0, -127)},
|
{"0x1p-1025", std::ldexp(1.0 / 4.0, -1023)},
|
||||||
{"0x1p-130", std::ldexp(1.0 / 8.0, -127)},
|
{"0x1p-1026", std::ldexp(1.0 / 8.0, -1023)},
|
||||||
{"-0x1p-127", -std::ldexp(1.0, -127)},
|
{"-0x1p-1023", -std::ldexp(1.0, -1023)},
|
||||||
{"-0x1p-128", -std::ldexp(1.0 / 2.0, -127)},
|
{"-0x1p-1024", -std::ldexp(1.0 / 2.0, -1023)},
|
||||||
{"-0x1p-129", -std::ldexp(1.0 / 4.0, -127)},
|
{"-0x1p-1025", -std::ldexp(1.0 / 4.0, -1023)},
|
||||||
{"-0x1p-130", -std::ldexp(1.0 / 8.0, -127)},
|
{"-0x1p-1026", -std::ldexp(1.0 / 8.0, -1023)},
|
||||||
|
{"0x1.8p-1023", std::ldexp(1.0, -1023) + (std::ldexp(1.0, -1023) / 2.0)},
|
||||||
|
{"0x1.8p-1024", std::ldexp(1.0, -1023) / 2.0 + (std::ldexp(1.0, -1023) / 4.0)},
|
||||||
|
|
||||||
{"0x1.8p-127", std::ldexp(1.0, -127) + (std::ldexp(1.0, -127) / 2.0)},
|
{"0x1p-127f", std::ldexp(1.0, -127)},
|
||||||
{"0x1.8p-128", std::ldexp(1.0, -127) / 2.0 + (std::ldexp(1.0, -127) / 4.0)},
|
{"0x1p-128f", std::ldexp(1.0 / 2.0, -127)},
|
||||||
|
{"0x1p-129f", std::ldexp(1.0 / 4.0, -127)},
|
||||||
|
{"0x1p-130f", std::ldexp(1.0 / 8.0, -127)},
|
||||||
|
{"-0x1p-127f", -std::ldexp(1.0, -127)},
|
||||||
|
{"-0x1p-128f", -std::ldexp(1.0 / 2.0, -127)},
|
||||||
|
{"-0x1p-129f", -std::ldexp(1.0 / 4.0, -127)},
|
||||||
|
{"-0x1p-130f", -std::ldexp(1.0 / 8.0, -127)},
|
||||||
|
{"0x1.8p-127f", std::ldexp(1.0, -127) + (std::ldexp(1.0, -127) / 2.0)},
|
||||||
|
{"0x1.8p-128f", std::ldexp(1.0, -127) / 2.0 + (std::ldexp(1.0, -127) / 4.0)},
|
||||||
|
|
||||||
|
// F64 extremities
|
||||||
|
{"0x1p-1074", MakeDouble(0, 0, 1)}, // +SmallestDenormal
|
||||||
|
{"0x1p-1073", MakeDouble(0, 0, 2)}, // +BiggerDenormal
|
||||||
|
{"0x1.ffffffffffffp-1027", MakeDouble(0, 0, 0xffffffffffff)}, // +LargestDenormal
|
||||||
|
{"-0x1p-1074", MakeDouble(1, 0, 1)}, // -SmallestDenormal
|
||||||
|
{"-0x1p-1073", MakeDouble(1, 0, 2)}, // -BiggerDenormal
|
||||||
|
{"-0x1.ffffffffffffp-1027", MakeDouble(1, 0, 0xffffffffffff)}, // -LargestDenormal
|
||||||
|
|
||||||
|
{"0x0.cafebeeff000dp-1022", MakeDouble(0, 0, 0xcafebeeff000d)}, // +Subnormal
|
||||||
|
{"-0x0.cafebeeff000dp-1022", MakeDouble(1, 0, 0xcafebeeff000d)}, // -Subnormal
|
||||||
|
{"0x1.2bfaf8p-1052", MakeDouble(0, 0, 0x4afebe)}, // +Subnormal
|
||||||
|
{"-0x1.2bfaf8p-1052", MakeDouble(1, 0, 0x4afebe)}, // +Subnormal
|
||||||
|
{"0x1.55554p-1055", MakeDouble(0, 0, 0xaaaaa)}, // +Subnormal
|
||||||
|
{"-0x1.55554p-1055", MakeDouble(1, 0, 0xaaaaa)}, // -Subnormal
|
||||||
|
|
||||||
// F32 extremities
|
// F32 extremities
|
||||||
{"0x1p-149", static_cast<double>(MakeFloat(0, 0, 1))}, // +SmallestDenormal
|
{"0x1p-149", static_cast<double>(MakeFloat(0, 0, 1))}, // +SmallestDenormal
|
||||||
|
@ -320,46 +357,32 @@ FloatLiteralTestCaseList HexFloatCases() {
|
||||||
{"-0x1p-148", static_cast<double>(MakeFloat(1, 0, 2))}, // -BiggerDenormal
|
{"-0x1p-148", static_cast<double>(MakeFloat(1, 0, 2))}, // -BiggerDenormal
|
||||||
{"-0x1.fffffcp-127", static_cast<double>(MakeFloat(1, 0, 0x7fffff))}, // -LargestDenormal
|
{"-0x1.fffffcp-127", static_cast<double>(MakeFloat(1, 0, 0x7fffff))}, // -LargestDenormal
|
||||||
|
|
||||||
{"0x1.2bfaf8p-127", static_cast<double>(MakeFloat(0, 0, 0xcafebe))}, // +Subnormal
|
{"0x0.cafebp-129", static_cast<double>(MakeFloat(0, 0, 0xcafeb))}, // +Subnormal
|
||||||
{"-0x1.2bfaf8p-127", static_cast<double>(MakeFloat(1, 0, 0xcafebe))}, // -Subnormal
|
{"-0x0.cafebp-129", static_cast<double>(MakeFloat(1, 0, 0xcafeb))}, // -Subnormal
|
||||||
|
{"0x1.2bfaf8p-127", static_cast<double>(MakeFloat(0, 0, 0x4afebe))}, // +Subnormal
|
||||||
|
{"-0x1.2bfaf8p-127", static_cast<double>(MakeFloat(1, 0, 0x4afebe))}, // -Subnormal
|
||||||
{"0x1.55554p-130", static_cast<double>(MakeFloat(0, 0, 0xaaaaa))}, // +Subnormal
|
{"0x1.55554p-130", static_cast<double>(MakeFloat(0, 0, 0xaaaaa))}, // +Subnormal
|
||||||
{"-0x1.55554p-130", static_cast<double>(MakeFloat(1, 0, 0xaaaaa))}, // -Subnormal
|
{"-0x1.55554p-130", static_cast<double>(MakeFloat(1, 0, 0xaaaaa))}, // -Subnormal
|
||||||
|
|
||||||
// Nan -> Infinity
|
|
||||||
{"0x1.8p+128", PosInf},
|
|
||||||
{"0x1.0002p+128", PosInf},
|
|
||||||
{"0x1.0018p+128", PosInf},
|
|
||||||
{"0x1.01ep+128", PosInf},
|
|
||||||
{"0x1.fffffep+128", PosInf},
|
|
||||||
{"-0x1.8p+128", NegInf},
|
|
||||||
{"-0x1.0002p+128", NegInf},
|
|
||||||
{"-0x1.0018p+128", NegInf},
|
|
||||||
{"-0x1.01ep+128", NegInf},
|
|
||||||
{"-0x1.fffffep+128", NegInf},
|
|
||||||
|
|
||||||
// Infinity
|
|
||||||
{"0x1p+128", PosInf},
|
|
||||||
{"-0x1p+128", NegInf},
|
|
||||||
{"0x32p+127", PosInf},
|
|
||||||
{"0x32p+500", PosInf},
|
|
||||||
{"-0x32p+127", NegInf},
|
|
||||||
{"-0x32p+500", NegInf},
|
|
||||||
|
|
||||||
// Overflow -> Infinity
|
|
||||||
{"0x1p+129", PosInf},
|
|
||||||
{"0x1.1p+128", PosInf},
|
|
||||||
{"-0x1p+129", NegInf},
|
|
||||||
{"-0x1.1p+128", NegInf},
|
|
||||||
{"0x1.0p2147483520", PosInf}, // INT_MAX - 127 (largest valid exponent)
|
|
||||||
|
|
||||||
// Underflow -> Zero
|
// Underflow -> Zero
|
||||||
{"0x1p-500", 0.0}, // Exponent underflows
|
{"0x1p-1074", 0.0}, // Exponent underflows
|
||||||
{"-0x1p-500", -0.0},
|
{"-0x1p-1074", 0.0},
|
||||||
{"0x0.00000000001p-126", 0.0}, // Fraction causes underflow
|
{"0x1p-5000", 0.0},
|
||||||
{"-0x0.0000000001p-127", -0.0},
|
{"-0x1p-5000", 0.0},
|
||||||
{"0x0.01p-142", 0.0},
|
{"0x0.00000000000000000000001p-1022", 0.0}, // Fraction causes underflow
|
||||||
{"-0x0.01p-142", -0.0}, // Fraction causes additional underflow
|
{"-0x0.0000000000000000000001p-1023", -0.0},
|
||||||
{"0x1.0p-2147483520", 0}, // -(INT_MAX - 127) (smallest valid exponent)
|
{"0x0.01p-1073", -0.0},
|
||||||
|
{"-0x0.01p-1073", -0.0}, // Fraction causes additional underflow
|
||||||
|
|
||||||
|
{"0x1p-150f", 0.0}, // Exponent underflows
|
||||||
|
{"-0x1p-150f", 0.0},
|
||||||
|
{"0x1p-500f", 0.0},
|
||||||
|
{"-0x1p-500f", -0.0},
|
||||||
|
{"0x0.00000000001p-126f", 0.0}, // Fraction causes underflow
|
||||||
|
{"-0x0.0000000001p-127f", -0.0},
|
||||||
|
{"0x0.01p-142f", 0.0},
|
||||||
|
{"-0x0.01p-142f", -0.0}, // Fraction causes additional underflow
|
||||||
|
{"0x1.0p-9223372036854774784", 0}, // -(INT64_MAX - 1023) (smallest valid exponent)
|
||||||
|
|
||||||
// Zero with non-zero exponent -> Zero
|
// Zero with non-zero exponent -> Zero
|
||||||
{"0x0p+0", 0.0},
|
{"0x0p+0", 0.0},
|
||||||
|
@ -369,22 +392,22 @@ FloatLiteralTestCaseList HexFloatCases() {
|
||||||
{"0x0p-9999999999", 0.0},
|
{"0x0p-9999999999", 0.0},
|
||||||
// Same, but with very large positive exponents that would cause overflow
|
// Same, but with very large positive exponents that would cause overflow
|
||||||
// if the mantissa were non-zero.
|
// if the mantissa were non-zero.
|
||||||
{"0x0p+4000000000", 0.0}, // 4 billion:
|
{"0x0p+10000000000000000000", 0.0}, // 10 quintillion (10,000,000,000,000,000,000)
|
||||||
{"0x0p+40000000000", 0.0}, // 40 billion
|
{"0x0p+100000000000000000000", 0.0}, // 100 quintillion (100,000,000,000,000,000,000)
|
||||||
{"-0x0p+40000000000", 0.0}, // As above 2, but negative mantissa
|
{"-0x0p+100000000000000000000", 0.0}, // As above 2, but negative mantissa
|
||||||
{"-0x0p+400000000000", 0.0},
|
{"-0x0p+1000000000000000000000", 0.0},
|
||||||
{"0x0.00p+4000000000", 0.0}, // As above 4, but with fractional part
|
{"0x0.00p+10000000000000000000", 0.0}, // As above 4, but with fractional part
|
||||||
{"0x0.00p+40000000000", 0.0},
|
{"0x0.00p+100000000000000000000", 0.0},
|
||||||
{"-0x0.00p+40000000000", 0.0},
|
{"-0x0.00p+100000000000000000000", 0.0},
|
||||||
{"-0x0.00p+400000000000", 0.0},
|
{"-0x0.00p+1000000000000000000000", 0.0},
|
||||||
{"0x0p-4000000000", 0.0}, // As above 8, but with negative exponents
|
{"0x0p-10000000000000000000", 0.0}, // As above 8, but with negative exponents
|
||||||
{"0x0p-40000000000", 0.0},
|
{"0x0p-100000000000000000000", 0.0},
|
||||||
{"-0x0p-40000000000", 0.0},
|
{"-0x0p-100000000000000000000", 0.0},
|
||||||
{"-0x0p-400000000000", 0.0},
|
{"-0x0p-1000000000000000000000", 0.0},
|
||||||
{"0x0.00p-4000000000", 0.0},
|
{"0x0.00p-10000000000000000000", 0.0},
|
||||||
{"0x0.00p-40000000000", 0.0},
|
{"0x0.00p-100000000000000000000", 0.0},
|
||||||
{"-0x0.00p-40000000000", 0.0},
|
{"-0x0.00p-100000000000000000000", 0.0},
|
||||||
{"-0x0.00p-400000000000", 0.0},
|
{"-0x0.00p-1000000000000000000000", 0.0},
|
||||||
|
|
||||||
// Test parsing
|
// Test parsing
|
||||||
{"0x0p0", 0.0},
|
{"0x0p0", 0.0},
|
||||||
|
@ -462,65 +485,144 @@ INSTANTIATE_TEST_SUITE_P(ParserImplFloatLiteralTest_HexFloat_UpperCase0X,
|
||||||
ParserImplFloatLiteralTest,
|
ParserImplFloatLiteralTest,
|
||||||
testing::ValuesIn(UpperCase0X(HexFloatCases())));
|
testing::ValuesIn(UpperCase0X(HexFloatCases())));
|
||||||
|
|
||||||
struct InvalidLiteralTestCase {
|
// <error, source>
|
||||||
const char* input;
|
using InvalidLiteralTestCase = std::tuple<const char*, const char*>;
|
||||||
const char* error_msg;
|
|
||||||
};
|
|
||||||
class ParserImplInvalidLiteralTest : public ParserImplTestWithParam<InvalidLiteralTestCase> {};
|
class ParserImplInvalidLiteralTest : public ParserImplTestWithParam<InvalidLiteralTestCase> {};
|
||||||
TEST_P(ParserImplInvalidLiteralTest, Parse) {
|
TEST_P(ParserImplInvalidLiteralTest, Parse) {
|
||||||
auto params = GetParam();
|
auto* error = std::get<0>(GetParam());
|
||||||
SCOPED_TRACE(params.input);
|
auto* source = std::get<1>(GetParam());
|
||||||
auto p = parser(params.input);
|
auto p = parser(source);
|
||||||
auto c = p->const_literal();
|
auto c = p->const_literal();
|
||||||
EXPECT_FALSE(c.matched);
|
EXPECT_FALSE(c.matched);
|
||||||
EXPECT_TRUE(c.errored);
|
EXPECT_TRUE(c.errored);
|
||||||
EXPECT_EQ(p->error(), params.error_msg);
|
EXPECT_EQ(p->error(), std::string(error));
|
||||||
ASSERT_EQ(c.value, nullptr);
|
ASSERT_EQ(c.value, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
InvalidLiteralTestCase invalid_hexfloat_mantissa_too_large_cases[] = {
|
INSTANTIATE_TEST_SUITE_P(
|
||||||
{"0x1.ffffffff8p0", "1:1: mantissa is too large for hex float"},
|
HexFloatMantissaTooLarge,
|
||||||
{"0x1f.fffffff8p0", "1:1: mantissa is too large for hex float"},
|
|
||||||
{"0x1ff.ffffff8p0", "1:1: mantissa is too large for hex float"},
|
|
||||||
{"0x1fff.fffff8p0", "1:1: mantissa is too large for hex float"},
|
|
||||||
{"0x1ffff.ffff8p0", "1:1: mantissa is too large for hex float"},
|
|
||||||
{"0x1fffff.fff8p0", "1:1: mantissa is too large for hex float"},
|
|
||||||
{"0x1ffffff.ff8p0", "1:1: mantissa is too large for hex float"},
|
|
||||||
{"0x1fffffff.f8p0", "1:1: mantissa is too large for hex float"},
|
|
||||||
{"0x1ffffffff.8p0", "1:1: mantissa is too large for hex float"},
|
|
||||||
{"0x1ffffffff8.p0", "1:1: mantissa is too large for hex float"},
|
|
||||||
};
|
|
||||||
INSTANTIATE_TEST_SUITE_P(ParserImplInvalidLiteralTest_HexFloatMantissaTooLarge,
|
|
||||||
ParserImplInvalidLiteralTest,
|
ParserImplInvalidLiteralTest,
|
||||||
testing::ValuesIn(invalid_hexfloat_mantissa_too_large_cases));
|
testing::Combine(testing::Values("1:1: mantissa is too large for hex float"),
|
||||||
|
testing::ValuesIn(std::vector<const char*>{
|
||||||
|
"0x1.ffffffffffffffff8p0",
|
||||||
|
"0x1f.fffffffffffffff8p0",
|
||||||
|
"0x1ff.ffffffffffffff8p0",
|
||||||
|
"0x1fff.fffffffffffff8p0",
|
||||||
|
"0x1ffff.ffffffffffff8p0",
|
||||||
|
"0x1fffff.fffffffffff8p0",
|
||||||
|
"0x1ffffff.ffffffffff8p0",
|
||||||
|
"0x1fffffff.fffffffff8p0",
|
||||||
|
"0x1ffffffff.ffffffff8p0",
|
||||||
|
"0x1fffffffff.fffffff8p0",
|
||||||
|
"0x1ffffffffff.ffffff8p0",
|
||||||
|
"0x1fffffffffff.fffff8p0",
|
||||||
|
"0x1ffffffffffff.ffff8p0",
|
||||||
|
"0x1fffffffffffff.fff8p0",
|
||||||
|
"0x1ffffffffffffff.ff8p0",
|
||||||
|
"0x1ffffffffffffffff.8p0",
|
||||||
|
"0x1ffffffffffffffff8.p0",
|
||||||
|
})));
|
||||||
|
|
||||||
InvalidLiteralTestCase invalid_hexfloat_exponent_too_large_cases[] = {
|
INSTANTIATE_TEST_SUITE_P(
|
||||||
{"0x1p+2147483521", "1:1: exponent is too large for hex float"},
|
HexFloatExponentTooLarge,
|
||||||
{"0x1p-2147483521", "1:1: exponent is too large for hex float"},
|
|
||||||
{"0x1p+4294967296", "1:1: exponent is too large for hex float"},
|
|
||||||
{"0x1p-4294967296", "1:1: exponent is too large for hex float"},
|
|
||||||
};
|
|
||||||
INSTANTIATE_TEST_SUITE_P(ParserImplInvalidLiteralTest_HexFloatExponentTooLarge,
|
|
||||||
ParserImplInvalidLiteralTest,
|
ParserImplInvalidLiteralTest,
|
||||||
testing::ValuesIn(invalid_hexfloat_exponent_too_large_cases));
|
testing::Combine(testing::Values("1:1: exponent is too large for hex float"),
|
||||||
|
testing::ValuesIn(std::vector<const char*>{
|
||||||
|
"0x1p+9223372036854774785",
|
||||||
|
"0x1p-9223372036854774785",
|
||||||
|
"0x1p+18446744073709551616",
|
||||||
|
"0x1p-18446744073709551616",
|
||||||
|
})));
|
||||||
|
|
||||||
InvalidLiteralTestCase invalid_hexfloat_exponent_missing_cases[] = {
|
INSTANTIATE_TEST_SUITE_P(
|
||||||
|
HexFloatMissingExponent,
|
||||||
|
ParserImplInvalidLiteralTest,
|
||||||
|
testing::Combine(testing::Values("1:1: expected an exponent value for hex float"),
|
||||||
|
testing::ValuesIn(std::vector<const char*>{
|
||||||
// Lower case p
|
// Lower case p
|
||||||
{"0x0p", "1:1: expected an exponent value for hex float"},
|
"0x0p",
|
||||||
{"0x0p+", "1:1: expected an exponent value for hex float"},
|
"0x0p+",
|
||||||
{"0x0p-", "1:1: expected an exponent value for hex float"},
|
"0x0p-",
|
||||||
{"0x1.0p", "1:1: expected an exponent value for hex float"},
|
"0x1.0p",
|
||||||
{"0x0.1p", "1:1: expected an exponent value for hex float"},
|
"0x0.1p",
|
||||||
// Upper case p
|
// Upper case p
|
||||||
{"0x0P", "1:1: expected an exponent value for hex float"},
|
"0x0P",
|
||||||
{"0x0P+", "1:1: expected an exponent value for hex float"},
|
"0x0P+",
|
||||||
{"0x0P-", "1:1: expected an exponent value for hex float"},
|
"0x0P-",
|
||||||
{"0x1.0P", "1:1: expected an exponent value for hex float"},
|
"0x1.0P",
|
||||||
{"0x0.1P", "1:1: expected an exponent value for hex float"},
|
"0x0.1P",
|
||||||
};
|
})));
|
||||||
INSTANTIATE_TEST_SUITE_P(ParserImplInvalidLiteralTest_HexFloatExponentMissing,
|
|
||||||
|
INSTANTIATE_TEST_SUITE_P(
|
||||||
|
NaNAFloat,
|
||||||
ParserImplInvalidLiteralTest,
|
ParserImplInvalidLiteralTest,
|
||||||
testing::ValuesIn(invalid_hexfloat_exponent_missing_cases));
|
testing::Combine(testing::Values("1:1: value cannot be represented as 'abstract-float'"),
|
||||||
|
testing::ValuesIn(std::vector<const char*>{
|
||||||
|
"0x1.8p+1024",
|
||||||
|
"0x1.0002p+1024",
|
||||||
|
"0x1.0018p+1024",
|
||||||
|
"0x1.01ep+1024",
|
||||||
|
"0x1.fffffep+1024",
|
||||||
|
"-0x1.8p+1024",
|
||||||
|
"-0x1.0002p+1024",
|
||||||
|
"-0x1.0018p+1024",
|
||||||
|
"-0x1.01ep+1024",
|
||||||
|
"-0x1.fffffep+1024",
|
||||||
|
})));
|
||||||
|
|
||||||
|
INSTANTIATE_TEST_SUITE_P(
|
||||||
|
NaNF32,
|
||||||
|
ParserImplInvalidLiteralTest,
|
||||||
|
testing::Combine(testing::Values("1:1: value cannot be represented as 'f32'"),
|
||||||
|
testing::ValuesIn(std::vector<const char*>{
|
||||||
|
"0x1.8p+128f",
|
||||||
|
"0x1.0002p+128f",
|
||||||
|
"0x1.0018p+128f",
|
||||||
|
"0x1.01ep+128f",
|
||||||
|
"0x1.fffffep+128f",
|
||||||
|
"-0x1.8p+128f",
|
||||||
|
"-0x1.0002p+128f",
|
||||||
|
"-0x1.0018p+128f",
|
||||||
|
"-0x1.01ep+128f",
|
||||||
|
"-0x1.fffffep+128f",
|
||||||
|
})));
|
||||||
|
|
||||||
|
INSTANTIATE_TEST_SUITE_P(
|
||||||
|
OverflowAFloat,
|
||||||
|
ParserImplInvalidLiteralTest,
|
||||||
|
testing::Combine(testing::Values("1:1: value cannot be represented as 'abstract-float'"),
|
||||||
|
testing::ValuesIn(std::vector<const char*>{
|
||||||
|
"0x1p+1024",
|
||||||
|
"-0x1p+1024",
|
||||||
|
"0x1.1p+1024",
|
||||||
|
"-0x1.1p+1024",
|
||||||
|
"0x1p+1025",
|
||||||
|
"-0x1p+1025",
|
||||||
|
"0x32p+1023",
|
||||||
|
"-0x32p+1023",
|
||||||
|
"0x32p+5000",
|
||||||
|
"-0x32p+5000",
|
||||||
|
"0x1.0p9223372036854774784",
|
||||||
|
"-0x1.0p9223372036854774784",
|
||||||
|
})));
|
||||||
|
|
||||||
|
INSTANTIATE_TEST_SUITE_P(
|
||||||
|
OverflowF32,
|
||||||
|
ParserImplInvalidLiteralTest,
|
||||||
|
testing::Combine(testing::Values("1:1: value cannot be represented as 'f32'"),
|
||||||
|
testing::ValuesIn(std::vector<const char*>{
|
||||||
|
"0x1p+128f",
|
||||||
|
"-0x1p+128f",
|
||||||
|
"0x1.1p+128f",
|
||||||
|
"-0x1.1p+128f",
|
||||||
|
"0x1p+129f",
|
||||||
|
"-0x1p+129f",
|
||||||
|
"0x32p+127f",
|
||||||
|
"-0x32p+127f",
|
||||||
|
"0x32p+500f",
|
||||||
|
"-0x32p+500f",
|
||||||
|
})));
|
||||||
|
|
||||||
TEST_F(ParserImplTest, ConstLiteral_FloatHighest) {
|
TEST_F(ParserImplTest, ConstLiteral_FloatHighest) {
|
||||||
const auto highest = std::numeric_limits<float>::max();
|
const auto highest = std::numeric_limits<float>::max();
|
||||||
|
|
|
@ -82,7 +82,7 @@ foobar
|
||||||
TEST_F(ParserImplTest, HandlesBadToken_InMiddle) {
|
TEST_F(ParserImplTest, HandlesBadToken_InMiddle) {
|
||||||
auto p = parser(R"(
|
auto p = parser(R"(
|
||||||
fn main() {
|
fn main() {
|
||||||
let f = 0x1p500000000000; // Exponent too big for hex float
|
let f = 0x1p10000000000000000000; // Exponent too big for hex float
|
||||||
return;
|
return;
|
||||||
})");
|
})");
|
||||||
|
|
||||||
|
@ -96,7 +96,7 @@ TEST_F(ParserImplTest, HandlesBadToken_AtModuleScope) {
|
||||||
fn main() {
|
fn main() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
0x1p5000000000000
|
0x1p10000000000000000000
|
||||||
)");
|
)");
|
||||||
|
|
||||||
ASSERT_FALSE(p->Parse());
|
ASSERT_FALSE(p->Parse());
|
||||||
|
|
Loading…
Reference in New Issue