The changes have been bootstrapped and regression tested on powerpc64le-linux and powerpc64-linux (m64/m32).
This patch implements the powerpc64le/ppc64 target hook and libgcc runtime support needed for GCC's existing C23 _BitInt front-end/middle-end machinery to work on rs6000. Previously TARGET_C_BITINT_TYPE_INFO was unimplemented on this target, so _BitInt was entirely unsupported. For n > 128 the ABI limb mode differs from the internal limb mode on little-endian ppc64 only: TImode 128-bit ABI chunks, matching aarch64's. On big-endian ppc64, gimple-lower-bitint.cc does not support abi_limb_mode != limb_mode(PR117584 comment 3), so the ABI limb equals the internal DImode limb there, matching what s390x (the only other big-endian _BitInt target) already does. ppc32 uses SImode limbs throughout with no ABI/internal split. The extended (padding-bit) field also differs by endianness: little- endian uses bitint_ext_undef (padding bits undefined, matching x86_64/aarch64). Big-endian uses bitint_ext_full, because gimple-lower-bitint.cc's __builtin_*_overflow lowering only sign/zero-extends the top limb's padding bits when bitint_extended is set; with bitint_ext_undef those bits are left unclean and combine incorrectly with the big-endian storage-order translation in that file, producing wrong overflow results for multi-limb (> 128-bit) _BitInt. s390x uses bitint_ext_full for the same reason, so ppc64 BE now matches it. 2026-07-26 Manjunath Matti <[email protected]> gcc/ PR target/117584 * config/rs6000/rs6000-call.cc (rs6000_bitint_large_p): New function. (rs6000_return_in_memory): Return large _BitInt/enum-backed _BitInt values via a hidden pointer, using rs6000_bitint_large_p and BITINT_TYPE_P. (rs6000_promote_function_mode): Do not promote BITINT_TYPE; its ABI mode is fixed by rs6000_bitint_type_info and must match rs6000_function_value. (rs6000_function_arg_boundary): Align _BitInt arguments to 128 bits. (rs6000_pass_by_reference): Pass large _BitInt/enum-backed _BitInt arguments by reference, using rs6000_bitint_large_p and BITINT_TYPE_P. * config/rs6000/rs6000.cc (TARGET_C_BITINT_TYPE_INFO): Define. (rs6000_function_value): Exclude BITINT_TYPE from sub-word integer return-value promotion. (rs6000_bitint_type_info): New function. libgcc/ * config/rs6000/float128-sed: Add __floatbitinttf/__floatbitintkf and __fixtfbitint/__fixkfbitint substitution rules. * config/rs6000/float128-sed-hw: Likewise. * config/rs6000/libgcc-glibc.ver (GCC_17.0.0): New version node, inheriting GCC_4.2.0; export __floatbitintkf, __fixkfbitint, __floatbitinttf, __fixtfbitint, __floatbitintsf, __fixsfbitint, __floatbitintdf, __fixdfbitint. * config/rs6000/t-float128 (fp128_softfp_funcs): Add floatbitintkf and fixkfbitint. (ibm128_bitint_funcs): New. (fp128_ppc_funcs): Add $(ibm128_bitint_funcs). (rs6000_bitint_bin_funcs, rs6000_bitint_bin_src): New, add to LIB2ADD_ST unconditionally. (rs6000_bitint_dec_funcs, rs6000_bitint_dec_src): New, add to LIB2ADD_ST when decimal_float is enabled. (ibm128_bitint_objs, IBM128_BITINT_CFLAGS): New. * config/rs6000/floatbitinttf-ibm128.c: New file. * config/rs6000/fixtfbitint-ibm128.c: New file. gcc/testsuite/ * gcc.target/powerpc/bitint-alignments-powerpc.c: New test. * gcc.target/powerpc/bitint-args-powerpc.c: Likewise. * gcc.target/powerpc/bitint-arith-powerpc.c: Likewise. * gcc.target/powerpc/bitint-bitwise-powerpc.c: Likewise. * gcc.target/powerpc/bitint-compare-powerpc.c: Likewise. * gcc.target/powerpc/bitint-conversions-powerpc.c: Likewise. * gcc.target/powerpc/bitint-endian-powerpc.c: Likewise. * gcc.target/powerpc/bitint-sizes-powerpc.c: Likewise. diff --git a/gcc/config/rs6000/rs6000-call.cc b/gcc/config/rs6000/rs6000-call.cc index 0da2c1f6071..ab86b9d67cd 100644 --- a/gcc/config/rs6000/rs6000-call.cc +++ b/gcc/config/rs6000/rs6000-call.cc @@ -400,6 +400,21 @@ rs6000_discover_homogeneous_aggregate (machine_mode mode, const_tree type, return false; } +/* Return true if a _BitInt with byte size SIZE must be passed or returned + by reference rather than in registers. SIZE < 0 (variable-length) is + always large. ELFv2 threshold is 16 bytes; AIX32/V4 is 8 bytes. + Centralises the threshold so rs6000_return_in_memory and + rs6000_pass_by_reference stay in sync. */ +static bool +rs6000_bitint_large_p (HOST_WIDE_INT size) +{ + if (size < 0) + return true; + if (DEFAULT_ABI == ABI_ELFv2) + return (unsigned HOST_WIDE_INT) size > 16; + return (unsigned HOST_WIDE_INT) size > (TARGET_64BIT ? 16 : 8); +} + /* Return a nonzero value to say to return the function value in memory, just as large structures are always returned. TYPE will be the data type of the value, and FNTYPE will be the type of the @@ -453,6 +468,14 @@ rs6000_return_in_memory (const_tree type, const_tree fntype ATTRIBUTE_UNUSED) IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (type)))); } + /* Handle _BitInt return convention before aggregate classification. + Small _BitInt fits in registers; large _BitInt is returned via a + hidden pointer. BITINT_TYPE_P covers both BITINT_TYPE and + ENUMERAL_TYPE backed by _BitInt (C2Y bit-precise enums). + Must agree with rs6000_pass_by_reference. */ + if (BITINT_TYPE_P (type)) + return rs6000_bitint_large_p (int_size_in_bytes (type)); + /* For the Darwin64 ABI, test if we can fit the return value in regs. */ if (TARGET_MACHO && rs6000_darwin64_abi @@ -696,11 +719,22 @@ init_cumulative_args (CUMULATIVE_ARGS *cum, tree fntype, values. */ machine_mode -rs6000_promote_function_mode (const_tree type ATTRIBUTE_UNUSED, +rs6000_promote_function_mode (const_tree type, machine_mode mode, int *punsignedp ATTRIBUTE_UNUSED, const_tree, int for_return ATTRIBUTE_UNUSED) { +/* _BitInt is never promoted: its ABI mode is fixed by + TARGET_C_BITINT_TYPE_INFO / rs6000_bitint_type_info and must match + what rs6000_function_value computes, which also excludes + BITINT_TYPE_P from promotion. Without this, when the big-endian + hook sets extended != bitint_ext_undef, explow.cc's + promote_function_mode no longer short-circuits for BITINT_TYPE and + reaches this hook, producing a promoted mode that mismatches the + return register mode and ICEs in emit_move_insn. */ + if (type && BITINT_TYPE_P (type)) + return mode; + if (GET_MODE_CLASS (mode) == MODE_INT && GET_MODE_SIZE (mode) < (TARGET_32BIT ? 4 : 8)) mode = TARGET_32BIT ? SImode : DImode; @@ -850,8 +884,8 @@ rs6000_function_arg_boundary (machine_mode mode, const_tree type) /* "Aggregate" means any AGGREGATE_TYPE except for single-element or homogeneous float/vector aggregates here. We already handled vector aggregates above, but still need to check for float here. */ - if (AGGREGATE_TYPE_P (type) - && !SCALAR_FLOAT_MODE_P (elt_mode)) + if ((AGGREGATE_TYPE_P (type) && !SCALAR_FLOAT_MODE_P (elt_mode)) + || BITINT_TYPE_P (type)) return 128; } @@ -1993,6 +2027,14 @@ rs6000_pass_by_reference (cumulative_args_t, const function_arg_info &arg) if (!arg.type) return 0; + /* Handle _BitInt before the generic checks. Large _BitInt must be + passed by reference so the ABI matches gimple-lower-bitint.cc output. + BITINT_TYPE_P covers both BITINT_TYPE and ENUMERAL_TYPE backed by + _BitInt (C2Y bit-precise enums). Thresholds mirror + rs6000_return_in_memory. */ + if (BITINT_TYPE_P (arg.type)) + return rs6000_bitint_large_p (int_size_in_bytes (arg.type)); + if (DEFAULT_ABI == ABI_V4 && TARGET_IEEEQUAD && FLOAT128_IEEE_P (TYPE_MODE (arg.type))) { diff --git a/gcc/config/rs6000/rs6000.cc b/gcc/config/rs6000/rs6000.cc index c620d1a31e8..76f29420977 100644 --- a/gcc/config/rs6000/rs6000.cc +++ b/gcc/config/rs6000/rs6000.cc @@ -1717,6 +1717,9 @@ static const scoped_attribute_specs *const rs6000_attribute_table[] = #undef TARGET_C_MODE_FOR_FLOATING_TYPE #define TARGET_C_MODE_FOR_FLOATING_TYPE rs6000_c_mode_for_floating_type +#undef TARGET_C_BITINT_TYPE_INFO +#define TARGET_C_BITINT_TYPE_INFO rs6000_bitint_type_info + #undef TARGET_INVALID_BINARY_OP #define TARGET_INVALID_BINARY_OP rs6000_invalid_binary_op @@ -24204,6 +24207,7 @@ rs6000_function_value (const_tree valtype, } if ((INTEGRAL_TYPE_P (valtype) + && !BITINT_TYPE_P (valtype) && GET_MODE_BITSIZE (mode) < (TARGET_32BIT ? 32 : 64)) || POINTER_TYPE_P (valtype)) mode = TARGET_32BIT ? SImode : DImode; @@ -24613,6 +24617,75 @@ rs6000_c_mode_for_floating_type (enum tree_index ti) return default_mode_for_floating_type (ti); } +/* Implement TARGET_C_BITINT_TYPE_INFO for PowerPC. + + Limb mode selection (matching aarch64 commit 5aa3fec38cc6): + n <= 8 : QImode + n <= 16 : HImode + n <= 32 : SImode + n <= 64 : DImode (ppc64) / SImode (ppc32) + n <= 128 : TImode (ppc64, single 128-bit limb) / SImode (ppc32) + n > 128 : DImode (ppc64, multi-limb for libgcc) / SImode (ppc32) + + ABI limb mode for n > 128: + ppc64 LE : TImode - 128-bit ABI chunks, matching aarch64. Different + abi_limb_mode and limb_mode works on LE because limb + ordering matches the array element order. + ppc64 BE : DImode = limb_mode. gimple-lower-bitint.cc does not + support abi_limb_mode != limb_mode on big-endian targets + even in GCC 16.1 (PR117584 comment 3). s390x, the only + other big-endian _BitInt target, uses DImode for both. + ppc32 : SImode = limb_mode (32-bit limbs throughout). + + extended: on little-endian, padding bits are undefined + (bitint_ext_undef), matching x86_64/aarch64. On big-endian, + gimple-lower-bitint.cc's __builtin_*_overflow lowering only + sign/zero-extends the top limb's padding bits when bitint_extended + is set; skipping that (as bitint_ext_undef would) leaves those bits + unclean when combined with the big-endian storage-order translation + in that file, producing wrong overflow results for multi-limb (> 128 + bit) _BitInt. s390x, the only other big-endian _BitInt target, uses + bitint_ext_full for exactly this reason, so ppc64 BE matches it. */ + + +static bool +rs6000_bitint_type_info (int n, struct bitint_info *info) +{ + if (n <= 8) + info->limb_mode = QImode; + else if (n <= 16) + info->limb_mode = HImode; + else if (n <= 32) + info->limb_mode = SImode; + else if (n <= 64) + info->limb_mode = TARGET_64BIT ? DImode : SImode; + else if (n <= 128) + /* Single TImode limb for 65..128-bit on ppc64, matching aarch64. + SImode limbs on ppc32 (multiple limbs for this range). */ + info->limb_mode = TARGET_64BIT ? TImode : SImode; + else + /* For n > 128 use DImode internally so libgcc's 64-bit limb-based + routines apply directly; ABI uses TImode on ppc64 LE (see below). + ppc32 continues with SImode limbs throughout. */ + info->limb_mode = TARGET_64BIT ? DImode : SImode; + + info->big_endian = BYTES_BIG_ENDIAN; + + info->extended = BYTES_BIG_ENDIAN ? bitint_ext_full : bitint_ext_undef; + + if (n <= (TARGET_64BIT ? 128 : 64)) + /* For n <= 128 the ABI limb equals the internal limb. */ + info->abi_limb_mode = info->limb_mode; + else if (TARGET_64BIT && !BYTES_BIG_ENDIAN) + /* ppc64 LE n > 128: 128-bit ABI chunks, matching aarch64. */ + info->abi_limb_mode = TImode; + else + /* ppc64 BE and ppc32 n > 128: ABI limb must equal internal limb. */ + info->abi_limb_mode = info->limb_mode; + + return true; +} + /* Target hook for invalid_arg_for_unprototyped_fn. */ static const char * invalid_arg_for_unprototyped_fn (const_tree typelist, const_tree funcdecl, const_tree val) diff --git a/gcc/testsuite/gcc.target/powerpc/bitint-alignments-powerpc.c b/gcc/testsuite/gcc.target/powerpc/bitint-alignments-powerpc.c new file mode 100644 index 00000000000..af950471096 --- /dev/null +++ b/gcc/testsuite/gcc.target/powerpc/bitint-alignments-powerpc.c @@ -0,0 +1,79 @@ +/* { dg-do run { target bitint } } */ +/* { dg-additional-options "-std=c23 -O2" } */ + +/* Test _BitInt alignment for PowerPC. + This test verifies that __alignof__(_BitInt(N)) returns the expected + alignment for various bit widths. */ + +int main(void) +{ + /* Test basic alignments */ + if (__alignof__(_BitInt(8)) != 1) + __builtin_abort(); + + if (__alignof__(_BitInt(16)) != 2) + __builtin_abort(); + + if (__alignof__(_BitInt(32)) != 4) + __builtin_abort(); + +#ifdef __powerpc64__ + /* 64-bit PowerPC: 64-bit alignment */ + if (__alignof__(_BitInt(64)) != 8) + __builtin_abort(); + + /* 128-bit _BitInt has 16-byte alignment on 64-bit */ + if (__alignof__(_BitInt(128)) != 16) + __builtin_abort(); +#else + /* 32-bit PowerPC: 32-bit alignment for 64-bit _BitInt */ + if (__alignof__(_BitInt(64)) != 4) + __builtin_abort(); +#endif + + /* Test unsigned variants */ + if (__alignof__(unsigned _BitInt(8)) != 1) + __builtin_abort(); + + if (__alignof__(unsigned _BitInt(32)) != 4) + __builtin_abort(); + +#ifdef __powerpc64__ + if (__alignof__(unsigned _BitInt(64)) != 8) + __builtin_abort(); + + if (__alignof__(unsigned _BitInt(128)) != 16) + __builtin_abort(); +#endif + + /* Test larger _BitInt alignments */ +#ifdef __powerpc64__ + /* Large (> 128-bit) _BitInt alignment follows abi_limb_mode: TImode + (16 bytes) on little-endian, DImode (8 bytes) on big-endian -- see + rs6000_bitint_type_info. */ +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ + if (__alignof__(_BitInt(200)) != 16) + __builtin_abort(); + + if (__alignof__(_BitInt(256)) != 16) + __builtin_abort(); +#else + if (__alignof__(_BitInt(200)) != 8) + __builtin_abort(); + + if (__alignof__(_BitInt(256)) != 8) + __builtin_abort(); +#endif +#else + /* Large _BitInt types align to limb size (4 bytes on 32-bit) */ + if (__alignof__(_BitInt(200)) != 4) + __builtin_abort(); + + if (__alignof__(_BitInt(256)) != 4) + __builtin_abort(); +#endif + + return 0; +} + +/* Test passes if all alignment checks succeed */ diff --git a/gcc/testsuite/gcc.target/powerpc/bitint-args-powerpc.c b/gcc/testsuite/gcc.target/powerpc/bitint-args-powerpc.c new file mode 100644 index 00000000000..0dce13a2f6e --- /dev/null +++ b/gcc/testsuite/gcc.target/powerpc/bitint-args-powerpc.c @@ -0,0 +1,105 @@ +/* { dg-do run { target bitint } } */ +/* { dg-additional-options "-std=c23 -O0" } */ + +/* Test _BitInt function arguments and return values for PowerPC. + This test verifies that _BitInt values can be passed to and + returned from functions correctly. */ + +_BitInt(32) add_bitint32(_BitInt(32) a, _BitInt(32) b) +{ + return a + b; +} + +_BitInt(64) add_bitint64(_BitInt(64) a, _BitInt(64) b) +{ + return a + b; +} + +_BitInt(128) add_bitint128(_BitInt(128) a, _BitInt(128) b) +{ + return a + b; +} + +#ifdef __powerpc64__ +/* Widths above the abi_limb_mode threshold should use the by-reference ABI. */ +_BitInt(129) add_bitint129(_BitInt(129) a, _BitInt(129) b) +{ + return a + b; +} + +_BitInt(129) echo_bitint129(_BitInt(129) a) +{ + return a; +} + +unsigned _BitInt(256) xor_bitint256(unsigned _BitInt(256) a, + unsigned _BitInt(256) b) +{ + return a ^ b; +} +#endif + +_BitInt(200) identity_bitint200(_BitInt(200) x) +{ + return x; +} + +_BitInt(200) add_large_bitint200(_BitInt(200) a, _BitInt(200) b) +{ + return a + b; +} + +int main(void) +{ + /* Test 32-bit _BitInt argument passing */ + _BitInt(32) result32 = add_bitint32(100, 200); + if (result32 != 300) + __builtin_abort(); + + /* Test 64-bit _BitInt argument passing */ + _BitInt(64) result64 = add_bitint64(1000000000LL, 2000000000LL); + if (result64 != 3000000000LL) + __builtin_abort(); + + /* Test _BitInt(128) on both ppc32 and ppc64. */ + { + _BitInt(128) a = ((_BitInt(128)) 1 << 100) + 5; + _BitInt(128) b = ((_BitInt(128)) 1 << 65) + 7; + _BitInt(128) sum = add_bitint128(a, b); + if (sum != (((_BitInt(128)) 1 << 100) + ((_BitInt(128)) 1 << 65) + 12)) + __builtin_abort(); + } + + /* Exercise large by-reference argument and return passing. */ + { + _BitInt(200) x = ((_BitInt(200)) 1 << 150) + ((_BitInt(200)) 1 << 99) + 11; + _BitInt(200) y = ((_BitInt(200)) 1 << 149) + ((_BitInt(200)) 1 << 80) + 13; + _BitInt(200) z = add_large_bitint200(x, y); + if (identity_bitint200(z) != z) + __builtin_abort(); + if (z != (((_BitInt(200)) 1 << 150) + ((_BitInt(200)) 1 << 149) + + ((_BitInt(200)) 1 << 99) + ((_BitInt(200)) 1 << 80) + 24)) + __builtin_abort(); + } + +#ifdef __powerpc64__ + { + _BitInt(129) x = ((_BitInt(129)) 1 << 100) + 17; + _BitInt(129) y = ((_BitInt(129)) 1 << 64) + 9; + _BitInt(129) z = add_bitint129(x, y); + if (z != (((_BitInt(129)) 1 << 100) + ((_BitInt(129)) 1 << 64) + 26)) + __builtin_abort(); + if (echo_bitint129(z) != z) + __builtin_abort(); + } + + { + unsigned _BitInt(256) x = ((unsigned _BitInt(256)) 0x12u << 200) | 0x34u; + unsigned _BitInt(256) y = ((unsigned _BitInt(256)) 0x55u << 200) | 0x0Fu; + if (xor_bitint256(x, y) != (((unsigned _BitInt(256)) 0x47u << 200) | 0x3Bu)) + __builtin_abort(); + } +#endif + + return 0; +} diff --git a/gcc/testsuite/gcc.target/powerpc/bitint-arith-powerpc.c b/gcc/testsuite/gcc.target/powerpc/bitint-arith-powerpc.c new file mode 100644 index 00000000000..a26559869f3 --- /dev/null +++ b/gcc/testsuite/gcc.target/powerpc/bitint-arith-powerpc.c @@ -0,0 +1,120 @@ +/* { dg-do run { target bitint } } */ +/* { dg-additional-options "-std=c23 -O0" } */ + +/* Test _BitInt arithmetic operations for PowerPC. + This test verifies that basic arithmetic operations work correctly + for various _BitInt sizes. */ + +int main(void) +{ + /* Test 32-bit arithmetic */ + { + _BitInt(32) a = 100; + _BitInt(32) b = 200; + _BitInt(32) sum = a + b; + _BitInt(32) diff = b - a; + _BitInt(32) prod = a * b; + _BitInt(32) quot = b / a; + + if (sum != 300) + __builtin_abort(); + if (diff != 100) + __builtin_abort(); + if (prod != 20000) + __builtin_abort(); + if (quot != 2) + __builtin_abort(); + } + + /* Test 64-bit arithmetic */ + { + _BitInt(64) a = 1000000000LL; + _BitInt(64) b = 2000000000LL; + _BitInt(64) sum = a + b; + _BitInt(64) prod = a * 3; + _BitInt(64) carry_test = 0xFFFFFFFFLL; + + if (sum != 3000000000LL) + __builtin_abort(); + if (prod != 3000000000LL) + __builtin_abort(); + carry_test = carry_test + 1; + if (carry_test != 0x100000000LL) + __builtin_abort(); + } + +#ifdef __powerpc64__ + /* Test 128-bit arithmetic (64-bit mode only) */ + { + _BitInt(128) a = 1; + _BitInt(128) b = 1; + + /* Compute 2^100 */ + for (int i = 0; i < 100; i++) + a = a * 2; + + /* Compute 2^50 */ + for (int i = 0; i < 50; i++) + b = b * 2; + + /* 2^100 / 2^50 should equal 2^50 */ + _BitInt(128) quot = a / b; + if (quot != b) + __builtin_abort(); + } +#endif + + /* Test signed arithmetic, including negative subtraction/borrow. */ + { + _BitInt(32) a = -100; + _BitInt(32) b = 50; + _BitInt(32) sum = a + b; + _BitInt(32) diff = a - b; + _BitInt(32) prod = a * b; + _BitInt(32) quot = a / b; + _BitInt(32) rem = a % b; + + if (sum != -50) + __builtin_abort(); + if (diff != -150) + __builtin_abort(); + if (prod != -5000) + __builtin_abort(); + if (quot != -2) + __builtin_abort(); + if (rem != 0) + __builtin_abort(); + } + + /* Test signed wraparound semantics for narrow _BitInt. */ + { + _BitInt(8) max = 127; + _BitInt(8) min = -128; + + if ((_BitInt(8)) (max + 1) != min) + __builtin_abort(); + if ((_BitInt(8)) (min - 1) != max) + __builtin_abort(); + } + + /* Test unsigned arithmetic, including modulo wraparound. */ + { + unsigned _BitInt(8) a = 255uwb; + unsigned _BitInt(8) b = 1uwb; + unsigned _BitInt(32) c = 100; + unsigned _BitInt(32) d = 200; + unsigned _BitInt(5) x = 31uwb; + + if ((unsigned _BitInt(8)) (a + b) != 0) + __builtin_abort(); + if ((unsigned _BitInt(8)) (b - 2uwb) != 255uwb) + __builtin_abort(); + if (c + d != 300) + __builtin_abort(); + x = x + 1; + if (x != 0) + __builtin_abort(); + } + + return 0; +} diff --git a/gcc/testsuite/gcc.target/powerpc/bitint-bitwise-powerpc.c b/gcc/testsuite/gcc.target/powerpc/bitint-bitwise-powerpc.c new file mode 100644 index 00000000000..73e5aa69392 --- /dev/null +++ b/gcc/testsuite/gcc.target/powerpc/bitint-bitwise-powerpc.c @@ -0,0 +1,82 @@ +/* { dg-do run { target bitint } } */ +/* { dg-additional-options "-std=c23 -O0" } */ + +/* Test _BitInt bitwise operations for PowerPC. + This test verifies that bitwise operations work correctly + for various _BitInt sizes. */ + +int main(void) +{ + /* Test 32-bit bitwise operations */ + { + _BitInt(32) a = 0xFF00FF00; + _BitInt(32) b = 0x00FF00FF; + + _BitInt(32) and_result = a & b; + _BitInt(32) or_result = a | b; + _BitInt(32) xor_result = a ^ b; + _BitInt(32) not_a = ~a; + + if (and_result != 0x00000000) + __builtin_abort(); + if (or_result != 0xFFFFFFFF) + __builtin_abort(); + if (xor_result != 0xFFFFFFFF) + __builtin_abort(); + if (not_a != 0x00FF00FF) + __builtin_abort(); + } + + /* Test shift operations */ + { + _BitInt(32) a = 1; + _BitInt(32) left = a << 10; + _BitInt(32) b = 1024; + _BitInt(32) right = b >> 5; + + if (left != 1024) + __builtin_abort(); + if (right != 32) + __builtin_abort(); + } + + /* Test 64-bit bitwise operations */ + { + _BitInt(64) a = 0xFFFFFFFF00000000LL; + _BitInt(64) b = 0x00000000FFFFFFFFLL; + + _BitInt(64) and_result = a & b; + _BitInt(64) or_result = a | b; + + if (and_result != 0) + __builtin_abort(); + if (or_result != 0xFFFFFFFFFFFFFFFFLL) + __builtin_abort(); + } + +#ifdef __powerpc64__ + /* Test 128-bit bitwise operations (64-bit mode only) */ + { + _BitInt(128) a = 1; + _BitInt(128) shifted = a << 100; + _BitInt(128) back = shifted >> 100; + + if (back != 1) + __builtin_abort(); + } +#endif + + /* Test with small _BitInt */ + { + unsigned _BitInt(8) a = 0xAA; + unsigned _BitInt(8) b = 0x55; + unsigned _BitInt(8) xor_result = a ^ b; + + if (xor_result != (unsigned _BitInt(8)) 0xFF) + __builtin_abort(); + } + + return 0; +} + +/* Test passes if all bitwise operations work correctly */ diff --git a/gcc/testsuite/gcc.target/powerpc/bitint-compare-powerpc.c b/gcc/testsuite/gcc.target/powerpc/bitint-compare-powerpc.c new file mode 100644 index 00000000000..8845dcbcb79 --- /dev/null +++ b/gcc/testsuite/gcc.target/powerpc/bitint-compare-powerpc.c @@ -0,0 +1,216 @@ +/* { dg-do run { target bitint } } */ +/* { dg-additional-options "-std=c23 -O0" } */ + +/* Test _BitInt comparison operations for PowerPC. + This test verifies that comparison operations work correctly + for various _BitInt sizes and signedness. */ + +int main(void) +{ + /* Test equality comparisons */ + { + _BitInt(32) a = 100; + _BitInt(32) b = 100; + _BitInt(32) c = 200; + + if (!(a == b)) + __builtin_abort(); + if (a == c) + __builtin_abort(); + if (!(a != c)) + __builtin_abort(); + if (a != b) + __builtin_abort(); + } + + /* Test less than comparisons */ + { + _BitInt(32) a = 50; + _BitInt(32) b = 100; + + if (!(a < b)) + __builtin_abort(); + if (b < a) + __builtin_abort(); + if (a < a) + __builtin_abort(); + } + + /* Test greater than comparisons */ + { + _BitInt(32) a = 200; + _BitInt(32) b = 100; + + if (!(a > b)) + __builtin_abort(); + if (b > a) + __builtin_abort(); + if (a > a) + __builtin_abort(); + } + + /* Test less than or equal comparisons */ + { + _BitInt(32) a = 100; + _BitInt(32) b = 100; + _BitInt(32) c = 200; + + if (!(a <= b)) + __builtin_abort(); + if (!(a <= c)) + __builtin_abort(); + if (c <= a) + __builtin_abort(); + } + + /* Test greater than or equal comparisons */ + { + _BitInt(32) a = 100; + _BitInt(32) b = 100; + _BitInt(32) c = 50; + + if (!(a >= b)) + __builtin_abort(); + if (!(a >= c)) + __builtin_abort(); + if (c >= a) + __builtin_abort(); + } + + /* Test signed comparisons with negative values */ + { + _BitInt(32) negative = -100; + _BitInt(32) positive = 100; + _BitInt(32) zero = 0; + + if (!(negative < zero)) + __builtin_abort(); + if (!(negative < positive)) + __builtin_abort(); + if (!(zero < positive)) + __builtin_abort(); + if (!(positive > negative)) + __builtin_abort(); + } + + /* Test unsigned comparisons */ + { + unsigned _BitInt(32) a = 100; + unsigned _BitInt(32) b = 200; + + if (!(a < b)) + __builtin_abort(); + if (!(b > a)) + __builtin_abort(); + } + + /* Test that large unsigned values compare correctly */ + { + unsigned _BitInt(32) large = 0xFFFFFFFFU; + unsigned _BitInt(32) small = 1; + + if (!(large > small)) + __builtin_abort(); + if (!(small < large)) + __builtin_abort(); + } + + /* Test 64-bit comparisons */ + { + _BitInt(64) a = 0x123456789ABCDEF0LL; + _BitInt(64) b = 0x123456789ABCDEF0LL; + _BitInt(64) c = 0x0FEDCBA987654321LL; + + if (!(a == b)) + __builtin_abort(); + if (!(a > c)) + __builtin_abort(); + if (!(c < a)) + __builtin_abort(); + } + +#ifdef __powerpc64__ + /* Test 128-bit comparisons (64-bit mode only). */ + { + _BitInt(128) a = ((_BitInt(128)) 1 << 100) + 7; + _BitInt(128) b = ((_BitInt(128)) 1 << 100) + 7; + _BitInt(128) c = ((_BitInt(128)) 1 << 100) + 3; + _BitInt(128) d = ((_BitInt(128)) 1 << 99) + 99; + + if (!(a == b)) + __builtin_abort(); + if (!(a > c)) + __builtin_abort(); + if (!(d < a)) + __builtin_abort(); + } +#endif + + /* Test cross-limb signed comparisons. */ + { + _BitInt(96) a = ((_BitInt(96)) 1 << 70) + 9; + _BitInt(96) b = ((_BitInt(96)) 1 << 70) + 3; + _BitInt(96) c = -(((_BitInt(96)) 1 << 70) - 5); + _BitInt(96) d = -(((_BitInt(96)) 1 << 70) - 9); + + if (!(a > b)) + __builtin_abort(); + if (!(b < a)) + __builtin_abort(); + if (!(c < d)) + __builtin_abort(); + if (!(d > c)) + __builtin_abort(); + if (!(c < 0)) + __builtin_abort(); + if (!(a > 0)) + __builtin_abort(); + } + + /* Test small _BitInt comparisons, including sub-word signed values. */ + { + _BitInt(8) a = 50; + _BitInt(8) b = 60; + _BitInt(8) neg = -64; + _BitInt(8) minus_one = -1; + + if (!(a < b)) + __builtin_abort(); + if (!(b > a)) + __builtin_abort(); + if (!(neg < minus_one)) + __builtin_abort(); + if (!(minus_one < 0)) + __builtin_abort(); + if (!(neg < 0)) + __builtin_abort(); + if (!((_BitInt(8)) 63 > (_BitInt(8)) -1)) + __builtin_abort(); + } + + /* Test sign handling when the sign bit lives in a partial top limb. */ + { + _BitInt(65) neg = -1; + _BitInt(65) pos = 1; + + if (!(neg < pos)) + __builtin_abort(); + if (!(neg < 0)) + __builtin_abort(); + if (!(pos > neg)) + __builtin_abort(); + } + + /* Test unsigned sub-word comparisons with the high bit set. */ + { + unsigned _BitInt(9) large = 511; + unsigned _BitInt(9) small = 1; + + if (!(large > small)) + __builtin_abort(); + if (!(small < large)) + __builtin_abort(); + } + + return 0; +} diff --git a/gcc/testsuite/gcc.target/powerpc/bitint-conversions-powerpc.c b/gcc/testsuite/gcc.target/powerpc/bitint-conversions-powerpc.c new file mode 100644 index 00000000000..aea534ee56d --- /dev/null +++ b/gcc/testsuite/gcc.target/powerpc/bitint-conversions-powerpc.c @@ -0,0 +1,221 @@ +/* { dg-do run { target bitint } } */ +/* { dg-additional-options "-std=c23 -O0" } */ + +/* Test _BitInt type conversions for PowerPC. + This test verifies that conversions between _BitInt types + and standard integer types work correctly. */ + +int main(void) +{ + /* Test conversion from int to _BitInt */ + { + int i = 42; + _BitInt(32) bi = i; + + if (bi != 42) + __builtin_abort(); + } + + /* Test conversion from _BitInt to int */ + { + _BitInt(32) bi = 100; + int i = (int)bi; + + if (i != 100) + __builtin_abort(); + } + + /* Test conversion between different _BitInt sizes */ + { + _BitInt(16) small = 1000; + _BitInt(32) medium = small; + _BitInt(64) large = medium; + + if (large != 1000) + __builtin_abort(); + } + + /* Test narrowing conversion */ + { + _BitInt(64) large = 0x12345678; + _BitInt(32) medium = (_BitInt(32))large; + + if (medium != 0x12345678) + __builtin_abort(); + } + + /* Test narrowing truncation where high bits are dropped. */ + { + _BitInt(64) wide = 0x12345678AABBCCDDLL; + _BitInt(32) narrow = (_BitInt(32)) wide; + + if (narrow != (_BitInt(32)) 0xAABBCCDD) + __builtin_abort(); + } + + /* Test truncation on narrowing to non-power-of-two widths. */ + { + _BitInt(32) large = 0x123; + _BitInt(9) small = (_BitInt(9)) large; + unsigned _BitInt(9) usmall = (unsigned _BitInt(9)) large; + + if (small != -221) + __builtin_abort(); + if (usmall != 0x123u) + __builtin_abort(); + } + + /* Test truncation from negative values. */ + { + _BitInt(16) neg = -1; + unsigned _BitInt(9) u = (unsigned _BitInt(9)) neg; + _BitInt(9) s = (_BitInt(9)) neg; + + if (u != 511) + __builtin_abort(); + if (s != -1) + __builtin_abort(); + } + + /* Test signed to unsigned conversion */ + { + _BitInt(32) signed_val = -1; + unsigned _BitInt(32) unsigned_val = (unsigned _BitInt(32))signed_val; + + if (unsigned_val != 0xFFFFFFFFU) + __builtin_abort(); + } + + /* Test unsigned to signed conversion */ + { + unsigned _BitInt(32) unsigned_val = 0x80000000U; + _BitInt(32) signed_val = (_BitInt(32))unsigned_val; + + if (signed_val >= 0) + __builtin_abort(); + } + + /* Test conversion from long long */ + { + long long ll = 0x123456789ABCDEF0LL; + _BitInt(64) bi = ll; + + if (bi != 0x123456789ABCDEF0LL) + __builtin_abort(); + } + + /* Test conversion to long long */ + { + _BitInt(64) bi = 0x0FEDCBA987654321LL; + long long ll = (long long)bi; + + if (ll != 0x0FEDCBA987654321LL) + __builtin_abort(); + } + +#ifdef __powerpc64__ + /* Test 128-bit conversions (64-bit mode only) */ + { + _BitInt(128) large = 1; + large = large << 100; + + /* Convert to 64-bit (should truncate) */ + _BitInt(64) medium = (_BitInt(64))large; + + /* Lower 64 bits should be 0 */ + if (medium != 0) + __builtin_abort(); + } +#endif + + /* Test conversion with sign extension */ + { + _BitInt(8) small = -1; + _BitInt(32) large = small; + + if (large != -1) + __builtin_abort(); + } + + /* Test sign extension across a limb boundary. */ + { + _BitInt(7) small = -1; + _BitInt(65) big = small; + + if (big != -1) + __builtin_abort(); + } + + /* Test conversion with zero extension */ + { + unsigned _BitInt(8) small = 255; + unsigned _BitInt(32) large = small; + + if (large != 255) + __builtin_abort(); + } + + /* Test conversion from floating-point types. */ + { + double d = 12345.0; + _BitInt(32) bi = (_BitInt(32)) d; + unsigned _BitInt(16) ubi = (unsigned _BitInt(16)) 255.0; + double large_d = 1234567890.0; + _BitInt(64) from_double = (_BitInt(64)) large_d; + + if (bi != 12345) + __builtin_abort(); + if (ubi != 255) + __builtin_abort(); + if (from_double != 1234567890wb) + __builtin_abort(); + } + + /* Test floating-point conversion with truncation toward zero. */ + { + double d1 = 12.75; + double d2 = -12.75; + _BitInt(16) a = (_BitInt(16)) d1; + _BitInt(16) b = (_BitInt(16)) d2; + + if (a != 12) + __builtin_abort(); + if (b != -12) + __builtin_abort(); + } + + /* Test conversion from char */ + { + char c = 'A'; + _BitInt(8) bi = c; + + if (bi != 'A') + __builtin_abort(); + } + + /* Test conversion to char */ + { + _BitInt(8) bi = 66; + char c = (char)bi; + + if (c != 'B') + __builtin_abort(); + } + + /* Test conversion from _BitInt to floating-point. */ + { + _BitInt(32) bi = 2048; + unsigned _BitInt(16) ubi = 511; + double d = (double) bi; + float f = (float) ubi; + + if (d != 2048.0) + __builtin_abort(); + if (f != 511.0f) + __builtin_abort(); + } + + return 0; +} + +/* Test passes if all conversions work correctly */ diff --git a/gcc/testsuite/gcc.target/powerpc/bitint-endian-powerpc.c b/gcc/testsuite/gcc.target/powerpc/bitint-endian-powerpc.c new file mode 100644 index 00000000000..0b5c2d00f8e --- /dev/null +++ b/gcc/testsuite/gcc.target/powerpc/bitint-endian-powerpc.c @@ -0,0 +1,127 @@ +/* { dg-do run { target bitint } } */ +/* { dg-additional-options "-std=c23 -O0" } */ + +/* Test _BitInt endianness handling for PowerPC. + This test verifies that _BitInt works correctly on both + big-endian and little-endian PowerPC systems. */ + +#include <string.h> + +static void +check_bytes (const unsigned char *bytes, const unsigned char *expected, int n) +{ + for (int i = 0; i < n; ++i) + if (bytes[i] != expected[i]) + __builtin_abort (); +} + +int main(void) +{ + /* Verify actual multi-limb byte layout. */ + { + _BitInt(96) val = 0x010203040506070809101112wb; + unsigned char bytes[12]; + + memcpy (bytes, &val, 12); + +#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ + if (bytes[0] != 0x01 || bytes[1] != 0x02 || bytes[10] != 0x11 + || bytes[11] != 0x12) + __builtin_abort (); +#else + if (bytes[0] != 0x12 || bytes[1] != 0x11 || bytes[10] != 0x02 + || bytes[11] != 0x01) + __builtin_abort (); +#endif + } + + /* Verify byte layout across a non-power-of-two multi-limb width. */ + { + unsigned _BitInt(65) val + = ((unsigned _BitInt(65)) 0x01u << 64) + | (unsigned _BitInt(65)) 0x02030405u; + unsigned char bytes[16] = { 0 }; + + memcpy (bytes, &val, sizeof (val)); + +#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ + if (bytes[7] != 0x01 || bytes[8] != 0x00 || bytes[12] != 0x02 + || bytes[13] != 0x03 || bytes[14] != 0x04 || bytes[15] != 0x05) + __builtin_abort (); +#else + if (bytes[0] != 0x05 || bytes[1] != 0x04 || bytes[2] != 0x03 + || bytes[3] != 0x02 || bytes[8] != 0x01) + __builtin_abort (); +#endif + } + + /* Also verify limb ordering across a larger multi-limb object. */ + { + unsigned _BitInt(160) value + = ((unsigned _BitInt(160)) 0x01020304u << 128) + | ((unsigned _BitInt(160)) 0x11121314u << 96) + | ((unsigned _BitInt(160)) 0x21222324u << 64) + | ((unsigned _BitInt(160)) 0x31323334u << 32) + | (unsigned _BitInt(160)) 0x41424344u; + const unsigned char *bytes = (const unsigned char *) &value; +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ + const unsigned char expected[20] + = { 0x44, 0x43, 0x42, 0x41, 0x34, 0x33, 0x32, 0x31, + 0x24, 0x23, 0x22, 0x21, 0x14, 0x13, 0x12, 0x11, + 0x04, 0x03, 0x02, 0x01 }; +#else + const unsigned char expected[20] + = { 0x01, 0x02, 0x03, 0x04, 0x11, 0x12, 0x13, 0x14, + 0x21, 0x22, 0x23, 0x24, 0x31, 0x32, 0x33, 0x34, + 0x41, 0x42, 0x43, 0x44 }; +#endif + check_bytes (bytes, expected, 20); + } + + /* Test that arithmetic works correctly regardless of endianness */ + { + _BitInt(64) a = 0x123456789ABCDEF0LL; + _BitInt(64) b = 0x0FEDCBA987654321LL; + _BitInt(64) sum = a + b; + + /* The sum should be correct regardless of byte order */ + if (sum != 0x2222222222222211LL) + __builtin_abort(); + } + +#ifdef __powerpc64__ + /* Test 128-bit values on 64-bit systems */ + { + _BitInt(128) value = 1; + + /* Shift left by 100 bits */ + value = value << 100; + + /* Shift right by 100 bits */ + value = value >> 100; + + /* Should get back to 1 */ + if (value != 1) + __builtin_abort(); + } +#endif + + /* Test that comparisons work correctly */ + { + _BitInt(32) a = 100; + _BitInt(32) b = 200; + + if (!(a < b)) + __builtin_abort(); + if (!(b > a)) + __builtin_abort(); + if (a >= b) + __builtin_abort(); + if (b <= a) + __builtin_abort(); + } + + return 0; +} + +/* Test passes if endianness handling is correct */ diff --git a/gcc/testsuite/gcc.target/powerpc/bitint-sizes-powerpc.c b/gcc/testsuite/gcc.target/powerpc/bitint-sizes-powerpc.c new file mode 100644 index 00000000000..28dfd598ccf --- /dev/null +++ b/gcc/testsuite/gcc.target/powerpc/bitint-sizes-powerpc.c @@ -0,0 +1,122 @@ +/* { dg-do run { target bitint } } */ +/* { dg-additional-options "-std=c23 -O0" } */ + +/* Test _BitInt size calculations for PowerPC. + This test verifies that _BitInt types have the expected sizes + based on the PowerPC implementation. */ + +#ifdef __powerpc64__ +__extension__ typedef __int128 bitint_int128_t; +#endif + +static long unsigned int +calc_size (int n) +{ + /* PowerPC _BitInt size calculation: + - N <= 8: 1 byte (QImode) + - N <= 16: 2 bytes (HImode) + - N <= 32: 4 bytes (SImode) + - N <= 64: 8 bytes (DImode) [64-bit mode only] + - N <= 128: 16 bytes (TImode) [64-bit mode only] + - N > 128: Multiple limbs (DImode or SImode based on TARGET_64BIT) + */ +#ifdef __powerpc64__ + /* 64-bit PowerPC. For n > 128, abi_limb_mode differs by endianness + (rs6000_bitint_type_info): TImode (16-byte) chunks on little-endian, + but DImode (8-byte) chunks on big-endian, since gimple-lower-bitint.cc + cannot support abi_limb_mode != limb_mode on big-endian. */ + if (n > 128) + { +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ + return ((n - 1)/128 + 1) * sizeof(bitint_int128_t); +#else + return ((n - 1)/64 + 1) * sizeof(long long); +#endif + } + if (n > 64) + return sizeof(bitint_int128_t); + if (n > 32) + return sizeof(long long); +#else + /* 32-bit PowerPC: SImode limbs throughout (no 64-bit limb, unlike + ppc64), so anything above a single 32-bit limb is sized in 4-byte + (sizeof(int)) increments, not 8-byte ones. */ + if (n > 32) + return ((n - 1)/32 + 1) * sizeof(int); +#endif + if (n > 16) + return sizeof(int); + if (n > 8) + return sizeof(short); + else + return sizeof(char); +} + +#define CHECK_SIZE(N) \ + if (sizeof(_BitInt(N)) != calc_size(N)) \ + __builtin_abort () + +#define CHECK_USIZE(N) \ + if (sizeof(unsigned _BitInt(N)) != calc_size(N)) \ + __builtin_abort (); + +int main (void) +{ + /* Small sizes */ + CHECK_USIZE(1); + CHECK_SIZE(2); + CHECK_SIZE(3); + CHECK_SIZE(7); + CHECK_SIZE(8); + + /* 9-16 bits */ + CHECK_SIZE(9); + CHECK_SIZE(13); + CHECK_SIZE(15); + CHECK_SIZE(16); + + /* 17-32 bits */ + CHECK_SIZE(17); + CHECK_SIZE(24); + CHECK_SIZE(31); + CHECK_SIZE(32); + + /* 33-64 bits */ + CHECK_SIZE(33); + CHECK_SIZE(42); + CHECK_SIZE(53); + CHECK_SIZE(63); + CHECK_SIZE(64); + +#ifdef __powerpc64__ + /* 65-128 bits (64-bit mode only) */ + CHECK_SIZE(65); + CHECK_SIZE(79); + CHECK_SIZE(96); + CHECK_SIZE(113); + CHECK_SIZE(127); + CHECK_SIZE(128); + + /* Large sizes (64-bit mode) */ + CHECK_SIZE(129); + CHECK_SIZE(153); + CHECK_SIZE(255); + CHECK_SIZE(256); + CHECK_SIZE(257); + CHECK_SIZE(353); + CHECK_SIZE(512); + CHECK_SIZE(620); + CHECK_SIZE(1024); + CHECK_SIZE(2048); +#else + /* 65-128 bits on 32-bit mode use multiple 32-bit limbs. */ + CHECK_SIZE(65); + CHECK_SIZE(79); + CHECK_SIZE(96); + CHECK_SIZE(113); + CHECK_SIZE(127); + CHECK_SIZE(128); +#endif + + return 0; +} diff --git a/libgcc/config/rs6000/fixtfbitint-ibm128.c b/libgcc/config/rs6000/fixtfbitint-ibm128.c new file mode 100644 index 00000000000..d046d9ad1f8 --- /dev/null +++ b/libgcc/config/rs6000/fixtfbitint-ibm128.c @@ -0,0 +1,42 @@ +/* Convert an IBM long double (IBM double-double, TF mode) to a _BitInt. + + Copyright (C) 2026 Free Software Foundation, Inc. + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 3, or (at your option) any later +version. + +GCC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +Under Section 7 of GPL version 3, you are granted additional +permissions described in the GCC Runtime Library Exception, version +3.1, as published by the Free Software Foundation. + +You should have received a copy of the GNU General Public License and +a copy of the GCC Runtime Library Exception along with this program; +see the files COPYING3 and COPYING.RUNTIME respectively. If not, see +<http://www.gnu.org/licenses/>. */ + +/* Compiled with -mabi=ibmlongdouble (long double = IBM double-double) + plus FP128_CFLAGS_SW (-mfloat128 for __float128 / KF mode access). + + Route through the IEEE 128-bit (__float128 / KF) representation: + TF (IBM DD) -> KF (via __extendkftf2; exact, KF has more precision) + KF -> _BitInt (via __fixkfbitint from fp128_softfp_funcs). */ + +typedef unsigned long long UBILtype; +typedef int SItype; + +extern void __fixkfbitint (UBILtype *, SItype, __float128); + +void +__fixtfbitint (UBILtype *r, SItype rprec, long double a) +{ + __fixkfbitint (r, rprec, (__float128) a); +} diff --git a/libgcc/config/rs6000/float128-sed b/libgcc/config/rs6000/float128-sed index c0fcddb1959..6b6b63c0581 100644 --- a/libgcc/config/rs6000/float128-sed +++ b/libgcc/config/rs6000/float128-sed @@ -12,6 +12,8 @@ s/__floattitf/__floattikf/g s/__floatuntitf/__floatuntikf/g s/__fixtfti/__fixkfti/g s/__fixunstfti/__fixunskfti/g +s/__floatbitinttf/__floatbitintkf/g +s/__fixtfbitint/__fixkfbitint/g s/__floatsitf/__floatsikf/g s/__floatunditf/__floatundikf/g s/__floatunsitf/__floatunsikf/g diff --git a/libgcc/config/rs6000/float128-sed-hw b/libgcc/config/rs6000/float128-sed-hw index 3d2bf556da1..87a727f349b 100644 --- a/libgcc/config/rs6000/float128-sed-hw +++ b/libgcc/config/rs6000/float128-sed-hw @@ -12,6 +12,8 @@ s/__floattitf/__floattikf_sw/g s/__floatuntitf/__floatuntikf_sw/g s/__fixtfti/__fixkfti_sw/g s/__fixunstfti/__fixunskfti_sw/g +s/__floatbitinttf/__floatbitintkf/g +s/__fixtfbitint/__fixkfbitint/g s/__floatsitf/__floatsikf_sw/g s/__floatunditf/__floatundikf_sw/g s/__floatunsitf/__floatunsikf_sw/g diff --git a/libgcc/config/rs6000/floatbitinttf-ibm128.c b/libgcc/config/rs6000/floatbitinttf-ibm128.c new file mode 100644 index 00000000000..8223e097478 --- /dev/null +++ b/libgcc/config/rs6000/floatbitinttf-ibm128.c @@ -0,0 +1,44 @@ +/* Convert a _BitInt to IBM long double (IBM double-double, TF mode). + + Copyright (C) 2026 Free Software Foundation, Inc. + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 3, or (at your option) any later +version. + +GCC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +Under Section 7 of GPL version 3, you are granted additional +permissions described in the GCC Runtime Library Exception, version +3.1, as published by the Free Software Foundation. + +You should have received a copy of the GNU General Public License and +a copy of the GCC Runtime Library Exception along with this program; +see the files COPYING3 and COPYING.RUNTIME respectively. If not, see +<http://www.gnu.org/licenses/>. */ + +/* Compiled with -mabi=ibmlongdouble (long double = IBM double-double) + plus FP128_CFLAGS_SW (-mfloat128 for __float128 / KF mode access). + + Route through the IEEE 128-bit (__float128 / KF) representation: + _BitInt -> KF (via __floatbitintkf from fp128_softfp_funcs) + KF -> TF (implicit narrowing; IBM DD has ~106 bits + effective precision vs KF's 113 bits, so no precision is + lost for values representable in IBM double-double). */ + +typedef unsigned long long UBILtype; +typedef int SItype; + +extern __float128 __floatbitintkf (const UBILtype *, SItype); + +long double +__floatbitinttf (const UBILtype *i, SItype iprec) +{ + return (long double) __floatbitintkf (i, iprec); +} diff --git a/libgcc/config/rs6000/libgcc-glibc.ver b/libgcc/config/rs6000/libgcc-glibc.ver index b1e4f0a47fe..fc842199e01 100644 --- a/libgcc/config/rs6000/libgcc-glibc.ver +++ b/libgcc/config/rs6000/libgcc-glibc.ver @@ -71,3 +71,15 @@ GCC_4.2.0 { __gcc_qunord %endif } + +%inherit GCC_17.0.0 GCC_4.2.0 +GCC_17.0.0 { + __floatbitintkf + __fixkfbitint + __floatbitinttf + __fixtfbitint + __floatbitintsf + __fixsfbitint + __floatbitintdf + __fixdfbitint +} diff --git a/libgcc/config/rs6000/t-float128 b/libgcc/config/rs6000/t-float128 index b09b5664af0..43a20f4e4e6 100644 --- a/libgcc/config/rs6000/t-float128 +++ b/libgcc/config/rs6000/t-float128 @@ -15,7 +15,8 @@ fp128_softfp_funcs = addkf3 subkf3 mulkf3 divkf3 negkf2 \ unordkf2 eqkf2 gekf2 lekf2 \ extendsfkf2 extenddfkf2 trunckfsf2 trunckfdf2 \ fixkfsi fixkfdi fixunskfsi fixunskfdi \ - floatsikf floatdikf floatunsikf floatundikf + floatsikf floatdikf floatunsikf floatundikf \ + floatbitintkf fixkfbitint fp128_softfp_src = $(addsuffix -sw.c,$(fp128_softfp_funcs)) fp128_softfp_static_obj = $(addsuffix -sw$(objext),$(fp128_softfp_funcs)) @@ -30,14 +31,53 @@ fp128_dec_funcs = _kf_to_sd _kf_to_dd _kf_to_td \ ibm128_dec_funcs = _tf_to_sd _tf_to_dd _tf_to_td \ _sd_to_tf _dd_to_tf _td_to_tf +# _BitInt <-> IBM long double (TF = IBM double-double) conversions. +# Implemented in libgcc/config/rs6000/{floatbitinttf-ibm128,fixtfbitint-ibm128}.c; +# compiled with -mabi=ibmlongdouble, delegating through the KF intermediary. +ibm128_bitint_funcs = floatbitinttf-ibm128 fixtfbitint-ibm128 + # New functions for software emulation fp128_ppc_funcs = floattikf-sw floatuntikf-sw \ fixkfti-sw fixunskfti-sw \ extendkftf2-sw trunctfkf2-sw \ - sfp-exceptions _mulkc3 _divkc3 _powikf2 + sfp-exceptions _mulkc3 _divkc3 _powikf2 \ + $(ibm128_bitint_funcs) + +# Plain binary float/double <-> _BitInt conversions. Unlike the DPD +# decimal set below, these are unconditional in the generic t-softfp +# mechanism (softfp_bitint_func_list): every target using t-softfp gets +# them regardless of decimal_float. t-softfp is not in the rs6000 +# tmake_file, so wire them up here directly from soft-fp/, matching the +# LIB2ADD_ST treatment in t-softfp. +rs6000_bitint_bin_funcs = fixsfbitint floatbitintsf \ + fixdfbitint floatbitintdf + +rs6000_bitint_bin_src = $(addprefix $(srcdir)/soft-fp/, \ + $(addsuffix .c,$(rs6000_bitint_bin_funcs))) + +LIB2ADD_ST += $(rs6000_bitint_bin_src) ifeq ($(decimal_float),yes) fp128_ppc_funcs += $(fp128_dec_funcs) + +# DPD _BitInt <-> decimal floating-point conversions, plus the TImode +# <-> decimal floating-point conversions _BitInt lowering depends on. +# t-softfp is not in the rs6000 tmake_file so these are never compiled +# via the generic softfp_bid_list mechanism. Wire them up here instead, +# sourced directly from libgcc/soft-fp/ and added to LIB2ADD_ST (static +# library only), matching the LIB2ADD_ST treatment in t-softfp. +rs6000_bitint_dec_funcs = bitintpow10 \ + fixsdbitint floatbitintsd \ + fixsdti fixunssdti floattisd floatuntisd \ + fixddbitint floatbitintdd \ + fixddti fixunsddti floattidd floatuntidd \ + fixtdbitint floatbitinttd \ + fixtdti fixunstdti floattitd floatuntitd + +rs6000_bitint_dec_src = $(addprefix $(srcdir)/soft-fp/, \ + $(addsuffix .c,$(rs6000_bitint_dec_funcs))) + +LIB2ADD_ST += $(rs6000_bitint_dec_src) endif fp128_ppc_src = $(addprefix $(srcdir)/config/rs6000/,$(addsuffix \ @@ -100,6 +140,12 @@ IBM128_CFLAGS_DECIMAL = -mno-gnu-attribute -Wno-psabi -mabi=ibmlongdouble $(fp128_dec_objs) : INTERNAL_CFLAGS += $(FP128_CFLAGS_DECIMAL) $(ibm128_dec_objs) : INTERNAL_CFLAGS += $(IBM128_CFLAGS_DECIMAL) +ibm128_bitint_objs = $(addsuffix $(objext),$(ibm128_bitint_funcs)) \ + $(addsuffix _s$(objext),$(ibm128_bitint_funcs)) + +IBM128_BITINT_CFLAGS = -mno-gnu-attribute -Wno-psabi -mabi=ibmlongdouble +$(ibm128_bitint_objs) : INTERNAL_CFLAGS += $(IBM128_BITINT_CFLAGS) + $(fp128_softfp_src) : $(srcdir)/soft-fp/$(subst -sw,,$(subst kf,tf,$@)) $(fp128_dep) @src="$(srcdir)/soft-fp/$(subst -sw,,$(subst kf,tf,$@))"; \ echo "Create $@"; \ -- 2.52.0
