Skip to content

Commit

Permalink
Implement __fixtfsi
Browse files Browse the repository at this point in the history
  • Loading branch information
AngheloAlf committed Nov 15, 2023
1 parent 4b458e6 commit caa84d7
Show file tree
Hide file tree
Showing 3 changed files with 151 additions and 1 deletion.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -122,3 +122,4 @@ the ABI passed to the `make` command.
- <https://gcc.gnu.org/onlinedocs/gccint/the-gcc-low-level-runtime-library/routines-for-floating-point-emulation.html>
- <https://gcc.gnu.org/onlinedocs/gccint/Soft-float-library-routines.html>
- <https://gcc.gnu.org/onlinedocs/gccint/the-gcc-low-level-runtime-library/routines-for-integer-arithmetic.html>
- <https://en.wikipedia.org/wiki/Quadruple-precision_floating-point_format>
20 changes: 19 additions & 1 deletion include/gcc_vr4300/types.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,20 @@
#include "macro.h"
#include "abi.h"

#if __STDC_VERSION__ >= 202311L
// do nothing
#else
typedef _Bool bool;
#endif

typedef __SIZE_TYPE__ size_t;

typedef __INT32_TYPE__ int32_t;
STATIC_ASSERT(sizeof(int32_t) == 4, "int32_t type's size is not 4");

typedef __UINT32_TYPE__ uint32_t;
STATIC_ASSERT(sizeof(uint32_t) == 4, "uint32_t type's size is not 4");

typedef __INT64_TYPE__ int64_t;
STATIC_ASSERT(sizeof(int64_t) == 8, "int64_t type's size is not 8");

Expand All @@ -29,6 +38,11 @@ STATIC_ASSERT(sizeof(float128) == 16, "float128 type's size is not 16");

typedef int fcmp __attribute__ ((mode (__libgcc_cmp_return__)));


#define INT32_MAX ((int32_t)0x7FFFFFFF)
#define INT32_MIN ((int32_t)0x80000000)


typedef union Float64Union {
float64 d;
int64_t ll;
Expand All @@ -38,8 +52,12 @@ typedef union Float64Union {
#if ABI_N32 || ABI_N64
typedef union Float128Union {
float128 ld;
uint64_t hex[2];
struct {
uint64_t upper;
uint64_t lower;
} hex;
} Float128Union;
STATIC_ASSERT(sizeof(Float128Union) == 16, "Float128Union type's size is not 16");
#endif

#endif
131 changes: 131 additions & 0 deletions src/soft_float/int_from_float/__fixtfsi.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
/* SPDX-FileCopyrightText: © 2023 Decompollaborate */
/* SPDX-License-Identifier: MIT */

#include "gcc_vr4300/types.h"
#include "gcc_vr4300/export.h"

#if ABI_N32 || ABI_N64
EXPORT(__fixtfsi);

/**
* Gets int32_t from float128.
*
* https://gcc.gnu.org/onlinedocs/gccint/the-gcc-low-level-runtime-library/routines-for-floating-point-emulation.html#c.__fixtfsi
*/
int32_t __fixtfsi(float128 a) {
// IEEE754 128-bit floats are encoded in 128 bits as follows:
// Sign bit: 1 bit (bit 127)
// Encoded exponent: 15 bits (bits 112 ~ 126)
// Fraction/Mantissa: 112 bits (bits 0 ~ 111)

Float128Union flt;
int32_t sign;
int32_t encodedExponent;
int32_t realExponent;
bool mantissaIsZero;
int32_t computedValue;

uint32_t truncantedMantissa;

flt.ld = a;

// If parameter is zero (or negative zero), then return zero
if (flt.hex.lower == 0) {
if ((flt.hex.upper == 0) || (flt.hex.upper & (1ULL << 63))) {
return 0;
}
}

sign = flt.hex.upper >> 63;
// Clear up the sign
flt.hex.upper &= ~(1ULL << 63);

encodedExponent = flt.hex.upper >> 48;
// Clear up the encoded exponent
flt.hex.upper &= ~0x7FFF000000000000ULL;

// Exponent bias: 0x3FFF
realExponent = encodedExponent - 0x3FFF;

mantissaIsZero = (flt.hex.upper == 0) && (flt.hex.lower == 0);

if (encodedExponent == 0) {
// subnormals
return 0;
}

if (encodedExponent == 0x7FFF) {
// Infinity and NaN

if (mantissaIsZero) {
// Infinity, return max int

// TODO: should this be handled in any other way?

if (sign) {
return INT32_MIN;
} else {
return INT32_MAX;
}
}

// NaN
return 0; // ?
}

// TODO: Figure out proper way to handle values outside of the int32_t range

if (realExponent < 0) {
// Value smaller than 1, truncate to zero (?)
return 0;
}

if (realExponent > 31) {
// Value is larger than int32_t, return int max

// TODO: should this be handled in any other way?

if (sign) {
return INT32_MIN;
} else {
return INT32_MAX;
}
}

if (mantissaIsZero) {
// If the mantissa is zero, just shift 1 by the exponent to get the value
computedValue = 1 << realExponent;

if (sign) {
computedValue *= -1;
}

return computedValue;
}

// We truncate the mantissa from 112 bits to 32 bits because an `int32_t` can't fit more than that
truncantedMantissa = flt.hex.upper >> 16;

// Manually compute the value
// The following algorithm only works for converting a float that is greater or equal than 1 but smaller than INT_MAX

// The implicit leading bit
computedValue = 1;

while (realExponent-- > 0) {
computedValue *= 2;

// Iterate the mantissa, from the upper bits to the lowest ones
if (truncantedMantissa & 0x80000000) {
computedValue += 1;
}
truncantedMantissa <<= 1;
}

if (sign) {
computedValue *= -1;
}

return computedValue;
}
#endif

0 comments on commit caa84d7

Please sign in to comment.