https://github.com/statham-arm created https://github.com/llvm/llvm-project/pull/179920
This commit adds C helper functions `dnan2`, `dnorm2` and `dunder` for handling the less critical edge cases of double-precision arithmetic, similar to `fnan2`, `fnorm2` and `funder` that were added in commit f7e652127772e93. It also adds a header file that defines some register aliases for handling double-precision numbers in AArch32 software floating point in an endianness-independent way, by providing aliases `xh` and `xl` for the high and low words of the first double-precision function argument, regardless of which of them is in r0 and which in r1, and similarly `yh` and `yl` for the second argument in r2/r3. >From cdcb4cc7e66916f2b4a068d38825b1e6244b589c Mon Sep 17 00:00:00 2001 From: Simon Tatham <[email protected]> Date: Thu, 29 Jan 2026 16:06:10 +0000 Subject: [PATCH] [compiler-rt][ARM] Double-precision FP support functions This commit adds C helper functions `dnan2`, `dnorm2` and `dunder` for handling the less critical edge cases of double-precision arithmetic, similar to `fnan2`, `fnorm2` and `funder` that were added in commit f7e652127772e93. It also adds a header file that defines some register aliases for handling double-precision numbers in AArch32 software floating point in an endianness-independent way, by providing aliases `xh` and `xl` for the high and low words of the first double-precision function argument, regardless of which of them is in r0 and which in r1, and similarly `yh` and `yl` for the second argument in r2/r3. --- compiler-rt/lib/builtins/CMakeLists.txt | 3 + compiler-rt/lib/builtins/arm/dnan2.c | 45 ++++++++++++++ compiler-rt/lib/builtins/arm/dnorm2.c | 59 +++++++++++++++++++ compiler-rt/lib/builtins/arm/dunder.c | 78 +++++++++++++++++++++++++ compiler-rt/lib/builtins/arm/endian.h | 37 ++++++++++++ 5 files changed, 222 insertions(+) create mode 100644 compiler-rt/lib/builtins/arm/dnan2.c create mode 100644 compiler-rt/lib/builtins/arm/dnorm2.c create mode 100644 compiler-rt/lib/builtins/arm/dunder.c create mode 100644 compiler-rt/lib/builtins/arm/endian.h diff --git a/compiler-rt/lib/builtins/CMakeLists.txt b/compiler-rt/lib/builtins/CMakeLists.txt index f43ef4743ff97..82ebfe0ccb322 100644 --- a/compiler-rt/lib/builtins/CMakeLists.txt +++ b/compiler-rt/lib/builtins/CMakeLists.txt @@ -455,6 +455,9 @@ if(COMPILER_RT_ARM_OPTIMIZED_FP AND BUILTIN_SUPPORTED_ARCH MATCHES "arm") arm/fnan2.c arm/fnorm2.c arm/funder.c + arm/dnan2.c + arm/dnorm2.c + arm/dunder.c ) endif() endif() diff --git a/compiler-rt/lib/builtins/arm/dnan2.c b/compiler-rt/lib/builtins/arm/dnan2.c new file mode 100644 index 0000000000000..3b3bb50d3f587 --- /dev/null +++ b/compiler-rt/lib/builtins/arm/dnan2.c @@ -0,0 +1,45 @@ +//===-- dnan2.c - Handle double-precision NaN inputs to binary operation --===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// This helper function is available for use by double-precision float +// arithmetic implementations to handle propagating NaNs from the input +// operands to the output, in a way that matches Arm hardware FP. +// +// On input, a and b are floating-point numbers in IEEE 754 encoding, and at +// least one of them must be a NaN. The return value is the correct output NaN. +// +// A signalling NaN in the input (with bit 51 clear) takes priority over any +// quiet NaN, and is adjusted on return by setting bit 51 to make it quiet. If +// both inputs are the same type of NaN then the first input takes priority: +// the input a is used instead of b. +// +//===----------------------------------------------------------------------===// + +#include <stdint.h> + +uint64_t __compiler_rt_dnan2(uint64_t a, uint64_t b) { + // Make shifted-left copies of a and b to discard the sign bit. Then add 1 at + // the bit position where the quiet vs signalling bit ended up. This squashes + // all the signalling NaNs to the top of the range of 64-bit values, from + // 0xfff0000000000001 to 0xffffffffffffffff inclusive; meanwhile, all the + // quiet NaN values wrap round to the bottom, from 0 to 0x000fffffffffffff + // inclusive. So we can detect a signalling NaN by asking if it's greater + // than 0xfff0000000000000, and a quiet one by asking if it's less than + // 0x0010000000000000. + uint64_t aadj = (a << 1) + 0x0010000000000000; + uint64_t badj = (b << 1) + 0x0010000000000000; + + if (aadj > 0xfff0000000000000) // a is a signalling NaN? + return a | 0x0008000000000000; // if so, return it with the quiet bit set + if (badj > 0xfff0000000000000) // b is a signalling NaN? + return b | 0x0008000000000000; // if so, return it with the quiet bit set + if (aadj < 0x0010000000000000) // a is a quiet NaN? + return a; // if so, return it + else /* expect (badj < 0x0010000000000000) */ + return b; // in that case b must be a quiet NaN +} diff --git a/compiler-rt/lib/builtins/arm/dnorm2.c b/compiler-rt/lib/builtins/arm/dnorm2.c new file mode 100644 index 0000000000000..da605ca22aabe --- /dev/null +++ b/compiler-rt/lib/builtins/arm/dnorm2.c @@ -0,0 +1,59 @@ +//===-- dnorm2.c - Handle double-precision denormal inputs to binary op ---===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// This helper function is available for use by double-precision float +// arithmetic implementations, to handle denormal inputs on entry by +// renormalizing the mantissa and modifying the exponent to match. +// +//===----------------------------------------------------------------------===// + +#include <stdint.h> + +// Structure containing the function's inputs and outputs. +// +// On entry: a, b are two input floating-point numbers, still in IEEE 754 +// encoding. expa and expb are the 8-bit exponents of those numbers, extracted +// and shifted down to the low 8 bits of the word, with no other change. +// Neither value should be zero, or have the maximum exponent (indicating an +// infinity or NaN). +// +// On exit: each of a and b contains the mantissa of the input value, with the +// leading 1 bit made explicit, and shifted up to bit 52 (the same place it +// would have been if the number was normalized already). If expa was zero +// (indicating that a was denormal) then it is now represented as a normalized +// number with an out-of-range exponent (zero or negative). The same applies to +// expb and b. +// +// The sign bits from the input floating-point numbers are discarded +// completely. The caller is expected to have stored those somewhere +// safe already. +struct dnorm2 { + uint64_t a, b; + uint32_t expa, expb; +}; + +void __compiler_rt_dnorm2(struct dnorm2 *values) { + values->a &= ~0xFFF0000000000000; + values->b &= ~0xFFF0000000000000; + + if (values->expa == 0) { + unsigned shift = __builtin_clzll(values->a) - 11; + values->a <<= shift; + values->expa = 1 - shift; + } else { + values->a |= 0x0010000000000000; + } + + if (values->expb == 0) { + unsigned shift = __builtin_clzll(values->b) - 11; + values->b <<= shift; + values->expb = 1 - shift; + } else { + values->b |= 0x0010000000000000; + } +} diff --git a/compiler-rt/lib/builtins/arm/dunder.c b/compiler-rt/lib/builtins/arm/dunder.c new file mode 100644 index 0000000000000..026bf76d50261 --- /dev/null +++ b/compiler-rt/lib/builtins/arm/dunder.c @@ -0,0 +1,78 @@ +//===-- dunder.c - Handle double-precision floating-point underflow -------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// This helper function is available for use by double-precision float +// arithmetic implementations to handle underflowed output values, if they were +// computed in the form of a normalized mantissa and an out-of-range exponent. +// +// On input: x should be a complete IEEE 754 floating-point value representing +// the desired output scaled up by 2^1536 (the same value that would have been +// passed to an underflow trap handler in IEEE 754:1985). +// +// This isn't enough information to re-round to the correct output denormal +// without also knowing whether x itself has already been rounded, and which +// way. 'errsign' gives this information, by indicating the sign of the value +// (true result - x). That is, if errsign > 0 it means the true value was +// larger (x was rounded down); if errsign < 0 then x was rounded up; if +// errsign == 0 then x represents the _exact_ desired output value. +// +//===----------------------------------------------------------------------===// + +#include <stdint.h> + +#define SIGNBIT 0x8000000000000000 +#define MANTSIZE 52 +#define BIAS 0x600 + +uint64_t __compiler_rt_dunder(uint64_t x, uint32_t errsign) { + uint64_t sign = x & SIGNBIT; + uint64_t exponent = (x << 1) >> 53; + + // Rule out exponents so small (or large!) that no denormalisation + // is needed. + if (exponent > BIAS) { + // Exponent 0x601 or above means a normalised number got here by + // mistake, so we just remove the 0x600 exponent bias and go + // straight home. + return x - ((uint64_t)BIAS << MANTSIZE); + } + uint32_t bits_lost = BIAS + 1 - exponent; + if (bits_lost > MANTSIZE + 1) { + // The implicit leading 1 of the intermediate value's mantissa is + // below the lowest mantissa bit of a denormal by at least 2 bits. + // Round down to 0 unconditionally. + return sign; + } + + // Make the full mantissa (with leading bit) at the top of the word. + uint64_t mantissa = 0x8000000000000000 | (x << 11); + // Adjust by 1 depending on the sign of the error. + mantissa -= errsign >> 31; + mantissa += (-errsign) >> 31; + + // Shift down to the output position, keeping the bits shifted off. + uint64_t outmant, shifted_off; + if (bits_lost == MANTSIZE + 1) { + // Special case for the exponent where we have to shift the whole + // of 'mantissa' off the bottom of the word. + outmant = 0; + shifted_off = mantissa; + } else { + outmant = mantissa >> (11 + bits_lost); + shifted_off = mantissa << (64 - (11 + bits_lost)); + } + + // Re-round. + if (shifted_off >> 63) { + outmant++; + if (!(shifted_off << 1)) + outmant &= ~1; // halfway case: round to even + } + + return sign | outmant; +} diff --git a/compiler-rt/lib/builtins/arm/endian.h b/compiler-rt/lib/builtins/arm/endian.h new file mode 100644 index 0000000000000..f603fa9cc678f --- /dev/null +++ b/compiler-rt/lib/builtins/arm/endian.h @@ -0,0 +1,37 @@ +//===-- endian.h - make double-prec software FP work in both endiannesses -===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// This header file should be included from assembly source code (not C). It +// defines two pairs of register aliases, for handling 64-bit values passed and +// returned from functions in the AArch32 integer registers: +// +// xh, xl the high and low words of a 64-bit value passed in {r0,r1} +// yh, yl the high and low words of a 64-bit value passed in {r2,r3} +// +// Which alias goes with which register depends on endianness. +// +//===----------------------------------------------------------------------===// + +#ifndef COMPILER_RT_ARM_FP_ENDIAN_H +#define COMPILER_RT_ARM_FP_ENDIAN_H + +#ifdef __BIG_ENDIAN__ +// Big-endian: high words are in lower-numbered registers. +xh .req r0 +xl .req r1 +yh .req r2 +yl .req r3 +#else +// Little-endian: low words are in lower-numbered registers. +xl .req r0 +xh .req r1 +yl .req r2 +yh .req r3 +#endif + +#endif // COMPILER_RT_ARM_FP_ENDIAN_H _______________________________________________ llvm-branch-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
