blob: 4cacf4b0ba5b523eef583a9e190e1779689b8f33 [file] [log] [blame] [edit]
/*
* 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 <assert.h>
#include <errno.h>
#include <math.h>
#include <stdlib.h>
#include <string.h>
#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<unsigned int>(c - '0') <= 9) {
*out = c - '0';
return Result::Ok;
} else if (static_cast<unsigned int>(c - 'a') <= 6) {
*out = 10 + (c - 'a');
return Result::Ok;
} else if (static_cast<unsigned int>(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<uint64_t>(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<uint64_t>(INT32_MAX) + 1)
return Result::Error;
value = UINT32_MAX - value + 1;
} else {
if (value > static_cast<uint64_t>(UINT32_MAX))
return Result::Error;
}
*out = static_cast<uint32_t>(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<uint32_t>(sign) << F32_SIGN_SHIFT) |
(static_cast<uint32_t>(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<uint64_t>(sign) << F64_SIGN_SHIFT) |
(static_cast<uint64_t>(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<uint64_t>(1) << shift))
significand += static_cast<uint64_t>(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