From patchwork Thu Mar 13 14:55:23 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Jonathan Wakely X-Patchwork-Id: 107833 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 6F9453857B9B for ; Thu, 13 Mar 2025 14:58:53 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 6F9453857B9B Authentication-Results: sourceware.org; dkim=pass (1024-bit key, unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=e8NhaQOk X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by sourceware.org (Postfix) with ESMTP id E25F23858D33 for ; Thu, 13 Mar 2025 14:57:10 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org E25F23858D33 Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=redhat.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=redhat.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org E25F23858D33 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=170.10.133.124 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1741877831; cv=none; b=tPK73Q1AeI//Arxl5nKEIJG3iMJkcS+mR4czXwS+fgF/V32DeGlvqHK80MsaDgWNzrQDFh6uII+VJuMSV0ghbUuUYaObnTot3KMdu007hgVs6QP+86DZ7Rzf88ga1larkLAqf8h6ftmHPTaGfM2NpLnxTCo5GneR86t6c0sK3XE= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1741877831; c=relaxed/simple; bh=+fR4bFtJceZit8wEy5T6p1CNURgIQ6cURoTitH8knT4=; h=DKIM-Signature:From:To:Subject:Date:Message-ID:MIME-Version; b=fq7PSPW7QGvnaFUDD3uxsZVhPKjkVNCBLd8W+z4dB7grmtjxCOja35phEpqjNe6oZDxmku+QKUOmI2EUIZ+msRgG02JW20TPmdN9nIg5dVrYcPFuiZMbKe6PmZBT/ShoNNPMX3qdaKJAwGqRYQhAggzr01U2FnOuyPG5H9OrKbM= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org E25F23858D33 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1741877830; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=KmnYTn1QEddcw2daOuGeFwtMR+i28zGGcMPQwxkCkPw=; b=e8NhaQOkFRg06gwlyNwK5qpuq3wxQmeor0c1OOSq2dsFkXAos0xr9rA6DxstVkaMgdyr41 LE6Ux0LDupGpqAj0p7BuQsj02UNck9xfgJR3wl+w6TGsgCNjoRbc/29Q8x6GA/phbd+Cnq d+SQmR/Erc5xniJc44W/Q0iMQYdKY+o= Received: from mx-prod-mc-02.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-70-NODWMe8kPfySMx7JYjCZfg-1; Thu, 13 Mar 2025 10:57:09 -0400 X-MC-Unique: NODWMe8kPfySMx7JYjCZfg-1 X-Mimecast-MFC-AGG-ID: NODWMe8kPfySMx7JYjCZfg_1741877828 Received: from mx-prod-int-02.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-02.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.15]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-02.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 8A4A61955E93 for ; Thu, 13 Mar 2025 14:57:08 +0000 (UTC) Received: from localhost (unknown [10.42.28.10]) by mx-prod-int-02.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 2091619373D7; Thu, 13 Mar 2025 14:57:07 +0000 (UTC) From: Jonathan Wakely To: gcc-patches@gcc.gnu.org Cc: Patrick Palka Subject: [committed v2 1/2] libstdc++: Implement for C++26 (P3370R1) Date: Thu, 13 Mar 2025 14:55:23 +0000 Message-ID: <20250313145706.1303288-1-jwakely@redhat.com> In-Reply-To: <20241203122054.281326-1-jwakely@redhat.com> References: <20241203122054.281326-1-jwakely@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.0 on 10.30.177.15 X-Mimecast-Spam-Score: 0 X-Mimecast-MFC-PROC-ID: 9viSr0mlc4ss2KONuJM3gD3CmoK0ksGVnsnIHPg8c_c_1741877828 X-Mimecast-Originator: redhat.com X-Spam-Status: No, score=-12.0 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, KAM_SHORT, RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H2, SPF_HELO_NONE, SPF_NONE, TXREP autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org X-BeenThere: gcc-patches@gcc.gnu.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Gcc-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: gcc-patches-bounces~patchwork=sourceware.org@gcc.gnu.org This is the first part of the P3370R1 proposal just approved by the committee in Wrocław. This adds C++ equivalents of the functions added to C23 by WG14 N3022. These functions are in the global namespace, but to avoid collisions with the same functions defined by other standard library implementations, this change defines them in namespace __gnu_cxx and then adds them to the global namespace. libstdc++-v3/ChangeLog: * include/Makefile.am: Add stdbit.h. * include/Makefile.in: Regenerate. * src/c++23/std.compat.cc.in: Export functions. * include/c_compatibility/stdbit.h: New file. * testsuite/20_util/stdbit/1.cc: New test. * testsuite/20_util/stdbit/2_neg.cc: New test. Reviewed-by: Patrick Palka --- Reviewed by Tomasz and Patrick at https://forge.sourceware.org/gcc/gcc-TEST/pulls/28 Tested x86_64-linux. Pushed to trunk. libstdc++-v3/include/Makefile.am | 1 + libstdc++-v3/include/Makefile.in | 1 + libstdc++-v3/include/c_compatibility/stdbit.h | 582 ++++++++++++++++++ libstdc++-v3/src/c++23/std.compat.cc.in | 29 + libstdc++-v3/testsuite/20_util/stdbit/1.cc | 320 ++++++++++ .../testsuite/20_util/stdbit/2_neg.cc | 45 ++ 6 files changed, 978 insertions(+) create mode 100644 libstdc++-v3/include/c_compatibility/stdbit.h create mode 100644 libstdc++-v3/testsuite/20_util/stdbit/1.cc create mode 100644 libstdc++-v3/testsuite/20_util/stdbit/2_neg.cc diff --git a/libstdc++-v3/include/Makefile.am b/libstdc++-v3/include/Makefile.am index de25aadd219..a8ff87fb600 100644 --- a/libstdc++-v3/include/Makefile.am +++ b/libstdc++-v3/include/Makefile.am @@ -911,6 +911,7 @@ c_compatibility_headers = \ ${c_compatibility_srcdir}/tgmath.h \ ${c_compatibility_srcdir}/math.h \ ${c_compatibility_srcdir}/stdatomic.h \ + ${c_compatibility_srcdir}/stdbit.h \ ${c_compatibility_srcdir}/stdlib.h endif diff --git a/libstdc++-v3/include/Makefile.in b/libstdc++-v3/include/Makefile.in index 5a20dfb69b0..859cbee53d6 100644 --- a/libstdc++-v3/include/Makefile.in +++ b/libstdc++-v3/include/Makefile.in @@ -1248,6 +1248,7 @@ c_compatibility_builddir = . @GLIBCXX_C_HEADERS_C_GLOBAL_TRUE@ ${c_compatibility_srcdir}/tgmath.h \ @GLIBCXX_C_HEADERS_C_GLOBAL_TRUE@ ${c_compatibility_srcdir}/math.h \ @GLIBCXX_C_HEADERS_C_GLOBAL_TRUE@ ${c_compatibility_srcdir}/stdatomic.h \ +@GLIBCXX_C_HEADERS_C_GLOBAL_TRUE@ ${c_compatibility_srcdir}/stdbit.h \ @GLIBCXX_C_HEADERS_C_GLOBAL_TRUE@ ${c_compatibility_srcdir}/stdlib.h @GLIBCXX_C_HEADERS_C_STD_TRUE@c_compatibility_headers = diff --git a/libstdc++-v3/include/c_compatibility/stdbit.h b/libstdc++-v3/include/c_compatibility/stdbit.h new file mode 100644 index 00000000000..1fb691e36c1 --- /dev/null +++ b/libstdc++-v3/include/c_compatibility/stdbit.h @@ -0,0 +1,582 @@ +// C compatibility header -*- C++ -*- + +// Copyright The GNU Toolchain Authors. +// +// This file is part of the GNU ISO C++ Library. This library 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. + +// This library 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 +// . + +/** @file include/stdbit.h + * This is a Standard C++ Library header. + */ + +#ifndef _GLIBCXX_STDBIT_H +#define _GLIBCXX_STDBIT_H + +#if __cplusplus > 202302L +#include + +#define __STDC_VERSION_STDBIT_H__ 202311L + +#define __STDC_ENDIAN_BIG__ __ORDER_BIG_ENDIAN__ +#define __STDC_ENDIAN_LITTLE__ __ORDER_LITTLE_ENDIAN__ +#define __STDC_ENDIAN_NATIVE__ __BYTE_ORDER__ + +#ifndef _GLIBCXX_DOXYGEN +// We define these in our own namespace, but let Doxygen think otherwise. +namespace __gnu_cxx _GLIBCXX_VISIBILITY(default) +{ +#endif + +/** Count the number of leading zero bits + * + * @param __value An unsigned integer. + * @since C++26 + * @{ + */ +template +inline unsigned int +stdc_leading_zeros(_Tp __value) +{ + static_assert(std::__unsigned_integer<_Tp>); + return std::countl_zero(__value); +} + +inline unsigned int +stdc_leading_zeros_uc(unsigned char __value) +{ return stdc_leading_zeros(__value); } + +inline unsigned int +stdc_leading_zeros_us(unsigned short __value) +{ return stdc_leading_zeros(__value); } + +inline unsigned int +stdc_leading_zeros_ui(unsigned int __value) +{ return stdc_leading_zeros(__value); } + +inline unsigned int +stdc_leading_zeros_ul(unsigned long int __value) +{ return stdc_leading_zeros(__value); } + +inline unsigned int +stdc_leading_zeros_ull(unsigned long long int __value) +{ return stdc_leading_zeros(__value); } +/// @} + +/** Count the number of leading one bits + * + * @param __value An unsigned integer. + * @since C++26 + * @{ + */ +template +inline unsigned int +stdc_leading_ones(_Tp __value) +{ + static_assert(std::__unsigned_integer<_Tp>); + return std::countl_one(__value); +} + +inline unsigned int +stdc_leading_ones_uc(unsigned char __value) +{ return stdc_leading_ones(__value); } + +inline unsigned int +stdc_leading_ones_us(unsigned short __value) +{ return stdc_leading_ones(__value); } + +inline unsigned int +stdc_leading_ones_ui(unsigned int __value) +{ return stdc_leading_ones(__value); } + +inline unsigned int +stdc_leading_ones_ul(unsigned long int __value) +{ return stdc_leading_ones(__value); } + +inline unsigned int +stdc_leading_ones_ull(unsigned long long int __value) +{ return stdc_leading_ones(__value); } +/// @} + +/** Count the number of trailing zero bits + * + * @param __value An unsigned integer. + * @since C++26 + * @{ + */ +template +inline unsigned int +stdc_trailing_zeros(_Tp __value) +{ + static_assert(std::__unsigned_integer<_Tp>); + return std::countr_zero(__value); +} + +inline unsigned int +stdc_trailing_zeros_uc(unsigned char __value) +{ return stdc_trailing_zeros(__value); } + +inline unsigned int +stdc_trailing_zeros_us(unsigned short __value) +{ return stdc_trailing_zeros(__value); } + +inline unsigned int +stdc_trailing_zeros_ui(unsigned int __value) +{ return stdc_trailing_zeros(__value); } + +inline unsigned int +stdc_trailing_zeros_ul(unsigned long int __value) +{ return stdc_trailing_zeros(__value); } + +inline unsigned int +stdc_trailing_zeros_ull(unsigned long long int __value) +{ return stdc_trailing_zeros(__value); } +/// @} + +/** Count the number of trailing one bits + * + * @param __value An unsigned integer. + * @since C++26 + * @{ + */ +template +inline unsigned int +stdc_trailing_ones(_Tp __value) +{ + static_assert(std::__unsigned_integer<_Tp>); + return std::countr_one(__value); +} + +inline unsigned int +stdc_trailing_ones_uc(unsigned char __value) +{ return stdc_trailing_ones(__value); } + +inline unsigned int +stdc_trailing_ones_us(unsigned short __value) +{ return stdc_trailing_ones(__value); } + +inline unsigned int +stdc_trailing_ones_ui(unsigned int __value) +{ return stdc_trailing_ones(__value); } + +inline unsigned int +stdc_trailing_ones_ul(unsigned long int __value) +{ return stdc_trailing_ones(__value); } + +inline unsigned int +stdc_trailing_ones_ull(unsigned long long int __value) +{ return stdc_trailing_ones(__value); } +/// @} + +/** Find the leftmost (i.e. most significant) zero bit + * + * @param __value An unsigned integer. + * @return The one-based index of the first zero bit counting from the left, + * or zero if there are no zero bits. + * @since C++26 + * @{ + */ +template +inline unsigned int +stdc_first_leading_zero(_Tp __value) +{ + static_assert(std::__unsigned_integer<_Tp>); + return __value == _Tp(-1) ? 0 : 1 + std::countl_one(__value); +} + +inline unsigned int +stdc_first_leading_zero_uc(unsigned char __value) +{ return stdc_first_leading_zero(__value); } + +inline unsigned int +stdc_first_leading_zero_us(unsigned short __value) +{ return stdc_first_leading_zero(__value); } + +inline unsigned int +stdc_first_leading_zero_ui(unsigned int __value) +{ return stdc_first_leading_zero(__value); } + +inline unsigned int +stdc_first_leading_zero_ul(unsigned long int __value) +{ return stdc_first_leading_zero(__value); } + +inline unsigned int +stdc_first_leading_zero_ull(unsigned long long int __value) +{ return stdc_first_leading_zero(__value); } +/// @} + +/** Find the leftmost (i.e. most significant) one bit + * + * @param __value An unsigned integer. + * @return The one-based index of the first one bit counting from the left, + * or zero if there are no one bits. + * @since C++26 + * @{ + */ +template +inline unsigned int +stdc_first_leading_one(_Tp __value) +{ + static_assert(std::__unsigned_integer<_Tp>); + return __value == 0 ? 0 : 1 + std::countl_zero(__value); +} + +inline unsigned int +stdc_first_leading_one_uc(unsigned char __value) +{ return stdc_first_leading_one(__value); } + +inline unsigned int +stdc_first_leading_one_us(unsigned short __value) +{ return stdc_first_leading_one(__value); } + +inline unsigned int +stdc_first_leading_one_ui(unsigned int __value) +{ return stdc_first_leading_one(__value); } + +inline unsigned int +stdc_first_leading_one_ul(unsigned long int __value) +{ return stdc_first_leading_one(__value); } + +inline unsigned int +stdc_first_leading_one_ull(unsigned long long int __value) +{ return stdc_first_leading_one(__value); } +/// @} + +/** Find the rightmost (i.e. least significant) zero bit + * + * @param __value An unsigned integer. + * @return The one-based index of the first zero bit counting from the right, + * or zero if there are no zero bits. + * @since C++26 + * @{ + */ +template +inline unsigned int +stdc_first_trailing_zero(_Tp __value) +{ + static_assert(std::__unsigned_integer<_Tp>); + return __value == _Tp(-1) ? 0 : 1 + std::countr_one(__value); +} + +inline unsigned int +stdc_first_trailing_zero_uc(unsigned char __value) +{ return stdc_first_trailing_zero(__value); } + +inline unsigned int +stdc_first_trailing_zero_us(unsigned short __value) +{ return stdc_first_trailing_zero(__value); } + +inline unsigned int +stdc_first_trailing_zero_ui(unsigned int __value) +{ return stdc_first_trailing_zero(__value); } + +inline unsigned int +stdc_first_trailing_zero_ul(unsigned long int __value) +{ return stdc_first_trailing_zero(__value); } + +inline unsigned int +stdc_first_trailing_zero_ull(unsigned long long int __value) +{ return stdc_first_trailing_zero(__value); } +/// @} + +/** Find the rightmost (i.e. least significant) one bit + * + * @param __value An unsigned integer. + * @return The one-based index of the first one bit counting from the right, + * or zero if there are no one bits. + * @since C++26 + * @{ + */ +template +inline unsigned int +stdc_first_trailing_one(_Tp __value) +{ + static_assert(std::__unsigned_integer<_Tp>); + return __value == 0 ? 0 : 1 + std::countr_zero(__value); +} + +inline unsigned int +stdc_first_trailing_one_uc(unsigned char __value) +{ return stdc_first_trailing_one(__value); } + +inline unsigned int +stdc_first_trailing_one_us(unsigned short __value) +{ return stdc_first_trailing_one(__value); } + +inline unsigned int +stdc_first_trailing_one_ui(unsigned int __value) +{ return stdc_first_trailing_one(__value); } + +inline unsigned int +stdc_first_trailing_one_ul(unsigned long int __value) +{ return stdc_first_trailing_one(__value); } + +inline unsigned int +stdc_first_trailing_one_ull(unsigned long long int __value) +{ return stdc_first_trailing_one(__value); } +/// @} + +/** Count zeros + * + * @param __value An unsigned integer. + * @return The total number of zero bits in `__value`. + * @since C++26 + * @{ + */ +template +inline unsigned int +stdc_count_zeros(_Tp __value) +{ + static_assert(std::__unsigned_integer<_Tp>); + return std::popcount(_Tp(~__value)); +} + +inline unsigned int +stdc_count_zeros_uc(unsigned char __value) +{ return stdc_count_zeros(__value); } + +inline unsigned int +stdc_count_zeros_us(unsigned short __value) +{ return stdc_count_zeros(__value); } + +inline unsigned int +stdc_count_zeros_ui(unsigned int __value) +{ return stdc_count_zeros(__value); } + +inline unsigned int +stdc_count_zeros_ul(unsigned long int __value) +{ return stdc_count_zeros(__value); } + +inline unsigned int +stdc_count_zeros_ull(unsigned long long int __value) +{ return stdc_count_zeros(__value); } +/// @} + +/** Count ones + * + * @param __value An unsigned integer. + * @return The total number of one bits in `__value`. + * @since C++26 + * @{ + */ +template +inline unsigned int +stdc_count_ones(_Tp __value) +{ + static_assert(std::__unsigned_integer<_Tp>); + return std::popcount(__value); +} + +inline unsigned int +stdc_count_ones_uc(unsigned char __value) +{ return stdc_count_ones(__value); } + +inline unsigned int +stdc_count_ones_us(unsigned short __value) +{ return stdc_count_ones(__value); } + +inline unsigned int +stdc_count_ones_ui(unsigned int __value) +{ return stdc_count_ones(__value); } + +inline unsigned int +stdc_count_ones_ul(unsigned long int __value) +{ return stdc_count_ones(__value); } + +inline unsigned int +stdc_count_ones_ull(unsigned long long int __value) +{ return stdc_count_ones(__value); } +/// @} + +/** Power of two check + * + * @param __value An unsigned integer. + * @return True if the value has a single bit set, false otherwise. + * @since C++26 + * @{ + */ +template +inline bool +stdc_has_single_bit(_Tp __value) +{ + static_assert(std::__unsigned_integer<_Tp>); + return std::has_single_bit(__value); +} + +inline bool +stdc_has_single_bit_uc(unsigned char __value) +{ return stdc_has_single_bit(__value); } + +inline bool +stdc_has_single_bit_us(unsigned short __value) +{ return stdc_has_single_bit(__value); } + +inline bool +stdc_has_single_bit_ui(unsigned int __value) +{ return stdc_has_single_bit(__value); } + +inline bool +stdc_has_single_bit_ul(unsigned long int __value) +{ return stdc_has_single_bit(__value); } + +inline bool +stdc_has_single_bit_ull(unsigned long long int __value) +{ return stdc_has_single_bit(__value); } +/// @} + +/** Bit width + * + * @param __value An unsigned integer. + * @return The minimum number of bits needed to represent `__value`. + * @since C++26 + * @{ + */ +template +inline unsigned int +stdc_bit_width(_Tp __value) +{ + static_assert(std::__unsigned_integer<_Tp>); + return std::bit_width(__value); +} + +inline unsigned int +stdc_bit_width_uc(unsigned char __value) +{ return stdc_bit_width(__value); } + +inline unsigned int +stdc_bit_width_us(unsigned short __value) +{ return stdc_bit_width(__value); } + +inline unsigned int +stdc_bit_width_ui(unsigned int __value) +{ return stdc_bit_width(__value); } + +inline unsigned int +stdc_bit_width_ul(unsigned long int __value) +{ return stdc_bit_width(__value); } + +inline unsigned int +stdc_bit_width_ull(unsigned long long int __value) +{ return stdc_bit_width(__value); } +/// @} + +/** Bit floor + * + * @param __value An unsigned integer. + * @return The largest power of two that is not greater than `__value`. + * @since C++26 + * @{ + */ +template +inline _Tp +stdc_bit_floor(_Tp __value) +{ + static_assert(std::__unsigned_integer<_Tp>); + return std::bit_floor(__value); +} + +inline unsigned char +stdc_bit_floor_uc(unsigned char __value) +{ return stdc_bit_floor(__value); } + +inline unsigned short +stdc_bit_floor_us(unsigned short __value) +{ return stdc_bit_floor(__value); } + +inline unsigned int +stdc_bit_floor_ui(unsigned int __value) +{ return stdc_bit_floor(__value); } + +inline unsigned long int +stdc_bit_floor_ul(unsigned long int __value) +{ return stdc_bit_floor(__value); } + +inline unsigned long long int +stdc_bit_floor_ull(unsigned long long int __value) +{ return stdc_bit_floor(__value); } +/// @} + +/** Bit ceiling + * + * Unlike `std::bit_ceil`, this is defined to return zero for values which + * are not representable in the return type. + * + * @param __value An unsigned integer. + * @return The smallest power of two that is not less than `__value`. + * @since C++26 + * @{ + */ +template +inline _Tp +stdc_bit_ceil(_Tp __value) +{ + static_assert(std::__unsigned_integer<_Tp>); + constexpr _Tp __msb = _Tp(1) << (__gnu_cxx::__int_traits<_Tp>::__digits - 1); + return (__value & __msb) ? 0 : std::bit_ceil(__value); +} + +inline unsigned char +stdc_bit_ceil_uc(unsigned char __value) +{ return stdc_bit_ceil(__value); } + +inline unsigned short +stdc_bit_ceil_us(unsigned short __value) +{ return stdc_bit_ceil(__value); } + +inline unsigned int +stdc_bit_ceil_ui(unsigned int __value) +{ return stdc_bit_ceil(__value); } + +inline unsigned long int +stdc_bit_ceil_ul(unsigned long int __value) +{ return stdc_bit_ceil(__value); } + +inline unsigned long long int +stdc_bit_ceil_ull(unsigned long long int __value) +{ return stdc_bit_ceil(__value); } +/// @} + +#ifndef _GLIBCXX_DOXYGEN +} // namespace __gnu_cxx +#define _GLIBCXX_STDBIT_FUNC(F) \ + using __gnu_cxx::F ## _uc; \ + using __gnu_cxx::F ## _us; \ + using __gnu_cxx::F ## _ui; \ + using __gnu_cxx::F ## _ul; \ + using __gnu_cxx::F ## _ull; \ + using __gnu_cxx::F +_GLIBCXX_STDBIT_FUNC(stdc_leading_zeros); +_GLIBCXX_STDBIT_FUNC(stdc_leading_ones); +_GLIBCXX_STDBIT_FUNC(stdc_trailing_zeros); +_GLIBCXX_STDBIT_FUNC(stdc_trailing_ones); +_GLIBCXX_STDBIT_FUNC(stdc_first_leading_zero); +_GLIBCXX_STDBIT_FUNC(stdc_first_leading_one); +_GLIBCXX_STDBIT_FUNC(stdc_first_trailing_zero); +_GLIBCXX_STDBIT_FUNC(stdc_first_trailing_one); +_GLIBCXX_STDBIT_FUNC(stdc_count_zeros); +_GLIBCXX_STDBIT_FUNC(stdc_count_ones); +_GLIBCXX_STDBIT_FUNC(stdc_has_single_bit); +_GLIBCXX_STDBIT_FUNC(stdc_bit_width); +_GLIBCXX_STDBIT_FUNC(stdc_bit_floor); +_GLIBCXX_STDBIT_FUNC(stdc_bit_ceil); +#undef _GLIBCXX_STDBIT_FUNC +#endif // !DOXYGEN +#endif // C++26 + +#endif // _GLIBCXX_STDBIT_H diff --git a/libstdc++-v3/src/c++23/std.compat.cc.in b/libstdc++-v3/src/c++23/std.compat.cc.in index 5ca4dc4f15b..3c25295850f 100644 --- a/libstdc++-v3/src/c++23/std.compat.cc.in +++ b/libstdc++-v3/src/c++23/std.compat.cc.in @@ -24,6 +24,35 @@ export module std.compat; export import std; +#include + +// +export +{ +#define _GLIBCXX_STDBIT_FUNC(F) \ + using __gnu_cxx::F ## _uc; \ + using __gnu_cxx::F ## _us; \ + using __gnu_cxx::F ## _ui; \ + using __gnu_cxx::F ## _ul; \ + using __gnu_cxx::F ## _ull; \ + using __gnu_cxx::F +_GLIBCXX_STDBIT_FUNC(stdc_leading_zeros); +_GLIBCXX_STDBIT_FUNC(stdc_leading_ones); +_GLIBCXX_STDBIT_FUNC(stdc_trailing_zeros); +_GLIBCXX_STDBIT_FUNC(stdc_trailing_ones); +_GLIBCXX_STDBIT_FUNC(stdc_first_leading_zero); +_GLIBCXX_STDBIT_FUNC(stdc_first_leading_one); +_GLIBCXX_STDBIT_FUNC(stdc_first_trailing_zero); +_GLIBCXX_STDBIT_FUNC(stdc_first_trailing_one); +_GLIBCXX_STDBIT_FUNC(stdc_count_zeros); +_GLIBCXX_STDBIT_FUNC(stdc_count_ones); +_GLIBCXX_STDBIT_FUNC(stdc_has_single_bit); +_GLIBCXX_STDBIT_FUNC(stdc_bit_width); +_GLIBCXX_STDBIT_FUNC(stdc_bit_floor); +_GLIBCXX_STDBIT_FUNC(stdc_bit_ceil); +#undef _GLIBCXX_STDBIT_FUNC +} + #define STD_COMPAT 1 // C library exports are appended from std-clib.cc.in. diff --git a/libstdc++-v3/testsuite/20_util/stdbit/1.cc b/libstdc++-v3/testsuite/20_util/stdbit/1.cc new file mode 100644 index 00000000000..c9f2b599d0d --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/stdbit/1.cc @@ -0,0 +1,320 @@ +// { dg-do run { target c++26 } } + +#include + +#if __STDC_VERSION_STDBIT_H__ != 202311L +# error "__STDC_VERSION_STDBIT_H__ not defined correctly in " +#endif +#ifndef __STDC_ENDIAN_BIG__ +# error "__STDC_ENDIAN_BIG__ is not defined in " +#endif +#ifndef __STDC_ENDIAN_LITTLE__ +# error "__STDC_ENDIAN_LITTLE__ is not defined in " +#endif +#ifndef __STDC_ENDIAN_NATIVE__ +# error "__STDC_ENDIAN_NATIVE__ is not defined in " +#endif + +#include +#include + +void +test_leading_zeros() +{ + VERIFY( stdc_leading_zeros_uc(0) == CHAR_BIT ); + VERIFY( stdc_leading_zeros_uc(UCHAR_MAX) == 0 ); + VERIFY( stdc_leading_zeros_uc(CHAR_MAX) == (CHAR_MIN ? 1 : 0) ); + VERIFY( stdc_leading_zeros_uc(UCHAR_MAX >> 3) == 3 ); + VERIFY( stdc_leading_zeros_uc((unsigned char)(UCHAR_MAX << 3)) == 0 ); + + // TODO: replace __X_WIDTH__ with the standard macros once they're in C++26 + VERIFY( stdc_leading_zeros_us(0) == __SHRT_WIDTH__ ); + VERIFY( stdc_leading_zeros_us(USHRT_MAX) == 0 ); + VERIFY( stdc_leading_zeros_us(USHRT_MAX >> 11) == 11 ); + + VERIFY( stdc_leading_zeros_ui(0) == __INT_WIDTH__ ); + VERIFY( stdc_leading_zeros_ui(UINT_MAX) == 0 ); + VERIFY( stdc_leading_zeros_ui(UINT_MAX >> 11) == 11 ); + + VERIFY( stdc_leading_zeros_ul(0) == __LONG_WIDTH__ ); + VERIFY( stdc_leading_zeros_ul(ULONG_MAX) == 0 ); + VERIFY( stdc_leading_zeros_ul(ULONG_MAX >> 19) == 19 ); + + VERIFY( stdc_leading_zeros_ull(0) == __LONG_LONG_WIDTH__ ); + VERIFY( stdc_leading_zeros_ull(ULLONG_MAX) == 0 ); + VERIFY( stdc_leading_zeros_ull(ULLONG_MAX >> 33) == 33 ); + VERIFY( stdc_leading_zeros_ull(7) == (__LONG_LONG_WIDTH__ - 3) ); + + VERIFY( stdc_leading_zeros(7U) == (__INT_WIDTH__ - 3) ); + VERIFY( stdc_leading_zeros(7UL) == (__LONG_WIDTH__ - 3) ); + VERIFY( stdc_leading_zeros(7ULL) == (__LONG_LONG_WIDTH__ - 3) ); + + static_assert( std::is_same_v ); +} + +void +test_leading_ones() +{ + VERIFY( stdc_leading_ones_uc(0) == 0 ); + VERIFY( stdc_leading_ones_uc(UCHAR_MAX) == CHAR_BIT ); + VERIFY( stdc_leading_ones_uc(CHAR_MAX) == (CHAR_MIN ? 0 : CHAR_BIT) ); + VERIFY( stdc_leading_ones_uc(UCHAR_MAX >> 3) == 0 ); + VERIFY( stdc_leading_ones_uc((unsigned char)(UCHAR_MAX << 3)) == (CHAR_BIT - 3) ); + + VERIFY( stdc_leading_ones_us(0) == 0 ); + VERIFY( stdc_leading_ones_us(USHRT_MAX) == __SHRT_WIDTH__ ); + VERIFY( stdc_leading_ones_us((unsigned short)(USHRT_MAX << 11)) == (__SHRT_WIDTH__ - 11) ); + VERIFY( stdc_leading_ones_us(USHRT_MAX >> 11) == 0 ); + + VERIFY( stdc_leading_ones_ui(0) == 0 ); + VERIFY( stdc_leading_ones_ui(UINT_MAX) == __INT_WIDTH__ ); + VERIFY( stdc_leading_ones_ui(UINT_MAX << 11) == (__INT_WIDTH__ - 11) ); + + VERIFY( stdc_leading_ones_ul(0) == 0 ); + VERIFY( stdc_leading_ones_ul(ULONG_MAX) == __LONG_WIDTH__ ); + VERIFY( stdc_leading_ones_ul(ULONG_MAX << 19) == (__LONG_WIDTH__ - 19) ); + + VERIFY( stdc_leading_ones_ull(0) == 0 ); + VERIFY( stdc_leading_ones_ull(ULLONG_MAX) == __LONG_LONG_WIDTH__ ); + VERIFY( stdc_leading_ones_ull(ULLONG_MAX << 33) == (__LONG_LONG_WIDTH__ - 33) ); + + VERIFY( stdc_leading_ones(-1U << 2) == (__INT_WIDTH__ - 2) ); + VERIFY( stdc_leading_ones(-1UL << 3) == (__LONG_WIDTH__ - 3) ); + VERIFY( stdc_leading_ones(-1ULL << 4) == (__LONG_LONG_WIDTH__ - 4) ); + + static_assert( std::is_same_v ); +} + +void +test_trailing_zeros() +{ + VERIFY( stdc_trailing_zeros_uc((unsigned char)(UCHAR_MAX << 5)) == 5 ); + VERIFY( stdc_trailing_zeros_us((unsigned short)(USHRT_MAX << 5)) == 5 ); + VERIFY( stdc_trailing_zeros_ui(UINT_MAX << 9) == 9 ); + VERIFY( stdc_trailing_zeros_ul(ULONG_MAX << 19) == 19 ); + VERIFY( stdc_trailing_zeros_ull(ULLONG_MAX << 39) == 39 ); + VERIFY( stdc_trailing_zeros(8U) == 3 ); + + static_assert( std::is_same_v ); +} + +void +test_trailing_ones() +{ + VERIFY( stdc_trailing_ones_uc((unsigned char)3) == 2 ); + VERIFY( stdc_trailing_ones_uc((unsigned char)135) == 3 ); + VERIFY( stdc_trailing_ones_us((unsigned short)7) == 3 ); + VERIFY( stdc_trailing_ones_us((unsigned short)23) == 3 ); + VERIFY( stdc_trailing_ones_us((unsigned short)235) == 2 ); + VERIFY( stdc_trailing_ones_ui(11U) == 2 ); + VERIFY( stdc_trailing_ones_ui(15U) == 4 ); + VERIFY( stdc_trailing_ones_ul(ULONG_MAX) == __LONG_WIDTH__ ); + VERIFY( stdc_trailing_ones_ull(ULLONG_MAX) == __LONG_LONG_WIDTH__ ); + VERIFY( stdc_trailing_ones(7U) == 3 ); + + static_assert( std::is_same_v ); +} + +void +test_first_leading_zero() +{ + VERIFY( stdc_first_leading_zero_uc(0) == 1 ); + VERIFY( stdc_first_leading_zero_uc(UCHAR_MAX) == 0 ); + VERIFY( stdc_first_leading_zero_uc(UCHAR_MAX ^ 0b111) == (CHAR_BIT - 2) ); + VERIFY( stdc_first_leading_zero_us(USHRT_MAX) == 0 ); + VERIFY( stdc_first_leading_zero_us(USHRT_MAX ^ 0b111) == (__SHRT_WIDTH__ - 2) ); + VERIFY( stdc_first_leading_zero_ui(UINT_MAX ^ 0b10111) == (__INT_WIDTH__ - 4) ); + VERIFY( stdc_first_leading_zero_ul(ULONG_MAX ^ 0b10111) == (__LONG_WIDTH__ - 4) ); + VERIFY( stdc_first_leading_zero_ull(ULLONG_MAX ^ 0b10111) == (__LONG_LONG_WIDTH__ - 4) ); + + VERIFY( stdc_first_leading_zero(0U) == 1 ); + VERIFY( stdc_first_leading_zero(-1U ^ 0b1111) == (__INT_WIDTH__ - 3) ); + + static_assert( std::is_same_v ); +} + +void +test_first_leading_one() +{ + VERIFY( stdc_first_leading_one_uc(0) == 0 ); + VERIFY( stdc_first_leading_one_uc(0b00100) == (CHAR_BIT - 2) ); + VERIFY( stdc_first_leading_one_ui(0b001100) == (__INT_WIDTH__ - 3) ); + VERIFY( stdc_first_leading_one_ul(0b101100) == (__LONG_WIDTH__ - 5) ); + VERIFY( stdc_first_leading_one_ull(0b1110000) == (__LONG_LONG_WIDTH__ - 6) ); + + VERIFY( stdc_first_leading_one(0U) == 0 ); + VERIFY( stdc_first_leading_one(-1U >> 4) == 5 ); + VERIFY( stdc_first_leading_one(-1ULL >> 43) == 44 ); + + static_assert( std::is_same_v ); +} + +void +test_first_trailing_zero() +{ + VERIFY( stdc_first_trailing_zero_uc(0) == 1 ); + VERIFY( stdc_first_trailing_zero_uc(1) == 2 ); + VERIFY( stdc_first_trailing_zero_uc(7) == 4 ); + VERIFY( stdc_first_trailing_zero_us(15) == 5 ); + VERIFY( stdc_first_trailing_zero_ui(15) == 5 ); + VERIFY( stdc_first_trailing_zero_ul(15) == 5 ); + VERIFY( stdc_first_trailing_zero_ull(15) == 5 ); + + VERIFY( stdc_first_trailing_zero(15U) == 5 ); + + static_assert( std::is_same_v ); +} + +void +test_first_trailing_one() +{ + VERIFY( stdc_first_trailing_one_uc(0) == 0 ); + VERIFY( stdc_first_trailing_one_uc(1) == 1 ); + VERIFY( stdc_first_trailing_one_uc(7) == 1 ); + VERIFY( stdc_first_trailing_one_us(16) == 5 ); + VERIFY( stdc_first_trailing_one_ui(16) == 5 ); + VERIFY( stdc_first_trailing_one_ul(16) == 5 ); + VERIFY( stdc_first_trailing_one_ull(16) == 5 ); + + VERIFY( stdc_first_trailing_one(16U) == 5 ); + VERIFY( stdc_first_trailing_one(-1ULL << 17) == 18 ); + + static_assert( std::is_same_v ); +} + +void +test_count_zeros() +{ + VERIFY( stdc_count_zeros_uc(0b101010) == (CHAR_BIT - 3) ); + VERIFY( stdc_count_zeros_us(0b1010101) == (__SHRT_WIDTH__ - 4) ); + VERIFY( stdc_count_zeros_ui(0b1010101111) == (__INT_WIDTH__ - 7) ); + VERIFY( stdc_count_zeros_ul(0b10101011110101) == (__LONG_WIDTH__ - 9) ); + VERIFY( stdc_count_zeros_ull(0b10101011110101) == (__LONG_LONG_WIDTH__ - 9) ); + + VERIFY( stdc_count_zeros(0b111UL) == (__LONG_WIDTH__ - 3) ); + VERIFY( stdc_count_zeros(0U) == __INT_WIDTH__ ); + + // std::popcount returns signed int, stdc_count_zeros_uc returns unsigned int. + static_assert( std::is_same_v ); + static_assert( std::is_same_v ); + static_assert( std::is_same_v ); + static_assert( std::is_same_v ); + static_assert( std::is_same_v ); + static_assert( std::is_same_v ); +} + +void +test_count_ones() +{ + VERIFY( stdc_count_ones_uc(0b101010) == 3 ); + VERIFY( stdc_count_ones_us(0b1010101) == 4 ); + VERIFY( stdc_count_ones_ui(0b1010101111) == 7 ); + VERIFY( stdc_count_ones_ul(0b10101011110101) == 9 ); + VERIFY( stdc_count_ones_ull(0b10101011110101) == 9 ); + + VERIFY( stdc_count_ones(0b10101011110101U) == 9 ); + VERIFY( stdc_count_ones(0U) == 0 ); + + // std::popcount returns signed int, stdc_count_ones_uc returns unsigned int. + static_assert( std::is_same_v ); + static_assert( std::is_same_v ); + static_assert( std::is_same_v ); + static_assert( std::is_same_v ); + static_assert( std::is_same_v ); + static_assert( std::is_same_v ); +} + +void +test_has_single_bit() +{ + VERIFY ( ! stdc_has_single_bit_uc(0) ); + VERIFY ( ! stdc_has_single_bit_uc(41) ); + VERIFY ( stdc_has_single_bit_uc(1) ); + VERIFY ( stdc_has_single_bit_uc(64) ); + VERIFY ( stdc_has_single_bit_us(1 << 7) ); + VERIFY ( stdc_has_single_bit_ui(1 << 11) ); + VERIFY ( stdc_has_single_bit_ul(1 << 14) ); + VERIFY ( stdc_has_single_bit_ull(1 << 24) ); + + VERIFY ( stdc_has_single_bit(64U) ); + VERIFY ( stdc_has_single_bit(128UL) ); + VERIFY ( ! stdc_has_single_bit(129UL) ); +} + +void +test_bit_width() +{ + VERIFY( stdc_bit_width_uc(0) == 0 ); + VERIFY( stdc_bit_width_uc(7) == 3 ); + VERIFY( stdc_bit_width_uc(0b10101) == 5 ); + VERIFY( stdc_bit_width_us(0b101010) == 6 ); + VERIFY( stdc_bit_width_ui(0b1010101) == 7 ); + VERIFY( stdc_bit_width_ul(ULONG_MAX) == __LONG_WIDTH__ ); + VERIFY( stdc_bit_width_ull(ULLONG_MAX >> 2) == (__LONG_LONG_WIDTH__ - 2) ); + + VERIFY( stdc_bit_width(0b1010101U) == 7U ); + + // std::bit_width returns signed int, stdc_bit_width returns unsigned int. + static_assert( std::is_same_v ); + static_assert( std::is_same_v ); + static_assert( std::is_same_v ); + static_assert( std::is_same_v ); + static_assert( std::is_same_v ); + static_assert( std::is_same_v ); +} + +void +test_bit_floor() +{ + VERIFY( stdc_bit_floor_uc(0) == 0 ); + VERIFY( stdc_bit_floor_uc(7) == 4 ); + VERIFY( stdc_bit_floor_uc(0b10101) == 0b10000 ); + VERIFY( stdc_bit_floor_us(0b101010) == 0b100000 ); + VERIFY( stdc_bit_floor_ui(0b1010101) == 0b1000000 ); + VERIFY( stdc_bit_floor_ul(ULONG_MAX) == (1ul << (__LONG_WIDTH__ - 1)) ); + VERIFY( stdc_bit_floor_ull(1000000) == (1ULL << 19) ); + + VERIFY( stdc_bit_floor(0b1010101U) == 0b1000000 ); +} + +void +test_bit_ceil() +{ + VERIFY( stdc_bit_ceil_uc(0) == 1 ); + VERIFY( stdc_bit_ceil_uc(1) == 1 ); + VERIFY( stdc_bit_ceil_uc(2) == 2 ); + VERIFY( stdc_bit_ceil_uc(3) == 4 ); + VERIFY( stdc_bit_ceil_us(11) == 16 ); + VERIFY( stdc_bit_ceil_ui(257) == 512 ); + VERIFY( stdc_bit_ceil_ul(1048) == 2048 ); + VERIFY( stdc_bit_ceil_ull(1000000) == (1ULL << 20) ); + + VERIFY( stdc_bit_ceil(0b1010101U) == 0b10000000 ); +} + +int main() +{ + test_leading_zeros(); + test_leading_ones(); + test_trailing_zeros(); + test_trailing_ones(); + test_first_leading_zero(); + test_first_leading_one(); + test_first_trailing_zero(); + test_first_trailing_one(); + test_count_zeros(); + test_count_ones(); + test_has_single_bit(); + test_bit_width(); + test_bit_floor(); + test_bit_ceil(); +} + +#include +// The standard doesn't require these values to match, +// so this is specific to libstdc++. +#ifdef _GLIBCXX_RELEASE +static_assert(__STDC_ENDIAN_BIG__ == (int)std::endian::big); +static_assert(__STDC_ENDIAN_LITTLE__ == (int)std::endian::little); +static_assert(__STDC_ENDIAN_NATIVE__ == (int)std::endian::native); +#endif diff --git a/libstdc++-v3/testsuite/20_util/stdbit/2_neg.cc b/libstdc++-v3/testsuite/20_util/stdbit/2_neg.cc new file mode 100644 index 00000000000..d08fdaf139e --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/stdbit/2_neg.cc @@ -0,0 +1,45 @@ +// { dg-do compile { target c++26 } } + +#include + +void +test_mandates() +{ + // Mandates: T is an unsigned integer type. + + ::stdc_leading_zeros(1); // { dg-error "here" } + ::stdc_leading_ones(1); // { dg-error "here" } + ::stdc_trailing_zeros(1); // { dg-error "here" } + ::stdc_trailing_ones(1); // { dg-error "here" } + ::stdc_first_leading_zero(1); // { dg-error "here" } + ::stdc_first_leading_one(1); // { dg-error "here" } + ::stdc_first_trailing_zero(1); // { dg-error "here" } + ::stdc_first_trailing_one(1); // { dg-error "here" } + ::stdc_count_zeros(1); // { dg-error "here" } + ::stdc_count_ones(1); // { dg-error "here" } + ::stdc_has_single_bit(1); // { dg-error "here" } + ::stdc_bit_width(1); // { dg-error "here" } + ::stdc_bit_floor(1); // { dg-error "here" } + ::stdc_bit_ceil(1); // { dg-error "here" } + + ::stdc_leading_zeros(1.0); // { dg-error "here" } + ::stdc_leading_ones(1.0); // { dg-error "here" } + ::stdc_trailing_zeros(1.0); // { dg-error "here" } + ::stdc_trailing_ones(1.0); // { dg-error "here" } + ::stdc_first_leading_zero(1.0); // { dg-error "here" } + ::stdc_first_leading_one(1.0); // { dg-error "here" } + ::stdc_first_trailing_zero(1.0); // { dg-error "here" } + ::stdc_first_trailing_one(1.0); // { dg-error "here" } + ::stdc_count_zeros(1.0); // { dg-error "here" } + ::stdc_count_ones(1.0); // { dg-error "here" } + ::stdc_has_single_bit(1.0); // { dg-error "here" } + ::stdc_bit_width(1.0); // { dg-error "here" } + ::stdc_bit_floor(1.0); // { dg-error "here" } + ::stdc_bit_ceil(1.0); // { dg-error "here" } +} + +// { dg-prune-output "static assertion failed" } +// { dg-prune-output "no matching function" } +// { dg-prune-output "wrong type" } +// { dg-prune-output "invalid operands" } +// { dg-prune-output "non-integral type" } From patchwork Thu Mar 13 14:55:24 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Jonathan Wakely X-Patchwork-Id: 107834 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id C36983858430 for ; Thu, 13 Mar 2025 14:59:38 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org C36983858430 Authentication-Results: sourceware.org; dkim=pass (1024-bit key, unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=GGhKFWpE X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by sourceware.org (Postfix) with ESMTP id 176403858D39 for ; Thu, 13 Mar 2025 14:57:14 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 176403858D39 Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=redhat.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=redhat.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 176403858D39 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=170.10.129.124 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1741877834; cv=none; b=xNJNK5bsAYM7nfCrRDer6oZBSy9CueQlNNliZvClNkb4WkDYGPluvCPDFKBnJKzokhs01LOkELl6zpUFEthh6yStr9A5QDBjptgYOSZo2lLtFxqEYcQunhJPygOCVQrHlS/rPe/RfW+TNvrsPUYJ+47TYVybEtZixkvt+H6qGm8= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1741877834; c=relaxed/simple; bh=Hy7NcGcWFLaW4qZPU7xxGOux+Y3T+S3WboI0Jlcp+ts=; h=DKIM-Signature:From:To:Subject:Date:Message-ID:MIME-Version; b=c/XQxllkif3YE9Equ08IaIPEV/dNLzYKAn/UHYZMr1Y9zECo4rLYulyq554pJcCGGexQu5H+1h8fQ7tnJUD3YoKtMvBlswYiwEaL9Lkc72MC3bj3FE33skHik/LQivyAsA4lOqcrVg5LGJVMPQUkJvol8Si4N/bvUF8XYV+N9DA= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 176403858D39 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1741877833; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=mAFxNmzgEdnXFQQeieO7jeb3LdjVlIihUhjGdqe2k8E=; b=GGhKFWpEaTQoNVDltQ7ThIzS7CqtdrCp8UI/6MPaixHrgDNZ1iBSS3GvCXN8ETh1aDK6iL 1HX/2E01uqKMYUPzJriXr2sWa0ekJNyKi7QaBxXmRS0pr7ZquIT34EcD2W2xt5JaNAFZr+ H5hBsdBM2QvtGL6kr81uTh/DccNFudQ= Received: from mx-prod-mc-03.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-59-PSG3Og0pMV6V4A5B2qEhKw-1; Thu, 13 Mar 2025 10:57:11 -0400 X-MC-Unique: PSG3Og0pMV6V4A5B2qEhKw-1 X-Mimecast-MFC-AGG-ID: PSG3Og0pMV6V4A5B2qEhKw_1741877830 Received: from mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.111]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-03.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 620F019560B7 for ; Thu, 13 Mar 2025 14:57:10 +0000 (UTC) Received: from localhost (unknown [10.42.28.10]) by mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id E59981800944; Thu, 13 Mar 2025 14:57:09 +0000 (UTC) From: Jonathan Wakely To: gcc-patches@gcc.gnu.org Cc: Patrick Palka Subject: [committed v2 2/2] libstdc++: Implement for C++26 (P3370R1) Date: Thu, 13 Mar 2025 14:55:24 +0000 Message-ID: <20250313145706.1303288-2-jwakely@redhat.com> In-Reply-To: <20250313145706.1303288-1-jwakely@redhat.com> References: <20241203122054.281326-1-jwakely@redhat.com> <20250313145706.1303288-1-jwakely@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.111 X-Mimecast-Spam-Score: 0 X-Mimecast-MFC-PROC-ID: D2_bxvdpsLNnRXb47P7YNyg946XfFGzFJcSQsyvFiCM_1741877830 X-Mimecast-Originator: redhat.com X-Spam-Status: No, score=-13.3 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, KAM_SHORT, RCVD_IN_DNSWL_NONE, RCVD_IN_HOSTKARMA_W, RCVD_IN_MSPIKE_H5, RCVD_IN_MSPIKE_WL, SPF_HELO_NONE, SPF_NONE, TXREP autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org X-BeenThere: gcc-patches@gcc.gnu.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Gcc-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: gcc-patches-bounces~patchwork=sourceware.org@gcc.gnu.org This is the second part of the P3370R1 proposal just approved by the committee in Wrocław. This adds C++ equivalents of the functions added to C23 by WG14 N2683. These functions are in the global namespace, but to avoid collisions with the same functions defined by other standard library implementations, this change defines them in namespace __gnu_cxx and then adds them to the global namespace. libstdc++-v3/ChangeLog: * include/Makefile.am: Add stdckdint.h. * include/Makefile.in: Regenerate. * src/c++23/std.compat.cc.in: Export functions. * include/c_compatibility/stdckdint.h: New file. * testsuite/26_numerics/stdckdint/1.cc: New test. * testsuite/26_numerics/stdckdint/2_neg.cc: New test. Reviewed-by: Patrick Palka --- Reviewed by Tomasz and Patrick at https://forge.sourceware.org/gcc/gcc-TEST/pulls/28 Tested x86_64-linux. Pushed to trunk. libstdc++-v3/include/Makefile.am | 1 + libstdc++-v3/include/Makefile.in | 1 + .../include/c_compatibility/stdckdint.h | 113 ++++++++++++++++++ libstdc++-v3/src/c++23/std.compat.cc.in | 8 ++ .../testsuite/26_numerics/stdckdint/1.cc | 63 ++++++++++ .../testsuite/26_numerics/stdckdint/2_neg.cc | 39 ++++++ 6 files changed, 225 insertions(+) create mode 100644 libstdc++-v3/include/c_compatibility/stdckdint.h create mode 100644 libstdc++-v3/testsuite/26_numerics/stdckdint/1.cc create mode 100644 libstdc++-v3/testsuite/26_numerics/stdckdint/2_neg.cc diff --git a/libstdc++-v3/include/Makefile.am b/libstdc++-v3/include/Makefile.am index a8ff87fb600..4dc771a540c 100644 --- a/libstdc++-v3/include/Makefile.am +++ b/libstdc++-v3/include/Makefile.am @@ -912,6 +912,7 @@ c_compatibility_headers = \ ${c_compatibility_srcdir}/math.h \ ${c_compatibility_srcdir}/stdatomic.h \ ${c_compatibility_srcdir}/stdbit.h \ + ${c_compatibility_srcdir}/stdckdint.h \ ${c_compatibility_srcdir}/stdlib.h endif diff --git a/libstdc++-v3/include/Makefile.in b/libstdc++-v3/include/Makefile.in index 859cbee53d6..0e3d09b3a75 100644 --- a/libstdc++-v3/include/Makefile.in +++ b/libstdc++-v3/include/Makefile.in @@ -1249,6 +1249,7 @@ c_compatibility_builddir = . @GLIBCXX_C_HEADERS_C_GLOBAL_TRUE@ ${c_compatibility_srcdir}/math.h \ @GLIBCXX_C_HEADERS_C_GLOBAL_TRUE@ ${c_compatibility_srcdir}/stdatomic.h \ @GLIBCXX_C_HEADERS_C_GLOBAL_TRUE@ ${c_compatibility_srcdir}/stdbit.h \ +@GLIBCXX_C_HEADERS_C_GLOBAL_TRUE@ ${c_compatibility_srcdir}/stdckdint.h \ @GLIBCXX_C_HEADERS_C_GLOBAL_TRUE@ ${c_compatibility_srcdir}/stdlib.h @GLIBCXX_C_HEADERS_C_STD_TRUE@c_compatibility_headers = diff --git a/libstdc++-v3/include/c_compatibility/stdckdint.h b/libstdc++-v3/include/c_compatibility/stdckdint.h new file mode 100644 index 00000000000..1de2d18dc1a --- /dev/null +++ b/libstdc++-v3/include/c_compatibility/stdckdint.h @@ -0,0 +1,113 @@ +// C compatibility header -*- C++ -*- + +// Copyright The GNU Toolchain Authors. +// +// This file is part of the GNU ISO C++ Library. This library 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. + +// This library 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 +// . + +/** @file include/stdckdint.h + * This is a Standard C++ Library header. + */ + +#ifndef _GLIBCXX_STDCKDINT_H +#define _GLIBCXX_STDCKDINT_H + +#if __cplusplus > 202302L +#include +#include + +#define __STDC_VERSION_STDCKDINT_H__ 202311L + +#ifndef _GLIBCXX_DOXYGEN +// We define these in our own namespace, but let Doxygen think otherwise. +namespace __gnu_cxx _GLIBCXX_VISIBILITY(default) +{ +#endif +/// @cond undocumented +namespace __detail +{ + template + concept __cv_unqual_signed_or_unsigned_integer_type + = std::same_as<_Tp, std::remove_cv_t<_Tp>> + && std::__is_standard_integer<_Tp>::value; +} +/// @endcond + +/** Checked integer arithmetic + * + * Performs arithmetic on `__a` and `__b` and stores the result in `*__result`, + * with overflow detection. + * The arithmetic is performed in infinite signed precision, without overflow, + * then converted to the result type, `_Tp1`. If the converted result is not + * equal to the infinite precision result, the stored result is wrapped to the + * width of `_Tp1` and `true` is returned. Otherwise, the stored result is + * correct and `false` is returned. + * + * @param __result A pointer to a signed or unsigned integer type. + * @param __a A signed or unsigned integer type. + * @param __b A signed or unsigned integer type. + * @return True if overflow occurred, false otherwise. + * @since C++26 + * @{ + */ +template + inline bool + ckd_add(_Tp1* __result, _Tp2 __a, _Tp3 __b) + { + using __gnu_cxx::__detail::__cv_unqual_signed_or_unsigned_integer_type; + static_assert(__cv_unqual_signed_or_unsigned_integer_type<_Tp1>); + static_assert(__cv_unqual_signed_or_unsigned_integer_type<_Tp2>); + static_assert(__cv_unqual_signed_or_unsigned_integer_type<_Tp3>); + return __builtin_add_overflow(__a, __b, __result); + } + +template + inline bool + ckd_sub(_Tp1* __result, _Tp2 __a, _Tp3 __b) + { + using __gnu_cxx::__detail::__cv_unqual_signed_or_unsigned_integer_type; + static_assert(__cv_unqual_signed_or_unsigned_integer_type<_Tp1>); + static_assert(__cv_unqual_signed_or_unsigned_integer_type<_Tp2>); + static_assert(__cv_unqual_signed_or_unsigned_integer_type<_Tp3>); + return __builtin_sub_overflow(__a, __b, __result); + } + +template + inline bool + ckd_mul(_Tp1* __result, _Tp2 __a, _Tp3 __b) + { + using __gnu_cxx::__detail::__cv_unqual_signed_or_unsigned_integer_type; + static_assert(__cv_unqual_signed_or_unsigned_integer_type<_Tp1>); + static_assert(__cv_unqual_signed_or_unsigned_integer_type<_Tp2>); + static_assert(__cv_unqual_signed_or_unsigned_integer_type<_Tp3>); + return __builtin_mul_overflow(__a, __b, __result); + } +/// @} +#ifndef _GLIBCXX_DOXYGEN +} + +using __gnu_cxx::ckd_add; +using __gnu_cxx::ckd_sub; +using __gnu_cxx::ckd_mul; +#endif + +#endif // C++26 + +#endif // _GLIBCXX_STDCKDINT_H diff --git a/libstdc++-v3/src/c++23/std.compat.cc.in b/libstdc++-v3/src/c++23/std.compat.cc.in index 3c25295850f..ba7ed0312ab 100644 --- a/libstdc++-v3/src/c++23/std.compat.cc.in +++ b/libstdc++-v3/src/c++23/std.compat.cc.in @@ -53,6 +53,14 @@ _GLIBCXX_STDBIT_FUNC(stdc_bit_ceil); #undef _GLIBCXX_STDBIT_FUNC } +// +export +{ + using __gnu_cxx::ckd_add; + using __gnu_cxx::ckd_sub; + using __gnu_cxx::ckd_mul; +} + #define STD_COMPAT 1 // C library exports are appended from std-clib.cc.in. diff --git a/libstdc++-v3/testsuite/26_numerics/stdckdint/1.cc b/libstdc++-v3/testsuite/26_numerics/stdckdint/1.cc new file mode 100644 index 00000000000..1402c834a7e --- /dev/null +++ b/libstdc++-v3/testsuite/26_numerics/stdckdint/1.cc @@ -0,0 +1,63 @@ +// { dg-do run { target c++26 } } + +#include + +#if __STDC_VERSION_STDCKDINT_H__ != 202311L +# error "__STDC_VERSION_STDCKDINT_H__ not defined correctly in " +#endif + +#include +#include + +void +test_add() +{ + int result; + bool overflow; + + overflow = ::ckd_add(&result, (unsigned)INT_MAX, 1LL); + VERIFY( overflow ); + VERIFY( result == INT_MIN ); + + overflow = ::ckd_add(&result, (long long)INT_MIN, -1); + VERIFY( overflow ); + VERIFY( result == INT_MAX ); + + overflow = ::ckd_add(&result, 99u, 100ll); + VERIFY( ! overflow ); + VERIFY( result == 199 ); +} + +void +test_sub() +{ + int result; + bool overflow; + + overflow = ::ckd_sub(&result, -1, -5); + VERIFY( ! overflow ); + VERIFY( result == 4 ); +} + +void +test_mul() +{ + long long result; + bool overflow; + + overflow = ::ckd_mul(&result, INT_MIN, -1); + VERIFY( ! overflow ); + VERIFY( result == -(long long)INT_MIN ); + + unsigned uresult; + overflow = ::ckd_mul(&uresult, INT_MIN, -1); + VERIFY( ! overflow ); + VERIFY( result == (unsigned)INT_MAX + 1u ); +} + +int main() +{ + test_add(); + test_sub(); + test_mul(); +} diff --git a/libstdc++-v3/testsuite/26_numerics/stdckdint/2_neg.cc b/libstdc++-v3/testsuite/26_numerics/stdckdint/2_neg.cc new file mode 100644 index 00000000000..7954da2d0d9 --- /dev/null +++ b/libstdc++-v3/testsuite/26_numerics/stdckdint/2_neg.cc @@ -0,0 +1,39 @@ +// { dg-do compile { target c++26 } } + +#include + +void +test_add(int i, char c, bool b) +{ + ::ckd_add(&i, c, 1); // { dg-error "here" } + ::ckd_add(&i, 1, c); // { dg-error "here" } + ::ckd_add(&i, b, 2); // { dg-error "here" } + ::ckd_add(&i, 2, b); // { dg-error "here" } + ::ckd_add(&c, 3, 3); // { dg-error "here" } + ::ckd_add((const int*)&i, 4, 4); // { dg-error "here" } +} + +void +test_sub(int i, char c, bool b) +{ + ::ckd_sub(&i, c, 1); // { dg-error "here" } + ::ckd_sub(&i, 1, c); // { dg-error "here" } + ::ckd_sub(&i, b, 2); // { dg-error "here" } + ::ckd_sub(&i, 2, b); // { dg-error "here" } + ::ckd_sub(&c, 3, 3); // { dg-error "here" } + ::ckd_sub((const int*)&i, 4, 4); // { dg-error "here" } +} + +void +test_mul(int i, char c, bool b) +{ + ::ckd_mul(&i, c, 1); // { dg-error "here" } + ::ckd_mul(&i, 1, c); // { dg-error "here" } + ::ckd_mul(&i, b, 2); // { dg-error "here" } + ::ckd_mul(&i, 2, b); // { dg-error "here" } + ::ckd_mul(&c, 3, 3); // { dg-error "here" } + ::ckd_mul((const int*)&i, 4, 4); // { dg-error "here" } +} + +// { dg-prune-output "static assertion failed" } +// { dg-prune-output "pointer to 'const'" }