/*
 * Copyright 2019 The libgav1 Authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef LIBGAV1_SRC_UTILS_COMPILER_ATTRIBUTES_H_
#define LIBGAV1_SRC_UTILS_COMPILER_ATTRIBUTES_H_

// A collection of compiler attribute checks and defines to control for
// compatibility across toolchains.

//------------------------------------------------------------------------------
// Language version, attribute and feature helpers.

// Detect c++17 support. Visual Studio sets __cplusplus to 199711L by default
// unless compiled with /Zc:__cplusplus, use the value controlled by /std
// instead.
// https://docs.microsoft.com/en-us/cpp/build/reference/zc-cplusplus
#if __cplusplus >= 201703L || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L)
#define LIBGAV1_CXX17 1
#else
#define LIBGAV1_CXX17 0
#endif

#if defined(__has_attribute)
#define LIBGAV1_HAS_ATTRIBUTE __has_attribute
#else
#define LIBGAV1_HAS_ATTRIBUTE(x) 0
#endif

#if defined(__has_feature)
#define LIBGAV1_HAS_FEATURE __has_feature
#else
#define LIBGAV1_HAS_FEATURE(x) 0
#endif

//------------------------------------------------------------------------------
// Sanitizer attributes.

#if LIBGAV1_HAS_FEATURE(address_sanitizer) || defined(__SANITIZE_ADDRESS__)
#define LIBGAV1_ASAN 1
#else
#define LIBGAV1_ASAN 0
#endif

#if LIBGAV1_HAS_FEATURE(memory_sanitizer)
#define LIBGAV1_MSAN 1
#else
#define LIBGAV1_MSAN 0
#endif

#if LIBGAV1_HAS_FEATURE(thread_sanitizer) || defined(__SANITIZE_THREAD__)
#define LIBGAV1_TSAN 1
#else
#define LIBGAV1_TSAN 0
#endif

//------------------------------------------------------------------------------
// AddressSanitizer support.

// Define the macros for AddressSanitizer manual memory poisoning. See
// https://github.com/google/sanitizers/wiki/AddressSanitizerManualPoisoning.
#if LIBGAV1_ASAN
#include <sanitizer/asan_interface.h>
#else
#define ASAN_POISON_MEMORY_REGION(addr, size) \
  (static_cast<void>(addr), static_cast<void>(size))
#define ASAN_UNPOISON_MEMORY_REGION(addr, size) \
  (static_cast<void>(addr), static_cast<void>(size))
#endif

//------------------------------------------------------------------------------
// Function attributes.
// GCC: https://gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html
// Clang: https://clang.llvm.org/docs/AttributeReference.html

#if defined(__GNUC__)
#define LIBGAV1_ALWAYS_INLINE __attribute__((always_inline)) inline
#elif defined(_MSC_VER)
#define LIBGAV1_ALWAYS_INLINE __forceinline
#else
#define LIBGAV1_ALWAYS_INLINE inline
#endif

// LIBGAV1_MUST_USE_RESULT
//
// Tells the compiler to warn about unused results.
//
// When annotating a function, it must appear as the first part of the
// declaration or definition. The compiler will warn if the return value from
// such a function is unused:
//
//   LIBGAV1_MUST_USE_RESULT Sprocket* AllocateSprocket();
//   AllocateSprocket();  // Triggers a warning.
//
// When annotating a class, it is equivalent to annotating every function which
// returns an instance.
//
//   class LIBGAV1_MUST_USE_RESULT Sprocket {};
//   Sprocket();  // Triggers a warning.
//
//   Sprocket MakeSprocket();
//   MakeSprocket();  // Triggers a warning.
//
// Note that references and pointers are not instances:
//
//   Sprocket* SprocketPointer();
//   SprocketPointer();  // Does *not* trigger a warning.
//
// LIBGAV1_MUST_USE_RESULT allows using cast-to-void to suppress the unused
// result warning. For that, warn_unused_result is used only for clang but not
// for gcc. https://gcc.gnu.org/bugzilla/show_bug.cgi?id=66425
#if LIBGAV1_HAS_ATTRIBUTE(nodiscard)
#define LIBGAV1_MUST_USE_RESULT [[nodiscard]]
#elif defined(__clang__) && LIBGAV1_HAS_ATTRIBUTE(warn_unused_result)
#define LIBGAV1_MUST_USE_RESULT __attribute__((warn_unused_result))
#else
#define LIBGAV1_MUST_USE_RESULT
#endif

// LIBGAV1_PRINTF_ATTRIBUTE
//
// Tells the compiler to perform `printf` format string checking if the
// compiler supports it; see the 'format' attribute in
// <https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html>.
//
// Note: As the GCC manual states, "[s]ince non-static C++ methods
// have an implicit 'this' argument, the arguments of such methods
// should be counted from two, not one."
#if LIBGAV1_HAS_ATTRIBUTE(format) || (defined(__GNUC__) && !defined(__clang__))
#define LIBGAV1_PRINTF_ATTRIBUTE(string_index, first_to_check) \
  __attribute__((__format__(__printf__, string_index, first_to_check)))
#else
#define LIBGAV1_PRINTF_ATTRIBUTE(string_index, first_to_check)
#endif

//------------------------------------------------------------------------------
// Thread annotations.

// LIBGAV1_GUARDED_BY()
//
// Documents if a shared field or global variable needs to be protected by a
// mutex. LIBGAV1_GUARDED_BY() allows the user to specify a particular mutex
// that should be held when accessing the annotated variable.
//
// Although this annotation cannot be applied to local variables, a local
// variable and its associated mutex can often be combined into a small class
// or struct, thereby allowing the annotation.
//
// Example:
//
//   class Foo {
//     Mutex mu_;
//     int p1_ LIBGAV1_GUARDED_BY(mu_);
//     ...
//   };
// TODO(b/133245043): this can be reenabled after a local MutexLock
// implementation is added with proper thread annotations.
#if 0  // LIBGAV1_HAS_ATTRIBUTE(guarded_by)
#define LIBGAV1_GUARDED_BY(x) __attribute__((guarded_by(x)))
#else
#define LIBGAV1_GUARDED_BY(x)
#endif

//------------------------------------------------------------------------------

#undef LIBGAV1_HAS_ATTRIBUTE
#undef LIBGAV1_HAS_FEATURE

#endif  // LIBGAV1_SRC_UTILS_COMPILER_ATTRIBUTES_H_