mirror of
https://github.com/intel/llvm.git
synced 2026-01-21 04:14:03 +08:00
[compiler-rt] Implement __extendxftf2 and __trunctfxf2 for x86_64 (#66918)
This patch implements __extendxftf2 (long double -> f128) and __trunctfxf2 (f128 -> long double) on x86_64. This is a preparation to unblock https://reviews.llvm.org/D53608, We intentionally do not modify compiler-rt/lib/builtins/fp_lib.h in this PR (in particular, to limit the scope and avoid exposing other functions on X86_64 in this PR). Instead, TODOs were added to use fp_lib.h once it is available. Test plan: 1. ninja check-compiler-rt (verified on X86_64 and on Aarch64) In particular, new tests (extendxftf2_test.c and trunctfxf2_test.c) were added. 2. compared the results of conversions with what other compilers (gcc) produce.
This commit is contained in:
committed by
GitHub
parent
e35cb730cf
commit
910a4bf5b7
@@ -280,6 +280,7 @@ endif ()
|
||||
# long double is not 80 bits on Android or MSVC.
|
||||
set(x86_80_BIT_SOURCES
|
||||
divxc3.c
|
||||
extendxftf2.c
|
||||
fixxfdi.c
|
||||
fixxfti.c
|
||||
fixunsxfdi.c
|
||||
@@ -291,6 +292,7 @@ set(x86_80_BIT_SOURCES
|
||||
floatuntixf.c
|
||||
mulxc3.c
|
||||
powixf2.c
|
||||
trunctfxf2.c
|
||||
)
|
||||
|
||||
if (NOT MSVC)
|
||||
|
||||
23
compiler-rt/lib/builtins/extendxftf2.c
Normal file
23
compiler-rt/lib/builtins/extendxftf2.c
Normal file
@@ -0,0 +1,23 @@
|
||||
//===-- lib/extendxftf2.c - long double -> quad conversion --------*- C -*-===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// Assumption: long double is a IEEE 80 bit floating point type padded to 128
|
||||
// bits.
|
||||
|
||||
// TODO: use fp_lib.h once QUAD_PRECISION is available on x86_64.
|
||||
#if __LDBL_MANT_DIG__ == 64 && defined(__x86_64__) && \
|
||||
(defined(__FLOAT128__) || defined(__SIZEOF_FLOAT128__))
|
||||
#define SRC_80
|
||||
#define DST_QUAD
|
||||
#include "fp_extend_impl.inc"
|
||||
|
||||
COMPILER_RT_ABI __float128 __extendxftf2(long double a) {
|
||||
return __extendXfYf2__(a);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -20,15 +20,22 @@
|
||||
typedef float src_t;
|
||||
typedef uint32_t src_rep_t;
|
||||
#define SRC_REP_C UINT32_C
|
||||
static const int srcSigBits = 23;
|
||||
static const int srcBits = sizeof(src_t) * CHAR_BIT;
|
||||
static const int srcSigFracBits = 23;
|
||||
// -1 accounts for the sign bit.
|
||||
static const int srcExpBits = srcBits - srcSigFracBits - 1;
|
||||
#define src_rep_t_clz clzsi
|
||||
|
||||
#elif defined SRC_DOUBLE
|
||||
typedef double src_t;
|
||||
typedef uint64_t src_rep_t;
|
||||
#define SRC_REP_C UINT64_C
|
||||
static const int srcSigBits = 52;
|
||||
static __inline int src_rep_t_clz(src_rep_t a) {
|
||||
static const int srcBits = sizeof(src_t) * CHAR_BIT;
|
||||
static const int srcSigFracBits = 52;
|
||||
// -1 accounts for the sign bit.
|
||||
static const int srcExpBits = srcBits - srcSigFracBits - 1;
|
||||
|
||||
static inline int src_rep_t_clz_impl(src_rep_t a) {
|
||||
#if defined __LP64__
|
||||
return __builtin_clzl(a);
|
||||
#else
|
||||
@@ -38,6 +45,18 @@ static __inline int src_rep_t_clz(src_rep_t a) {
|
||||
return 32 + clzsi(a & REP_C(0xffffffff));
|
||||
#endif
|
||||
}
|
||||
#define src_rep_t_clz src_rep_t_clz_impl
|
||||
|
||||
#elif defined SRC_80
|
||||
typedef long double src_t;
|
||||
typedef __uint128_t src_rep_t;
|
||||
#define SRC_REP_C (__uint128_t)
|
||||
// sign bit, exponent and significand occupy the lower 80 bits.
|
||||
static const int srcBits = 80;
|
||||
static const int srcSigFracBits = 63;
|
||||
// -1 accounts for the sign bit.
|
||||
// -1 accounts for the explicitly stored integer bit.
|
||||
static const int srcExpBits = srcBits - srcSigFracBits - 1 - 1;
|
||||
|
||||
#elif defined SRC_HALF
|
||||
#ifdef COMPILER_RT_HAS_FLOAT16
|
||||
@@ -47,7 +66,11 @@ typedef uint16_t src_t;
|
||||
#endif
|
||||
typedef uint16_t src_rep_t;
|
||||
#define SRC_REP_C UINT16_C
|
||||
static const int srcSigBits = 10;
|
||||
static const int srcBits = sizeof(src_t) * CHAR_BIT;
|
||||
static const int srcSigFracBits = 10;
|
||||
// -1 accounts for the sign bit.
|
||||
static const int srcExpBits = srcBits - srcSigFracBits - 1;
|
||||
|
||||
#define src_rep_t_clz __builtin_clz
|
||||
|
||||
#else
|
||||
@@ -58,28 +81,75 @@ static const int srcSigBits = 10;
|
||||
typedef float dst_t;
|
||||
typedef uint32_t dst_rep_t;
|
||||
#define DST_REP_C UINT32_C
|
||||
static const int dstSigBits = 23;
|
||||
static const int dstBits = sizeof(dst_t) * CHAR_BIT;
|
||||
static const int dstSigFracBits = 23;
|
||||
// -1 accounts for the sign bit.
|
||||
static const int dstExpBits = dstBits - dstSigFracBits - 1;
|
||||
|
||||
#elif defined DST_DOUBLE
|
||||
typedef double dst_t;
|
||||
typedef uint64_t dst_rep_t;
|
||||
#define DST_REP_C UINT64_C
|
||||
static const int dstSigBits = 52;
|
||||
static const int dstBits = sizeof(dst_t) * CHAR_BIT;
|
||||
static const int dstSigFracBits = 52;
|
||||
// -1 accounts for the sign bit.
|
||||
static const int dstExpBits = dstBits - dstSigFracBits - 1;
|
||||
|
||||
#elif defined DST_QUAD
|
||||
// TODO: use fp_lib.h once QUAD_PRECISION is available on x86_64.
|
||||
#if __LDBL_MANT_DIG__ == 113
|
||||
typedef long double dst_t;
|
||||
#elif defined(__x86_64__) && \
|
||||
(defined(__FLOAT128__) || defined(__SIZEOF_FLOAT128__))
|
||||
typedef __float128 dst_t;
|
||||
#endif
|
||||
typedef __uint128_t dst_rep_t;
|
||||
#define DST_REP_C (__uint128_t)
|
||||
static const int dstSigBits = 112;
|
||||
static const int dstBits = sizeof(dst_t) * CHAR_BIT;
|
||||
static const int dstSigFracBits = 112;
|
||||
// -1 accounts for the sign bit.
|
||||
static const int dstExpBits = dstBits - dstSigFracBits - 1;
|
||||
|
||||
#else
|
||||
#error Destination should be single, double, or quad precision!
|
||||
#endif // end destination precision
|
||||
|
||||
// End of specialization parameters. Two helper routines for conversion to and
|
||||
// from the representation of floating-point data as integer values follow.
|
||||
// End of specialization parameters.
|
||||
|
||||
static __inline src_rep_t srcToRep(src_t x) {
|
||||
// TODO: These helper routines should be placed into fp_lib.h
|
||||
// Currently they depend on macros/constants defined above.
|
||||
|
||||
static inline src_rep_t extract_sign_from_src(src_rep_t x) {
|
||||
const src_rep_t srcSignMask = SRC_REP_C(1) << (srcBits - 1);
|
||||
return (x & srcSignMask) >> (srcBits - 1);
|
||||
}
|
||||
|
||||
static inline src_rep_t extract_exp_from_src(src_rep_t x) {
|
||||
const int srcSigBits = srcBits - 1 - srcExpBits;
|
||||
const src_rep_t srcExpMask = ((SRC_REP_C(1) << srcExpBits) - 1) << srcSigBits;
|
||||
return (x & srcExpMask) >> srcSigBits;
|
||||
}
|
||||
|
||||
static inline src_rep_t extract_sig_frac_from_src(src_rep_t x) {
|
||||
const src_rep_t srcSigFracMask = (SRC_REP_C(1) << srcSigFracBits) - 1;
|
||||
return x & srcSigFracMask;
|
||||
}
|
||||
|
||||
#ifdef src_rep_t_clz
|
||||
static inline int clz_in_sig_frac(src_rep_t sigFrac) {
|
||||
const int skip = (sizeof(dst_t) * CHAR_BIT - srcBits) + 1 + srcExpBits;
|
||||
return src_rep_t_clz(sigFrac) - skip;
|
||||
}
|
||||
#endif
|
||||
|
||||
static inline dst_rep_t construct_dst_rep(dst_rep_t sign, dst_rep_t exp, dst_rep_t sigFrac) {
|
||||
return (sign << (dstBits - 1)) | (exp << (dstBits - 1 - dstExpBits)) | sigFrac;
|
||||
}
|
||||
|
||||
// Two helper routines for conversion to and from the representation of
|
||||
// floating-point data as integer values follow.
|
||||
|
||||
static inline src_rep_t srcToRep(src_t x) {
|
||||
const union {
|
||||
src_t f;
|
||||
src_rep_t i;
|
||||
@@ -87,7 +157,7 @@ static __inline src_rep_t srcToRep(src_t x) {
|
||||
return rep.i;
|
||||
}
|
||||
|
||||
static __inline dst_t dstFromRep(dst_rep_t x) {
|
||||
static inline dst_t dstFromRep(dst_rep_t x) {
|
||||
const union {
|
||||
dst_t f;
|
||||
dst_rep_t i;
|
||||
|
||||
@@ -37,71 +37,72 @@
|
||||
|
||||
#include "fp_extend.h"
|
||||
|
||||
// The source type may use a usual IEEE-754 interchange format or Intel 80-bit
|
||||
// format. In particular, for the source type srcSigFracBits may be not equal to
|
||||
// srcSigBits. The destination type is assumed to be one of IEEE-754 standard
|
||||
// types.
|
||||
static __inline dst_t __extendXfYf2__(src_t a) {
|
||||
// Various constants whose values follow from the type parameters.
|
||||
// Any reasonable optimizer will fold and propagate all of these.
|
||||
const int srcBits = sizeof(src_t) * CHAR_BIT;
|
||||
const int srcExpBits = srcBits - srcSigBits - 1;
|
||||
const int srcInfExp = (1 << srcExpBits) - 1;
|
||||
const int srcExpBias = srcInfExp >> 1;
|
||||
|
||||
const src_rep_t srcMinNormal = SRC_REP_C(1) << srcSigBits;
|
||||
const src_rep_t srcInfinity = (src_rep_t)srcInfExp << srcSigBits;
|
||||
const src_rep_t srcSignMask = SRC_REP_C(1) << (srcSigBits + srcExpBits);
|
||||
const src_rep_t srcAbsMask = srcSignMask - 1;
|
||||
const src_rep_t srcQNaN = SRC_REP_C(1) << (srcSigBits - 1);
|
||||
const src_rep_t srcNaNCode = srcQNaN - 1;
|
||||
|
||||
const int dstBits = sizeof(dst_t) * CHAR_BIT;
|
||||
const int dstExpBits = dstBits - dstSigBits - 1;
|
||||
const int dstInfExp = (1 << dstExpBits) - 1;
|
||||
const int dstExpBias = dstInfExp >> 1;
|
||||
|
||||
const dst_rep_t dstMinNormal = DST_REP_C(1) << dstSigBits;
|
||||
|
||||
// Break a into a sign and representation of the absolute value.
|
||||
const src_rep_t aRep = srcToRep(a);
|
||||
const src_rep_t aAbs = aRep & srcAbsMask;
|
||||
const src_rep_t sign = aRep & srcSignMask;
|
||||
dst_rep_t absResult;
|
||||
const src_rep_t srcSign = extract_sign_from_src(aRep);
|
||||
const src_rep_t srcExp = extract_exp_from_src(aRep);
|
||||
const src_rep_t srcSigFrac = extract_sig_frac_from_src(aRep);
|
||||
|
||||
// If sizeof(src_rep_t) < sizeof(int), the subtraction result is promoted
|
||||
// to (signed) int. To avoid that, explicitly cast to src_rep_t.
|
||||
if ((src_rep_t)(aAbs - srcMinNormal) < srcInfinity - srcMinNormal) {
|
||||
dst_rep_t dstSign = srcSign;
|
||||
dst_rep_t dstExp;
|
||||
dst_rep_t dstSigFrac;
|
||||
|
||||
if (srcExp >= 1 && srcExp < srcInfExp) {
|
||||
// a is a normal number.
|
||||
// Extend to the destination type by shifting the significand and
|
||||
// exponent into the proper position and rebiasing the exponent.
|
||||
absResult = (dst_rep_t)aAbs << (dstSigBits - srcSigBits);
|
||||
absResult += (dst_rep_t)(dstExpBias - srcExpBias) << dstSigBits;
|
||||
dstExp = (dst_rep_t)srcExp + (dst_rep_t)(dstExpBias - srcExpBias);
|
||||
dstSigFrac = (dst_rep_t)srcSigFrac << (dstSigFracBits - srcSigFracBits);
|
||||
}
|
||||
|
||||
else if (aAbs >= srcInfinity) {
|
||||
else if (srcExp == srcInfExp) {
|
||||
// a is NaN or infinity.
|
||||
// Conjure the result by beginning with infinity, then setting the qNaN
|
||||
// bit (if needed) and right-aligning the rest of the trailing NaN
|
||||
// payload field.
|
||||
absResult = (dst_rep_t)dstInfExp << dstSigBits;
|
||||
absResult |= (dst_rep_t)(aAbs & srcQNaN) << (dstSigBits - srcSigBits);
|
||||
absResult |= (dst_rep_t)(aAbs & srcNaNCode) << (dstSigBits - srcSigBits);
|
||||
dstExp = dstInfExp;
|
||||
dstSigFrac = (dst_rep_t)srcSigFrac << (dstSigFracBits - srcSigFracBits);
|
||||
}
|
||||
|
||||
else if (aAbs) {
|
||||
else if (srcSigFrac) {
|
||||
// a is denormal.
|
||||
// renormalize the significand and clear the leading bit, then insert
|
||||
// the correct adjusted exponent in the destination type.
|
||||
const int scale = src_rep_t_clz(aAbs) - src_rep_t_clz(srcMinNormal);
|
||||
absResult = (dst_rep_t)aAbs << (dstSigBits - srcSigBits + scale);
|
||||
absResult ^= dstMinNormal;
|
||||
const int resultExponent = dstExpBias - srcExpBias - scale + 1;
|
||||
absResult |= (dst_rep_t)resultExponent << dstSigBits;
|
||||
if (srcExpBits == dstExpBits) {
|
||||
// The exponent fields are identical and this is a denormal number, so all
|
||||
// the non-significand bits are zero. In particular, this branch is always
|
||||
// taken when we extend a denormal F80 to F128.
|
||||
dstExp = 0;
|
||||
dstSigFrac = ((dst_rep_t)srcSigFrac) << (dstSigFracBits - srcSigFracBits);
|
||||
} else {
|
||||
#ifndef src_rep_t_clz
|
||||
// If src_rep_t_clz is not defined this branch must be unreachable.
|
||||
__builtin_unreachable();
|
||||
#else
|
||||
// Renormalize the significand and clear the leading bit.
|
||||
// For F80 -> F128 this codepath is unused.
|
||||
const int scale = clz_in_sig_frac(srcSigFrac) + 1;
|
||||
dstExp = dstExpBias - srcExpBias - scale + 1;
|
||||
dstSigFrac = (dst_rep_t)srcSigFrac
|
||||
<< (dstSigFracBits - srcSigFracBits + scale);
|
||||
const dst_rep_t dstMinNormal = DST_REP_C(1) << (dstBits - 1 - dstExpBits);
|
||||
dstSigFrac ^= dstMinNormal;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
else {
|
||||
// a is zero.
|
||||
absResult = 0;
|
||||
dstExp = 0;
|
||||
dstSigFrac = 0;
|
||||
}
|
||||
|
||||
// Apply the signbit to the absolute value.
|
||||
const dst_rep_t result = absResult | (dst_rep_t)sign << (dstBits - srcBits);
|
||||
const dst_rep_t result = construct_dst_rep(dstSign, dstExp, dstSigFrac);
|
||||
return dstFromRep(result);
|
||||
}
|
||||
|
||||
@@ -19,19 +19,34 @@
|
||||
typedef float src_t;
|
||||
typedef uint32_t src_rep_t;
|
||||
#define SRC_REP_C UINT32_C
|
||||
static const int srcSigBits = 23;
|
||||
static const int srcBits = sizeof(src_t) * CHAR_BIT;
|
||||
static const int srcSigFracBits = 23;
|
||||
// -1 accounts for the sign bit.
|
||||
static const int srcExpBits = srcBits - srcSigFracBits - 1;
|
||||
|
||||
#elif defined SRC_DOUBLE
|
||||
typedef double src_t;
|
||||
typedef uint64_t src_rep_t;
|
||||
#define SRC_REP_C UINT64_C
|
||||
static const int srcSigBits = 52;
|
||||
static const int srcBits = sizeof(src_t) * CHAR_BIT;
|
||||
static const int srcSigFracBits = 52;
|
||||
// -1 accounts for the sign bit.
|
||||
static const int srcExpBits = srcBits - srcSigFracBits - 1;
|
||||
|
||||
#elif defined SRC_QUAD
|
||||
// TODO: use fp_lib.h once QUAD_PRECISION is available on x86_64.
|
||||
#if __LDBL_MANT_DIG__ == 113
|
||||
typedef long double src_t;
|
||||
#elif defined(__x86_64__) && \
|
||||
(defined(__FLOAT128__) || defined(__SIZEOF_FLOAT128__))
|
||||
typedef __float128 src_t;
|
||||
#endif
|
||||
typedef __uint128_t src_rep_t;
|
||||
#define SRC_REP_C (__uint128_t)
|
||||
static const int srcSigBits = 112;
|
||||
static const int srcBits = sizeof(src_t) * CHAR_BIT;
|
||||
static const int srcSigFracBits = 112;
|
||||
// -1 accounts for the sign bit.
|
||||
static const int srcExpBits = srcBits - srcSigFracBits - 1;
|
||||
|
||||
#else
|
||||
#error Source should be double precision or quad precision!
|
||||
@@ -41,13 +56,29 @@ static const int srcSigBits = 112;
|
||||
typedef double dst_t;
|
||||
typedef uint64_t dst_rep_t;
|
||||
#define DST_REP_C UINT64_C
|
||||
static const int dstSigBits = 52;
|
||||
static const int dstBits = sizeof(dst_t) * CHAR_BIT;
|
||||
static const int dstSigFracBits = 52;
|
||||
// -1 accounts for the sign bit.
|
||||
static const int dstExpBits = dstBits - dstSigFracBits - 1;
|
||||
|
||||
#elif defined DST_80
|
||||
typedef long double dst_t;
|
||||
typedef __uint128_t dst_rep_t;
|
||||
#define DST_REP_C (__uint128_t)
|
||||
static const int dstBits = 80;
|
||||
static const int dstSigFracBits = 63;
|
||||
// -1 accounts for the sign bit.
|
||||
// -1 accounts for the explicitly stored integer bit.
|
||||
static const int dstExpBits = dstBits - dstSigFracBits - 1 - 1;
|
||||
|
||||
#elif defined DST_SINGLE
|
||||
typedef float dst_t;
|
||||
typedef uint32_t dst_rep_t;
|
||||
#define DST_REP_C UINT32_C
|
||||
static const int dstSigBits = 23;
|
||||
static const int dstBits = sizeof(dst_t) * CHAR_BIT;
|
||||
static const int dstSigFracBits = 23;
|
||||
// -1 accounts for the sign bit.
|
||||
static const int dstExpBits = dstBits - dstSigFracBits - 1;
|
||||
|
||||
#elif defined DST_HALF
|
||||
#ifdef COMPILER_RT_HAS_FLOAT16
|
||||
@@ -57,22 +88,56 @@ typedef uint16_t dst_t;
|
||||
#endif
|
||||
typedef uint16_t dst_rep_t;
|
||||
#define DST_REP_C UINT16_C
|
||||
static const int dstSigBits = 10;
|
||||
static const int dstBits = sizeof(dst_t) * CHAR_BIT;
|
||||
static const int dstSigFracBits = 10;
|
||||
// -1 accounts for the sign bit.
|
||||
static const int dstExpBits = dstBits - dstSigFracBits - 1;
|
||||
|
||||
#elif defined DST_BFLOAT
|
||||
typedef __bf16 dst_t;
|
||||
typedef uint16_t dst_rep_t;
|
||||
#define DST_REP_C UINT16_C
|
||||
static const int dstSigBits = 7;
|
||||
static const int dstBits = sizeof(dst_t) * CHAR_BIT;
|
||||
static const int dstSigFracBits = 7;
|
||||
// -1 accounts for the sign bit.
|
||||
static const int dstExpBits = dstBits - dstSigFracBits - 1;
|
||||
|
||||
#else
|
||||
#error Destination should be single precision or double precision!
|
||||
#endif // end destination precision
|
||||
|
||||
// TODO: These helper routines should be placed into fp_lib.h
|
||||
// Currently they depend on macros/constants defined above.
|
||||
|
||||
static inline src_rep_t extract_sign_from_src(src_rep_t x) {
|
||||
const src_rep_t srcSignMask = SRC_REP_C(1) << (srcBits - 1);
|
||||
return (x & srcSignMask) >> (srcBits - 1);
|
||||
}
|
||||
|
||||
static inline src_rep_t extract_exp_from_src(src_rep_t x) {
|
||||
const int srcSigBits = srcBits - 1 - srcExpBits;
|
||||
const src_rep_t srcExpMask = ((SRC_REP_C(1) << srcExpBits) - 1) << srcSigBits;
|
||||
return (x & srcExpMask) >> srcSigBits;
|
||||
}
|
||||
|
||||
static inline src_rep_t extract_sig_frac_from_src(src_rep_t x) {
|
||||
const src_rep_t srcSigFracMask = (SRC_REP_C(1) << srcSigFracBits) - 1;
|
||||
return x & srcSigFracMask;
|
||||
}
|
||||
|
||||
static inline dst_rep_t construct_dst_rep(dst_rep_t sign, dst_rep_t exp, dst_rep_t sigFrac) {
|
||||
dst_rep_t result = (sign << (dstBits - 1)) | (exp << (dstBits - 1 - dstExpBits)) | sigFrac;
|
||||
// Set the explicit integer bit in F80 if present.
|
||||
if (dstBits == 80 && exp) {
|
||||
result |= (DST_REP_C(1) << dstSigFracBits);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// End of specialization parameters. Two helper routines for conversion to and
|
||||
// from the representation of floating-point data as integer values follow.
|
||||
|
||||
static __inline src_rep_t srcToRep(src_t x) {
|
||||
static inline src_rep_t srcToRep(src_t x) {
|
||||
const union {
|
||||
src_t f;
|
||||
src_rep_t i;
|
||||
@@ -80,7 +145,7 @@ static __inline src_rep_t srcToRep(src_t x) {
|
||||
return rep.i;
|
||||
}
|
||||
|
||||
static __inline dst_t dstFromRep(dst_rep_t x) {
|
||||
static inline dst_t dstFromRep(dst_rep_t x) {
|
||||
const union {
|
||||
dst_t f;
|
||||
dst_rep_t i;
|
||||
|
||||
@@ -38,102 +38,118 @@
|
||||
|
||||
#include "fp_trunc.h"
|
||||
|
||||
// The destination type may use a usual IEEE-754 interchange format or Intel
|
||||
// 80-bit format. In particular, for the destination type dstSigFracBits may be
|
||||
// not equal to dstSigBits. The source type is assumed to be one of IEEE-754
|
||||
// standard types.
|
||||
static __inline dst_t __truncXfYf2__(src_t a) {
|
||||
// Various constants whose values follow from the type parameters.
|
||||
// Any reasonable optimizer will fold and propagate all of these.
|
||||
const int srcBits = sizeof(src_t) * CHAR_BIT;
|
||||
const int srcExpBits = srcBits - srcSigBits - 1;
|
||||
const int srcInfExp = (1 << srcExpBits) - 1;
|
||||
const int srcExpBias = srcInfExp >> 1;
|
||||
|
||||
const src_rep_t srcMinNormal = SRC_REP_C(1) << srcSigBits;
|
||||
const src_rep_t srcSignificandMask = srcMinNormal - 1;
|
||||
const src_rep_t srcInfinity = (src_rep_t)srcInfExp << srcSigBits;
|
||||
const src_rep_t srcSignMask = SRC_REP_C(1) << (srcSigBits + srcExpBits);
|
||||
const src_rep_t srcAbsMask = srcSignMask - 1;
|
||||
const src_rep_t roundMask = (SRC_REP_C(1) << (srcSigBits - dstSigBits)) - 1;
|
||||
const src_rep_t halfway = SRC_REP_C(1) << (srcSigBits - dstSigBits - 1);
|
||||
const src_rep_t srcQNaN = SRC_REP_C(1) << (srcSigBits - 1);
|
||||
const src_rep_t srcMinNormal = SRC_REP_C(1) << srcSigFracBits;
|
||||
const src_rep_t roundMask =
|
||||
(SRC_REP_C(1) << (srcSigFracBits - dstSigFracBits)) - 1;
|
||||
const src_rep_t halfway = SRC_REP_C(1)
|
||||
<< (srcSigFracBits - dstSigFracBits - 1);
|
||||
const src_rep_t srcQNaN = SRC_REP_C(1) << (srcSigFracBits - 1);
|
||||
const src_rep_t srcNaNCode = srcQNaN - 1;
|
||||
|
||||
const int dstBits = sizeof(dst_t) * CHAR_BIT;
|
||||
const int dstExpBits = dstBits - dstSigBits - 1;
|
||||
const int dstInfExp = (1 << dstExpBits) - 1;
|
||||
const int dstExpBias = dstInfExp >> 1;
|
||||
|
||||
const int underflowExponent = srcExpBias + 1 - dstExpBias;
|
||||
const int overflowExponent = srcExpBias + dstInfExp - dstExpBias;
|
||||
const src_rep_t underflow = (src_rep_t)underflowExponent << srcSigBits;
|
||||
const src_rep_t overflow = (src_rep_t)overflowExponent << srcSigBits;
|
||||
|
||||
const dst_rep_t dstQNaN = DST_REP_C(1) << (dstSigBits - 1);
|
||||
const dst_rep_t dstQNaN = DST_REP_C(1) << (dstSigFracBits - 1);
|
||||
const dst_rep_t dstNaNCode = dstQNaN - 1;
|
||||
|
||||
// Break a into a sign and representation of the absolute value.
|
||||
const src_rep_t aRep = srcToRep(a);
|
||||
const src_rep_t aAbs = aRep & srcAbsMask;
|
||||
const src_rep_t sign = aRep & srcSignMask;
|
||||
dst_rep_t absResult;
|
||||
const src_rep_t srcSign = extract_sign_from_src(aRep);
|
||||
const src_rep_t srcExp = extract_exp_from_src(aRep);
|
||||
const src_rep_t srcSigFrac = extract_sig_frac_from_src(aRep);
|
||||
|
||||
const int tailBits = srcBits - dstBits;
|
||||
if (srcExpBits == dstExpBits && ((aRep >> tailBits) << tailBits) == aRep) {
|
||||
// Same size exponents and a's significand tail is 0. Remove tail.
|
||||
dst_rep_t result = aRep >> tailBits;
|
||||
return dstFromRep(result);
|
||||
dst_rep_t dstSign = srcSign;
|
||||
dst_rep_t dstExp;
|
||||
dst_rep_t dstSigFrac;
|
||||
|
||||
// Same size exponents and a's significand tail is 0.
|
||||
// The significand can be truncated and the exponent can be copied over.
|
||||
const int sigFracTailBits = srcSigFracBits - dstSigFracBits;
|
||||
if (srcExpBits == dstExpBits &&
|
||||
((aRep >> sigFracTailBits) << sigFracTailBits) == aRep) {
|
||||
dstExp = srcExp;
|
||||
dstSigFrac = (dst_rep_t)(srcSigFrac >> sigFracTailBits);
|
||||
return dstFromRep(construct_dst_rep(dstSign, dstExp, dstSigFrac));
|
||||
}
|
||||
|
||||
if (aAbs - underflow < aAbs - overflow) {
|
||||
const int dstExpCandidate = ((int)srcExp - srcExpBias) + dstExpBias;
|
||||
if (dstExpCandidate >= 1 && dstExpCandidate < dstInfExp) {
|
||||
// The exponent of a is within the range of normal numbers in the
|
||||
// destination format. We can convert by simply right-shifting with
|
||||
// destination format. We can convert by simply right-shifting with
|
||||
// rounding and adjusting the exponent.
|
||||
absResult = aAbs >> (srcSigBits - dstSigBits);
|
||||
absResult -= (dst_rep_t)(srcExpBias - dstExpBias) << dstSigBits;
|
||||
dstExp = dstExpCandidate;
|
||||
dstSigFrac = (dst_rep_t)(srcSigFrac >> sigFracTailBits);
|
||||
|
||||
const src_rep_t roundBits = aAbs & roundMask;
|
||||
const src_rep_t roundBits = srcSigFrac & roundMask;
|
||||
// Round to nearest.
|
||||
if (roundBits > halfway)
|
||||
absResult++;
|
||||
dstSigFrac++;
|
||||
// Tie to even.
|
||||
else if (roundBits == halfway)
|
||||
absResult += absResult & 1;
|
||||
} else if (aAbs > srcInfinity) {
|
||||
dstSigFrac += dstSigFrac & 1;
|
||||
|
||||
// Rounding has changed the exponent.
|
||||
if (dstSigFrac >= (DST_REP_C(1) << dstSigFracBits)) {
|
||||
dstExp += 1;
|
||||
dstSigFrac ^= (DST_REP_C(1) << dstSigFracBits);
|
||||
}
|
||||
} else if (srcExp == srcInfExp && srcSigFrac) {
|
||||
// a is NaN.
|
||||
// Conjure the result by beginning with infinity, setting the qNaN
|
||||
// bit and inserting the (truncated) trailing NaN field.
|
||||
absResult = (dst_rep_t)dstInfExp << dstSigBits;
|
||||
absResult |= dstQNaN;
|
||||
absResult |=
|
||||
((aAbs & srcNaNCode) >> (srcSigBits - dstSigBits)) & dstNaNCode;
|
||||
} else if (aAbs >= overflow) {
|
||||
// a overflows to infinity.
|
||||
absResult = (dst_rep_t)dstInfExp << dstSigBits;
|
||||
dstExp = dstInfExp;
|
||||
dstSigFrac = dstQNaN;
|
||||
dstSigFrac |= ((srcSigFrac & srcNaNCode) >> sigFracTailBits) & dstNaNCode;
|
||||
} else if ((int)srcExp >= overflowExponent) {
|
||||
dstExp = dstInfExp;
|
||||
dstSigFrac = 0;
|
||||
} else {
|
||||
// a underflows on conversion to the destination type or is an exact
|
||||
// zero. The result may be a denormal or zero. Extract the exponent
|
||||
// to get the shift amount for the denormalization.
|
||||
const int aExp = aAbs >> srcSigBits;
|
||||
const int shift = srcExpBias - dstExpBias - aExp + 1;
|
||||
src_rep_t significand = srcSigFrac;
|
||||
int shift = srcExpBias - dstExpBias - srcExp;
|
||||
|
||||
const src_rep_t significand = (aRep & srcSignificandMask) | srcMinNormal;
|
||||
if (srcExp) {
|
||||
// Set the implicit integer bit if the source is a normal number.
|
||||
significand |= srcMinNormal;
|
||||
shift += 1;
|
||||
}
|
||||
|
||||
// Right shift by the denormalization amount with sticky.
|
||||
if (shift > srcSigBits) {
|
||||
absResult = 0;
|
||||
if (shift > srcSigFracBits) {
|
||||
dstExp = 0;
|
||||
dstSigFrac = 0;
|
||||
} else {
|
||||
const bool sticky = (significand << (srcBits - shift)) != 0;
|
||||
dstExp = 0;
|
||||
const bool sticky = shift && ((significand << (srcBits - shift)) != 0);
|
||||
src_rep_t denormalizedSignificand = significand >> shift | sticky;
|
||||
absResult = denormalizedSignificand >> (srcSigBits - dstSigBits);
|
||||
dstSigFrac = denormalizedSignificand >> sigFracTailBits;
|
||||
const src_rep_t roundBits = denormalizedSignificand & roundMask;
|
||||
// Round to nearest
|
||||
if (roundBits > halfway)
|
||||
absResult++;
|
||||
dstSigFrac++;
|
||||
// Ties to even
|
||||
else if (roundBits == halfway)
|
||||
absResult += absResult & 1;
|
||||
dstSigFrac += dstSigFrac & 1;
|
||||
|
||||
// Rounding has changed the exponent.
|
||||
if (dstSigFrac >= (DST_REP_C(1) << dstSigFracBits)) {
|
||||
dstExp += 1;
|
||||
dstSigFrac ^= (DST_REP_C(1) << dstSigFracBits);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Apply the signbit to the absolute value.
|
||||
const dst_rep_t result = absResult | sign >> (srcBits - dstBits);
|
||||
return dstFromRep(result);
|
||||
return dstFromRep(construct_dst_rep(dstSign, dstExp, dstSigFrac));
|
||||
}
|
||||
|
||||
24
compiler-rt/lib/builtins/trunctfxf2.c
Normal file
24
compiler-rt/lib/builtins/trunctfxf2.c
Normal file
@@ -0,0 +1,24 @@
|
||||
//===-- lib/trunctfsf2.c - long double -> quad conversion ---------*- C -*-===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// Assumption: long double is a IEEE 80 bit floating point type padded to 128
|
||||
// bits.
|
||||
|
||||
// TODO: use fp_lib.h once QUAD_PRECISION is available on x86_64.
|
||||
#if __LDBL_MANT_DIG__ == 64 && defined(__x86_64__) && \
|
||||
(defined(__FLOAT128__) || defined(__SIZEOF_FLOAT128__))
|
||||
|
||||
#define SRC_QUAD
|
||||
#define DST_80
|
||||
#include "fp_trunc_impl.inc"
|
||||
|
||||
COMPILER_RT_ABI long double __trunctfxf2(__float128 a) {
|
||||
return __truncXfYf2__(a);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -16,7 +16,7 @@ int test__addtf3(long double a, long double b,
|
||||
uint64_t expectedHi, uint64_t expectedLo)
|
||||
{
|
||||
long double x = __addtf3(a, b);
|
||||
int ret = compareResultLD(x, expectedHi, expectedLo);
|
||||
int ret = compareResultF128(x, expectedHi, expectedLo);
|
||||
|
||||
if (ret){
|
||||
printf("error in test__addtf3(%.20Lf, %.20Lf) = %.20Lf, "
|
||||
|
||||
@@ -15,7 +15,7 @@ int test__divtf3(long double a, long double b,
|
||||
uint64_t expectedHi, uint64_t expectedLo)
|
||||
{
|
||||
long double x = __divtf3(a, b);
|
||||
int ret = compareResultLD(x, expectedHi, expectedLo);
|
||||
int ret = compareResultF128(x, expectedHi, expectedLo);
|
||||
|
||||
if (ret){
|
||||
printf("error in test__divtf3(%.20Le, %.20Le) = %.20Le, "
|
||||
|
||||
@@ -13,7 +13,7 @@ COMPILER_RT_ABI long double __extenddftf2(double a);
|
||||
int test__extenddftf2(double a, uint64_t expectedHi, uint64_t expectedLo)
|
||||
{
|
||||
long double x = __extenddftf2(a);
|
||||
int ret = compareResultLD(x, expectedHi, expectedLo);
|
||||
int ret = compareResultF128(x, expectedHi, expectedLo);
|
||||
|
||||
if (ret){
|
||||
printf("error in test__extenddftf2(%f) = %.20Lf, "
|
||||
|
||||
@@ -12,7 +12,7 @@ COMPILER_RT_ABI long double __extendhftf2(TYPE_FP16 a);
|
||||
|
||||
int test__extendhftf2(TYPE_FP16 a, uint64_t expectedHi, uint64_t expectedLo) {
|
||||
long double x = __extendhftf2(a);
|
||||
int ret = compareResultLD(x, expectedHi, expectedLo);
|
||||
int ret = compareResultF128(x, expectedHi, expectedLo);
|
||||
|
||||
if (ret) {
|
||||
printf("error in test__extendhftf2(%#.4x) = %.20Lf, "
|
||||
|
||||
@@ -13,7 +13,7 @@ COMPILER_RT_ABI long double __extendsftf2(float a);
|
||||
int test__extendsftf2(float a, uint64_t expectedHi, uint64_t expectedLo)
|
||||
{
|
||||
long double x = __extendsftf2(a);
|
||||
int ret = compareResultLD(x, expectedHi, expectedLo);
|
||||
int ret = compareResultF128(x, expectedHi, expectedLo);
|
||||
|
||||
if (ret)
|
||||
{
|
||||
|
||||
74
compiler-rt/test/builtins/Unit/extendxftf2_test.c
Normal file
74
compiler-rt/test/builtins/Unit/extendxftf2_test.c
Normal file
@@ -0,0 +1,74 @@
|
||||
// RUN: %clang_builtins %s %librt -o %t && %run %t
|
||||
// REQUIRES: librt_has_extendxftf2
|
||||
|
||||
#include "int_lib.h"
|
||||
#include <stdio.h>
|
||||
|
||||
#if __LDBL_MANT_DIG__ == 64 && defined(__x86_64__) && \
|
||||
(defined(__FLOAT128__) || defined(__SIZEOF_FLOAT128__))
|
||||
|
||||
#include "fp_test.h"
|
||||
|
||||
COMPILER_RT_ABI __float128 __extendxftf2(long double a);
|
||||
|
||||
int test__extendxftf2(long double a, uint64_t expectedHi, uint64_t expectedLo) {
|
||||
__float128 x = __extendxftf2(a);
|
||||
int ret = compareResultF128(x, expectedHi, expectedLo);
|
||||
|
||||
if (ret) {
|
||||
printf("error in __extendxftf2(%.20Lf) = %.20Lf, "
|
||||
"expected %.20Lf\n",
|
||||
a, x, fromRep128(expectedHi, expectedLo));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
char assumption_1[sizeof(long double) * CHAR_BIT == 128] = {0};
|
||||
|
||||
#endif
|
||||
|
||||
int main() {
|
||||
#if __LDBL_MANT_DIG__ == 64 && defined(__x86_64__) && \
|
||||
(defined(__FLOAT128__) || defined(__SIZEOF_FLOAT128__))
|
||||
// qNaN
|
||||
if (test__extendxftf2(makeQNaN80(), UINT64_C(0x7fff800000000000),
|
||||
UINT64_C(0x0)))
|
||||
return 1;
|
||||
// NaN
|
||||
if (test__extendxftf2(makeNaN80(UINT64_C(0x3fffffffffffffff)),
|
||||
UINT64_C(0x7fff7fffffffffff),
|
||||
UINT64_C(0xfffe000000000000)))
|
||||
return 1;
|
||||
// inf
|
||||
if (test__extendxftf2(makeInf80(), UINT64_C(0x7fff000000000000),
|
||||
UINT64_C(0x0)))
|
||||
return 1;
|
||||
// zero
|
||||
if (test__extendxftf2(0.0, UINT64_C(0x0), UINT64_C(0x0)))
|
||||
return 1;
|
||||
if (test__extendxftf2(0x1.23456789abcdefp+5, UINT64_C(0x400423456789abcd),
|
||||
UINT64_C(0xf000000000000000)))
|
||||
return 1;
|
||||
if (test__extendxftf2(0x1.edcba987654321fp-9, UINT64_C(0x3ff6edcba9876543),
|
||||
UINT64_C(0x2000000000000000)))
|
||||
return 1;
|
||||
if (test__extendxftf2(0x1.23456789abcdefp+45, UINT64_C(0x402c23456789abcd),
|
||||
UINT64_C(0xf000000000000000)))
|
||||
return 1;
|
||||
if (test__extendxftf2(0x1.edcba987654321fp-45, UINT64_C(0x3fd2edcba9876543),
|
||||
UINT64_C(0x2000000000000000)))
|
||||
return 1;
|
||||
// denormal number
|
||||
if (test__extendxftf2(1e-4932L, UINT64_C(0x00004c248f91e526),
|
||||
UINT64_C(0xafe0000000000000)))
|
||||
return 1;
|
||||
// denormal number
|
||||
if (test__extendxftf2(2e-4932L, UINT64_C(0x000098491f23ca4d),
|
||||
UINT64_C(0x5fc0000000000000)))
|
||||
return 1;
|
||||
#else
|
||||
printf("skipped\n");
|
||||
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
@@ -17,7 +17,7 @@ COMPILER_RT_ABI long double __floatditf(di_int a);
|
||||
int test__floatditf(di_int a, uint64_t expectedHi, uint64_t expectedLo)
|
||||
{
|
||||
long double x = __floatditf(a);
|
||||
int ret = compareResultLD(x, expectedHi, expectedLo);
|
||||
int ret = compareResultF128(x, expectedHi, expectedLo);
|
||||
|
||||
if (ret)
|
||||
printf("error in __floatditf(%Ld) = %.20Lf, "
|
||||
|
||||
@@ -13,7 +13,7 @@ COMPILER_RT_ABI long double __floatsitf(si_int a);
|
||||
int test__floatsitf(si_int a, uint64_t expectedHi, uint64_t expectedLo)
|
||||
{
|
||||
long double x = __floatsitf(a);
|
||||
int ret = compareResultLD(x, expectedHi, expectedLo);
|
||||
int ret = compareResultF128(x, expectedHi, expectedLo);
|
||||
|
||||
if (ret)
|
||||
{
|
||||
|
||||
@@ -17,7 +17,7 @@ COMPILER_RT_ABI long double __floatunditf(du_int a);
|
||||
int test__floatunditf(du_int a, uint64_t expectedHi, uint64_t expectedLo)
|
||||
{
|
||||
long double x = __floatunditf(a);
|
||||
int ret = compareResultLD(x, expectedHi, expectedLo);
|
||||
int ret = compareResultF128(x, expectedHi, expectedLo);
|
||||
|
||||
if (ret)
|
||||
printf("error in __floatunditf(%Lu) = %.20Lf, "
|
||||
|
||||
@@ -13,7 +13,7 @@ COMPILER_RT_ABI long double __floatunsitf(su_int a);
|
||||
int test__floatunsitf(su_int a, uint64_t expectedHi, uint64_t expectedLo)
|
||||
{
|
||||
long double x = __floatunsitf(a);
|
||||
int ret = compareResultLD(x, expectedHi, expectedLo);
|
||||
int ret = compareResultF128(x, expectedHi, expectedLo);
|
||||
|
||||
if (ret){
|
||||
printf("error in test__floatunsitf(%u) = %.20Lf, "
|
||||
|
||||
@@ -9,6 +9,18 @@
|
||||
#define TYPE_FP16 uint16_t
|
||||
#endif
|
||||
|
||||
// TODO: Switch to using fp_lib.h once QUAD_PRECISION is available on x86_64.
|
||||
#if __LDBL_MANT_DIG__ == 113 || \
|
||||
((__LDBL_MANT_DIG__ == 64) && defined(__x86_64__) && \
|
||||
(defined(__FLOAT128__) || defined(__SIZEOF_FLOAT128__)))
|
||||
#if __LDBL_MANT_DIG__ == 113
|
||||
#define TYPE_FP128 long double
|
||||
#else
|
||||
#define TYPE_FP128 __float128
|
||||
#endif
|
||||
#define TEST_COMPILER_RT_HAS_FLOAT128
|
||||
#endif
|
||||
|
||||
enum EXPECTED_RESULT {
|
||||
LESS_0, LESS_EQUAL_0, EQUAL_0, GREATER_0, GREATER_EQUAL_0, NEQUAL_0
|
||||
};
|
||||
@@ -38,11 +50,10 @@ static inline double fromRep64(uint64_t x)
|
||||
return ret;
|
||||
}
|
||||
|
||||
#if __LDBL_MANT_DIG__ == 113
|
||||
static inline long double fromRep128(uint64_t hi, uint64_t lo)
|
||||
{
|
||||
#ifdef TEST_COMPILER_RT_HAS_FLOAT128
|
||||
static inline TYPE_FP128 fromRep128(uint64_t hi, uint64_t lo) {
|
||||
__uint128_t x = ((__uint128_t)hi << 64) + lo;
|
||||
long double ret;
|
||||
TYPE_FP128 ret;
|
||||
memcpy(&ret, &x, 16);
|
||||
return ret;
|
||||
}
|
||||
@@ -73,9 +84,8 @@ static inline uint64_t toRep64(double x)
|
||||
return ret;
|
||||
}
|
||||
|
||||
#if __LDBL_MANT_DIG__ == 113
|
||||
static inline __uint128_t toRep128(long double x)
|
||||
{
|
||||
#ifdef TEST_COMPILER_RT_HAS_FLOAT128
|
||||
static inline __uint128_t toRep128(TYPE_FP128 x) {
|
||||
__uint128_t ret;
|
||||
memcpy(&ret, &x, 16);
|
||||
return ret;
|
||||
@@ -136,25 +146,23 @@ static inline int compareResultD(double result,
|
||||
return 1;
|
||||
}
|
||||
|
||||
#if __LDBL_MANT_DIG__ == 113
|
||||
#ifdef TEST_COMPILER_RT_HAS_FLOAT128
|
||||
// return 0 if equal
|
||||
// use two 64-bit integers instead of one 128-bit integer
|
||||
// because 128-bit integer constant can't be assigned directly
|
||||
static inline int compareResultLD(long double result,
|
||||
uint64_t expectedHi,
|
||||
uint64_t expectedLo)
|
||||
{
|
||||
static inline int compareResultF128(TYPE_FP128 result, uint64_t expectedHi,
|
||||
uint64_t expectedLo) {
|
||||
__uint128_t rep = toRep128(result);
|
||||
uint64_t hi = rep >> 64;
|
||||
uint64_t lo = rep;
|
||||
|
||||
if (hi == expectedHi && lo == expectedLo){
|
||||
if (hi == expectedHi && lo == expectedLo) {
|
||||
return 0;
|
||||
}
|
||||
// test other possible NaN representation(signal NaN)
|
||||
else if (expectedHi == 0x7fff800000000000UL && expectedLo == 0x0UL){
|
||||
else if (expectedHi == 0x7fff800000000000UL && expectedLo == 0x0UL) {
|
||||
if ((hi & 0x7fff000000000000UL) == 0x7fff000000000000UL &&
|
||||
((hi & 0xffffffffffffUL) > 0 || lo > 0)){
|
||||
((hi & 0xffffffffffffUL) > 0 || lo > 0)) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -232,9 +240,45 @@ static inline double makeQNaN64(void)
|
||||
return fromRep64(0x7ff8000000000000UL);
|
||||
}
|
||||
|
||||
#if __LDBL_MANT_DIG__ == 113
|
||||
static inline long double makeQNaN128(void)
|
||||
{
|
||||
#if __LDBL_MANT_DIG__ == 64 && defined(__x86_64__)
|
||||
static inline long double F80FromRep128(uint64_t hi, uint64_t lo) {
|
||||
__uint128_t x = ((__uint128_t)hi << 64) + lo;
|
||||
long double ret;
|
||||
memcpy(&ret, &x, 16);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline __uint128_t F80ToRep128(long double x) {
|
||||
__uint128_t ret;
|
||||
memcpy(&ret, &x, 16);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int compareResultF80(long double result, uint64_t expectedHi,
|
||||
uint64_t expectedLo) {
|
||||
__uint128_t rep = F80ToRep128(result);
|
||||
// F80 occupies the lower 80 bits of __uint128_t.
|
||||
uint64_t hi = (rep >> 64) & ((1UL << (80 - 64)) - 1);
|
||||
uint64_t lo = rep;
|
||||
return !(hi == expectedHi && lo == expectedLo);
|
||||
}
|
||||
|
||||
static inline long double makeQNaN80(void) {
|
||||
return F80FromRep128(0x7fffUL, 0xc000000000000000UL);
|
||||
}
|
||||
|
||||
static inline long double makeNaN80(uint64_t rand) {
|
||||
return F80FromRep128(0x7fffUL,
|
||||
0x8000000000000000 | (rand & 0x3fffffffffffffff));
|
||||
}
|
||||
|
||||
static inline long double makeInf80(void) {
|
||||
return F80FromRep128(0x7fffUL, 0x8000000000000000UL);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef TEST_COMPILER_RT_HAS_FLOAT128
|
||||
static inline TYPE_FP128 makeQNaN128(void) {
|
||||
return fromRep128(0x7fff800000000000UL, 0x0UL);
|
||||
}
|
||||
#endif
|
||||
@@ -254,9 +298,8 @@ static inline double makeNaN64(uint64_t rand)
|
||||
return fromRep64(0x7ff0000000000000UL | (rand & 0xfffffffffffffUL));
|
||||
}
|
||||
|
||||
#if __LDBL_MANT_DIG__ == 113
|
||||
static inline long double makeNaN128(uint64_t rand)
|
||||
{
|
||||
#ifdef TEST_COMPILER_RT_HAS_FLOAT128
|
||||
static inline TYPE_FP128 makeNaN128(uint64_t rand) {
|
||||
return fromRep128(0x7fff000000000000UL | (rand & 0xffffffffffffUL), 0x0UL);
|
||||
}
|
||||
#endif
|
||||
@@ -286,14 +329,12 @@ static inline double makeNegativeInf64(void)
|
||||
return fromRep64(0xfff0000000000000UL);
|
||||
}
|
||||
|
||||
#if __LDBL_MANT_DIG__ == 113
|
||||
static inline long double makeInf128(void)
|
||||
{
|
||||
#ifdef TEST_COMPILER_RT_HAS_FLOAT128
|
||||
static inline TYPE_FP128 makeInf128(void) {
|
||||
return fromRep128(0x7fff000000000000UL, 0x0UL);
|
||||
}
|
||||
|
||||
static inline long double makeNegativeInf128(void)
|
||||
{
|
||||
static inline TYPE_FP128 makeNegativeInf128(void) {
|
||||
return fromRep128(0xffff000000000000UL, 0x0UL);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -15,7 +15,7 @@ int test__multf3(long double a, long double b,
|
||||
uint64_t expectedHi, uint64_t expectedLo)
|
||||
{
|
||||
long double x = __multf3(a, b);
|
||||
int ret = compareResultLD(x, expectedHi, expectedLo);
|
||||
int ret = compareResultF128(x, expectedHi, expectedLo);
|
||||
|
||||
if (ret){
|
||||
printf("error in test__multf3(%.20Lf, %.20Lf) = %.20Lf, "
|
||||
|
||||
@@ -16,7 +16,7 @@ int test__subtf3(long double a, long double b,
|
||||
uint64_t expectedHi, uint64_t expectedLo)
|
||||
{
|
||||
long double x = __subtf3(a, b);
|
||||
int ret = compareResultLD(x, expectedHi, expectedLo);
|
||||
int ret = compareResultF128(x, expectedHi, expectedLo);
|
||||
|
||||
if (ret){
|
||||
printf("error in test__subtf3(%.20Lf, %.20Lf) = %.20Lf, "
|
||||
|
||||
97
compiler-rt/test/builtins/Unit/trunctfxf2_test.c
Normal file
97
compiler-rt/test/builtins/Unit/trunctfxf2_test.c
Normal file
@@ -0,0 +1,97 @@
|
||||
// RUN: %clang_builtins %s %librt -o %t && %run %t
|
||||
// REQUIRES: librt_has_trunctfxf2
|
||||
|
||||
#include "int_lib.h"
|
||||
#include <stdio.h>
|
||||
|
||||
#if __LDBL_MANT_DIG__ == 64 && defined(__x86_64__) && \
|
||||
(defined(__FLOAT128__) || defined(__SIZEOF_FLOAT128__))
|
||||
|
||||
#include "fp_test.h"
|
||||
|
||||
COMPILER_RT_ABI long double __trunctfxf2(__float128 a);
|
||||
|
||||
int test__trunctfxf2(__float128 a, uint64_t expectedHi, uint64_t expectedLo) {
|
||||
long double x = __trunctfxf2(a);
|
||||
int ret = compareResultF80(x, expectedHi, expectedLo);
|
||||
;
|
||||
if (ret) {
|
||||
printf("error in __trunctfxf2(%.20Lf) = %.20Lf, "
|
||||
"expected %.20Lf\n",
|
||||
a, x, fromRep128(expectedHi, expectedLo));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
char assumption_1[sizeof(long double) * CHAR_BIT == 128] = {0};
|
||||
|
||||
#endif
|
||||
|
||||
int main() {
|
||||
#if __LDBL_MANT_DIG__ == 64 && defined(__x86_64__) && \
|
||||
(defined(__FLOAT128__) || defined(__SIZEOF_FLOAT128__))
|
||||
// qNaN
|
||||
if (test__trunctfxf2(makeQNaN128(), UINT64_C(0x7FFF),
|
||||
UINT64_C(0xC000000000000000)))
|
||||
return 1;
|
||||
// NaN
|
||||
if (test__trunctfxf2(makeNaN128(UINT64_C(0x810000000000)), UINT64_C(0x7FFF),
|
||||
UINT64_C(0xC080000000000000)))
|
||||
return 1;
|
||||
// inf
|
||||
if (test__trunctfxf2(makeInf128(), UINT64_C(0x7FFF),
|
||||
UINT64_C(0x8000000000000000)))
|
||||
return 1;
|
||||
// zero
|
||||
if (test__trunctfxf2(0.0Q, UINT64_C(0x0), UINT64_C(0x0)))
|
||||
return 1;
|
||||
if (test__trunctfxf2(0x1.af23456789bbaaab347645365cdep+5L, UINT64_C(0x4004),
|
||||
UINT64_C(0xd791a2b3c4ddd556)))
|
||||
return 1;
|
||||
if (test__trunctfxf2(0x1.dedafcff354b6ae9758763545432p-9L, UINT64_C(0x3ff6),
|
||||
UINT64_C(0xef6d7e7f9aa5b575)))
|
||||
return 1;
|
||||
if (test__trunctfxf2(0x1.2f34dd5f437e849b4baab754cdefp+4534L,
|
||||
UINT64_C(0x51b5), UINT64_C(0x979a6eafa1bf424e)))
|
||||
return 1;
|
||||
if (test__trunctfxf2(0x1.edcbff8ad76ab5bf46463233214fp-435L, UINT64_C(0x3e4c),
|
||||
UINT64_C(0xf6e5ffc56bb55ae0)))
|
||||
return 1;
|
||||
|
||||
// Test rounding near halfway.
|
||||
__float128 halfwayPlus =
|
||||
fromRep128(UINT64_C(0x7ffa000000000000),
|
||||
((UINT64_C(1) << (112 - 63 - 1)) + UINT64_C(1)));
|
||||
if (test__trunctfxf2(halfwayPlus, UINT64_C(0x7ffa),
|
||||
UINT64_C(0x8000000000000001)))
|
||||
return 1;
|
||||
__float128 halfwayExactOdd = fromRep128(
|
||||
UINT64_C(0x7ffa000000000000),
|
||||
((UINT64_C(1) << (112 - 63)) + (UINT64_C(1) << (112 - 63 - 1))));
|
||||
if (test__trunctfxf2(halfwayExactOdd, UINT64_C(0x7ffa),
|
||||
UINT64_C(0x8000000000000002)))
|
||||
return 1;
|
||||
__float128 halfwayExactEven =
|
||||
fromRep128(UINT64_C(0x7ffa000000000000), (UINT64_C(1) << (112 - 63 - 1)));
|
||||
if (test__trunctfxf2(halfwayExactEven, UINT64_C(0x7ffa),
|
||||
UINT64_C(0x8000000000000000)))
|
||||
return 1;
|
||||
__float128 halfwayRoundingWillChangeExponent =
|
||||
fromRep128(UINT64_C(0x7ffaffffffffffff), UINT64_C(0xffff000000000001));
|
||||
if (test__trunctfxf2(halfwayRoundingWillChangeExponent, UINT64_C(0x7ffb),
|
||||
UINT64_C(0x8000000000000000)))
|
||||
return 1;
|
||||
|
||||
// denormal number
|
||||
if (test__trunctfxf2(1e-4932Q, UINT64_C(0), UINT64_C(0x261247c8f29357f0)))
|
||||
return 1;
|
||||
// denormal number
|
||||
if (test__trunctfxf2(2e-4932Q, UINT64_C(0), UINT64_C(0x4c248f91e526afe0)))
|
||||
return 1;
|
||||
|
||||
#else
|
||||
printf("skipped\n");
|
||||
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user