chromium/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_base/compiler_specific.h

// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef PARTITION_ALLOC_PARTITION_ALLOC_BASE_COMPILER_SPECIFIC_H_
#define PARTITION_ALLOC_PARTITION_ALLOC_BASE_COMPILER_SPECIFIC_H_

#include "partition_alloc/build_config.h"

// A wrapper around `__has_cpp_attribute`.
#if defined(__has_cpp_attribute)
#define PA_HAS_CPP_ATTRIBUTE(x)
#else
#define PA_HAS_CPP_ATTRIBUTE
#endif

// A wrapper around `__has_attribute`, similar to PA_HAS_CPP_ATTRIBUTE.
#if defined(__has_attribute)
#define PA_HAS_ATTRIBUTE(x)
#else
#define PA_HAS_ATTRIBUTE
#endif

// A wrapper around `__has_builtin`, similar to PA_HAS_CPP_ATTRIBUTE.
#if defined(__has_builtin)
#define PA_HAS_BUILTIN(x)
#else
#define PA_HAS_BUILTIN
#endif

// Annotate a function indicating it should not be inlined.
// Use like:
//   NOINLINE void DoStuff() { ... }
#if defined(__clang__) && PA_HAS_ATTRIBUTE(noinline)
#define PA_NOINLINE
#elif PA_BUILDFLAG(PA_COMPILER_GCC) && PA_HAS_ATTRIBUTE(noinline)
#define PA_NOINLINE
#elif PA_BUILDFLAG(PA_COMPILER_MSVC)
#define PA_NOINLINE
#else
#define PA_NOINLINE
#endif

#if defined(__clang__) && defined(NDEBUG) && PA_HAS_ATTRIBUTE(always_inline)
#define PA_ALWAYS_INLINE
#elif PA_BUILDFLAG(PA_COMPILER_GCC) && defined(NDEBUG) && \
    PA_HAS_ATTRIBUTE(always_inline)
#define PA_ALWAYS_INLINE
#elif PA_BUILDFLAG(PA_COMPILER_MSVC) && defined(NDEBUG)
#define PA_ALWAYS_INLINE
#else
#define PA_ALWAYS_INLINE
#endif

// Annotate a function indicating it should never be tail called. Useful to make
// sure callers of the annotated function are never omitted from call-stacks.
// To provide the complementary behavior (prevent the annotated function from
// being omitted) look at NOINLINE. Also note that this doesn't prevent code
// folding of multiple identical caller functions into a single signature. To
// prevent code folding, see NO_CODE_FOLDING() in base/debug/alias.h.
// Use like:
//   void NOT_TAIL_CALLED FooBar();
#if defined(__clang__) && PA_HAS_ATTRIBUTE(not_tail_called)
#define PA_NOT_TAIL_CALLED
#else
#define PA_NOT_TAIL_CALLED
#endif

// Annotate a function indicating it must be tail called.
// Can be used only on return statements, even for functions returning void.
// Caller and callee must have the same number of arguments and its types must
// be "similar".
#if defined(__clang__) && PA_HAS_ATTRIBUTE(musttail)
#define PA_MUSTTAIL
#else
#define PA_MUSTTAIL
#endif

// In case the compiler supports it PA_NO_UNIQUE_ADDRESS evaluates to the C++20
// attribute [[no_unique_address]]. This allows annotating data members so that
// they need not have an address distinct from all other non-static data members
// of its class.
//
// References:
// * https://en.cppreference.com/w/cpp/language/attributes/no_unique_address
// * https://wg21.link/dcl.attr.nouniqueaddr
#if PA_BUILDFLAG(PA_COMPILER_MSVC) && \
    PA_HAS_CPP_ATTRIBUTE(msvc::no_unique_address)
// Unfortunately MSVC ignores [[no_unique_address]] (see
// https://devblogs.microsoft.com/cppblog/msvc-cpp20-and-the-std-cpp20-switch/#msvc-extensions-and-abi),
// and clang-cl matches it for ABI compatibility reasons. We need to prefer
// [[msvc::no_unique_address]] when available if we actually want any effect.
#define PA_NO_UNIQUE_ADDRESS
#elif PA_HAS_CPP_ATTRIBUTE(no_unique_address)
#define PA_NO_UNIQUE_ADDRESS
#else
#define PA_NO_UNIQUE_ADDRESS
#endif

// Tells the compiler a function is using a printf-style format string.
// |format_param| is the one-based index of the format string parameter;
// |dots_param| is the one-based index of the "..." parameter.
// For v*printf functions (which take a va_list), pass 0 for dots_param.
// (This is undocumented but matches what the system C headers do.)
// For member functions, the implicit this parameter counts as index 1.
#if (PA_BUILDFLAG(PA_COMPILER_GCC) || defined(__clang__)) && \
    PA_HAS_ATTRIBUTE(format)
#define PA_PRINTF_FORMAT(format_param, dots_param)
#else
#define PA_PRINTF_FORMAT
#endif

// Sanitizers annotations.
#if PA_HAS_ATTRIBUTE(no_sanitize)
#define PA_NO_SANITIZE(what)
#endif
#if !defined(PA_NO_SANITIZE)
#define PA_NO_SANITIZE
#endif

// MemorySanitizer annotations.
#if defined(MEMORY_SANITIZER)
#include <sanitizer/msan_interface.h>

// Mark a memory region fully initialized.
// Use this to annotate code that deliberately reads uninitialized data, for
// example a GC scavenging root set pointers from the stack.
#define PA_MSAN_UNPOISON
#else  // MEMORY_SANITIZER
#define PA_MSAN_UNPOISON(p, size)
#endif  // MEMORY_SANITIZER

