Skip to content

Commit

Permalink
[builtins] Start to refactor int to fp conversion functions to use a …
Browse files Browse the repository at this point in the history
…common implementation (#66903)

After this patch, the softfp implementations of floatdidf and floatundidf
use a common implementation (int_to_fp.h and int_to_fp_impl.inc). This
roughly follows the pattern used for a wide range of other builtins,
e.g. fp_trunc_impl.inc.

Currently there is substantial copy and paste for the various int to fp
conversion functions, with just a few constants being changed. This is a
barrier to maintainability, and it's also not attractive to copy this
approach as we introduce additional int to fp conversion functions for
bf16 and half (which we currently lack, but need - see
<https://reviews.llvm.org/D157509>).

I've opted to conservatively start by replacing just two functions,
leaving a follow-up patch to replace others that follow the same
pattern. Also, for better or worse I've left the logic in float[un]didf
largely unchanged other than using a similar approach to
fp_trunc_impl.inc to remove the constants that are tied to a specific
output floating point format.
  • Loading branch information
asb committed Oct 15, 2023
1 parent 0823cb7 commit 6dfea56
Show file tree
Hide file tree
Showing 4 changed files with 130 additions and 91 deletions.
52 changes: 5 additions & 47 deletions compiler-rt/lib/builtins/floatdidf.c
Expand Up @@ -45,53 +45,11 @@ COMPILER_RT_ABI double __floatdidf(di_int a) {
// flags to set, and we don't want to code-gen to an unknown soft-float
// implementation.

COMPILER_RT_ABI double __floatdidf(di_int a) {
if (a == 0)
return 0.0;
const unsigned N = sizeof(di_int) * CHAR_BIT;
const di_int s = a >> (N - 1);
a = (du_int)(a ^ s) - s;
int sd = N - __builtin_clzll(a); // number of significant digits
int e = sd - 1; // exponent
if (sd > DBL_MANT_DIG) {
// start: 0000000000000000000001xxxxxxxxxxxxxxxxxxxxxxPQxxxxxxxxxxxxxxxxxx
// finish: 000000000000000000000000000000000000001xxxxxxxxxxxxxxxxxxxxxxPQR
// 12345678901234567890123456
// 1 = msb 1 bit
// P = bit DBL_MANT_DIG-1 bits to the right of 1
// Q = bit DBL_MANT_DIG bits to the right of 1
// R = "or" of all bits to the right of Q
switch (sd) {
case DBL_MANT_DIG + 1:
a <<= 1;
break;
case DBL_MANT_DIG + 2:
break;
default:
a = ((du_int)a >> (sd - (DBL_MANT_DIG + 2))) |
((a & ((du_int)(-1) >> ((N + DBL_MANT_DIG + 2) - sd))) != 0);
};
// finish:
a |= (a & 4) != 0; // Or P into R
++a; // round - this step may add a significant bit
a >>= 2; // dump Q and R
// a is now rounded to DBL_MANT_DIG or DBL_MANT_DIG+1 bits
if (a & ((du_int)1 << DBL_MANT_DIG)) {
a >>= 1;
++e;
}
// a is now rounded to DBL_MANT_DIG bits
} else {
a <<= (DBL_MANT_DIG - sd);
// a is now rounded to DBL_MANT_DIG bits
}
double_bits fb;
fb.u.s.high = ((su_int)s & 0x80000000) | // sign
((su_int)(e + 1023) << 20) | // exponent
((su_int)(a >> 32) & 0x000FFFFF); // mantissa-high
fb.u.s.low = (su_int)a; // mantissa-low
return fb.f;
}
#define SRC_I64
#define DST_DOUBLE
#include "int_to_fp_impl.inc"

COMPILER_RT_ABI double __floatdidf(di_int a) { return __floatXiYf__(a); }
#endif

#if defined(__ARM_EABI__)
Expand Down
49 changes: 5 additions & 44 deletions compiler-rt/lib/builtins/floatundidf.c
Expand Up @@ -51,50 +51,11 @@ COMPILER_RT_ABI double __floatundidf(du_int a) {
// flags to set, and we don't want to code-gen to an unknown soft-float
// implementation.

COMPILER_RT_ABI double __floatundidf(du_int a) {
if (a == 0)
return 0.0;
const unsigned N = sizeof(du_int) * CHAR_BIT;
int sd = N - __builtin_clzll(a); // number of significant digits
int e = sd - 1; // exponent
if (sd > DBL_MANT_DIG) {
// start: 0000000000000000000001xxxxxxxxxxxxxxxxxxxxxxPQxxxxxxxxxxxxxxxxxx
// finish: 000000000000000000000000000000000000001xxxxxxxxxxxxxxxxxxxxxxPQR
// 12345678901234567890123456
// 1 = msb 1 bit
// P = bit DBL_MANT_DIG-1 bits to the right of 1
// Q = bit DBL_MANT_DIG bits to the right of 1
// R = "or" of all bits to the right of Q
switch (sd) {
case DBL_MANT_DIG + 1:
a <<= 1;
break;
case DBL_MANT_DIG + 2:
break;
default:
a = (a >> (sd - (DBL_MANT_DIG + 2))) |
((a & ((du_int)(-1) >> ((N + DBL_MANT_DIG + 2) - sd))) != 0);
};
// finish:
a |= (a & 4) != 0; // Or P into R
++a; // round - this step may add a significant bit
a >>= 2; // dump Q and R
// a is now rounded to DBL_MANT_DIG or DBL_MANT_DIG+1 bits
if (a & ((du_int)1 << DBL_MANT_DIG)) {
a >>= 1;
++e;
}
// a is now rounded to DBL_MANT_DIG bits
} else {
a <<= (DBL_MANT_DIG - sd);
// a is now rounded to DBL_MANT_DIG bits
}
double_bits fb;
fb.u.s.high = ((su_int)(e + 1023) << 20) | // exponent
((su_int)(a >> 32) & 0x000FFFFF); // mantissa-high
fb.u.s.low = (su_int)a; // mantissa-low
return fb.f;
}
#define SRC_U64
#define DST_DOUBLE
#include "int_to_fp_impl.inc"

COMPILER_RT_ABI double __floatundidf(du_int a) { return __floatXiYf__(a); }
#endif

#if defined(__ARM_EABI__)
Expand Down
51 changes: 51 additions & 0 deletions compiler-rt/lib/builtins/int_to_fp.h
@@ -0,0 +1,51 @@
//===-- int_to_fp.h - integer to floating point conversion ----------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// Set source and destination defines in order to use a correctly
// parameterised floatXiYf implementation.
//
//===----------------------------------------------------------------------===//

#ifndef INT_TO_FP_H
#define INT_TO_FP_H

#include "int_lib.h"

#if defined SRC_I64
typedef int64_t src_t;
typedef uint64_t usrc_t;
static __inline int clzSrcT(usrc_t x) { return __builtin_clzll(x); }

#elif defined SRC_U64
typedef uint64_t src_t;
typedef uint64_t usrc_t;
static __inline int clzSrcT(usrc_t x) { return __builtin_clzll(x); }

#else
#error Source should be a handled integer type.
#endif

#if defined DST_DOUBLE
typedef double dst_t;
typedef uint64_t dst_rep_t;
#define DST_REP_C UINT64_C
static const int dstSigBits = 52;

#else
#error Destination should be a handled floating point type
#endif

static __inline dst_t dstFromRep(dst_rep_t x) {
const union {
dst_t f;
dst_rep_t i;
} rep = {.i = x};
return rep.f;
}

#endif // INT_TO_FP_H
69 changes: 69 additions & 0 deletions compiler-rt/lib/builtins/int_to_fp_impl.inc
@@ -0,0 +1,69 @@
//===-- int_to_fp_impl.inc - integer to floating point conversion ---------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// Thsi file implements a generic conversion from an integer type to an
// IEEE-754 floating point type, allowing a common implementation to be hsared
// without copy and paste.
//
//===----------------------------------------------------------------------===//

#include "int_to_fp.h"

static __inline dst_t __floatXiYf__(src_t a) {
if (a == 0)
return 0.0;
const int dstMantDig = dstSigBits + 1;
const int srcBits = sizeof(src_t) * CHAR_BIT;
const int srcIsSigned = ((src_t)-1) < 0;
const src_t s = srcIsSigned ? a >> (srcBits - 1) : 0;
a = (usrc_t)(a ^ s) - s;
int sd = srcBits - clzSrcT(a); // number of significant digits
int e = sd - 1; // exponent
if (sd > dstMantDig) {
// start: 0000000000000000000001xxxxxxxxxxxxxxxxxxxxxxPQxxxxxxxxxxxxxxxxxx
// finish: 000000000000000000000000000000000000001xxxxxxxxxxxxxxxxxxxxxxPQR
// 12345678901234567890123456
// 1 = msb 1 bit
// P = bit dstMantDig-1 bits to the right of 1
// Q = bit dstMantDig bits to the right of 1
// R = "or" of all bits to the right of Q
switch (sd) {
case dstMantDig + 1:
a <<= 1;
break;
case dstMantDig + 2:
break;
default:
a = ((usrc_t)a >> (sd - (dstMantDig + 2))) |
((a & ((usrc_t)(-1) >> ((srcBits + dstMantDig + 2) - sd))) != 0);
};
// finish:
a |= (a & 4) != 0; // Or P into R
++a; // round - this step may add a significant bit
a >>= 2; // dump Q and R
// a is now rounded to dstMantDig or dstMantDig+1 bits
if (a & ((usrc_t)1 << dstMantDig)) {
a >>= 1;
++e;
}
// a is now rounded to dstMantDig bits
} else {
a <<= (dstMantDig - sd);
// a is now rounded to dstMantDig bits
}
const int dstBits = sizeof(dst_t) * CHAR_BIT;
const dst_rep_t dstSignMask = DST_REP_C(1) << (dstBits - 1);
const int dstExpBits = dstBits - dstSigBits - 1;
const int dstExpBias = (1 << (dstExpBits - 1)) - 1;
const dst_rep_t dstSignificandMask = (DST_REP_C(1) << dstSigBits) - 1;
// Combine sign, exponent, and mantissa.
const dst_rep_t result = ((dst_rep_t)s & dstSignMask) |
((dst_rep_t)(e + dstExpBias) << dstSigBits) |
((dst_rep_t)(a) & dstSignificandMask);
return dstFromRep(result);
}

4 comments on commit 6dfea56

@kazutakahirata
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm getting the following warning:

compiler-rt/lib/builtins/int_to_fp_impl.inc:36:10: error: expression is not an integer constant expression; folding it to a constant is a GNU extension [-Werror,-Wgnu-folding-constant]
    case dstMantDig + 1:
         ^~~~~~~~~~~~~~

Is there any way you could take a look? Thanks in advance!

@kazutakahirata
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed with 781424c.

@itf
Copy link
Contributor

@itf itf commented on 6dfea56 Oct 19, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am getting the warning:

/int_to_fp_impl.inc:60:29: error: shift count is negative [-Werror,-Wshift-count-negative]
   60 |   const int dstExpBias = (1 << (dstExpBits - 1)) - 1;
      |                             ^  ~~~~~~~~~~~~~~~~
1 error generated.

@asb

@itf
Copy link
Contributor

@itf itf commented on 6dfea56 Oct 19, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

to reproduce:

clang -nostdlibinc compiler-rt/lib/builtins/floatuntitf.c -fsyntax-only -target arm64-apple-ios11.0-simulator

Please sign in to comment.