The changes have been bootstrapped and regression tested on powerpc64le-linux and powerpc64-linux (m64/m32).
Changes from V1: * The major change being limb mode now DImode on 64-bit PowerPC and SImode on 32-bit PowerPC. * Padding bits above the precision in the most significant limb are unspecified(bitint_ext_undef). So the same rule describes ppc64le, ppc64 big-endian and ppc32. * Additional powerpc specific test cases. 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. rs6000_bitint_type_info selects the limb mode by precision: QImode, HImode or SImode for n <= 8, 16 or 32 respectively, and above that the widest integer that fits in one general purpose register, i.e. DImode on 64-bit PowerPC and SImode on 32-bit PowerPC. The ABI limb mode always equals the internal limb mode, so a _BitInt wider than one limb is an array of limbs, ordered least to most significant on little-endian and most to least significant on big-endian, exactly like any other multi-word integer. Padding bits above the precision in the most significant limb are unspecified (bitint_ext_undef). Neither property is conditional on endianness, so the same rule describes ppc64le, ppc64 big-endian and ppc32. Large _BitInt -- more than 16 bytes under ELFv2, more than 8 bytes otherwise -- is passed and returned by reference, as aggregates of those sizes already are. The threshold is computed in one place, rs6000_bitint_large_p, so that rs6000_return_in_memory and rs6000_pass_by_reference cannot drift apart. Both use BITINT_TYPE_P rather than a TREE_CODE test, so that C2Y bit-precise enums backed by _BitInt are handled identically to plain _BitInt. rs6000_function_value and rs6000_promote_function_mode exclude BITINT_TYPE from the generic sub-word integer promotion, so that the mode used for a _BitInt return value agrees with the one the middle end computes for the type; promoting it there would produce a mode that does not match the return register and ICE in emit_move_insn. Eleven new powerpc execution tests covering alignment, size, argument and return passing (including the by-reference threshold and the transition around 128 bits), arithmetic, bitwise and shift operations, comparisons, conversions, byte layout, struct layout and varargs. 2026-08-19 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 values, and enums backed by _BitInt, via a hidden pointer. (rs6000_promote_function_mode): Do not promote BITINT_TYPE. (rs6000_pass_by_reference): Pass large _BitInt values, and enums backed by _BitInt, by reference. * 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 and __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-abi-call.c: New test. * gcc.target/powerpc/bitint-alignments-powerpc.c: New test. * gcc.target/powerpc/bitint-args-powerpc.c: New test. * gcc.target/powerpc/bitint-arith-powerpc.c: New test. * gcc.target/powerpc/bitint-bitwise-powerpc.c: New test. * gcc.target/powerpc/bitint-compare-powerpc.c: New test. * gcc.target/powerpc/bitint-conversions-powerpc.c: New test. * gcc.target/powerpc/bitint-endian-powerpc.c: New test. * gcc.target/powerpc/bitint-sizes-powerpc.c: New test. * gcc.target/powerpc/bitint-struct-layout.c: New test. * gcc.target/powerpc/bitint-varargs.c: New test. diff --git a/gcc/config/rs6000/rs6000-call.cc b/gcc/config/rs6000/rs6000-call.cc index 0da2c1f6071..ea70f23d5c5 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,20 @@ 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 and must agree with what + rs6000_function_value computes, which likewise excludes + BITINT_TYPE_P from promotion; promoting here would yield a mode + that does not match the return register and ICE 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; @@ -1993,6 +2025,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 99555988913..d68bff19d36 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 @@ -24207,6 +24210,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; @@ -24616,6 +24620,46 @@ 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: + n <= 8 : QImode + n <= 16 : HImode + n <= 32 : SImode + n > 32 : DImode (64-bit PowerPC) / SImode (32-bit PowerPC) + + The ABI limb mode always equals the internal limb mode, so a _BitInt + wider than a single limb is an array of DImode limbs on 64-bit + PowerPC and of SImode limbs on 32-bit PowerPC, ordered least to most + significant on little-endian and most to least significant on + big-endian, like any other multi-word integer. This is what x86_64 + and s390x do as well; keeping abi_limb_mode == limb_mode also avoids + the abi_limb_mode != limb_mode paths in gimple-lower-bitint.cc, + which are not supported on big-endian targets. + + Padding bits above the precision in the most significant limb are + unspecified (bitint_ext_undef) on both endiannesses, matching + x86_64 and aarch64. */ + +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 + info->limb_mode = TARGET_64BIT ? DImode : SImode; + + info->abi_limb_mode = info->limb_mode; + info->big_endian = BYTES_BIG_ENDIAN; + info->extended = bitint_ext_undef; + + 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-abi-call.c b/gcc/testsuite/gcc.target/powerpc/bitint-abi-call.c new file mode 100644 index 00000000000..387e0afb97f --- /dev/null +++ b/gcc/testsuite/gcc.target/powerpc/bitint-abi-call.c @@ -0,0 +1,208 @@ +/* { dg-do run { target bitint } } */ +/* { dg-additional-options "-std=c23 -O2" } */ + +/* Verify rs6000 ABI handling for C23 _BitInt. + This test exercises: + - by-value argument passing + - return values + - mixed integer/_BitInt arguments + - ABI transition at _BitInt(129) + - multiple _BitInt arguments +*/ + +typedef _BitInt(8) bi8_t; +typedef _BitInt(16) bi16_t; +typedef _BitInt(32) bi32_t; +typedef _BitInt(65) bi65_t; +typedef _BitInt(128) bi128_t; +typedef _BitInt(129) bi129_t; + +static bi65_t +make65 (void) +{ + return ((bi65_t) 1 << 60) + 17; +} + +static bi128_t +make128 (void) +{ + return ((bi128_t) 1 << 100) + 12345; +} + +static bi129_t +make129 (void) +{ + return ((bi129_t) 1 << 120) + 77; +} + +__attribute__((noinline)) +static bi8_t +id8 (bi8_t x) +{ + return x; +} + +__attribute__((noinline)) +static bi16_t +id16 (bi16_t x) +{ + return x; +} + +__attribute__((noinline)) +static bi32_t +id32 (bi32_t x) +{ + return x; +} + +__attribute__((noinline)) +static bi65_t +id65 (bi65_t x) +{ + return x; +} + +__attribute__((noinline)) +static bi128_t +id128 (bi128_t x) +{ + return x; +} + +__attribute__((noinline)) +static bi129_t +id129 (bi129_t x) +{ + return x; +} + +/* Exercise multiple _BitInt arguments. */ + +__attribute__((noinline)) +static bi65_t +combine65 (bi65_t a, bi65_t b) +{ + return (a << 5) + b; +} + +__attribute__((noinline)) +static bi128_t +combine128 (bi128_t a, bi128_t b) +{ + return (a << 17) ^ b; +} + +__attribute__((noinline)) +static bi129_t +combine129 (bi129_t a, bi129_t b) +{ + return (a << 19) + b; +} + +/* Exercise mixed integer/_BitInt argument passing. */ + +__attribute__((noinline)) +static bi65_t +mix65 (int i, bi65_t x, long l, bi65_t y) +{ + return x + y + (bi65_t) i + (bi65_t) l; +} + +__attribute__((noinline)) +static bi128_t +mix128 (unsigned int u, bi128_t x, + unsigned long v, bi128_t y) +{ + return (x ^ y) + (bi128_t) u + (bi128_t) v; +} + +__attribute__((noinline)) +static bi129_t +mix129 (int i, bi129_t x, long l, bi129_t y) +{ + return (x << 3) + y + (bi129_t) i + (bi129_t) l; +} + +/* Exercise register/stack transitions. */ + +__attribute__((noinline)) +static bi65_t +many65 (bi65_t a, + bi65_t b, + bi65_t c, + bi65_t d, + bi65_t e, + bi65_t f) +{ + return a + b + c + d + e + f; +} + +int +main (void) +{ + bi8_t a8 = 123; + bi16_t a16 = 30000; + bi32_t a32 = 0x12345678; + + bi65_t a65 = make65 (); + bi65_t b65 = ((bi65_t) 1 << 42) + 5; + + bi128_t a128 = make128 (); + bi128_t b128 = ((bi128_t) 1 << 70) + 67890; + + bi129_t a129 = make129 (); + bi129_t b129 = ((bi129_t) 1 << 63) + 88; + + /* Identity. */ + + if (id8 (a8) != a8) + __builtin_abort (); + + if (id16 (a16) != a16) + __builtin_abort (); + + if (id32 (a32) != a32) + __builtin_abort (); + + if (id65 (a65) != a65) + __builtin_abort (); + + if (id128 (a128) != a128) + __builtin_abort (); + + if (id129 (a129) != a129) + __builtin_abort (); + + /* Multiple _BitInt arguments. */ + + if (combine65 (a65, b65) != ((a65 << 5) + b65)) + __builtin_abort (); + + if (combine128 (a128, b128) != ((a128 << 17) ^ b128)) + __builtin_abort (); + + if (combine129 (a129, b129) != ((a129 << 19) + b129)) + __builtin_abort (); + + /* Mixed signatures. */ + + if (mix65 (5, a65, 7, b65) + != a65 + b65 + (bi65_t) 12) + __builtin_abort (); + + if (mix128 (9, a128, 11, b128) + != ((a128 ^ b128) + (bi128_t) 20)) + __builtin_abort (); + + if (mix129 (3, a129, 4, b129) + != ((a129 << 3) + b129 + (bi129_t) 7)) + __builtin_abort (); + + /* Many arguments. */ + + if (many65 (1, 2, 3, 4, 5, 6) != 21) + __builtin_abort (); + + return 0; +} 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..941d29bd8d8 --- /dev/null +++ b/gcc/testsuite/gcc.target/powerpc/bitint-alignments-powerpc.c @@ -0,0 +1,70 @@ +/* { dg-do run { target bitint } } */ +/* { dg-additional-options "-std=c23 -O2" } */ + +/* Test _BitInt alignment for PowerPC (uniform DImode ABI). + _BitInt(33..N) uses DImode (8-byte) limbs on ppc64 and SImode + (4-byte) limbs on ppc32. All multi-limb types are 8-byte aligned + on ppc64 and 4-byte aligned on ppc32, regardless of endianness. */ + +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: DImode alignment (8 bytes) for 33..N bit _BitInt */ + if (__alignof__(_BitInt(64)) != 8) + __builtin_abort(); + + /* _BitInt(65..128) = two DImode limbs: 8-byte aligned */ + if (__alignof__(_BitInt(128)) != 8) + __builtin_abort(); +#else + /* 32-bit PowerPC: SImode alignment (4 bytes) for 33..N 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)) != 8) + __builtin_abort(); +#endif + + /* Large (> 128-bit) _BitInt: DImode limbs => 8-byte aligned on ppc64 */ +#ifdef __powerpc64__ + /* Uniform DImode: 8 bytes on all ppc64, both endiannesses. */ + if (__alignof__(_BitInt(200)) != 8) + __builtin_abort(); + + if (__alignof__(_BitInt(256)) != 8) + __builtin_abort(); +#else + /* 32-bit PowerPC: SImode limbs => 4-byte aligned */ + 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..dbed9bbabb6 --- /dev/null +++ b/gcc/testsuite/gcc.target/powerpc/bitint-sizes-powerpc.c @@ -0,0 +1,97 @@ +/* { dg-do run { target bitint } } */ +/* { dg-additional-options "-std=c23 -O0" } */ + +/* Test _BitInt size calculations for PowerPC (uniform DImode ABI). + ppc64: DImode (8-byte) limbs for all widths > 32 bits. + ppc32: SImode (4-byte) limbs throughout. */ + +static long unsigned int +calc_size (int n) +{ +#ifdef __powerpc64__ +/* 64-bit PowerPC: uniform DImode (8-byte) limbs for all n > 32. + _BitInt(65..128) = 2 limbs = 16 bytes (same on LE and BE). + _BitInt(>128) = ceil(n/64) * 8 bytes. */ + if (n > 32) + return ((n - 1) / 64 + 1) * sizeof (long long); +#else + /* 32-bit PowerPC: SImode (4-byte) limbs throughout. */ + 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/gcc/testsuite/gcc.target/powerpc/bitint-struct-layout.c b/gcc/testsuite/gcc.target/powerpc/bitint-struct-layout.c new file mode 100644 index 00000000000..71bbc86dc3b --- /dev/null +++ b/gcc/testsuite/gcc.target/powerpc/bitint-struct-layout.c @@ -0,0 +1,79 @@ +/* { dg-do run { target bitint } } */ +/* { dg-additional-options "-std=c23" } */ + +#include <stddef.h> + +struct S1 +{ + char c; + _BitInt(65) x; +}; + +struct S2 +{ + _BitInt(65) x; + char c; +}; + +struct S3 +{ + int i; + _BitInt(128) x; +}; + +struct S4 +{ + _BitInt(128) x; + int i; +}; + +static void +check_s1 (void) +{ + if (__alignof__ (struct S1) != __alignof__ (_BitInt(65))) + __builtin_abort (); + + if (offsetof (struct S1, x) % __alignof__ (_BitInt(65)) != 0) + __builtin_abort (); + + if (sizeof (struct S1) < offsetof (struct S1, x) + sizeof (_BitInt(65))) + __builtin_abort (); +} + +static void +check_s2 (void) +{ + if (__alignof__ (struct S2) != __alignof__ (_BitInt(65))) + __builtin_abort (); + + if (offsetof (struct S2, x) != 0) + __builtin_abort (); +} + +static void +check_s3 (void) +{ + if (__alignof__ (struct S3) != __alignof__ (_BitInt(128))) + __builtin_abort (); + + if (offsetof (struct S3, x) % __alignof__ (_BitInt(128)) != 0) + __builtin_abort (); +} + +static void +check_s4 (void) +{ + if (offsetof (struct S4, x) != 0) + __builtin_abort (); +} + +int +main (void) +{ + check_s1 (); + check_s2 (); + check_s3 (); + check_s4 (); + return 0; +} + diff --git a/gcc/testsuite/gcc.target/powerpc/bitint-varargs.c b/gcc/testsuite/gcc.target/powerpc/bitint-varargs.c new file mode 100644 index 00000000000..d3be025cd17 --- /dev/null +++ b/gcc/testsuite/gcc.target/powerpc/bitint-varargs.c @@ -0,0 +1,52 @@ +/* { dg-do run { target bitint } } */ +/* { dg-additional-options "-std=c23" } */ + +#include <stdarg.h> + +static _BitInt(65) +sum65 (int n, ...) +{ + va_list ap; + va_start (ap, n); + + _BitInt(65) s = 0; + + while (n--) + s += va_arg (ap, _BitInt(65)); + + va_end (ap); + return s; +} + +static _BitInt(128) +sum128 (int n, ...) +{ + va_list ap; + va_start (ap, n); + + _BitInt(128) s = 0; + + while (n--) + s += va_arg (ap, _BitInt(128)); + + va_end (ap); + return s; +} + +int +main (void) +{ + _BitInt(65) a = (_BitInt(65)) 1 << 40; + _BitInt(65) b = (_BitInt(65)) 1 << 39; + + if (sum65 (2, a, b) != a + b) + __builtin_abort (); + + _BitInt(128) x = (_BitInt(128)) 1 << 100; + _BitInt(128) y = (_BitInt(128)) 123456789; + + if (sum128 (2, x, y) != x + y) + __builtin_abort (); + + 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
