/* * Copyright (c) Meta Platforms, Inc. and affiliates. * All rights reserved. * * This source code is licensed under the BSD-style license found in the * LICENSE file in the root directory of this source tree. */ /** * @file * Compiler utility macros. */ #pragma once /* * Compiler support checks. Follows the logic used by pytorch/c10/util/C++17.h * but may support older versions. */ // https://gcc.gnu.org/projects/cxx-status.html#cxx17 #if !defined(__clang__) && !defined(_MSC_VER) && defined(__GNUC__) && \ __GNUC__ < 7 #error \ "You're trying to build ExecuTorch with a too old version of GCC. We need GCC 7 or later." #endif // https://clang.llvm.org/cxx_status.html#cxx17 #if defined(__clang__) && __clang_major__ < 5 #error \ "You're trying to build ExecuTorch with a too old version of Clang. We need Clang 5 or later." #endif #if (defined(_MSC_VER) && (!defined(_MSVC_LANG) || _MSVC_LANG < 201703L)) || \ (!defined(_MSC_VER) && __cplusplus < 201703L) #error "You need C++17 to compile ExecuTorch" #endif #if defined(_MSC_VER) && (defined(min) || defined(max)) #error \ "Macro clash with min and max -- define NOMINMAX when compiling your program on Windows" #endif /* * Define annotations aliasing C++ declaration attributes. * See all C++ declaration attributes here: * https://en.cppreference.com/w/cpp/language/attributes * * Note that ExecuTorch supports a lower C++ standard version than all standard * attributes. Therefore, some annotations are defined using their Clang/GNU * counterparts. * * GNU attribute definitions: * https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html */ #define ET_NORETURN [[noreturn]] #define ET_NOINLINE __attribute__((noinline)) #define ET_INLINE __attribute__((always_inline)) inline #define ET_INLINE_ATTRIBUTE __attribute__((always_inline)) #if defined(__GNUC__) #define ET_UNREACHABLE() __builtin_unreachable() #elif defined(_MSC_VER) #define ET_UNREACHABLE() __assume(0) #else // defined(__GNUC__) #define ET_UNREACHABLE() \ while (1) \ ; #endif // defined(__GNUC__) #define ET_DEPRECATED [[deprecated]] #define ET_EXPERIMENTAL \ [[deprecated("This API is experimental and may change without notice.")]] #define ET_FALLTHROUGH [[fallthrough]] #define ET_NODISCARD [[nodiscard]] #define ET_UNUSED [[maybe_unused]] // UNLIKELY Macro // example // if ET_UNLIKELY(a > 10 && b < 5) { // do something // } #if (__cplusplus) >= 202002L #define ET_LIKELY(expr) (expr) [[likely]] #define ET_UNLIKELY(expr) (expr) [[unlikely]] #else #define ET_LIKELY(expr) (expr) #define ET_UNLIKELY(expr) (expr) #endif // (__cplusplus) >= 202002L /// Define a C symbol with weak linkage. #ifdef _MSC_VER // There currently doesn't seem to be a great way to do this in Windows and // given that weak linkage is not really critical on Windows, we'll just leave // it as a stub. #define ET_WEAK #else #define ET_WEAK __attribute__((weak)) #endif /** * Annotation marking a function as printf-like, providing compiler support * for format string argument checking. */ #ifdef _MSC_VER #include #define ET_PRINTFLIKE(_string_index, _va_index) _Printf_format_string_ #else #define ET_PRINTFLIKE(_string_index, _va_index) \ __attribute__((format(printf, _string_index, _va_index))) #endif #ifndef __has_builtin #define __has_builtin(x) (0) #endif #if __has_builtin(__builtin_strrchr) /// Name of the source file without a directory string. #define ET_SHORT_FILENAME (__builtin_strrchr("/" __FILE__, '/') + 1) #else #define ET_SHORT_FILENAME __FILE__ #endif #if __has_builtin(__builtin_LINE) /// Current line as an integer. #define ET_LINE __builtin_LINE() #else #define ET_LINE __LINE__ #endif // __has_builtin(__builtin_LINE) #if __has_builtin(__builtin_FUNCTION) /// Name of the current function as a const char[]. #define ET_FUNCTION __builtin_FUNCTION() #else #define ET_FUNCTION __FUNCTION__ #endif // __has_builtin(__builtin_FUNCTION) // As of G3 RJ-2024.3 toolchain, zu format specifier is not supported for Xtensa #if defined(__XTENSA__) #define ET_PRIsize_t "lu" #else #define ET_PRIsize_t "zu" #endif // Whether the compiler supports GNU statement expressions. // https://gcc.gnu.org/onlinedocs/gcc/Statement-Exprs.html #ifndef ET_HAVE_GNU_STATEMENT_EXPRESSIONS #if (defined(__GNUC__) && __GNUC__ >= 3) || defined(__clang__) #define ET_HAVE_GNU_STATEMENT_EXPRESSIONS 1 #else #define ET_HAVE_GNU_STATEMENT_EXPRESSIONS 0 #endif #endif // ifndef // Define size_t and ssize_t. #ifndef _MSC_VER #include #else #include using ssize_t = ptrdiff_t; #endif /** * Platform-specific aligned memory allocation and deallocation. * * Usage: * void* ptr = ET_ALIGNED_ALLOC(alignment, size); * // use ptr... * ET_ALIGNED_FREE(ptr); * * Note: alignment must be a power of 2 and size must be an integral multiple of * alignment. */ #if defined(_MSC_VER) #include #define ET_ALIGNED_ALLOC(alignment, size) \ _aligned_malloc(((size + alignment - 1) & ~(alignment - 1)), (alignment)) #define ET_ALIGNED_FREE(ptr) _aligned_free(ptr) #elif defined(__APPLE__) #include // For posix_memalign and free inline void* et_apple_aligned_alloc(size_t alignment, size_t size) { void* ptr = nullptr; // The address of the allocated memory must be a multiple of sizeof(void*). if (alignment < sizeof(void*)) { alignment = sizeof(void*); } if (posix_memalign( &ptr, alignment, (size + alignment - 1) & ~(alignment - 1)) != 0) { return nullptr; } return ptr; } #define ET_ALIGNED_ALLOC(alignment, size) \ et_apple_aligned_alloc((alignment), (size)) #define ET_ALIGNED_FREE(ptr) free(ptr) #elif __has_builtin(__builtin_aligned_alloc) || defined(_ISOC11_SOURCE) // Linux and posix systems that support aligned_alloc and are >= C++17. #include #define ET_ALIGNED_ALLOC(alignment, size) \ ::aligned_alloc(alignment, (size + alignment - 1) & ~(alignment - 1)) #define ET_ALIGNED_FREE(ptr) free(ptr) #else // If the platform doesn't support aligned_alloc, fallback to malloc. #include #include inline void* et_aligned_malloc(size_t alignment, size_t size) { // Place to store the offset to the original pointer. size_t offset_size = sizeof(uint16_t); // Malloc extra space for offset + alignment. size_t alloc_size = size + offset_size + alignment - 1; void* ptr = std::malloc(alloc_size); if (ptr == nullptr) { // Malloc failed. return nullptr; } uintptr_t addr = reinterpret_cast(ptr); // Align the address past addr + offset_size bytes. // This provides space to store the offset before the aligned pointer. addr = addr + offset_size; uintptr_t aligned_ptr = (addr + alignment - 1) & ~(alignment - 1); // Check that alignment didn't overflow the buffer. if (reinterpret_cast(aligned_ptr) + size > reinterpret_cast(ptr) + alloc_size) { std::free(ptr); return nullptr; } // Store the offset to the original pointer. // Used to free the original allocated buffer. *(reinterpret_cast(aligned_ptr) - 1) = (uint16_t)(reinterpret_cast(aligned_ptr) - reinterpret_cast(ptr)); return reinterpret_cast(aligned_ptr); } inline void et_aligned_free(void* ptr) { if (ptr == nullptr) { return; } // Get the original pointer using the offset. uint16_t* original_ptr = reinterpret_cast( reinterpret_cast(ptr) - *(reinterpret_cast(ptr) - 1)); std::free(original_ptr); } #define ET_ALIGNED_ALLOC(alignment, size) et_aligned_malloc((alignment), (size)) #define ET_ALIGNED_FREE(ptr) et_aligned_free(ptr) #endif // DEPRECATED: Use the non-underscore-prefixed versions instead. // TODO(T199005537): Remove these once all users have stopped using them. #define __ET_DEPRECATED ET_DEPRECATED #define __ET_FALLTHROUGH ET_FALLTHROUGH #define __ET_FUNCTION ET_FUNCTION #define __ET_HAVE_GNU_STATEMENT_EXPRESSIONS ET_HAVE_GNU_STATEMENT_EXPRESSIONS #define __ET_INLINE ET_INLINE #define __ET_LIKELY ET_LIKELY #define __ET_LINE ET_LINE #define __ET_NODISCARD ET_NODISCARD #define __ET_NOINLINE ET_NOINLINE #define __ET_NORETURN ET_NORETURN #define __ET_PRINTFLIKE ET_PRINTFLIKE #define __ET_SHORT_FILENAME ET_SHORT_FILENAME #define __ET_UNLIKELY ET_UNLIKELY #define __ET_UNREACHABLE ET_UNREACHABLE #define __ET_UNUSED ET_UNUSED #define __ET_WEAK ET_WEAK