// Copyright 2021 The IREE Authors
//
// Licensed under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

#ifndef IREE_BASE_ATTRIBUTES_H_
#define IREE_BASE_ATTRIBUTES_H_

#include "iree/base/target_platform.h"

//===----------------------------------------------------------------------===//
// API/ABI interop
//===----------------------------------------------------------------------===//

// Denotes a method exported by the IREE API.
// Any call annotated with this will be relatively stable.
// Calls without this are considered private to the IREE implementation and
// should not be relied upon.
#if defined(__cplusplus)
#define IREE_API_EXPORT extern "C"
#else
#define IREE_API_EXPORT
#endif  // __cplusplus

#if defined(__cplusplus)
#define IREE_API_EXPORT_VARIABLE extern "C"
#else
#define IREE_API_EXPORT_VARIABLE extern
#endif  // __cplusplus

// Denotes a function pointer that is exposed as part of the IREE API.
// Example:
//   iree_status_t(IREE_API_PTR* some_callback)(int value);
#define IREE_API_PTR

//===----------------------------------------------------------------------===//
// IREE_HAVE_ATTRIBUTE
//===----------------------------------------------------------------------===//

// Queries for [[attribute]] identifiers in modern compilers.
#if defined(__has_attribute)
#define IREE_HAVE_ATTRIBUTE(x) __has_attribute(x)
#else
#define IREE_HAVE_ATTRIBUTE(x) 0
#endif  // __has_attribute

//===----------------------------------------------------------------------===//
// IREE_HAVE_BUILTIN
//===----------------------------------------------------------------------===//

#if defined(__has_builtin)
#define IREE_HAVE_BUILTIN(x) __has_builtin(x)
#else
#define IREE_HAVE_BUILTIN(x) 0
#endif  // __has_builtin

//===----------------------------------------------------------------------===//
// IREE_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-4.7.0/gcc/Function-Attributes.html>.
#if IREE_HAVE_ATTRIBUTE(format) || (defined(__GNUC__) && !defined(__clang__))
#define IREE_PRINTF_ATTRIBUTE(string_index, first_to_check) \
  __attribute__((__format__(__printf__, string_index, first_to_check)))
#else
// TODO(benvanik): use _Printf_format_string_ in SAL for MSVC.
#define IREE_PRINTF_ATTRIBUTE(string_index, first_to_check)
#endif  // IREE_HAVE_ATTRIBUTE

//===----------------------------------------------------------------------===//
// IREE_ATTRIBUTE_NORETURN
//===----------------------------------------------------------------------===//

// Tells the compiler that a given function never returns.
#if IREE_HAVE_ATTRIBUTE(noreturn) || (defined(__GNUC__) && !defined(__clang__))
#define IREE_ATTRIBUTE_NORETURN __attribute__((noreturn))
#elif defined(_MSC_VER)
#define IREE_ATTRIBUTE_NORETURN __declspec(noreturn)
#else
#define IREE_ATTRIBUTE_NORETURN
#endif  // IREE_HAVE_ATTRIBUTE(noreturn)

//===----------------------------------------------------------------------===//
// IREE_MUST_USE_RESULT
//===----------------------------------------------------------------------===//

// Annotation for function return values that ensures that they are used by the
// caller.
#if IREE_HAVE_ATTRIBUTE(nodiscard)
#define IREE_MUST_USE_RESULT [[nodiscard]]
#elif (defined(__clang__) && IREE_HAVE_ATTRIBUTE(warn_unused_result)) || \
    (defined(__GNUC__) && (__GNUC__ >= 4))
#define IREE_MUST_USE_RESULT __attribute__((warn_unused_result))
#elif defined(_MSC_VER) && (_MSC_VER >= 1700)
#define IREE_MUST_USE_RESULT _Check_return_
#else
#define IREE_MUST_USE_RESULT
#endif  // IREE_HAVE_ATTRIBUTE(nodiscard)

//===----------------------------------------------------------------------===//
// IREE_RESTRICT
//===----------------------------------------------------------------------===//

// `restrict` keyword, not supported by some older compilers.
// We define our own macro in case dependencies use `restrict` differently.
#if defined(_MSC_VER) && _MSC_VER >= 1900
#define IREE_RESTRICT __restrict
#elif defined(_MSC_VER)
#define IREE_RESTRICT
#elif defined(__cplusplus)
#define IREE_RESTRICT __restrict__
#else
#define IREE_RESTRICT restrict
#endif  // _MSC_VER

//===----------------------------------------------------------------------===//
// IREE_ATTRIBUTE_ALWAYS_INLINE / IREE_ATTRIBUTE_NOINLINE
//===----------------------------------------------------------------------===//

// Forces functions to either inline or not inline. Introduced in gcc 3.1.
#if IREE_HAVE_ATTRIBUTE(always_inline) || \
    (defined(__GNUC__) && !defined(__clang__))
#define IREE_ATTRIBUTE_ALWAYS_INLINE __attribute__((always_inline))
#else
#define IREE_ATTRIBUTE_ALWAYS_INLINE
#endif  // IREE_HAVE_ATTRIBUTE(always_inline)

#if IREE_HAVE_ATTRIBUTE(noinline) || (defined(__GNUC__) && !defined(__clang__))
#define IREE_ATTRIBUTE_NOINLINE __attribute__((noinline))
#else
#define IREE_ATTRIBUTE_NOINLINE
#endif  // IREE_HAVE_ATTRIBUTE(noinline)

//===----------------------------------------------------------------------===//
// IREE_ATTRIBUTE_HOT / IREE_ATTRIBUTE_COLD
//===----------------------------------------------------------------------===//

