/* * 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 "literal.h" #include #include #include #include #include #define HEX_DIGIT_BITS 4 /* The PLUS_ONE values are used because normal IEEE floats have an implicit * leading one, so they have an additional bit of precision. */ #define F32_SIGN_SHIFT 31 #define F32_SIG_BITS 23 #define F32_SIG_MASK 0x7fffff #define F32_SIG_PLUS_ONE_BITS 24 #define F32_SIG_PLUS_ONE_MASK 0xffffff #define F32_EXP_MASK 0xff #define F32_MIN_EXP -127 #define F32_MAX_EXP 128 #define F32_EXP_BIAS 127 #define F32_QUIET_NAN_TAG 0x400000 #define F64_SIGN_SHIFT 63 #define F64_SIG_BITS 52 #define F64_SIG_MASK 0xfffffffffffffULL #define F64_SIG_PLUS_ONE_BITS 53 #define F64_SIG_PLUS_ONE_MASK 0x1fffffffffffffULL #define F64_EXP_MASK 0x7ff #define F64_MIN_EXP -1023 #define F64_MAX_EXP 1024 #define F64_EXP_BIAS 1023 #define F64_QUIET_NAN_TAG 0x8000000000000ULL namespace wabt { static const char s_hex_digits[] = "0123456789abcdef"; Result parse_hexdigit(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; } /* return 1 if the non-NULL-terminated string starting with |start| and ending with |end| starts with the NULL-terminated string |prefix|. */ static bool string_starts_with(const char* start, const char* end, const char* prefix) { while (start < end && *prefix) { if (*start != *prefix) return false; start++; prefix++; } return *prefix == 0; } Result parse_uint64(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; for (; s < end; ++s) { uint32_t digit; if (WABT_FAILED(parse_hexdigit(*s, &digit))) return Result::Error; uint64_t old_value = value; value = value * 16 + digit; /* check for overflow */ if (old_value > value) return Result::Error; } } else { for (; s < end; ++s) { uint32_t digit = (*s - '0'); if (digit > 9) return Result::Error; uint64_t old_value = value; value = value * 10 + digit; /* check for overflow */ if (old_value > value) return Result::Error; } } if (s != end) return Result::Error; *out = value; return Result::Ok; } Result parse_int64(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 = parse_uint64(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; } Result parse_int32(const char* s, const char* end, uint32_t* out, ParseIntType parse_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++; } if (WABT_FAILED(parse_uint64(s, end, &value))) return Result::Error; if (has_sign) { /* abs(INT32_MIN) == INT32_MAX + 1 */ if (value > static_cast(INT32_MAX) + 1) return Result::Error; value = UINT32_MAX - value + 1; } else { if (value > static_cast(UINT32_MAX)) return Result::Error; } *out = static_cast(value); return Result::Ok; } /* floats */ static uint32_t make_float(bool sign, int exp, uint32_t sig) { assert(exp >= F32_MIN_EXP && exp <= F32_MAX_EXP); assert(sig <= F32_SIG_MASK); return (static_cast(sign) << F32_SIGN_SHIFT) | (static_cast(exp + F32_EXP_BIAS) << F32_SIG_BITS) | sig; } static uint32_t shift_float_and_round_to_nearest(uint32_t significand, int shift) { assert(shift > 0); /* round ties to even */ if (significand & (1U << shift)) significand += 1U << (shift - 1); significand >>= shift; return significand; } static Result parse_float_nan(const char* s, const char* end, uint32_t* out_bits) { bool is_neg = false; if (*s == '-') { is_neg = true; s++; } else if (*s == '+') { s++; } assert(string_starts_with(s, end, "nan")); s += 3; uint32_t tag; if (s != end) { tag = 0; assert(string_starts_with(s, end, ":0x")); s += 3; for (; s < end; ++s) { uint32_t digit; if (WABT_FAILED(parse_hexdigit(*s, &digit))) return Result::Error; tag = tag * 16 + digit; /* check for overflow */ if (tag > F32_SIG_MASK) return Result::Error; } /* NaN cannot have a zero tag, that is reserved for infinity */ if (tag == 0) return Result::Error; } else { tag = F32_QUIET_NAN_TAG; } *out_bits = make_float(is_neg, F32_MAX_EXP, tag); return Result::Ok; } static void parse_float_hex(const char* s, const char* end, uint32_t* out_bits) { bool is_neg = false; if (*s == '-') { is_neg = true; s++; } else if (*s == '+') { s++; } assert(string_starts_with(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; uint32_t significand = 0; /* how much to shift |significand| if a non-zero value is appended */ int significand_shift = 0; int significand_bits = 0; /* bits of |significand| */ int significand_exponent = 0; /* exponent adjustment due to dot placement */ for (; s < end; ++s) { uint32_t digit; if (*s == '.') { if (significand != 0) significand_exponent += significand_shift; significand_shift = 0; seen_dot = true; continue; } else if (WABT_FAILED(parse_hexdigit(*s, &digit))) { break; } significand_shift += HEX_DIGIT_BITS; if (digit != 0 && (significand == 0 || significand_bits + significand_shift <= F32_SIG_BITS + 1 + HEX_DIGIT_BITS)) { if (significand != 0) significand <<= significand_shift; if (seen_dot) significand_exponent -= significand_shift; significand += digit; significand_shift = 0; significand_bits += HEX_DIGIT_BITS; } } if (!seen_dot) significand_exponent += significand_shift; if (significand == 0) { /* 0 or -0 */ *out_bits = make_float(is_neg, F32_MIN_EXP, 0); return; } int exponent = 0; bool exponent_is_neg = false; if (s < end) { assert(*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) { uint32_t digit = (*s - '0'); assert(digit <= 9); exponent = exponent * 10 + digit; if (exponent + significand_exponent_add >= F32_MAX_EXP) break; } } if (exponent_is_neg) exponent = -exponent; significand_bits = sizeof(uint32_t) * 8 - wabt_clz_u32(significand); /* -1 for the implicit 1 bit of the significand */ exponent += significand_exponent + significand_bits - 1; if (exponent >= F32_MAX_EXP) { /* inf or -inf */ *out_bits = make_float(is_neg, F32_MAX_EXP, 0); } else if (exponent <= F32_MIN_EXP) { /* maybe subnormal */ if (significand_bits > F32_SIG_BITS) { significand = shift_float_and_round_to_nearest( significand, significand_bits - F32_SIG_BITS); } else if (significand_bits < F32_SIG_BITS) { significand <<= (F32_SIG_BITS - significand_bits); } int shift = F32_MIN_EXP - exponent; if (shift < F32_SIG_BITS) { if (shift) { significand = shift_float_and_round_to_nearest(significand, shift) & F32_SIG_MASK; } exponent = F32_MIN_EXP; if (significand != 0) { *out_bits = make_float(is_neg, exponent, significand); return; } } /* not subnormal, too small; return 0 or -0 */ *out_bits = make_float(is_neg, F32_MIN_EXP, 0); } else { /* normal value */ if (significand_bits > F32_SIG_PLUS_ONE_BITS) { significand = shift_float_and_round_to_nearest( significand, significand_bits - F32_SIG_PLUS_ONE_BITS); if (significand > F32_SIG_PLUS_ONE_MASK) exponent++; } else if (significand_bits < F32_SIG_PLUS_ONE_BITS) { significand <<= (F32_SIG_PLUS_ONE_BITS - significand_bits); } *out_bits = make_float(is_neg, exponent, significand & F32_SIG_MASK); } } static void parse_float_infinity(const char* s, const char* end, uint32_t* out_bits) { bool is_neg = false; if (*s == '-') { is_neg = true; s++; } else if (*s == '+') { s++; } assert(string_starts_with(s, end, "infinity")); *out_bits = make_float(is_neg, F32_MAX_EXP, 0); } Result parse_float(LiteralType literal_type, const char* s, const char* end, uint32_t* out_bits) { #if COMPILER_IS_MSVC if (literal_type == LiteralType::Int && string_starts_with(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: { errno = 0; char* endptr; float value; value = strtof(s, &endptr); if (endptr != end || ((value == 0 || value == HUGE_VALF || value == -HUGE_VALF) && errno != 0)) return Result::Error; memcpy(out_bits, &value, sizeof(value)); return Result::Ok; } case LiteralType::Hexfloat: parse_float_hex(s, end, out_bits); return Result::Ok; case LiteralType::Infinity: parse_float_infinity(s, end, out_bits); return Result::Ok; case LiteralType::Nan: return parse_float_nan(s, end, out_bits); default: assert(0); return Result::Error; } } void write_float_hex(char* out, size_t size, uint32_t bits) { /* 1234567890123456 */ /* -0x#.######p-### */ /* -nan:0x###### */ /* -infinity */ char buffer[WABT_MAX_FLOAT_HEX]; char* p = buffer; bool is_neg = (bits >> F32_SIGN_SHIFT); int exp = ((bits >> F32_SIG_BITS) & F32_EXP_MASK) - F32_EXP_BIAS; uint32_t sig = bits & F32_SIG_MASK; if (is_neg) *p++ = '-'; if (exp == F32_MAX_EXP) { /* infinity or nan */ if (sig == 0) { strcpy(p, "infinity"); p += 8; } else { strcpy(p, "nan"); p += 3; if (sig != F32_QUIET_NAN_TAG) { strcpy(p, ":0x"); p += 3; /* skip leading zeroes */ int num_nybbles = sizeof(uint32_t) * 8 / 4; while ((sig & 0xf0000000) == 0) { sig <<= 4; num_nybbles--; } while (num_nybbles) { uint32_t nybble = (sig >> (sizeof(uint32_t) * 8 - 4)) & 0xf; *p++ = s_hex_digits[nybble]; sig <<= 4; --num_nybbles; } } } } else { bool is_zero = sig == 0 && exp == F32_MIN_EXP; strcpy(p, "0x"); p += 2; *p++ = is_zero ? '0' : '1'; /* shift sig up so the top 4-bits are at the top of the uint32 */ sig <<= sizeof(uint32_t) * 8 - F32_SIG_BITS; if (sig) { if (exp == F32_MIN_EXP) { /* subnormal; shift the significand up, and shift out the implicit 1 */ uint32_t leading_zeroes = wabt_clz_u32(sig); if (leading_zeroes < 31) sig <<= leading_zeroes + 1; else sig = 0; exp -= leading_zeroes; } *p++ = '.'; while (sig) { uint32_t nybble = (sig >> (sizeof(uint32_t) * 8 - 4)) & 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 >= 100) *p++ = '1'; 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'; } /* doubles */ static uint64_t make_double(bool sign, int exp, uint64_t sig) { assert(exp >= F64_MIN_EXP && exp <= F64_MAX_EXP); assert(sig <= F64_SIG_MASK); return (static_cast(sign) << F64_SIGN_SHIFT) | (static_cast(exp + F64_EXP_BIAS) << F64_SIG_BITS) | sig; } static uint64_t shift_double_and_round_to_nearest(uint64_t significand, int shift) { assert(shift > 0); /* round ties to even */ if (significand & (static_cast(1) << shift)) significand += static_cast(1) << (shift - 1); significand >>= shift; return significand; } static Result parse_double_nan(const char* s, const char* end, uint64_t* out_bits) { bool is_neg = false; if (*s == '-') { is_neg = true; s++; } else if (*s == '+') { s++; } assert(string_starts_with(s, end, "nan")); s += 3; uint64_t tag; if (s != end) { tag = 0; if (!string_starts_with(s, end, ":0x")) return Result::Error; s += 3; for (; s < end; ++s) { uint32_t digit; if (WABT_FAILED(parse_hexdigit(*s, &digit))) return Result::Error; tag = tag * 16 + digit; /* check for overflow */ if (tag > F64_SIG_MASK) return Result::Error; } /* NaN cannot have a zero tag, that is reserved for infinity */ if (tag == 0) return Result::Error; } else { tag = F64_QUIET_NAN_TAG; } *out_bits = make_double(is_neg, F64_MAX_EXP, tag); return Result::Ok; } static void parse_double_hex(const char* s, const char* end, uint64_t* out_bits) { bool is_neg = false; if (*s == '-') { is_neg = true; s++; } else if (*s == '+') { s++; } assert(string_starts_with(s, end, "0x")); s += 2; /* see the similar comment in parse_float_hex */ bool seen_dot = false; uint64_t significand = 0; /* how much to shift |significand| if a non-zero value is appended */ int significand_shift = 0; int significand_bits = 0; /* bits of |significand| */ int significand_exponent = 0; /* exponent adjustment due to dot placement */ for (; s < end; ++s) { uint32_t digit; if (*s == '.') { if (significand != 0) significand_exponent += significand_shift; significand_shift = 0; seen_dot = true; continue; } else if (WABT_FAILED(parse_hexdigit(*s, &digit))) { break; } significand_shift += HEX_DIGIT_BITS; if (digit != 0 && (significand == 0 || significand_bits + significand_shift <= F64_SIG_BITS + 1 + HEX_DIGIT_BITS)) { if (significand != 0) significand <<= significand_shift; if (seen_dot) significand_exponent -= significand_shift; significand += digit; significand_shift = 0; significand_bits += HEX_DIGIT_BITS; } } if (!seen_dot) significand_exponent += significand_shift; if (significand == 0) { /* 0 or -0 */ *out_bits = make_double(is_neg, F64_MIN_EXP, 0); return; } int exponent = 0; bool exponent_is_neg = false; if (s < end) { assert(*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) { uint32_t digit = (*s - '0'); assert(digit <= 9); exponent = exponent * 10 + digit; if (exponent + significand_exponent_add >= F64_MAX_EXP) break; } } if (exponent_is_neg) exponent = -exponent; significand_bits = sizeof(uint64_t) * 8 - wabt_clz_u64(significand); /* -1 for the implicit 1 bit of the significand */ exponent += significand_exponent + significand_bits - 1; if (exponent >= F64_MAX_EXP) { /* inf or -inf */ *out_bits = make_double(is_neg, F64_MAX_EXP, 0); } else if (exponent <= F64_MIN_EXP) { /* maybe subnormal */ if (significand_bits > F64_SIG_BITS) { significand = shift_double_and_round_to_nearest( significand, significand_bits - F64_SIG_BITS); } else if (significand_bits < F64_SIG_BITS) { significand <<= (F64_SIG_BITS - significand_bits); } int shift = F64_MIN_EXP - exponent; if (shift < F64_SIG_BITS) { if (shift) { significand = shift_double_and_round_to_nearest(significand, shift) & F64_SIG_MASK; } exponent = F64_MIN_EXP; if (significand != 0) { *out_bits = make_double(is_neg, exponent, significand); return; } } /* not subnormal, too small; return 0 or -0 */ *out_bits = make_double(is_neg, F64_MIN_EXP, 0); } else { /* normal value */ if (significand_bits > F64_SIG_PLUS_ONE_BITS) { significand = shift_double_and_round_to_nearest( significand, significand_bits - F64_SIG_PLUS_ONE_BITS); if (significand > F64_SIG_PLUS_ONE_MASK) exponent++; } else if (significand_bits < F64_SIG_PLUS_ONE_BITS) { significand <<= (F64_SIG_PLUS_ONE_BITS - significand_bits); } *out_bits = make_double(is_neg, exponent, significand & F64_SIG_MASK); } } static void parse_double_infinity(const char* s, const char* end, uint64_t* out_bits) { bool is_neg = false; if (*s == '-') { is_neg = true; s++; } else if (*s == '+') { s++; } assert(string_starts_with(s, end, "infinity")); *out_bits = make_double(is_neg, F64_MAX_EXP, 0); } Result parse_double(LiteralType literal_type, const char* s, const char* end, uint64_t* out_bits) { #if COMPILER_IS_MSVC if (literal_type == LiteralType::Int && string_starts_with(s, end, "0x")) { // Some MSVC crt implementation of strtod doesn't support hex strings literal_type = LiteralType::Hexfloat; } #endif switch (literal_type) { case LiteralType::Int: case LiteralType::Float: { errno = 0; char* endptr; double value; value = strtod(s, &endptr); if (endptr != end || ((value == 0 || value == HUGE_VAL || value == -HUGE_VAL) && errno != 0)) return Result::Error; memcpy(out_bits, &value, sizeof(value)); return Result::Ok; } case LiteralType::Hexfloat: parse_double_hex(s, end, out_bits); return Result::Ok; case LiteralType::Infinity: parse_double_infinity(s, end, out_bits); return Result::Ok; case LiteralType::Nan: return parse_double_nan(s, end, out_bits); default: assert(0); return Result::Error; } } void write_double_hex(char* out, size_t size, uint64_t bits) { /* 123456789012345678901234 */ /* -0x#.#############p-#### */ /* -nan:0x############# */ /* -infinity */ char buffer[WABT_MAX_DOUBLE_HEX]; char* p = buffer; bool is_neg = (bits >> F64_SIGN_SHIFT); int exp = ((bits >> F64_SIG_BITS) & F64_EXP_MASK) - F64_EXP_BIAS; uint64_t sig = bits & F64_SIG_MASK; if (is_neg) *p++ = '-'; if (exp == F64_MAX_EXP) { /* infinity or nan */ if (sig == 0) { strcpy(p, "infinity"); p += 8; } else { strcpy(p, "nan"); p += 3; if (sig != F64_QUIET_NAN_TAG) { strcpy(p, ":0x"); p += 3; /* skip leading zeroes */ int num_nybbles = sizeof(uint64_t) * 8 / 4; while ((sig & 0xf000000000000000ULL) == 0) { sig <<= 4; num_nybbles--; } while (num_nybbles) { uint32_t nybble = (sig >> (sizeof(uint64_t) * 8 - 4)) & 0xf; *p++ = s_hex_digits[nybble]; sig <<= 4; --num_nybbles; } } } } else { bool is_zero = sig == 0 && exp == F64_MIN_EXP; strcpy(p, "0x"); p += 2; *p++ = is_zero ? '0' : '1'; /* shift sig up so the top 4-bits are at the top of the uint32 */ sig <<= sizeof(uint64_t) * 8 - F64_SIG_BITS; if (sig) { if (exp == F64_MIN_EXP) { /* subnormal; shift the significand up, and shift out the implicit 1 */ uint32_t leading_zeroes = wabt_clz_u64(sig); if (leading_zeroes < 63) sig <<= leading_zeroes + 1; else sig = 0; exp -= leading_zeroes; } *p++ = '.'; while (sig) { uint32_t nybble = (sig >> (sizeof(uint64_t) * 8 - 4)) & 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'; } } // namespace wabt