diff --git a/src/vm/fp_functions.cpp b/src/vm/fp_functions.cpp index e51db7d..2791e76 100644 --- a/src/vm/fp_functions.cpp +++ b/src/vm/fp_functions.cpp @@ -56,6 +56,28 @@ extern "C" { uint32_t fget_flags() { return softfloat_exceptionFlags & 0x1f; } +uint16_t fclass_h(uint16_t v1) { + + float16_t a{v1}; + union ui16_f16 uA; + uint_fast16_t uiA; + + uA.f = a; + uiA = uA.ui; + + bool infOrNaN = expF16UI(uiA) == 0xFF; + bool subnormalOrZero = expF16UI(uiA) == 0; + bool sign = signF16UI(uiA); + bool fracZero = fracF16UI(uiA) == 0; + bool isNaN = isNaNF16UI(uiA); + bool isSNaN = softfloat_isSigNaNF16UI(uiA); + + return (sign && infOrNaN && fracZero) << 0 | (sign && !infOrNaN && !subnormalOrZero) << 1 | + (sign && subnormalOrZero && !fracZero) << 2 | (sign && subnormalOrZero && fracZero) << 3 | (!sign && infOrNaN && fracZero) << 7 | + (!sign && !infOrNaN && !subnormalOrZero) << 6 | (!sign && subnormalOrZero && !fracZero) << 5 | + (!sign && subnormalOrZero && fracZero) << 4 | (isNaN && isSNaN) << 8 | (isNaN && !isSNaN) << 9; +} + uint32_t fadd_s(uint32_t v1, uint32_t v2, uint8_t mode) { float32_t v1f{v1}, v2f{v2}; softfloat_roundingMode = rmm_map.at(mode); @@ -455,3 +477,271 @@ uint32_t unbox_s(uint64_t v) { return v & std::numeric_limits::max(); } } + +template T constexpr defaultNaN(); +template <> uint16_t constexpr defaultNaN() { return defaultNaNF16UI; } +template <> uint32_t constexpr defaultNaN() { return defaultNaNF32UI; } +template <> uint64_t constexpr defaultNaN() { return defaultNaNF64UI; } +template T constexpr posInf(); +template <> uint16_t constexpr posInf() { return 0x7C00; } +template <> uint32_t constexpr posInf() { return 0x7F800000; } +template <> uint64_t constexpr posInf() { return 0x7FF0000000000000; } +template T constexpr negInf(); +template <> uint16_t constexpr negInf() { return 0xFC00; } +template <> uint32_t constexpr negInf() { return 0xFF800000; } +template <> uint64_t constexpr negInf() { return 0xFFF0000000000000; } +template T constexpr negZero(); +template <> uint16_t constexpr negZero() { return 0x8000; } +template <> uint32_t constexpr negZero() { return 0x80000000; } +template <> uint64_t constexpr negZero() { return 0x8000000000000000; } +/* +function nxFlag() -> bits(5) = 0b_00001 softfloat_flag_inexact = 1, +function ufFlag() -> bits(5) = 0b_00010 softfloat_flag_underflow = 2, +function ofFlag() -> bits(5) = 0b_00100 softfloat_flag_overflow = 4, +function dzFlag() -> bits(5) = 0b_01000 softfloat_flag_infinite = 8, +function nvFlag() -> bits(5) = 0b_10000 softfloat_flag_invalid = 16 +*/ +template bool rsqrt_check(T fclass_val, bool& subnormal, T& ret_val) { + softfloat_exceptionFlags = 0; + switch(fclass_val) { + case 0x0001: { + softfloat_exceptionFlags |= softfloat_flag_invalid; + ret_val = defaultNaN(); + return true; + } + case 0x0002: { + softfloat_exceptionFlags |= softfloat_flag_invalid; + ret_val = defaultNaN(); + return true; + } + case 0x0004: { + softfloat_exceptionFlags |= softfloat_flag_invalid; + ret_val = defaultNaN(); + return true; + } + case 0x0100: { + softfloat_exceptionFlags |= softfloat_flag_invalid; + ret_val = defaultNaN(); + return true; + } + case 0x0200: { + ret_val = defaultNaN(); + return true; + } + case 0x0008: { + softfloat_exceptionFlags |= softfloat_flag_infinite; + ret_val = negInf(); + return true; + } + case 0x0010: { + softfloat_exceptionFlags |= softfloat_flag_infinite; + ret_val = posInf(); + return true; + } + case 0x0080: { + ret_val = 0; + return true; + } + case 0x0020: { + subnormal = true; + } + default: + return false; + } +} +static constexpr std::array, 2> rsqrt_table{ + {{ + 52, 51, 50, 48, 47, 46, 44, 43, 42, 41, 40, 39, 38, 36, 35, 34, 33, 32, 31, 30, 30, 29, 28, 27, 26, 25, 24, 23, 23, 22, 21, 20, + 19, 19, 18, 17, 16, 16, 15, 14, 14, 13, 12, 12, 11, 10, 10, 9, 9, 8, 7, 7, 6, 6, 5, 4, 4, 3, 3, 2, 2, 1, 1, 0, + }, + {127, 125, 123, 121, 119, 118, 116, 114, 113, 111, 109, 108, 106, 105, 103, 102, 100, 99, 97, 96, 95, 93, + 92, 91, 90, 88, 87, 86, 85, 84, 83, 82, 80, 79, 78, 77, 76, 75, 74, 73, 72, 71, 70, 70, + 69, 68, 67, 66, 65, 64, 63, 63, 62, 61, 60, 59, 59, 58, 57, 56, 56, 55, 54, 53}}}; + +uint64_t constexpr frsqrt7_general(const unsigned s, const unsigned e, const uint64_t sign, const int64_t exp, const uint64_t sig, + const bool subnormal) { + int64_t normalized_exp = exp; + uint64_t normalized_sig = sig; + if(subnormal) { + unsigned nr_leadingzeros = __builtin_clzll(sig) - (64 - s); + normalized_exp = -nr_leadingzeros; + normalized_sig = (sig << (1 + nr_leadingzeros)) & ((1ULL << s) - 1); + } + unsigned exp_idx = normalized_exp & 1; + unsigned sig_idx = (normalized_sig >> (s - 6)) & 0x3f; + // The output of the table becomes the seven high bits of the result significand (after the leading one); the remainder of the + // result significand is zero. + uint64_t out_sig = rsqrt_table[exp_idx][sig_idx] << (s - 7); + // The output exponent equals floor((3*B - 1 - the normalized input exponent) / 2), where B is the exponent bias. + unsigned bias = 1UL << (e - 1); + uint64_t out_exp = (3 * (bias - 1) - 1 - normalized_exp) / 2; + // The output sign equals the input sign. + return (sign << (s + e)) | (out_exp << s) | out_sig; +} + +uint16_t frsqrt7_h(uint16_t v) { + bool subnormal = false; + uint16_t ret_val = 0; + if(rsqrt_check(fclass_h(v), subnormal, ret_val)) { + return ret_val; + } + uint16_t sig = fracF64UI(v); + int16_t exp = expF64UI(v); + uint16_t sign = signF64UI(v); + unsigned constexpr e = 5; + unsigned constexpr s = 10; + return frsqrt7_general(s, e, sign, exp, sig, subnormal); +} +uint32_t frsqrt7_s(uint32_t v) { + bool subnormal = false; + uint32_t ret_val = 0; + if(rsqrt_check(fclass_s(v), subnormal, ret_val)) { + return ret_val; + } + uint32_t sig = fracF32UI(v); + int exp = expF32UI(v); + uint32_t sign = signF32UI(v); + unsigned constexpr e = 8; + unsigned constexpr s = 23; + return frsqrt7_general(s, e, sign, exp, sig, subnormal); +} +uint64_t frsqrt7_d(uint64_t v) { + bool subnormal = false; + uint64_t ret_val = 0; + if(rsqrt_check(fclass_d(v), subnormal, ret_val)) { + return ret_val; + } + uint64_t sig = fracF64UI(v); + int exp = expF64UI(v); + uint64_t sign = signF64UI(v); + unsigned constexpr e = 11; + unsigned constexpr s = 58; + return frsqrt7_general(s, e, sign, exp, sig, subnormal); +} + +template bool recip_check(T fclass_val, bool& subnormal, uint64_t& ret_val) { + softfloat_exceptionFlags = 0; + switch(fclass_val) { + case 0x0001: { + ret_val = negZero(); + return true; + } + case 0x0080: { + ret_val = 0; + return true; + } + case 0x0008: { + softfloat_exceptionFlags |= softfloat_flag_infinite; + ret_val = negInf(); + return true; + } + case 0x0010: { + softfloat_exceptionFlags |= softfloat_flag_infinite; + ret_val = posInf(); + return true; + } + case 0x0100: { + softfloat_exceptionFlags |= softfloat_flag_invalid; + ret_val = defaultNaN(); + return true; + } + case 0x0200: { + ret_val = defaultNaN(); + return true; + } + case 0x0004: { + subnormal = true; + return false; + } + case 0x0020: { + subnormal = true; + return false; + } + default: { + subnormal = false; + return false; + } + } +} +static constexpr std::array rec_table{ + {127, 125, 123, 121, 119, 117, 116, 114, 112, 110, 109, 107, 105, 104, 102, 100, 99, 97, 96, 94, 93, 91, 90, 88, 87, 85, + 84, 83, 81, 80, 79, 77, 76, 75, 74, 72, 71, 70, 69, 68, 66, 65, 64, 63, 62, 61, 60, 59, 58, 57, 56, 55, + 54, 53, 52, 51, 50, 49, 48, 47, 46, 45, 44, 43, 42, 41, 40, 40, 39, 38, 37, 36, 35, 35, 34, 33, 32, 31, + 31, 30, 29, 28, 28, 27, 26, 25, 25, 24, 23, 23, 22, 21, 21, 20, 19, 19, 18, 17, 17, 16, 15, 15, 14, 14, + 13, 12, 12, 11, 11, 10, 9, 9, 8, 8, 7, 7, 6, 5, 5, 4, 4, 3, 3, 2, 2, 1, 1, 0}}; +bool frec_general(uint64_t& res, const unsigned s, const unsigned e, const uint64_t sign, const int64_t exp, const uint64_t sig, + const bool subnormal, uint8_t mode) { + int nr_leadingzeros = __builtin_clzll(sig) - (64 - s); + int64_t normalized_exp = subnormal ? -nr_leadingzeros : exp; + uint64_t normalized_sig = subnormal ? ((sig << (1 + nr_leadingzeros)) & ((1ULL << s) - 1)) : sig; + unsigned idx = (normalized_sig >> (s - 7)) & 0x7f; + unsigned bias = 1UL << (e - 1); + uint64_t mid_exp = 2 * (bias - 1) - 1 - normalized_exp; + uint64_t mid_sig = rec_table[idx] << (s - 7); + + uint64_t out_exp = mid_exp; + uint64_t out_sig = mid_sig; + if(mid_exp == 0) { + out_exp = mid_exp; + out_sig = (mid_sig >> 1) | (1ULL << (s - 1)); + } else if(mid_exp == (1ULL << e) - 1) { + out_exp = 0; + out_sig = (mid_sig >> 2) | (1ULL << (s - 2)); + } + if(subnormal && nr_leadingzeros > 1) { + if((mode == 0b001) || (mode == 0b010 && sign == 0b0) || (mode == 0b011 && sign == 0b1)) { + res = (sign << (s + e)) | ((1ULL << (e - 1)) - 1) << s | ((1ULL << s) - 1); + return true; + } else { + res = (sign << (s + e)) | ((1ULL << e) - 1) << s; + return true; + } + } + res = (sign << (s + e)) | (out_exp << s) | out_sig; + return false; +} +uint16_t frec7_h(uint16_t v, uint8_t mode) { + bool subnormal = false; + uint64_t ret_val = 0; + if(recip_check(fclass_h(v), subnormal, ret_val)) { + return ret_val; + } + uint16_t sig = fracF16UI(v); + int exp = expF16UI(v); + uint16_t sign = signF16UI(v); + unsigned constexpr e = 5; + unsigned constexpr s = 10; + if(frec_general(ret_val, s, e, sign, exp, sig, subnormal, mode)) + softfloat_exceptionFlags |= (softfloat_flag_inexact | softfloat_flag_overflow); + return ret_val; +} +uint32_t frec7_s(uint32_t v, uint8_t mode) { + bool subnormal = false; + uint64_t ret_val = 0; + if(recip_check(fclass_s(v), subnormal, ret_val)) { + return ret_val; + } + uint32_t sig = fracF32UI(v); + int exp = expF32UI(v); + uint32_t sign = signF32UI(v); + unsigned constexpr e = 8; + unsigned constexpr s = 23; + if(frec_general(ret_val, s, e, sign, exp, sig, subnormal, mode)) + softfloat_exceptionFlags |= (softfloat_flag_inexact | softfloat_flag_overflow); + return ret_val; +} +uint64_t frec7_d(uint64_t v, uint8_t mode) { + bool subnormal = false; + uint64_t ret_val = 0; + if(recip_check(fclass_s(v), subnormal, ret_val)) { + return ret_val; + } + uint64_t sig = fracF64UI(v); + int exp = expF64UI(v); + uint64_t sign = signF64UI(v); + unsigned constexpr e = 11; + unsigned constexpr s = 58; + if(frec_general(ret_val, s, e, sign, exp, sig, subnormal, mode)) + softfloat_exceptionFlags |= (softfloat_flag_inexact | softfloat_flag_overflow); + return ret_val; +} \ No newline at end of file diff --git a/src/vm/fp_functions.h b/src/vm/fp_functions.h index c0466a7..7c83668 100644 --- a/src/vm/fp_functions.h +++ b/src/vm/fp_functions.h @@ -39,6 +39,8 @@ extern "C" { uint32_t fget_flags(); +uint16_t frsqrt7_h(uint16_t v); +uint16_t frec7_h(uint16_t v, uint8_t mode); uint32_t fadd_s(uint32_t v1, uint32_t v2, uint8_t mode); uint32_t fsub_s(uint32_t v1, uint32_t v2, uint8_t mode); uint32_t fmul_s(uint32_t v1, uint32_t v2, uint8_t mode); @@ -49,6 +51,8 @@ uint32_t fcvt_s(uint32_t v1, uint32_t op, uint8_t mode); uint32_t fmadd_s(uint32_t v1, uint32_t v2, uint32_t v3, uint32_t op, uint8_t mode); uint32_t fsel_s(uint32_t v1, uint32_t v2, uint32_t op); uint32_t fclass_s(uint32_t v1); +uint32_t frsqrt7_s(uint32_t v); +uint32_t frec7_s(uint32_t v, uint8_t mode); uint32_t fconv_d2f(uint64_t v1, uint8_t mode); uint64_t fconv_f2d(uint32_t v1, uint8_t mode); uint64_t fadd_d(uint64_t v1, uint64_t v2, uint8_t mode); @@ -61,6 +65,8 @@ uint64_t fcvt_d(uint64_t v1, uint32_t op, uint8_t mode); uint64_t fmadd_d(uint64_t v1, uint64_t v2, uint64_t v3, uint32_t op, uint8_t mode); uint64_t fsel_d(uint64_t v1, uint64_t v2, uint32_t op); uint64_t fclass_d(uint64_t v1); +uint64_t frsqrt7_d(uint64_t v); +uint64_t frec7_d(uint64_t v, uint8_t mode); uint64_t fcvt_32_64(uint32_t v1, uint32_t op, uint8_t mode); uint32_t fcvt_64_32(uint64_t v1, uint32_t op, uint8_t mode); uint32_t unbox_s(uint64_t v);