// Tells GCC that a function is hot or cold. GCC can use this information to
// improve static analysis, i.e. a conditional branch to a cold function
// is likely to be not-taken.
// This annotation is used for function declarations.
//
// Example:
//   int foo() IREE_ATTRIBUTE_HOT;
#if IREE_HAVE_ATTRIBUTE(hot) || (defined(__GNUC__) && !defined(__clang__))
#define IREE_ATTRIBUTE_HOT __attribute__((hot))
#else
#define IREE_ATTRIBUTE_HOT
#endif  // IREE_HAVE_ATTRIBUTE(hot)

#if IREE_HAVE_ATTRIBUTE(cold) || (defined(__GNUC__) && !defined(__clang__))
#define IREE_ATTRIBUTE_COLD __attribute__((cold))
#else
#define IREE_ATTRIBUTE_COLD
#endif  // IREE_HAVE_ATTRIBUTE(cold)

//===----------------------------------------------------------------------===//
// Portable explicit prefetch hints
//===----------------------------------------------------------------------===//

#if IREE_HAVE_BUILTIN(__builtin_prefetch) || defined(__GNUC__)
#define IREE_BUILTIN_PREFETCH_RO(ptr, hint) \
  __builtin_prefetch((ptr), /*rw=*/0, hint)
#define IREE_BUILTIN_PREFETCH_RW(ptr, hint) \
  __builtin_prefetch((ptr), /*rw=*/1, hint)
#else
#define IREE_BUILTIN_PREFETCH_RO(ptr, hint)
#define IREE_BUILTIN_PREFETCH_RW(ptr, hint)
#endif  // IREE_HAVE_BUILTIN(__builtin_prefetch)

// This is how the 0--3 locality hint is interpreted by both Clang and GCC on
// both arm64 and x86-64.
enum {
  IREE_BUILTIN_PREFETCH_LOCALITY_NONE = 0,  // No locality. Data accessed once.
  IREE_BUILTIN_PREFETCH_LOCALITY_L3 = 1,    // Some locality. Try to keep in L3.
  IREE_BUILTIN_PREFETCH_LOCALITY_L2 = 2,    // More locality. Try to keep in L2.
  IREE_BUILTIN_PREFETCH_LOCALITY_L1 = 3,    // Most locality. Try to keep in L1.
};

//===----------------------------------------------------------------------===//
// IREE_LIKELY / IREE_UNLIKELY
//===----------------------------------------------------------------------===//

// Compiler hint that can be used to indicate conditions that are very very very
// likely or unlikely. This is most useful for ensuring that unlikely cases such
// as error handling are moved off the mainline code path such that the code is
// only paged in when an error occurs.
//
// Example:
//   if (IREE_UNLIKELY(something_failed)) {
//     return do_expensive_error_logging();
//   }
#if defined(__GNUC__) || defined(__clang__)
#define IREE_LIKELY(x) (__builtin_expect(!!(x), 1))
#define IREE_UNLIKELY(x) (__builtin_expect(!!(x), 0))
#else
#define IREE_LIKELY(x) (x)
#define IREE_UNLIKELY(x) (x)
#endif  // IREE_HAVE_ATTRIBUTE(likely)

//===----------------------------------------------------------------------===//
// IREE_ATTRIBUTE_PACKED
//===----------------------------------------------------------------------===//

#if IREE_HAVE_ATTRIBUTE(packed) || (defined(__GNUC__) && !defined(__clang__))
#define IREE_ATTRIBUTE_PACKED __attribute__((__packed__))
#else
#define IREE_ATTRIBUTE_PACKED
#endif  // IREE_HAVE_ATTRIBUTE(packed)

//===----------------------------------------------------------------------===//
// IREE_ATTRIBUTE_UNUSED
//===----------------------------------------------------------------------===//

// Hints that a variable is _maybe_ unused. This is primarily to quiet
// diagnostic messages about unused variables that crop up around variables
// passed to assert/logging/etc that gets stripped in certain configurations.
//
// Example:
//   int some_info IREE_ATTRIBUTE_UNUSED = compute_debug_info();
//   assert(some_info > 0);  // stripped in NDEBUG
#if IREE_HAVE_ATTRIBUTE(maybe_unused) && defined(__clang__)
#define IREE_ATTRIBUTE_UNUSED __attribute__((maybe_unused))
#elif IREE_HAVE_ATTRIBUTE(unused) || (defined(__GNUC__) && !defined(__clang__))
#define IREE_ATTRIBUTE_UNUSED __attribute__((unused))
#else
#define IREE_ATTRIBUTE_UNUSED
#endif  // IREE_HAVE_ATTRIBUTE(maybe_unused / unused)

//===----------------------------------------------------------------------===//
// IREE_ATTRIBUTE_WEAK
//===----------------------------------------------------------------------===//

// Declares a symbol as weakly-linked, i.e. overridable at linking time.
// This is only supported on certain toolchains, and has no effect on other
// toolchains. Support is indicated by IREE_HAVE_ATTRIBUTE_WEAK.

#if IREE_HAVE_ATTRIBUTE(weak) || (defined(__GNUC__) && !defined(__clang__))
#define IREE_ATTRIBUTE_WEAK __attribute__((weak))
#define IREE_HAVE_ATTRIBUTE_WEAK 1
#else
#define IREE_ATTRIBUTE_WEAK
#define IREE_HAVE_ATTRIBUTE_WEAK 0
#endif  // IREE_HAVE_ATTRIBUTE(weak)

#endif  // IREE_BASE_ATTRIBUTES_H_
