mirror of
https://gcc.gnu.org/git/gcc.git
synced 2024-12-24 03:25:20 +08:00
76b75018b3
The last missing piece of the C++17 standard library is the hardware intereference size constants. Much of the delay in implementing these has been due to uncertainty about what the right values are, and even whether there is a single constant value that is suitable; the destructive interference size is intended to be used in structure layout, so program ABIs will depend on it. In principle, both of these values should be the same as the target's L1 cache line size. When compiling for a generic target that is intended to support a range of target CPUs with different cache line sizes, the constructive size should probably be the minimum size, and the destructive size the maximum, unless you are constrained by ABI compatibility with previous code. From discussion on gcc-patches, I've come to the conclusion that the solution to the difficulty of choosing stable values is to give up on it, and instead encourage only uses where ABI stability is unimportant: in particular, uses where the ABI is shared at most between translation units built at the same time with the same flags. To that end, I've added a warning for any use of the constant value of std::hardware_destructive_interference_size in a header or module export. Appropriate uses within a project can disable the warning. A previous iteration of this patch included an -finterference-tune flag to make the value vary with -mtune; this iteration makes that the default behavior, which should be appropriate for all reasonable uses of the variable. The previous default of "stable-ish" seems to me likely to have been more of an attractive nuisance; since we can't promise actual stability, we should instead make proper uses more convenient. JF Bastien's implementation proposal is summarized at https://github.com/itanium-cxx-abi/cxx-abi/issues/74 I implement this by adding new --params for the two sizes. Targets can override these values in targetm.target_option.override() to support a range of values for the generic target; otherwise, both will default to the L1 cache line size. 64 bytes still seems correct for all x86. I'm not sure why he proposed 64/64 for generic 32-bit ARM, since the Cortex A9 has a 32-byte cache line, so I'd think 32/64 would make more sense. He proposed 64/128 for generic AArch64, but since the A64FX now has a 256B cache line, I've changed that to 64/256. Other arch maintainers are invited to set ranges for their generic targets if that seems better than using the default cache line size for both values. With the above choice to reject stability as a goal, getting these values "right" is now just a matter of what we want the default optimization to be, and we can feel free to adjust them as CPUs with different cache lines become more and less common. gcc/ChangeLog: * params.opt: Add destructive-interference-size and constructive-interference-size. * doc/invoke.texi: Document them. * config/aarch64/aarch64.c (aarch64_override_options_internal): Set them. * config/arm/arm.c (arm_option_override): Set them. * config/i386/i386-options.c (ix86_option_override_internal): Set them. gcc/c-family/ChangeLog: * c.opt: Add -Winterference-size. * c-cppbuiltin.c (cpp_atomic_builtins): Add __GCC_DESTRUCTIVE_SIZE and __GCC_CONSTRUCTIVE_SIZE. gcc/cp/ChangeLog: * constexpr.c (maybe_warn_about_constant_value): Complain about std::hardware_destructive_interference_size. (cxx_eval_constant_expression): Call it. * decl.c (cxx_init_decl_processing): Check --param *-interference-size values. libstdc++-v3/ChangeLog: * include/std/version: Define __cpp_lib_hardware_interference_size. * libsupc++/new: Define hardware interference size variables. gcc/testsuite/ChangeLog: * g++.dg/warn/Winterference.H: New file. * g++.dg/warn/Winterference.C: New test. * g++.target/aarch64/interference.C: New test. * g++.target/arm/interference.C: New test. * g++.target/i386/interference.C: New test.
239 lines
8.4 KiB
C++
239 lines
8.4 KiB
C++
// The -*- C++ -*- dynamic memory management header.
|
|
|
|
// Copyright (C) 1994-2021 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/>.
|
|
|
|
/** @file new
|
|
* This is a Standard C++ Library header.
|
|
*
|
|
* The header @c new defines several functions to manage dynamic memory and
|
|
* handling memory allocation errors; see
|
|
* https://gcc.gnu.org/onlinedocs/libstdc++/manual/dynamic_memory.html
|
|
* for more.
|
|
*/
|
|
|
|
#ifndef _NEW
|
|
#define _NEW
|
|
|
|
#pragma GCC system_header
|
|
|
|
#include <bits/c++config.h>
|
|
#include <bits/exception.h>
|
|
|
|
#pragma GCC visibility push(default)
|
|
|
|
extern "C++" {
|
|
|
|
namespace std
|
|
{
|
|
/**
|
|
* @brief Exception possibly thrown by @c new.
|
|
* @ingroup exceptions
|
|
*
|
|
* @c bad_alloc (or classes derived from it) is used to report allocation
|
|
* errors from the throwing forms of @c new. */
|
|
class bad_alloc : public exception
|
|
{
|
|
public:
|
|
bad_alloc() throw() { }
|
|
|
|
#if __cplusplus >= 201103L
|
|
bad_alloc(const bad_alloc&) = default;
|
|
bad_alloc& operator=(const bad_alloc&) = default;
|
|
#endif
|
|
|
|
// This declaration is not useless:
|
|
// http://gcc.gnu.org/onlinedocs/gcc-3.0.2/gcc_6.html#SEC118
|
|
virtual ~bad_alloc() throw();
|
|
|
|
// See comment in eh_exception.cc.
|
|
virtual const char* what() const throw();
|
|
};
|
|
|
|
#if __cplusplus >= 201103L
|
|
class bad_array_new_length : public bad_alloc
|
|
{
|
|
public:
|
|
bad_array_new_length() throw() { }
|
|
|
|
// This declaration is not useless:
|
|
// http://gcc.gnu.org/onlinedocs/gcc-3.0.2/gcc_6.html#SEC118
|
|
virtual ~bad_array_new_length() throw();
|
|
|
|
// See comment in eh_exception.cc.
|
|
virtual const char* what() const throw();
|
|
};
|
|
#endif
|
|
|
|
#if __cpp_aligned_new
|
|
enum class align_val_t: size_t {};
|
|
#endif
|
|
|
|
struct nothrow_t
|
|
{
|
|
#if __cplusplus >= 201103L
|
|
explicit nothrow_t() = default;
|
|
#endif
|
|
};
|
|
|
|
extern const nothrow_t nothrow;
|
|
|
|
/** If you write your own error handler to be called by @c new, it must
|
|
* be of this type. */
|
|
typedef void (*new_handler)();
|
|
|
|
/// Takes a replacement handler as the argument, returns the
|
|
/// previous handler.
|
|
new_handler set_new_handler(new_handler) throw();
|
|
|
|
#if __cplusplus >= 201103L
|
|
/// Return the current new handler.
|
|
new_handler get_new_handler() noexcept;
|
|
#endif
|
|
} // namespace std
|
|
|
|
//@{
|
|
/** These are replaceable signatures:
|
|
* - normal single new and delete (no arguments, throw @c bad_alloc on error)
|
|
* - normal array new and delete (same)
|
|
* - @c nothrow single new and delete (take a @c nothrow argument, return
|
|
* @c NULL on error)
|
|
* - @c nothrow array new and delete (same)
|
|
*
|
|
* Placement new and delete signatures (take a memory address argument,
|
|
* does nothing) may not be replaced by a user's program.
|
|
*/
|
|
_GLIBCXX_NODISCARD void* operator new(std::size_t) _GLIBCXX_THROW (std::bad_alloc)
|
|
__attribute__((__externally_visible__));
|
|
_GLIBCXX_NODISCARD void* operator new[](std::size_t) _GLIBCXX_THROW (std::bad_alloc)
|
|
__attribute__((__externally_visible__));
|
|
void operator delete(void*) _GLIBCXX_USE_NOEXCEPT
|
|
__attribute__((__externally_visible__));
|
|
void operator delete[](void*) _GLIBCXX_USE_NOEXCEPT
|
|
__attribute__((__externally_visible__));
|
|
#if __cpp_sized_deallocation
|
|
void operator delete(void*, std::size_t) _GLIBCXX_USE_NOEXCEPT
|
|
__attribute__((__externally_visible__));
|
|
void operator delete[](void*, std::size_t) _GLIBCXX_USE_NOEXCEPT
|
|
__attribute__((__externally_visible__));
|
|
#endif
|
|
_GLIBCXX_NODISCARD void* operator new(std::size_t, const std::nothrow_t&) _GLIBCXX_USE_NOEXCEPT
|
|
__attribute__((__externally_visible__, __alloc_size__ (1), __malloc__));
|
|
_GLIBCXX_NODISCARD void* operator new[](std::size_t, const std::nothrow_t&) _GLIBCXX_USE_NOEXCEPT
|
|
__attribute__((__externally_visible__, __alloc_size__ (1), __malloc__));
|
|
void operator delete(void*, const std::nothrow_t&) _GLIBCXX_USE_NOEXCEPT
|
|
__attribute__((__externally_visible__));
|
|
void operator delete[](void*, const std::nothrow_t&) _GLIBCXX_USE_NOEXCEPT
|
|
__attribute__((__externally_visible__));
|
|
#if __cpp_aligned_new
|
|
_GLIBCXX_NODISCARD void* operator new(std::size_t, std::align_val_t)
|
|
__attribute__((__externally_visible__, __alloc_size__ (1), __malloc__));
|
|
_GLIBCXX_NODISCARD void* operator new(std::size_t, std::align_val_t, const std::nothrow_t&)
|
|
_GLIBCXX_USE_NOEXCEPT __attribute__((__externally_visible__, __alloc_size__ (1), __malloc__));
|
|
void operator delete(void*, std::align_val_t)
|
|
_GLIBCXX_USE_NOEXCEPT __attribute__((__externally_visible__));
|
|
void operator delete(void*, std::align_val_t, const std::nothrow_t&)
|
|
_GLIBCXX_USE_NOEXCEPT __attribute__((__externally_visible__));
|
|
_GLIBCXX_NODISCARD void* operator new[](std::size_t, std::align_val_t)
|
|
__attribute__((__externally_visible__, __alloc_size__ (1), __malloc__));
|
|
_GLIBCXX_NODISCARD void* operator new[](std::size_t, std::align_val_t, const std::nothrow_t&)
|
|
_GLIBCXX_USE_NOEXCEPT __attribute__((__externally_visible__, __alloc_size__ (1), __malloc__));
|
|
void operator delete[](void*, std::align_val_t)
|
|
_GLIBCXX_USE_NOEXCEPT __attribute__((__externally_visible__));
|
|
void operator delete[](void*, std::align_val_t, const std::nothrow_t&)
|
|
_GLIBCXX_USE_NOEXCEPT __attribute__((__externally_visible__));
|
|
#if __cpp_sized_deallocation
|
|
void operator delete(void*, std::size_t, std::align_val_t)
|
|
_GLIBCXX_USE_NOEXCEPT __attribute__((__externally_visible__));
|
|
void operator delete[](void*, std::size_t, std::align_val_t)
|
|
_GLIBCXX_USE_NOEXCEPT __attribute__((__externally_visible__));
|
|
#endif // __cpp_sized_deallocation
|
|
#endif // __cpp_aligned_new
|
|
|
|
// Default placement versions of operator new.
|
|
_GLIBCXX_NODISCARD inline void* operator new(std::size_t, void* __p) _GLIBCXX_USE_NOEXCEPT
|
|
{ return __p; }
|
|
_GLIBCXX_NODISCARD inline void* operator new[](std::size_t, void* __p) _GLIBCXX_USE_NOEXCEPT
|
|
{ return __p; }
|
|
|
|
// Default placement versions of operator delete.
|
|
inline void operator delete (void*, void*) _GLIBCXX_USE_NOEXCEPT { }
|
|
inline void operator delete[](void*, void*) _GLIBCXX_USE_NOEXCEPT { }
|
|
//@}
|
|
} // extern "C++"
|
|
|
|
#if __cplusplus >= 201703L
|
|
namespace std
|
|
{
|
|
#ifdef _GLIBCXX_HAVE_BUILTIN_LAUNDER
|
|
#define __cpp_lib_launder 201606
|
|
/// Pointer optimization barrier [ptr.launder]
|
|
template<typename _Tp>
|
|
[[nodiscard]] constexpr _Tp*
|
|
launder(_Tp* __p) noexcept
|
|
{ return __builtin_launder(__p); }
|
|
|
|
// The program is ill-formed if T is a function type or
|
|
// (possibly cv-qualified) void.
|
|
|
|
template<typename _Ret, typename... _Args _GLIBCXX_NOEXCEPT_PARM>
|
|
void launder(_Ret (*)(_Args...) _GLIBCXX_NOEXCEPT_QUAL) = delete;
|
|
template<typename _Ret, typename... _Args _GLIBCXX_NOEXCEPT_PARM>
|
|
void launder(_Ret (*)(_Args......) _GLIBCXX_NOEXCEPT_QUAL) = delete;
|
|
|
|
void launder(void*) = delete;
|
|
void launder(const void*) = delete;
|
|
void launder(volatile void*) = delete;
|
|
void launder(const volatile void*) = delete;
|
|
#endif // _GLIBCXX_HAVE_BUILTIN_LAUNDER
|
|
|
|
#ifdef __GCC_DESTRUCTIVE_SIZE
|
|
# define __cpp_lib_hardware_interference_size 201703L
|
|
inline constexpr size_t hardware_destructive_interference_size = __GCC_DESTRUCTIVE_SIZE;
|
|
inline constexpr size_t hardware_constructive_interference_size = __GCC_CONSTRUCTIVE_SIZE;
|
|
#endif // __GCC_DESTRUCTIVE_SIZE
|
|
}
|
|
#endif // C++17
|
|
|
|
#if __cplusplus > 201703L
|
|
namespace std
|
|
{
|
|
/// Tag type used to declare a class-specific operator delete that can
|
|
/// invoke the destructor before deallocating the memory.
|
|
struct destroying_delete_t
|
|
{
|
|
explicit destroying_delete_t() = default;
|
|
};
|
|
/// Tag variable of type destroying_delete_t.
|
|
inline constexpr destroying_delete_t destroying_delete{};
|
|
}
|
|
// Only define the feature test macro if the compiler supports the feature:
|
|
#if __cpp_impl_destroying_delete
|
|
# define __cpp_lib_destroying_delete 201806L
|
|
#endif
|
|
#endif // C++20
|
|
|
|
#pragma GCC visibility pop
|
|
|
|
#endif
|