diff --git a/compiler-rt/lib/builtins/CMakeLists.txt b/compiler-rt/lib/builtins/CMakeLists.txt index 753d08273ea54..4f210a5c0fef9 100644 --- a/compiler-rt/lib/builtins/CMakeLists.txt +++ b/compiler-rt/lib/builtins/CMakeLists.txt @@ -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) diff --git a/compiler-rt/lib/builtins/extendxftf2.c b/compiler-rt/lib/builtins/extendxftf2.c new file mode 100644 index 0000000000000..20911fe7cf2a0 --- /dev/null +++ b/compiler-rt/lib/builtins/extendxftf2.c @@ -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 diff --git a/compiler-rt/lib/builtins/fp_extend.h b/compiler-rt/lib/builtins/fp_extend.h index eee4722bf90e6..86b32be12d55f 100644 --- a/compiler-rt/lib/builtins/fp_extend.h +++ b/compiler-rt/lib/builtins/fp_extend.h @@ -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. + +// 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) { +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; diff --git a/compiler-rt/lib/builtins/fp_extend_impl.inc b/compiler-rt/lib/builtins/fp_extend_impl.inc index d1c9c02a00c53..e16b55d150d2e 100644 --- a/compiler-rt/lib/builtins/fp_extend_impl.inc +++ b/compiler-rt/lib/builtins/fp_extend_impl.inc @@ -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); + + dst_rep_t dstSign = srcSign; + dst_rep_t dstExp; + dst_rep_t dstSigFrac; - // 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) { + 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); } diff --git a/compiler-rt/lib/builtins/fp_trunc.h b/compiler-rt/lib/builtins/fp_trunc.h index 91f614528ab3f..ea13dc2efae54 100644 --- a/compiler-rt/lib/builtins/fp_trunc.h +++ b/compiler-rt/lib/builtins/fp_trunc.h @@ -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; diff --git a/compiler-rt/lib/builtins/fp_trunc_impl.inc b/compiler-rt/lib/builtins/fp_trunc_impl.inc index e235f45965a72..f68492495697f 100644 --- a/compiler-rt/lib/builtins/fp_trunc_impl.inc +++ b/compiler-rt/lib/builtins/fp_trunc_impl.inc @@ -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); + + dst_rep_t dstSign = srcSign; + dst_rep_t dstExp; + dst_rep_t dstSigFrac; - 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); + // 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)); } diff --git a/compiler-rt/lib/builtins/trunctfxf2.c b/compiler-rt/lib/builtins/trunctfxf2.c new file mode 100644 index 0000000000000..4a22a602b3817 --- /dev/null +++ b/compiler-rt/lib/builtins/trunctfxf2.c @@ -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 diff --git a/compiler-rt/test/builtins/Unit/addtf3_test.c b/compiler-rt/test/builtins/Unit/addtf3_test.c index fe2e2c80f655b..e6986c236a64f 100644 --- a/compiler-rt/test/builtins/Unit/addtf3_test.c +++ b/compiler-rt/test/builtins/Unit/addtf3_test.c @@ -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, " diff --git a/compiler-rt/test/builtins/Unit/divtf3_test.c b/compiler-rt/test/builtins/Unit/divtf3_test.c index 927d0b826f8f5..da6465636e923 100644 --- a/compiler-rt/test/builtins/Unit/divtf3_test.c +++ b/compiler-rt/test/builtins/Unit/divtf3_test.c @@ -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, " diff --git a/compiler-rt/test/builtins/Unit/extenddftf2_test.c b/compiler-rt/test/builtins/Unit/extenddftf2_test.c index 04a346887661b..fcc030ca92202 100644 --- a/compiler-rt/test/builtins/Unit/extenddftf2_test.c +++ b/compiler-rt/test/builtins/Unit/extenddftf2_test.c @@ -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, " diff --git a/compiler-rt/test/builtins/Unit/extendhftf2_test.c b/compiler-rt/test/builtins/Unit/extendhftf2_test.c index 7d3ea3049e8a1..5de17379093af 100644 --- a/compiler-rt/test/builtins/Unit/extendhftf2_test.c +++ b/compiler-rt/test/builtins/Unit/extendhftf2_test.c @@ -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, " diff --git a/compiler-rt/test/builtins/Unit/extendsftf2_test.c b/compiler-rt/test/builtins/Unit/extendsftf2_test.c index 19dd5b02c07bd..6ce9bd81a3dd9 100644 --- a/compiler-rt/test/builtins/Unit/extendsftf2_test.c +++ b/compiler-rt/test/builtins/Unit/extendsftf2_test.c @@ -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) { diff --git a/compiler-rt/test/builtins/Unit/extendxftf2_test.c b/compiler-rt/test/builtins/Unit/extendxftf2_test.c new file mode 100644 index 0000000000000..f5211875438c7 --- /dev/null +++ b/compiler-rt/test/builtins/Unit/extendxftf2_test.c @@ -0,0 +1,74 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t +// REQUIRES: librt_has_extendxftf2 + +#include "int_lib.h" +#include + +#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; +} diff --git a/compiler-rt/test/builtins/Unit/floatditf_test.c b/compiler-rt/test/builtins/Unit/floatditf_test.c index 4d5da32ec25d4..fe7a5fd86ae84 100644 --- a/compiler-rt/test/builtins/Unit/floatditf_test.c +++ b/compiler-rt/test/builtins/Unit/floatditf_test.c @@ -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, " diff --git a/compiler-rt/test/builtins/Unit/floatsitf_test.c b/compiler-rt/test/builtins/Unit/floatsitf_test.c index 751a4a9b9207a..b6571b9ba223d 100644 --- a/compiler-rt/test/builtins/Unit/floatsitf_test.c +++ b/compiler-rt/test/builtins/Unit/floatsitf_test.c @@ -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) { diff --git a/compiler-rt/test/builtins/Unit/floatunditf_test.c b/compiler-rt/test/builtins/Unit/floatunditf_test.c index d44ae7934145a..8da78da976029 100644 --- a/compiler-rt/test/builtins/Unit/floatunditf_test.c +++ b/compiler-rt/test/builtins/Unit/floatunditf_test.c @@ -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, " diff --git a/compiler-rt/test/builtins/Unit/floatunsitf_test.c b/compiler-rt/test/builtins/Unit/floatunsitf_test.c index f0a6c63eb8379..b6b1ba0457399 100644 --- a/compiler-rt/test/builtins/Unit/floatunsitf_test.c +++ b/compiler-rt/test/builtins/Unit/floatunsitf_test.c @@ -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, " diff --git a/compiler-rt/test/builtins/Unit/fp_test.h b/compiler-rt/test/builtins/Unit/fp_test.h index e54dfc108e718..f095ae0701d77 100644 --- a/compiler-rt/test/builtins/Unit/fp_test.h +++ b/compiler-rt/test/builtins/Unit/fp_test.h @@ -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 diff --git a/compiler-rt/test/builtins/Unit/multf3_test.c b/compiler-rt/test/builtins/Unit/multf3_test.c index 3bf6ab24cec02..543b55899ce82 100644 --- a/compiler-rt/test/builtins/Unit/multf3_test.c +++ b/compiler-rt/test/builtins/Unit/multf3_test.c @@ -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, " diff --git a/compiler-rt/test/builtins/Unit/subtf3_test.c b/compiler-rt/test/builtins/Unit/subtf3_test.c index 377ae95a9a7d7..724fa4820d99d 100644 --- a/compiler-rt/test/builtins/Unit/subtf3_test.c +++ b/compiler-rt/test/builtins/Unit/subtf3_test.c @@ -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, " diff --git a/compiler-rt/test/builtins/Unit/trunctfxf2_test.c b/compiler-rt/test/builtins/Unit/trunctfxf2_test.c new file mode 100644 index 0000000000000..53024ef139624 --- /dev/null +++ b/compiler-rt/test/builtins/Unit/trunctfxf2_test.c @@ -0,0 +1,97 @@ +// RUN: %clang_builtins %s %librt -o %t && %run %t +// REQUIRES: librt_has_trunctfxf2 + +#include "int_lib.h" +#include + +#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; +}