xref: /linux-6.15/lib/zstd/common/compiler.h (revision 65d1f550)
1*65d1f550SNick Terrell /* SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause */
2e0c1b49fSNick Terrell /*
3*65d1f550SNick Terrell  * Copyright (c) Meta Platforms, Inc. and affiliates.
4e0c1b49fSNick Terrell  * All rights reserved.
5e0c1b49fSNick Terrell  *
6e0c1b49fSNick Terrell  * This source code is licensed under both the BSD-style license (found in the
7e0c1b49fSNick Terrell  * LICENSE file in the root directory of this source tree) and the GPLv2 (found
8e0c1b49fSNick Terrell  * in the COPYING file in the root directory of this source tree).
9e0c1b49fSNick Terrell  * You may select, at your option, one of the above-listed licenses.
10e0c1b49fSNick Terrell  */
11e0c1b49fSNick Terrell 
12e0c1b49fSNick Terrell #ifndef ZSTD_COMPILER_H
13e0c1b49fSNick Terrell #define ZSTD_COMPILER_H
14e0c1b49fSNick Terrell 
15*65d1f550SNick Terrell #include <linux/types.h>
16*65d1f550SNick Terrell 
172aa14b1aSNick Terrell #include "portability_macros.h"
182aa14b1aSNick Terrell 
19e0c1b49fSNick Terrell /*-*******************************************************
20e0c1b49fSNick Terrell *  Compiler specifics
21e0c1b49fSNick Terrell *********************************************************/
22e0c1b49fSNick Terrell /* force inlining */
23e0c1b49fSNick Terrell 
241974990cSNick Terrell #if !defined(ZSTD_NO_INLINE)
25e0c1b49fSNick Terrell #if (defined(__GNUC__) && !defined(__STRICT_ANSI__)) || defined(__cplusplus) || defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L   /* C99 */
26e0c1b49fSNick Terrell #  define INLINE_KEYWORD inline
27e0c1b49fSNick Terrell #else
28e0c1b49fSNick Terrell #  define INLINE_KEYWORD
29e0c1b49fSNick Terrell #endif
30e0c1b49fSNick Terrell 
31e0c1b49fSNick Terrell #define FORCE_INLINE_ATTR __attribute__((always_inline))
32e0c1b49fSNick Terrell 
331974990cSNick Terrell #else
341974990cSNick Terrell 
351974990cSNick Terrell #define INLINE_KEYWORD
361974990cSNick Terrell #define FORCE_INLINE_ATTR
371974990cSNick Terrell 
381974990cSNick Terrell #endif
39e0c1b49fSNick Terrell 
40e0c1b49fSNick Terrell /*
41e0c1b49fSNick Terrell   On MSVC qsort requires that functions passed into it use the __cdecl calling conversion(CC).
422aa14b1aSNick Terrell   This explicitly marks such functions as __cdecl so that the code will still compile
43e0c1b49fSNick Terrell   if a CC other than __cdecl has been made the default.
44e0c1b49fSNick Terrell */
45e0c1b49fSNick Terrell #define WIN_CDECL
46e0c1b49fSNick Terrell 
47*65d1f550SNick Terrell /* UNUSED_ATTR tells the compiler it is okay if the function is unused. */
48*65d1f550SNick Terrell #define UNUSED_ATTR __attribute__((unused))
49*65d1f550SNick Terrell 
50e0c1b49fSNick Terrell /*
51e0c1b49fSNick Terrell  * FORCE_INLINE_TEMPLATE is used to define C "templates", which take constant
52e0c1b49fSNick Terrell  * parameters. They must be inlined for the compiler to eliminate the constant
53e0c1b49fSNick Terrell  * branches.
54e0c1b49fSNick Terrell  */
55*65d1f550SNick Terrell #define FORCE_INLINE_TEMPLATE static INLINE_KEYWORD FORCE_INLINE_ATTR UNUSED_ATTR
56e0c1b49fSNick Terrell /*
57e0c1b49fSNick Terrell  * HINT_INLINE is used to help the compiler generate better code. It is *not*
58e0c1b49fSNick Terrell  * used for "templates", so it can be tweaked based on the compilers
59e0c1b49fSNick Terrell  * performance.
60e0c1b49fSNick Terrell  *
61e0c1b49fSNick Terrell  * gcc-4.8 and gcc-4.9 have been shown to benefit from leaving off the
62e0c1b49fSNick Terrell  * always_inline attribute.
63e0c1b49fSNick Terrell  *
64e0c1b49fSNick Terrell  * clang up to 5.0.0 (trunk) benefit tremendously from the always_inline
65e0c1b49fSNick Terrell  * attribute.
66e0c1b49fSNick Terrell  */
67e0c1b49fSNick Terrell #if !defined(__clang__) && defined(__GNUC__) && __GNUC__ >= 4 && __GNUC_MINOR__ >= 8 && __GNUC__ < 5
68e0c1b49fSNick Terrell #  define HINT_INLINE static INLINE_KEYWORD
69e0c1b49fSNick Terrell #else
70*65d1f550SNick Terrell #  define HINT_INLINE FORCE_INLINE_TEMPLATE
71e0c1b49fSNick Terrell #endif
72e0c1b49fSNick Terrell 
73*65d1f550SNick Terrell /* "soft" inline :
74*65d1f550SNick Terrell  * The compiler is free to select if it's a good idea to inline or not.
75*65d1f550SNick Terrell  * The main objective is to silence compiler warnings
76*65d1f550SNick Terrell  * when a defined function in included but not used.
77*65d1f550SNick Terrell  *
78*65d1f550SNick Terrell  * Note : this macro is prefixed `MEM_` because it used to be provided by `mem.h` unit.
79*65d1f550SNick Terrell  * Updating the prefix is probably preferable, but requires a fairly large codemod,
80*65d1f550SNick Terrell  * since this name is used everywhere.
81*65d1f550SNick Terrell  */
82*65d1f550SNick Terrell #ifndef MEM_STATIC  /* already defined in Linux Kernel mem.h */
83*65d1f550SNick Terrell #define MEM_STATIC static __inline UNUSED_ATTR
84*65d1f550SNick Terrell #endif
85e0c1b49fSNick Terrell 
86e0c1b49fSNick Terrell /* force no inlining */
87e0c1b49fSNick Terrell #define FORCE_NOINLINE static __attribute__((__noinline__))
88e0c1b49fSNick Terrell 
89e0c1b49fSNick Terrell 
90e0c1b49fSNick Terrell /* target attribute */
91e0c1b49fSNick Terrell #define TARGET_ATTRIBUTE(target) __attribute__((__target__(target)))
92e0c1b49fSNick Terrell 
932aa14b1aSNick Terrell /* Target attribute for BMI2 dynamic dispatch.
942aa14b1aSNick Terrell  * Enable lzcnt, bmi, and bmi2.
952aa14b1aSNick Terrell  * We test for bmi1 & bmi2. lzcnt is included in bmi1.
96e0c1b49fSNick Terrell  */
972aa14b1aSNick Terrell #define BMI2_TARGET_ATTRIBUTE TARGET_ATTRIBUTE("lzcnt,bmi,bmi2")
98e0c1b49fSNick Terrell 
99e0c1b49fSNick Terrell /* prefetch
100e0c1b49fSNick Terrell  * can be disabled, by declaring NO_PREFETCH build macro */
101e0c1b49fSNick Terrell #if ( (__GNUC__ >= 4) || ( (__GNUC__ == 3) && (__GNUC_MINOR__ >= 1) ) )
102e0c1b49fSNick Terrell #  define PREFETCH_L1(ptr)  __builtin_prefetch((ptr), 0 /* rw==read */, 3 /* locality */)
103e0c1b49fSNick Terrell #  define PREFETCH_L2(ptr)  __builtin_prefetch((ptr), 0 /* rw==read */, 2 /* locality */)
104e0c1b49fSNick Terrell #elif defined(__aarch64__)
105*65d1f550SNick Terrell #  define PREFETCH_L1(ptr)  do { __asm__ __volatile__("prfm pldl1keep, %0" ::"Q"(*(ptr))); } while (0)
106*65d1f550SNick Terrell #  define PREFETCH_L2(ptr)  do { __asm__ __volatile__("prfm pldl2keep, %0" ::"Q"(*(ptr))); } while (0)
107e0c1b49fSNick Terrell #else
108*65d1f550SNick Terrell #  define PREFETCH_L1(ptr) do { (void)(ptr); } while (0)  /* disabled */
109*65d1f550SNick Terrell #  define PREFETCH_L2(ptr) do { (void)(ptr); } while (0)  /* disabled */
110e0c1b49fSNick Terrell #endif  /* NO_PREFETCH */
111e0c1b49fSNick Terrell 
112e0c1b49fSNick Terrell #define CACHELINE_SIZE 64
113e0c1b49fSNick Terrell 
114*65d1f550SNick Terrell #define PREFETCH_AREA(p, s)                              \
115*65d1f550SNick Terrell     do {                                                 \
116e0c1b49fSNick Terrell         const char* const _ptr = (const char*)(p);       \
117e0c1b49fSNick Terrell         size_t const _size = (size_t)(s);                \
118e0c1b49fSNick Terrell         size_t _pos;                                     \
119e0c1b49fSNick Terrell         for (_pos=0; _pos<_size; _pos+=CACHELINE_SIZE) { \
120e0c1b49fSNick Terrell             PREFETCH_L2(_ptr + _pos);                    \
121e0c1b49fSNick Terrell         }                                                \
122*65d1f550SNick Terrell     } while (0)
123e0c1b49fSNick Terrell 
124e0c1b49fSNick Terrell /* vectorization
1252aa14b1aSNick Terrell  * older GCC (pre gcc-4.3 picked as the cutoff) uses a different syntax,
1262aa14b1aSNick Terrell  * and some compilers, like Intel ICC and MCST LCC, do not support it at all. */
1272aa14b1aSNick Terrell #if !defined(__INTEL_COMPILER) && !defined(__clang__) && defined(__GNUC__) && !defined(__LCC__)
128e0c1b49fSNick Terrell #  if (__GNUC__ == 4 && __GNUC_MINOR__ > 3) || (__GNUC__ >= 5)
129e0c1b49fSNick Terrell #    define DONT_VECTORIZE __attribute__((optimize("no-tree-vectorize")))
130e0c1b49fSNick Terrell #  else
131e0c1b49fSNick Terrell #    define DONT_VECTORIZE _Pragma("GCC optimize(\"no-tree-vectorize\")")
132e0c1b49fSNick Terrell #  endif
133e0c1b49fSNick Terrell #else
134e0c1b49fSNick Terrell #  define DONT_VECTORIZE
135e0c1b49fSNick Terrell #endif
136e0c1b49fSNick Terrell 
137e0c1b49fSNick Terrell /* Tell the compiler that a branch is likely or unlikely.
138e0c1b49fSNick Terrell  * Only use these macros if it causes the compiler to generate better code.
139e0c1b49fSNick Terrell  * If you can remove a LIKELY/UNLIKELY annotation without speed changes in gcc
140e0c1b49fSNick Terrell  * and clang, please do.
141e0c1b49fSNick Terrell  */
142e0c1b49fSNick Terrell #define LIKELY(x) (__builtin_expect((x), 1))
143e0c1b49fSNick Terrell #define UNLIKELY(x) (__builtin_expect((x), 0))
144e0c1b49fSNick Terrell 
1452aa14b1aSNick Terrell #if __has_builtin(__builtin_unreachable) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5)))
146*65d1f550SNick Terrell #  define ZSTD_UNREACHABLE do { assert(0), __builtin_unreachable(); } while (0)
1472aa14b1aSNick Terrell #else
148*65d1f550SNick Terrell #  define ZSTD_UNREACHABLE do { assert(0); } while (0)
1492aa14b1aSNick Terrell #endif
1502aa14b1aSNick Terrell 
151e0c1b49fSNick Terrell /* disable warnings */
152e0c1b49fSNick Terrell 
1532aa14b1aSNick Terrell /* compile time determination of SIMD support */
154e0c1b49fSNick Terrell 
155e0c1b49fSNick Terrell /* C-language Attributes are added in C23. */
156e0c1b49fSNick Terrell #if defined(__STDC_VERSION__) && (__STDC_VERSION__ > 201710L) && defined(__has_c_attribute)
157e0c1b49fSNick Terrell # define ZSTD_HAS_C_ATTRIBUTE(x) __has_c_attribute(x)
158e0c1b49fSNick Terrell #else
159e0c1b49fSNick Terrell # define ZSTD_HAS_C_ATTRIBUTE(x) 0
160e0c1b49fSNick Terrell #endif
161e0c1b49fSNick Terrell 
162e0c1b49fSNick Terrell /* Only use C++ attributes in C++. Some compilers report support for C++
163e0c1b49fSNick Terrell  * attributes when compiling with C.
164e0c1b49fSNick Terrell  */
165e0c1b49fSNick Terrell #define ZSTD_HAS_CPP_ATTRIBUTE(x) 0
166e0c1b49fSNick Terrell 
167e0c1b49fSNick Terrell /* Define ZSTD_FALLTHROUGH macro for annotating switch case with the 'fallthrough' attribute.
168e0c1b49fSNick Terrell  * - C23: https://en.cppreference.com/w/c/language/attributes/fallthrough
169e0c1b49fSNick Terrell  * - CPP17: https://en.cppreference.com/w/cpp/language/attributes/fallthrough
170e0c1b49fSNick Terrell  * - Else: __attribute__((__fallthrough__))
171e0c1b49fSNick Terrell  */
172e0c1b49fSNick Terrell #define ZSTD_FALLTHROUGH fallthrough
173e0c1b49fSNick Terrell 
1742aa14b1aSNick Terrell /*-**************************************************************
175*65d1f550SNick Terrell *  Alignment
1762aa14b1aSNick Terrell *****************************************************************/
177e0c1b49fSNick Terrell 
178*65d1f550SNick Terrell /* @return 1 if @u is a 2^n value, 0 otherwise
179*65d1f550SNick Terrell  * useful to check a value is valid for alignment restrictions */
ZSTD_isPower2(size_t u)180*65d1f550SNick Terrell MEM_STATIC int ZSTD_isPower2(size_t u) {
181*65d1f550SNick Terrell     return (u & (u-1)) == 0;
182*65d1f550SNick Terrell }
183*65d1f550SNick Terrell 
1842aa14b1aSNick Terrell /* this test was initially positioned in mem.h,
1852aa14b1aSNick Terrell  * but this file is removed (or replaced) for linux kernel
1862aa14b1aSNick Terrell  * so it's now hosted in compiler.h,
1872aa14b1aSNick Terrell  * which remains valid for both user & kernel spaces.
1882aa14b1aSNick Terrell  */
189e0c1b49fSNick Terrell 
1902aa14b1aSNick Terrell #ifndef ZSTD_ALIGNOF
1912aa14b1aSNick Terrell /* covers gcc, clang & MSVC */
1922aa14b1aSNick Terrell /* note : this section must come first, before C11,
1932aa14b1aSNick Terrell  * due to a limitation in the kernel source generator */
1942aa14b1aSNick Terrell #  define ZSTD_ALIGNOF(T) __alignof(T)
1952aa14b1aSNick Terrell 
1962aa14b1aSNick Terrell #endif /* ZSTD_ALIGNOF */
1972aa14b1aSNick Terrell 
198*65d1f550SNick Terrell #ifndef ZSTD_ALIGNED
199*65d1f550SNick Terrell /* C90-compatible alignment macro (GCC/Clang). Adjust for other compilers if needed. */
200*65d1f550SNick Terrell #define ZSTD_ALIGNED(a) __attribute__((aligned(a)))
201*65d1f550SNick Terrell #endif /* ZSTD_ALIGNED */
202*65d1f550SNick Terrell 
203*65d1f550SNick Terrell 
2042aa14b1aSNick Terrell /*-**************************************************************
2052aa14b1aSNick Terrell *  Sanitizer
2062aa14b1aSNick Terrell *****************************************************************/
2072aa14b1aSNick Terrell 
208*65d1f550SNick Terrell /*
209*65d1f550SNick Terrell  * Zstd relies on pointer overflow in its decompressor.
210*65d1f550SNick Terrell  * We add this attribute to functions that rely on pointer overflow.
211*65d1f550SNick Terrell  */
212*65d1f550SNick Terrell #ifndef ZSTD_ALLOW_POINTER_OVERFLOW_ATTR
213*65d1f550SNick Terrell #  if __has_attribute(no_sanitize)
214*65d1f550SNick Terrell #    if !defined(__clang__) && defined(__GNUC__) && __GNUC__ < 8
215*65d1f550SNick Terrell        /* gcc < 8 only has signed-integer-overlow which triggers on pointer overflow */
216*65d1f550SNick Terrell #      define ZSTD_ALLOW_POINTER_OVERFLOW_ATTR __attribute__((no_sanitize("signed-integer-overflow")))
217*65d1f550SNick Terrell #    else
218*65d1f550SNick Terrell        /* older versions of clang [3.7, 5.0) will warn that pointer-overflow is ignored. */
219*65d1f550SNick Terrell #      define ZSTD_ALLOW_POINTER_OVERFLOW_ATTR __attribute__((no_sanitize("pointer-overflow")))
220*65d1f550SNick Terrell #    endif
221*65d1f550SNick Terrell #  else
222*65d1f550SNick Terrell #    define ZSTD_ALLOW_POINTER_OVERFLOW_ATTR
223*65d1f550SNick Terrell #  endif
224*65d1f550SNick Terrell #endif
225*65d1f550SNick Terrell 
226*65d1f550SNick Terrell /*
227*65d1f550SNick Terrell  * Helper function to perform a wrapped pointer difference without triggering
228*65d1f550SNick Terrell  * UBSAN.
229*65d1f550SNick Terrell  *
230*65d1f550SNick Terrell  * @returns lhs - rhs with wrapping
231*65d1f550SNick Terrell  */
232*65d1f550SNick Terrell MEM_STATIC
233*65d1f550SNick Terrell ZSTD_ALLOW_POINTER_OVERFLOW_ATTR
ZSTD_wrappedPtrDiff(unsigned char const * lhs,unsigned char const * rhs)234*65d1f550SNick Terrell ptrdiff_t ZSTD_wrappedPtrDiff(unsigned char const* lhs, unsigned char const* rhs)
235*65d1f550SNick Terrell {
236*65d1f550SNick Terrell     return lhs - rhs;
237*65d1f550SNick Terrell }
238*65d1f550SNick Terrell 
239*65d1f550SNick Terrell /*
240*65d1f550SNick Terrell  * Helper function to perform a wrapped pointer add without triggering UBSAN.
241*65d1f550SNick Terrell  *
242*65d1f550SNick Terrell  * @return ptr + add with wrapping
243*65d1f550SNick Terrell  */
244*65d1f550SNick Terrell MEM_STATIC
245*65d1f550SNick Terrell ZSTD_ALLOW_POINTER_OVERFLOW_ATTR
ZSTD_wrappedPtrAdd(unsigned char const * ptr,ptrdiff_t add)246*65d1f550SNick Terrell unsigned char const* ZSTD_wrappedPtrAdd(unsigned char const* ptr, ptrdiff_t add)
247*65d1f550SNick Terrell {
248*65d1f550SNick Terrell     return ptr + add;
249*65d1f550SNick Terrell }
250*65d1f550SNick Terrell 
251*65d1f550SNick Terrell /*
252*65d1f550SNick Terrell  * Helper function to perform a wrapped pointer subtraction without triggering
253*65d1f550SNick Terrell  * UBSAN.
254*65d1f550SNick Terrell  *
255*65d1f550SNick Terrell  * @return ptr - sub with wrapping
256*65d1f550SNick Terrell  */
257*65d1f550SNick Terrell MEM_STATIC
258*65d1f550SNick Terrell ZSTD_ALLOW_POINTER_OVERFLOW_ATTR
ZSTD_wrappedPtrSub(unsigned char const * ptr,ptrdiff_t sub)259*65d1f550SNick Terrell unsigned char const* ZSTD_wrappedPtrSub(unsigned char const* ptr, ptrdiff_t sub)
260*65d1f550SNick Terrell {
261*65d1f550SNick Terrell     return ptr - sub;
262*65d1f550SNick Terrell }
263*65d1f550SNick Terrell 
264*65d1f550SNick Terrell /*
265*65d1f550SNick Terrell  * Helper function to add to a pointer that works around C's undefined behavior
266*65d1f550SNick Terrell  * of adding 0 to NULL.
267*65d1f550SNick Terrell  *
268*65d1f550SNick Terrell  * @returns `ptr + add` except it defines `NULL + 0 == NULL`.
269*65d1f550SNick Terrell  */
270*65d1f550SNick Terrell MEM_STATIC
ZSTD_maybeNullPtrAdd(unsigned char * ptr,ptrdiff_t add)271*65d1f550SNick Terrell unsigned char* ZSTD_maybeNullPtrAdd(unsigned char* ptr, ptrdiff_t add)
272*65d1f550SNick Terrell {
273*65d1f550SNick Terrell     return add > 0 ? ptr + add : ptr;
274*65d1f550SNick Terrell }
275*65d1f550SNick Terrell 
276*65d1f550SNick Terrell /* Issue #3240 reports an ASAN failure on an llvm-mingw build. Out of an
277*65d1f550SNick Terrell  * abundance of caution, disable our custom poisoning on mingw. */
278*65d1f550SNick Terrell #ifdef __MINGW32__
279*65d1f550SNick Terrell #ifndef ZSTD_ASAN_DONT_POISON_WORKSPACE
280*65d1f550SNick Terrell #define ZSTD_ASAN_DONT_POISON_WORKSPACE 1
281*65d1f550SNick Terrell #endif
282*65d1f550SNick Terrell #ifndef ZSTD_MSAN_DONT_POISON_WORKSPACE
283*65d1f550SNick Terrell #define ZSTD_MSAN_DONT_POISON_WORKSPACE 1
284*65d1f550SNick Terrell #endif
285*65d1f550SNick Terrell #endif
286*65d1f550SNick Terrell 
287e0c1b49fSNick Terrell 
288e0c1b49fSNick Terrell 
289e0c1b49fSNick Terrell #endif /* ZSTD_COMPILER_H */
290