/* * Copyright 2016 WebAssembly Community Group participants * * 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 "wabt/literal.h" #include #include #include #include #include #include #include #include namespace wabt { namespace { template struct FloatTraitsBase {}; // The "PlusOne" values are used because normal IEEE floats have an implicit // leading one, so they have an additional bit of precision. template <> struct FloatTraitsBase { using Uint = uint32_t; static constexpr int kBits = sizeof(Uint) * 8; static constexpr int kSigBits = 23; static constexpr float kHugeVal = HUGE_VALF; static constexpr int kMaxHexBufferSize = WABT_MAX_FLOAT_HEX; static float Strto(const char* s, char** endptr) { return strtof(s, endptr); } }; template <> struct FloatTraitsBase { using Uint = uint64_t; static constexpr int kBits = sizeof(Uint) * 8; static constexpr int kSigBits = 52; static constexpr float kHugeVal = HUGE_VAL; static constexpr int kMaxHexBufferSize = WABT_MAX_DOUBLE_HEX; static double Strto(const char* s, char** endptr) { return strtod(s, endptr); } }; template struct FloatTraits : FloatTraitsBase { using Uint = typename FloatTraitsBase::Uint; using FloatTraitsBase::kBits; using FloatTraitsBase::kSigBits; static constexpr int kExpBits = kBits - kSigBits - 1; static constexpr int kSignShift = kBits - 1; static constexpr Uint kSigMask = (Uint(1) << kSigBits) - 1; static constexpr int kSigPlusOneBits = kSigBits + 1; static constexpr Uint kSigPlusOneMask = (Uint(1) << kSigPlusOneBits) - 1; static constexpr int kExpMask = (1 << kExpBits) - 1; static constexpr int kMaxExp = 1 << (kExpBits - 1); static constexpr int kMinExp = -kMaxExp + 1; static constexpr int kExpBias = -kMinExp; static constexpr Uint kQuietNanTag = Uint(1) << (kSigBits - 1); }; template class FloatParser { public: using Traits = FloatTraits; using Uint = typename Traits::Uint; using Float = T; static Result Parse(LiteralType, const char* s, const char* end, Uint* out_bits); private: static bool StringStartsWith(const char* start, const char* end, const char* prefix); static Uint Make(bool sign, int exp, Uint sig); static Uint ShiftAndRoundToNearest(Uint significand, int shift, bool seen_trailing_non_zero); static Result ParseFloat(const char* s, const char* end, Uint* out_bits); static Result ParseNan(const char* s, const char* end, Uint* out_bits); static Result ParseHex(const char* s, const char* end, Uint* out_bits); static void ParseInfinity(const char* s, const char* end, Uint* out_bits); }; template class FloatWriter { public: using Traits = FloatTraits; using Uint = typename Traits::Uint; static void WriteHex(char* out, size_t size, Uint bits); }; // Return 1 if the non-NULL-terminated string starting with |start| and ending // with |end| starts with the NULL-terminated string |prefix|. template // static bool FloatParser::StringStartsWith(const char* start, const char* end, const char* prefix) { while (start < end && *prefix) { if (*start != *prefix) { return false; } start++; prefix++; } return *prefix == 0; } // static template Result FloatParser::ParseFloat(const char* s, const char* end, Uint* out_bits) { // Here is the normal behavior for strtof/strtod: // // input | errno | output | // --------------------------------- // overflow | ERANGE | +-HUGE_VAL | // underflow | ERANGE | 0.0 | // otherwise | 0 | value | // // So normally we need to clear errno before calling strto{f,d}, and check // afterward whether it was set to ERANGE. // // glibc seems to have a bug where // strtof("340282356779733661637539395458142568448") will return HUGE_VAL, // but will not set errno to ERANGE. Since this function is only called when // we know that we have parsed a "normal" number (i.e. not "inf"), we know // that if we ever get HUGE_VAL, it must be overflow. // // The WebAssembly spec also ignores underflow, so we don't need to check for // ERANGE at all. // WebAssembly floats can contain underscores, but strto* can't parse those, // so remove them first. assert(s <= end); const size_t kBufferSize = end - s + 1; // +1 for \0. char* buffer = static_cast(alloca(kBufferSize)); auto buffer_end = std::copy_if(s, end, buffer, [](char c) -> bool { return c != '_'; }); assert(buffer_end < buffer + kBufferSize); *buffer_end = 0; char* endptr; Float value = Traits::Strto(buffer, &endptr); if (endptr != buffer_end || (value == Traits::kHugeVal || value == -Traits::kHugeVal)) { return Result::Error; } memcpy(out_bits, &value, sizeof(value)); return Result::Ok; } // static template typename FloatParser::Uint FloatParser::Make(bool sign, int exp, Uint sig) { assert(exp >= Traits::kMinExp && exp <= Traits::kMaxExp); assert(sig <= Traits::kSigMask); return (Uint(sign) << Traits::kSignShift) | (Uint(exp + Traits::kExpBias) << Traits::kSigBits) | sig; } // static template typename FloatParser::Uint FloatParser::ShiftAndRoundToNearest( Uint significand, int shift, bool seen_trailing_non_zero) { assert(shift > 0); // Round ties to even. if ((significand & (Uint(1) << shift)) || seen_trailing_non_zero) { significand += Uint(1) << (shift - 1); } significand >>= shift; return significand; } // static template Result FloatParser::ParseNan(const char* s, const char* end, Uint* out_bits) { bool is_neg = false; if (*s == '-') { is_neg = true; s++; } else if (*s == '+') { s++; } assert(StringStartsWith(s, end, "nan")); s += 3; Uint tag; if (s != end) { tag = 0; assert(StringStartsWith(s, end, ":0x")); s += 3; for (; s < end; ++s) { if (*s == '_') { continue; } uint32_t digit; CHECK_RESULT(ParseHexdigit(*s, &digit)); tag = tag * 16 + digit; // Check for overflow. if (tag > Traits::kSigMask) { return Result::Error; } } // NaN cannot have a zero tag, that is reserved for infinity. if (tag == 0) { return Result::Error; } } else { tag = Traits::kQuietNanTag; } *out_bits = Make(is_neg, Traits::kMaxExp, tag); return Result::Ok; } // static template Result FloatParser::ParseHex(const char* s, const char* end, Uint* out_bits) { bool is_neg = false; if (*s == '-') { is_neg = true; s++; } else if (*s == '+') { s++; } assert(StringStartsWith(s, end, "0x")); s += 2; // Loop over the significand; everything up to the 'p'. // This code is a bit nasty because we want to support extra zeroes anywhere // without having to use many significand bits. // e.g. // 0x00000001.0p0 => significand = 1, significand_exponent = 0 // 0x10000000.0p0 => significand = 1, significand_exponent = 28 // 0x0.000001p0 => significand = 1, significand_exponent = -24 bool seen_dot = false; bool seen_trailing_non_zero = false; Uint significand = 0; int significand_exponent = 0; // Exponent adjustment due to dot placement. for (; s < end; ++s) { uint32_t digit; if (*s == '_') { continue; } else if (*s == '.') { seen_dot = true; } else if (Succeeded(ParseHexdigit(*s, &digit))) { if (Traits::kBits - Clz(significand) <= Traits::kSigPlusOneBits) { significand = (significand << 4) + digit; if (seen_dot) { significand_exponent -= 4; } } else { if (!seen_trailing_non_zero && digit != 0) { seen_trailing_non_zero = true; } if (!seen_dot) { significand_exponent += 4; } } } else { break; } } if (significand == 0) { // 0 or -0. *out_bits = Make(is_neg, Traits::kMinExp, 0); return Result::Ok; } int exponent = 0; bool exponent_is_neg = false; if (s < end) { assert(*s == 'p' || *s == 'P'); s++; // Exponent is always positive, but significand_exponent is signed. // significand_exponent_add is negated if exponent will be negative, so it // can be easily summed to see if the exponent is too large (see below). int significand_exponent_add = 0; if (*s == '-') { exponent_is_neg = true; significand_exponent_add = -significand_exponent; s++; } else if (*s == '+') { s++; significand_exponent_add = significand_exponent; } for (; s < end; ++s) { if (*s == '_') { continue; } uint32_t digit = (*s - '0'); assert(digit <= 9); exponent = exponent * 10 + digit; if (exponent + significand_exponent_add >= Traits::kMaxExp) { break; } } } if (exponent_is_neg) { exponent = -exponent; } int significand_bits = Traits::kBits - Clz(significand); // -1 for the implicit 1 bit of the significand. exponent += significand_exponent + significand_bits - 1; if (exponent <= Traits::kMinExp) { // Maybe subnormal. auto update_seen_trailing_non_zero = [&](int shift) { assert(shift > 0); auto mask = (Uint(1) << (shift - 1)) - 1; seen_trailing_non_zero |= (significand & mask) != 0; }; // Normalize significand. if (significand_bits > Traits::kSigBits) { int shift = significand_bits - Traits::kSigBits; update_seen_trailing_non_zero(shift); significand >>= shift; } else if (significand_bits < Traits::kSigBits) { significand <<= (Traits::kSigBits - significand_bits); } int shift = Traits::kMinExp - exponent; if (shift <= Traits::kSigBits) { if (shift) { update_seen_trailing_non_zero(shift); significand = ShiftAndRoundToNearest(significand, shift, seen_trailing_non_zero) & Traits::kSigMask; } exponent = Traits::kMinExp; if (significand != 0) { *out_bits = Make(is_neg, exponent, significand); return Result::Ok; } } // Not subnormal, too small; return 0 or -0. *out_bits = Make(is_neg, Traits::kMinExp, 0); } else { // Maybe Normal value. if (significand_bits > Traits::kSigPlusOneBits) { significand = ShiftAndRoundToNearest( significand, significand_bits - Traits::kSigPlusOneBits, seen_trailing_non_zero); if (significand > Traits::kSigPlusOneMask) { exponent++; } } else if (significand_bits < Traits::kSigPlusOneBits) { significand <<= (Traits::kSigPlusOneBits - significand_bits); } if (exponent >= Traits::kMaxExp) { // Would be inf or -inf, but the spec doesn't allow rounding hex-floats to // infinity. return Result::Error; } *out_bits = Make(is_neg, exponent, significand & Traits::kSigMask); } return Result::Ok; } // static template void FloatParser::ParseInfinity(const char* s, const char* end, Uint* out_bits) { bool is_neg = false; if (*s == '-') { is_neg = true; s++; } else if (*s == '+') { s++; } assert(StringStartsWith(s, end, "inf")); *out_bits = Make(is_neg, Traits::kMaxExp, 0); } // static template Result FloatParser::Parse(LiteralType literal_type, const char* s, const char* end, Uint* out_bits) { #if COMPILER_IS_MSVC if (literal_type == LiteralType::Int && StringStartsWith(s, end, "0x")) { // Some MSVC crt implementation of strtof doesn't support hex strings literal_type = LiteralType::Hexfloat; } #endif switch (literal_type) { case LiteralType::Int: case LiteralType::Float: return ParseFloat(s, end, out_bits); case LiteralType::Hexfloat: return ParseHex(s, end, out_bits); case LiteralType::Infinity: ParseInfinity(s, end, out_bits); return Result::Ok; case LiteralType::Nan: return ParseNan(s, end, out_bits); } WABT_UNREACHABLE; } // static template void FloatWriter::WriteHex(char* out, size_t size, Uint bits) { static constexpr int kNumNybbles = Traits::kBits / 4; static constexpr int kTopNybbleShift = Traits::kBits - 4; static constexpr Uint kTopNybble = Uint(0xf) << kTopNybbleShift; static const char s_hex_digits[] = "0123456789abcdef"; char buffer[Traits::kMaxHexBufferSize]; char* p = buffer; bool is_neg = (bits >> Traits::kSignShift); int exp = ((bits >> Traits::kSigBits) & Traits::kExpMask) - Traits::kExpBias; Uint sig = bits & Traits::kSigMask; if (is_neg) { *p++ = '-'; } if (exp == Traits::kMaxExp) { // Infinity or nan. if (sig == 0) { strcpy(p, "inf"); p += 3; } else { strcpy(p, "nan"); p += 3; if (sig != Traits::kQuietNanTag) { strcpy(p, ":0x"); p += 3; // Skip leading zeroes. int num_nybbles = kNumNybbles; while ((sig & kTopNybble) == 0) { sig <<= 4; num_nybbles--; } while (num_nybbles) { Uint nybble = (sig >> kTopNybbleShift) & 0xf; *p++ = s_hex_digits[nybble]; sig <<= 4; --num_nybbles; } } } } else { bool is_zero = sig == 0 && exp == Traits::kMinExp; strcpy(p, "0x"); p += 2; *p++ = is_zero ? '0' : '1'; // Shift sig up so the top 4-bits are at the top of the Uint. sig <<= Traits::kBits - Traits::kSigBits; if (sig) { if (exp == Traits::kMinExp) { // Subnormal; shift the significand up, and shift out the implicit 1. Uint leading_zeroes = Clz(sig); if (leading_zeroes < Traits::kSignShift) { sig <<= leading_zeroes + 1; } else { sig = 0; } exp -= leading_zeroes; } *p++ = '.'; while (sig) { int nybble = (sig >> kTopNybbleShift) & 0xf; *p++ = s_hex_digits[nybble]; sig <<= 4; } } *p++ = 'p'; if (is_zero) { strcpy(p, "+0"); p += 2; } else { if (exp < 0) { *p++ = '-'; exp = -exp; } else { *p++ = '+'; } if (exp >= 1000) { *p++ = '1'; } if (exp >= 100) { *p++ = '0' + (exp / 100) % 10; } if (exp >= 10) { *p++ = '0' + (exp / 10) % 10; } *p++ = '0' + exp % 10; } } size_t len = p - buffer; if (len >= size) { len = size - 1; } memcpy(out, buffer, len); out[len] = '\0'; } } // end anonymous namespace Result ParseHexdigit(char c, uint32_t* out) { if (static_cast(c - '0') <= 9) { *out = c - '0'; return Result::Ok; } else if (static_cast(c - 'a') < 6) { *out = 10 + (c - 'a'); return Result::Ok; } else if (static_cast(c - 'A') < 6) { *out = 10 + (c - 'A'); return Result::Ok; } return Result::Error; } Result ParseUint64(const char* s, const char* end, uint64_t* out) { if (s == end) { return Result::Error; } uint64_t value = 0; if (*s == '0' && s + 1 < end && s[1] == 'x') { s += 2; if (s == end) { return Result::Error; } constexpr uint64_t kMaxDiv16 = UINT64_MAX / 16; constexpr uint64_t kMaxMod16 = UINT64_MAX % 16; for (; s < end; ++s) { uint32_t digit; if (*s == '_') { continue; } CHECK_RESULT(ParseHexdigit(*s, &digit)); // Check for overflow. if (value > kMaxDiv16 || (value == kMaxDiv16 && digit > kMaxMod16)) { return Result::Error; } value = value * 16 + digit; } } else { constexpr uint64_t kMaxDiv10 = UINT64_MAX / 10; constexpr uint64_t kMaxMod10 = UINT64_MAX % 10; for (; s < end; ++s) { if (*s == '_') { continue; } uint32_t digit = (*s - '0'); if (digit > 9) { return Result::Error; } // Check for overflow. if (value > kMaxDiv10 || (value == kMaxDiv10 && digit > kMaxMod10)) { return Result::Error; } value = value * 10 + digit; } } if (s != end) { return Result::Error; } *out = value; return Result::Ok; } Result ParseInt64(const char* s, const char* end, uint64_t* out, ParseIntType parse_type) { bool has_sign = false; if (*s == '-' || *s == '+') { if (parse_type == ParseIntType::UnsignedOnly) { return Result::Error; } if (*s == '-') { has_sign = true; } s++; } uint64_t value = 0; Result result = ParseUint64(s, end, &value); if (has_sign) { // abs(INT64_MIN) == INT64_MAX + 1. if (value > static_cast(INT64_MAX) + 1) { return Result::Error; } value = UINT64_MAX - value + 1; } *out = value; return result; } namespace { uint32_t AddWithCarry(uint32_t x, uint32_t y, uint32_t* carry) { // Increments *carry if the addition overflows, otherwise leaves carry alone. if ((0xffffffff - x) < y) { ++*carry; } return x + y; } void Mul10(v128* v) { // Multiply-by-10 decomposes into (x << 3) + (x << 1). We implement those // operations with carrying from smaller quads of the v128 to the larger // quads. constexpr uint32_t kTopThreeBits = 0xe0000000; constexpr uint32_t kTopBit = 0x80000000; uint32_t carry_into_v1 = ((v->u32(0) & kTopThreeBits) >> 29) + ((v->u32(0) & kTopBit) >> 31); v->set_u32(0, AddWithCarry(v->u32(0) << 3, v->u32(0) << 1, &carry_into_v1)); uint32_t carry_into_v2 = ((v->u32(1) & kTopThreeBits) >> 29) + ((v->u32(1) & kTopBit) >> 31); v->set_u32(1, AddWithCarry(v->u32(1) << 3, v->u32(1) << 1, &carry_into_v2)); v->set_u32(1, AddWithCarry(v->u32(1), carry_into_v1, &carry_into_v2)); uint32_t carry_into_v3 = ((v->u32(2) & kTopThreeBits) >> 29) + ((v->u32(2) & kTopBit) >> 31); v->set_u32(2, AddWithCarry(v->u32(2) << 3, v->u32(2) << 1, &carry_into_v3)); v->set_u32(2, AddWithCarry(v->u32(2), carry_into_v2, &carry_into_v3)); v->set_u32(3, v->u32(3) * 10 + carry_into_v3); } } // namespace Result ParseUint128(const char* s, const char* end, v128* out) { if (s == end) { return Result::Error; } out->set_zero(); while (true) { uint32_t digit = (*s - '0'); if (digit > 9) { return Result::Error; } uint32_t carry_into_v1 = 0; uint32_t carry_into_v2 = 0; uint32_t carry_into_v3 = 0; uint32_t overflow = 0; out->set_u32(0, AddWithCarry(out->u32(0), digit, &carry_into_v1)); out->set_u32(1, AddWithCarry(out->u32(1), carry_into_v1, &carry_into_v2)); out->set_u32(2, AddWithCarry(out->u32(2), carry_into_v2, &carry_into_v3)); out->set_u32(3, AddWithCarry(out->u32(3), carry_into_v3, &overflow)); if (overflow) { return Result::Error; } ++s; if (s == end) { break; } Mul10(out); } return Result::Ok; } template Result ParseInt(const char* s, const char* end, U* out, ParseIntType parse_type) { using S = typename std::make_signed::type; uint64_t value; bool has_sign = false; if (*s == '-' || *s == '+') { if (parse_type == ParseIntType::UnsignedOnly) { return Result::Error; } if (*s == '-') { has_sign = true; } s++; } CHECK_RESULT(ParseUint64(s, end, &value)); if (has_sign) { // abs(INTN_MIN) == INTN_MAX + 1. if (value > static_cast(std::numeric_limits::max()) + 1) { return Result::Error; } value = std::numeric_limits::max() - value + 1; } else { if (value > static_cast(std::numeric_limits::max())) { return Result::Error; } } *out = static_cast(value); return Result::Ok; } Result ParseInt8(const char* s, const char* end, uint8_t* out, ParseIntType parse_type) { return ParseInt(s, end, out, parse_type); } Result ParseInt16(const char* s, const char* end, uint16_t* out, ParseIntType parse_type) { return ParseInt(s, end, out, parse_type); } Result ParseInt32(const char* s, const char* end, uint32_t* out, ParseIntType parse_type) { return ParseInt(s, end, out, parse_type); } Result ParseFloat(LiteralType literal_type, const char* s, const char* end, uint32_t* out_bits) { return FloatParser::Parse(literal_type, s, end, out_bits); } Result ParseDouble(LiteralType literal_type, const char* s, const char* end, uint64_t* out_bits) { return FloatParser::Parse(literal_type, s, end, out_bits); } void WriteFloatHex(char* buffer, size_t size, uint32_t bits) { return FloatWriter::WriteHex(buffer, size, bits); } void WriteDoubleHex(char* buffer, size_t size, uint64_t bits) { return FloatWriter::WriteHex(buffer, size, bits); } void WriteUint128(char* buffer, size_t size, v128 bits) { uint64_t digits; uint64_t remainder; char reversed_buffer[40]; size_t len = 0; do { remainder = bits.u32(3); for (int i = 3; i != 0; --i) { digits = remainder / 10; remainder = ((remainder - digits * 10) << 32) + bits.u32(i - 1); bits.set_u32(i, digits); } digits = remainder / 10; remainder = remainder - digits * 10; bits.set_u32(0, digits); char remainder_buffer[21]; snprintf(remainder_buffer, 21, "%" PRIu64, remainder); int remainder_buffer_len = strlen(remainder_buffer); assert(len + remainder_buffer_len < sizeof(reversed_buffer)); memcpy(&reversed_buffer[len], remainder_buffer, remainder_buffer_len); len += remainder_buffer_len; } while (!bits.is_zero()); size_t truncated_tail = 0; if (len >= size) { truncated_tail = len - size + 1; len = size - 1; } std::reverse_copy(reversed_buffer + truncated_tail, reversed_buffer + len + truncated_tail, buffer); buffer[len] = '\0'; } } // namespace wabt