Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 24 additions & 4 deletions models/cpu/iss/flexfloat/flexfloat.c
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,10 @@ int_t flexfloat_rounding_value(const flexfloat_t *a, int_fast16_t exp, bool sign

#endif // FLEXFLOAT_ROUNDING

// RISC-V RMM support: with FE_TONEAREST set, a tie (round bit 1, sticky 0) is
// resolved away from zero instead of to even. See flexfloat.h.
int flexfloat_rmm = 0;

void flexfloat_sanitize(flexfloat_t *a)
{
bool sign;
Expand Down Expand Up @@ -281,7 +285,8 @@ void flexfloat_sanitize(flexfloat_t *a)
#endif
// Rounding mode
int mode = fegetround();
if(mode == FE_TONEAREST && flexfloat_nearest_rounding(a, exp))
if(mode == FE_TONEAREST && (flexfloat_rmm ? flexfloat_round_bit(a, exp)
: flexfloat_nearest_rounding(a, exp)))
{
int_t rounding_value = flexfloat_rounding_value(a, exp, sign);
a->value += CAST_TO_FP(rounding_value);
Expand Down Expand Up @@ -362,14 +367,29 @@ void flexfloat_sanitize(flexfloat_t *a)
#endif
exp = inf_exp;
}
else if(exp >= inf_exp) // Out of bounds for target format: set infinity
else if(exp >= inf_exp) // Out of bounds for target format: overflow
{
#ifdef FLEXFLOAT_FLAGS
// Raise the proper overflow exception
feraiseexcept(FE_OVERFLOW | FE_INEXACT);
#endif
exp = inf_exp;
frac = UINT_C(0);
// IEEE 754 overflow: directed roundings that point away from the
// overflow direction clamp to the largest finite value instead of
// infinity (toward-zero always; downward for positive, upward for
// negative). Nearest (even or ties-away) overflows to infinity.
int ovf_mode = fegetround();
if (ovf_mode == FE_TOWARDZERO ||
(ovf_mode == FE_DOWNWARD && !sign) ||
(ovf_mode == FE_UPWARD && sign))
{
exp = inf_exp - 1;
frac = (UINT_C(1) << a->desc.frac_bits) - 1;
}
else
{
exp = inf_exp;
frac = UINT_C(0);
}
}

// printf("ENCODING: %d %d %lu\n", sign, exp, frac);
Expand Down
5 changes: 5 additions & 0 deletions models/cpu/iss/flexfloat/flexfloat.h
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,11 @@ uint_t flexfloat_denorm_frac(const flexfloat_t *a, int_fast16_t exp);
uint_t flexfloat_pack(flexfloat_desc_t desc, bool sign, int_fast16_t exp, uint_t frac);
void flexfloat_sanitize(flexfloat_t *a);

// Round-to-nearest, ties away from zero (RISC-V RMM). The C fenv has no such
// mode: callers set FE_TONEAREST plus this flag, and flexfloat_sanitize then
// resolves ties away from zero instead of to even.
extern int flexfloat_rmm;


// Bit-level access

Expand Down
35 changes: 22 additions & 13 deletions models/cpu/iss/include/isa_lib/int.h
Original file line number Diff line number Diff line change
Expand Up @@ -1719,8 +1719,9 @@ static inline unsigned long int setFFRoundingMode(Iss *s, unsigned long int mode
fesetround(FE_UPWARD);
break;
case 4:
printf("Unimplemented roudning mode nearest ties to max magnitude");
exit(-1);
// RMM has no fenv equivalent: nearest plus the flexfloat ties-away flag.
fesetround(FE_TONEAREST);
flexfloat_rmm = 1;
break;
case 7:
{
Expand All @@ -1739,8 +1740,8 @@ static inline unsigned long int setFFRoundingMode(Iss *s, unsigned long int mode
fesetround(FE_UPWARD);
break;
case 4:
printf("Unimplemented roudning mode nearest ties to max magnitude");
exit(-1);
fesetround(FE_TONEAREST);
flexfloat_rmm = 1;
break;
}
}
Expand All @@ -1750,6 +1751,7 @@ static inline unsigned long int setFFRoundingMode(Iss *s, unsigned long int mode

static inline void restoreFFRoundingMode(unsigned long int mode)
{
flexfloat_rmm = 0;
fesetround(mode);
}

Expand Down Expand Up @@ -1972,27 +1974,34 @@ static inline int64_t lib_flexfloat_cvt_w_ff_round(Iss *s, unsigned long int a,
int32_t result_int = double_to_int(s, ff_a.value);
if (neg)
{
result_int = -result_int;
/* Two's-complement asymmetry: |INT32_MIN| = 2^31 is not representable
* as a positive int32, so the magnitude above saturated to INT32_MAX
* and negating it gives INT32_MIN+1 (off by one). A negative value
* whose magnitude reaches/overflows 2^31 must saturate to INT32_MIN. */
if (nearbyint(ff_a.value) >= 2147483648.0)
result_int = INT32_MIN;
else
result_int = -result_int;
}
restoreFFRoundingMode(new_round);
restoreFFRoundingMode(old);
return iss_get_signed_value(result_int, 32);
}

static inline int64_t lib_flexfloat_cvt_wu_ff_round(Iss *s, unsigned long int a, uint8_t e, uint8_t m, unsigned long int round)
{
int old;
bool neg = false;
unsigned long int new_round = round == 4 ? 2 : round;
old = setFFRoundingMode(s, new_round);
FF_INIT_1(a, e, m)
if (round == 4)
{
if (ff_a.value < 0)
{
ff_a.value = -ff_a.value;
neg = true;
}
ff_a.value += 0.5f;
/* Unsigned conversion of a negative value saturates to 0, with the
* proper flags, inside double_to_uint - do NOT negate the input on
* this path (there is no sign to re-apply afterwards, unlike the
* signed sibling). Only the non-negative RMM magnitude needs the
* +0.5 nudge. */
if (ff_a.value >= 0)
ff_a.value += 0.5f;
}
int32_t result_int = double_to_uint(s, ff_a.value);
restoreFFRoundingMode(old);
Expand Down