// Compiler feature-detection.
// clang.llvm.org/docs/LanguageExtensions.html#has-feature-and-has-extension
#if defined(__has_feature)
#define PA_HAS_FEATURE(FEATURE)
#else
#define PA_HAS_FEATURE
#endif

// The ANALYZER_ASSUME_TRUE(bool arg) macro adds compiler-specific hints
// to Clang which control what code paths are statically analyzed,
// and is meant to be used in conjunction with assert & assert-like functions.
// The expression is passed straight through if analysis isn't enabled.
//
// ANALYZER_SKIP_THIS_PATH() suppresses static analysis for the current
// codepath and any other branching codepaths that might follow.
#if defined(__clang_analyzer__)

namespace partition_alloc::internal {

inline constexpr bool AnalyzerNoReturn() __attribute__((analyzer_noreturn)) {
  return false;
}

inline constexpr bool AnalyzerAssumeTrue(bool arg) {
  // PartitionAllocAnalyzerNoReturn() is invoked and analysis is terminated if
  // |arg| is false.
  return arg || AnalyzerNoReturn();
}

}  // namespace partition_alloc::internal

#define PA_ANALYZER_ASSUME_TRUE
#define PA_ANALYZER_SKIP_THIS_PATH

#else  // !defined(__clang_analyzer__)

#define PA_ANALYZER_ASSUME_TRUE(arg)
#define PA_ANALYZER_SKIP_THIS_PATH()

#endif  // defined(__clang_analyzer__)

// Use nomerge attribute to disable optimization of merging multiple same calls.
#if defined(__clang__) && PA_HAS_ATTRIBUTE(nomerge)
#define PA_NOMERGE
#else
#define PA_NOMERGE
#endif

// Marks a type as being eligible for the "trivial" ABI despite having a
// non-trivial destructor or copy/move constructor. Such types can be relocated
// after construction by simply copying their memory, which makes them eligible
// to be passed in registers. The canonical example is std::unique_ptr.
//
// Use with caution; this has some subtle effects on constructor/destructor
// ordering and will be very incorrect if the type relies on its address
// remaining constant. When used as a function argument (by value), the value
// may be constructed in the caller's stack frame, passed in a register, and
// then used and destructed in the callee's stack frame. A similar thing can
// occur when values are returned.
//
// TRIVIAL_ABI is not needed for types which have a trivial destructor and
// copy/move constructors, such as base::TimeTicks and other POD.
//
// It is also not likely to be effective on types too large to be passed in one
// or two registers on typical target ABIs.
//
// See also:
//   https://clang.llvm.org/docs/AttributeReference.html#trivial-abi
//   https://libcxx.llvm.org/docs/DesignDocs/UniquePtrTrivialAbi.html
#if defined(__clang__) && PA_HAS_ATTRIBUTE(trivial_abi)
#define PA_TRIVIAL_ABI
#else
#define PA_TRIVIAL_ABI
#endif

// Requires constant initialization. See constinit in C++20. Allows to rely on a
// variable being initialized before execution, and not requiring a global
// constructor.
#if PA_HAS_ATTRIBUTE(require_constant_initialization)
#define PA_CONSTINIT
#endif
#if !defined(PA_CONSTINIT)
#define PA_CONSTINIT
#endif

#if defined(__clang__)
#define PA_GSL_POINTER
#else
#define PA_GSL_POINTER
#endif

// Constexpr destructors were introduced in C++20. PartitionAlloc's minimum
// supported C++ version is C++17.
#if defined(__cpp_constexpr) && __cpp_constexpr >= 201907L
#define PA_CONSTEXPR_DTOR
#else
#define PA_CONSTEXPR_DTOR
#endif

// PA_LIFETIME_BOUND indicates that a resource owned by a function parameter or
// implicit object parameter is retained by the return value of the annotated
// function (or, for a parameter of a constructor, in the value of the
// constructed object). This attribute causes warnings to be produced if a
// temporary object does not live long enough.
//
// When applied to a reference parameter, the referenced object is assumed to be
// retained by the return value of the function. When applied to a non-reference
// parameter (for example, a pointer or a class type), all temporaries
// referenced by the parameter are assumed to be retained by the return value of
// the function.
//
// See also the upstream documentation:
// https://clang.llvm.org/docs/AttributeReference.html#lifetimebound
//
// This attribute is based on `ABSL_ATTRIBUTE_LIFETIME_BOUND`, but:
// * A separate definition is provided to avoid PartitionAlloc => Abseil
//   dependency
// * The definition is tweaked to avoid `__attribute__(lifetime))` because it
//   can't be applied in the same places as `[[clang::lifetimebound]]`.  In
//   particular `operator T*&() && __attribute__(lifetime))` fails to compile on
//   `clang` with the following error: 'lifetimebound' attribute only applies to
//   parameters and implicit object parameters
#if PA_HAS_CPP_ATTRIBUTE(clang::lifetimebound)
#define PA_LIFETIME_BOUND
#else
#define PA_LIFETIME_BOUND
#endif

// Clang instrumentation may allocate, leading to reentrancy in the allocator,
// and crashes when generating a PGO profile. This attribute disables profiling
// for a function.
//
// See
// https://clang.llvm.org/docs/AttributeReference.html#no-profile-instrument-function
#if PA_HAS_CPP_ATTRIBUTE(gnu::no_profile_instrument_function)
#define PA_NOPROFILE
#else
#define PA_NOPROFILE
#endif

#endif  // PARTITION_ALLOC_PARTITION_ALLOC_BASE_COMPILER_SPECIFIC_H_