[compiler-rt] [builtins] Start to refactor int to fp conversion functions to use a common implementation (PR #66903)

Alex Bradbury via llvm-commits llvm-commits at lists.llvm.org
Tue Sep 26 21:48:08 PDT 2023


https://github.com/asb updated https://github.com/llvm/llvm-project/pull/66903

>From 9730c2e92bccdd2464d28956302da93dbd35e161 Mon Sep 17 00:00:00 2001
From: Alex Bradbury <asb at igalia.com>
Date: Wed, 20 Sep 2023 13:41:56 +0100
Subject: [PATCH] [compiler-rt] Start to refactor int to fp conversion
 functions to use a common implementation

After this path, 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 welcome feedback on the approach taken here, as well as the best way
to move forward to land it. 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.
---
 compiler-rt/lib/builtins/floatdidf.c        | 52 ++--------------
 compiler-rt/lib/builtins/floatundidf.c      | 49 ++-------------
 compiler-rt/lib/builtins/int_to_fp.h        | 49 +++++++++++++++
 compiler-rt/lib/builtins/int_to_fp_impl.inc | 69 +++++++++++++++++++++
 4 files changed, 128 insertions(+), 91 deletions(-)
 create mode 100644 compiler-rt/lib/builtins/int_to_fp.h
 create mode 100644 compiler-rt/lib/builtins/int_to_fp_impl.inc

diff --git a/compiler-rt/lib/builtins/floatdidf.c b/compiler-rt/lib/builtins/floatdidf.c
index c994aad3f079ef3..6da81f7a05bf233 100644
--- a/compiler-rt/lib/builtins/floatdidf.c
+++ b/compiler-rt/lib/builtins/floatdidf.c
@@ -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__)
diff --git a/compiler-rt/lib/builtins/floatundidf.c b/compiler-rt/lib/builtins/floatundidf.c
index 2ec802cdc134ff0..9743e96ec67912a 100644
--- a/compiler-rt/lib/builtins/floatundidf.c
+++ b/compiler-rt/lib/builtins/floatundidf.c
@@ -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__)
diff --git a/compiler-rt/lib/builtins/int_to_fp.h b/compiler-rt/lib/builtins/int_to_fp.h
new file mode 100644
index 000000000000000..67cea0804252ca6
--- /dev/null
+++ b/compiler-rt/lib/builtins/int_to_fp.h
@@ -0,0 +1,49 @@
+//===-- 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;
+
+#elif defined SRC_U64
+typedef uint64_t src_t;
+typedef uint64_t usrc_t;
+
+#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
diff --git a/compiler-rt/lib/builtins/int_to_fp_impl.inc b/compiler-rt/lib/builtins/int_to_fp_impl.inc
new file mode 100644
index 000000000000000..01c4d1f32cd8eed
--- /dev/null
+++ b/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 - __builtin_clzll(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);
+}



More information about the llvm-commits mailing list