FastLED 3.9.15
Loading...
Searching...
No Matches
compiler_control.h
Go to the documentation of this file.
1#pragma once
2
3#include "platforms/is_platform.h" // IWYU pragma: keep
4#include "fl/stl/align.h"
5
6// Stringify helper for pragma arguments
7#define FL_STRINGIFY2(x) #x
8#define FL_STRINGIFY(x) FL_STRINGIFY2(x)
9
10// BEGIN BASE MACROS
11#if defined(FL_IS_CLANG)
12 #define FL_DISABLE_WARNING_PUSH _Pragma("clang diagnostic push")
13 #define FL_DISABLE_WARNING_POP _Pragma("clang diagnostic pop")
14 // Usage: FL_DISABLE_WARNING(float-equal)
15 #define FL_DISABLE_WARNING(warning) _Pragma(FL_STRINGIFY(clang diagnostic ignored "-W" #warning))
16
17#elif defined(FL_IS_GCC) && FL_GCC_VERSION >= 406
18 #define FL_DISABLE_WARNING_PUSH _Pragma("GCC diagnostic push")
19 #define FL_DISABLE_WARNING_POP _Pragma("GCC diagnostic pop")
20 // Usage: FL_DISABLE_WARNING(float-equal)
21 #define FL_DISABLE_WARNING(warning) _Pragma(FL_STRINGIFY(GCC diagnostic ignored "-W" #warning))
22#else
23 #define FL_DISABLE_WARNING_PUSH
24 #define FL_DISABLE_WARNING_POP
25 #define FL_DISABLE_WARNING(warning)
26#endif
27// END BASE MACROS
28
29// WARNING SPECIFIC MACROS THAT MAY NOT BE UNIVERSAL.
30#if defined(FL_IS_CLANG)
31 #define FL_DISABLE_WARNING_GLOBAL_CONSTRUCTORS \
32 FL_DISABLE_WARNING(global-constructors)
33 #define FL_DISABLE_WARNING_SELF_ASSIGN \
34 FL_DISABLE_WARNING(self-assign)
35 #define FL_DISABLE_WARNING_SELF_ASSIGN_OVERLOADED \
36 FL_DISABLE_WARNING(self-assign-overloaded)
37 // Clang doesn't have format-truncation warning, use no-op
38 #define FL_DISABLE_FORMAT_TRUNCATION
39 #define FL_DISABLE_WARNING_NULL_DEREFERENCE FL_DISABLE_WARNING(null-dereference)
40 #define FL_DISABLE_WARNING_IMPLICIT_FALLTHROUGH
41 #define FL_DISABLE_WARNING_UNUSED_PARAMETER FL_DISABLE_WARNING(unused-parameter)
42 #define FL_DISABLE_WARNING_RETURN_TYPE
43 #define FL_DISABLE_WARNING_IMPLICIT_INT_CONVERSION FL_DISABLE_WARNING(implicit-int-conversion)
44 #define FL_DISABLE_WARNING_FLOAT_CONVERSION FL_DISABLE_WARNING(float-conversion)
45 #define FL_DISABLE_WARNING_SIGN_CONVERSION FL_DISABLE_WARNING(sign-conversion)
46 #define FL_DISABLE_WARNING_SHORTEN_64_TO_32 FL_DISABLE_WARNING(shorten-64-to-32)
47 // Clang: -Wdeprecated-volatile warns about volatile in parameters/return types (C++20)
48 #define FL_DISABLE_WARNING_VOLATILE FL_DISABLE_WARNING(deprecated-volatile)
49 #define FL_DISABLE_WARNING_DEPRECATED_REGISTER FL_DISABLE_WARNING(deprecated-register)
50 // Clang doesn't have maybe-uninitialized warning (GCC-only), use no-op
51 #define FL_DISABLE_WARNING_MAYBE_UNINITIALIZED
52 // Clang doesn't have subobject-linkage warning, use no-op
53 #define FL_DISABLE_WARNING_SUBOBJECT_LINKAGE
54 // C++14/17 extension warnings (for compatibility when using SIMD intrinsic headers)
55 #define FL_DISABLE_WARNING_C14_EXTENSIONS FL_DISABLE_WARNING(c++14-extensions)
56 #define FL_DISABLE_WARNING_C17_EXTENSIONS FL_DISABLE_WARNING(c++17-extensions)
57 // Clang doesn't have class-memaccess warning, use no-op
58 #define FL_DISABLE_WARNING_CLASS_MEMACCESS
59 // Clang doesn't have maybe-uninitialized warning (uses -Wuninitialized instead)
60 #define FL_DISABLE_WARNING_MAYBE_UNINITIALIZED
61#elif defined(FL_IS_GCC) && FL_GCC_VERSION >= 406
62 // GCC doesn't have global-constructors warning, use no-op
63 #define FL_DISABLE_WARNING_GLOBAL_CONSTRUCTORS
64 // GCC doesn't have self-assign warning, use no-op
65 #define FL_DISABLE_WARNING_SELF_ASSIGN
66 // GCC doesn't have self-assign-overloaded warning, use no-op
67 #define FL_DISABLE_WARNING_SELF_ASSIGN_OVERLOADED
68 // GCC has format-truncation warning
69 #define FL_DISABLE_FORMAT_TRUNCATION \
70 FL_DISABLE_WARNING(format-truncation)
71 #define FL_DISABLE_WARNING_NULL_DEREFERENCE
72 #define FL_DISABLE_WARNING_UNUSED_PARAMETER \
73 FL_DISABLE_WARNING(unused-parameter)
74 #define FL_DISABLE_WARNING_RETURN_TYPE \
75 FL_DISABLE_WARNING(return-type)
76
77 // implicit-fallthrough warning requires GCC >= 7.0
78 #if FL_GCC_VERSION >= 700
79 #define FL_DISABLE_WARNING_IMPLICIT_FALLTHROUGH FL_DISABLE_WARNING(implicit-fallthrough)
80 #else
81 #define FL_DISABLE_WARNING_IMPLICIT_FALLTHROUGH
82 #endif
83 // GCC doesn't support these conversion warnings on older versions
84 #define FL_DISABLE_WARNING_FLOAT_CONVERSION
85 #define FL_DISABLE_WARNING_SIGN_CONVERSION
86 #define FL_DISABLE_WARNING_IMPLICIT_INT_CONVERSION
87 // GCC doesn't have shorten-64-to-32 warning, use no-op
88 #define FL_DISABLE_WARNING_SHORTEN_64_TO_32
89 // volatile warning requires GCC >= 10.0 (added in GCC 10)
90 #if defined(FL_IS_AVR) || (FL_GCC_VERSION < 1000)
91 #define FL_DISABLE_WARNING_VOLATILE
92 #else
93 #define FL_DISABLE_WARNING_VOLATILE FL_DISABLE_WARNING(volatile)
94 #endif
95 // GCC has subobject-linkage warning (requires GCC >= 5.0)
96 #if FL_GCC_VERSION >= 500
97 #define FL_DISABLE_WARNING_SUBOBJECT_LINKAGE FL_DISABLE_WARNING(subobject-linkage)
98 #else
99 #define FL_DISABLE_WARNING_SUBOBJECT_LINKAGE
100 #endif
101 // GCC doesn't have C++14/17 extension warnings, use no-op
102 #define FL_DISABLE_WARNING_C14_EXTENSIONS
103 #define FL_DISABLE_WARNING_C17_EXTENSIONS
104 // GCC has maybe-uninitialized warning
105 #define FL_DISABLE_WARNING_MAYBE_UNINITIALIZED FL_DISABLE_WARNING(maybe-uninitialized)
106 // GCC 8+: -Wclass-memaccess warns when memset/memcpy used on non-trivial types
107 #if FL_GCC_VERSION >= 800
108 #define FL_DISABLE_WARNING_CLASS_MEMACCESS FL_DISABLE_WARNING(class-memaccess)
109 #else
110 #define FL_DISABLE_WARNING_CLASS_MEMACCESS
111 #endif
112 // GCC has maybe-uninitialized warning
113 #define FL_DISABLE_WARNING_MAYBE_UNINITIALIZED FL_DISABLE_WARNING(maybe-uninitialized)
114 // GCC 7+: 'register' deprecated in C++11, removed in C++17 (-Wregister)
115 #if FL_GCC_VERSION >= 700
116 #define FL_DISABLE_WARNING_DEPRECATED_REGISTER FL_DISABLE_WARNING(register)
117 #else
118 #define FL_DISABLE_WARNING_DEPRECATED_REGISTER
119 #endif
120#else
121 #define FL_DISABLE_WARNING_GLOBAL_CONSTRUCTORS
122 #define FL_DISABLE_WARNING_SELF_ASSIGN
123 #define FL_DISABLE_WARNING_SELF_ASSIGN_OVERLOADED
124 #define FL_DISABLE_FORMAT_TRUNCATION
125 #define FL_DISABLE_WARNING_NULL_DEREFERENCE
126 #define FL_DISABLE_WARNING_UNUSED_PARAMETER
127 #define FL_DISABLE_WARNING_RETURN_TYPE
128 #define FL_DISABLE_WARNING_IMPLICIT_INT_CONVERSION
129 #define FL_DISABLE_WARNING_FLOAT_CONVERSION
130 #define FL_DISABLE_WARNING_SIGN_CONVERSION
131 #define FL_DISABLE_WARNING_SHORTEN_64_TO_32
132 #define FL_DISABLE_WARNING_VOLATILE
133 #define FL_DISABLE_WARNING_DEPRECATED_REGISTER
134 // Other compilers don't have maybe-uninitialized warning, use no-op
135 #define FL_DISABLE_WARNING_MAYBE_UNINITIALIZED
136 // Other compilers don't have subobject-linkage warning, use no-op
137 #define FL_DISABLE_WARNING_SUBOBJECT_LINKAGE
138 // Other compilers don't have C++14/17 extension warnings, use no-op
139 #define FL_DISABLE_WARNING_C14_EXTENSIONS
140 #define FL_DISABLE_WARNING_C17_EXTENSIONS
141 // Other compilers don't have class-memaccess warning, use no-op
142 #define FL_DISABLE_WARNING_CLASS_MEMACCESS
143 #define FL_DISABLE_WARNING_MAYBE_UNINITIALIZED
144#endif
145
146// END WARNING SPECIFIC MACROS THAT MAY NOT BE UNIVERSAL.
147
148// Escape hatch for platform-specific pragmas.
149// Place FL_ALLOW_PLATFORM_PRAGMA on the line before a raw #pragma directive
150// to suppress the platform_pragma_checker lint. Use only when the FL_DISABLE_WARNING
151// macros above cannot express the needed pragma (e.g. non-diagnostic pragmas).
152// Prefer FL_DISABLE_WARNING_PUSH / FL_DISABLE_WARNING(name) / FL_DISABLE_WARNING_POP.
153#define FL_ALLOW_PLATFORM_PRAGMA /* platform-specific pragma — lint escape hatch */
154
155// Convenient diagnostic control aliases
156#define FL_DIAGNOSTIC_PUSH FL_DISABLE_WARNING_PUSH
157#define FL_DIAGNOSTIC_POP FL_DISABLE_WARNING_POP
158#define FL_DIAGNOSTIC_IGNORE_C14_EXTENSIONS FL_DISABLE_WARNING_C14_EXTENSIONS
159
160// Fast math optimization controls with additional aggressive flags
161#if defined(FL_IS_CLANG)
162 #define FL_FAST_MATH_BEGIN \
163 _Pragma("clang diagnostic push") \
164 _Pragma("STDC FP_CONTRACT ON")
165
166 #define FL_FAST_MATH_END _Pragma("clang diagnostic pop")
167
168#elif defined(FL_IS_GCC)
169 #define FL_FAST_MATH_BEGIN \
170 _Pragma("GCC push_options") \
171 _Pragma("GCC optimize (\"fast-math\")") \
172 _Pragma("GCC optimize (\"tree-vectorize\")") \
173 _Pragma("GCC optimize (\"unroll-loops\")")
174
175 #define FL_FAST_MATH_END _Pragma("GCC pop_options")
176
177#elif defined(FL_IS_WIN_MSVC)
178 #define FL_FAST_MATH_BEGIN __pragma(float_control(precise, off))
179 #define FL_FAST_MATH_END __pragma(float_control(precise, on))
180#else
181 #define FL_FAST_MATH_BEGIN /* nothing */
182 #define FL_FAST_MATH_END /* nothing */
183#endif
184
185// Optimization Level O3
186// GCC: #pragma GCC optimize("O3") forces O3 regardless of command-line level.
187// Clang: Has NO equivalent pragma — #pragma clang optimize on/off only
188// toggles between disabled and the command-line level (e.g., -O0 in debug).
189// Instead, we push __attribute__((hot)) onto all functions in the region
190// as a best-effort hint. For true forced optimization in -O0 builds on
191// Clang, the build system must set per-file -O2/-O3 flags.
192#if defined(FL_IS_CLANG)
193 #define FL_OPTIMIZATION_LEVEL_O3_BEGIN \
194 _Pragma("clang attribute push(__attribute__((hot)), apply_to = function)")
195 #define FL_OPTIMIZATION_LEVEL_O3_END \
196 _Pragma("clang attribute pop")
197
198#elif defined(FL_IS_GCC)
199 #define FL_OPTIMIZATION_LEVEL_O3_BEGIN \
200 _Pragma("GCC push_options") \
201 _Pragma("GCC optimize (\"O3\")")
202
203 #define FL_OPTIMIZATION_LEVEL_O3_END _Pragma("GCC pop_options")
204#else
205 #define FL_OPTIMIZATION_LEVEL_O3_BEGIN /* nothing */
206 #define FL_OPTIMIZATION_LEVEL_O3_END /* nothing */
207#endif
208
209// Optimization Level O0 (Debug/No optimization)
210#if defined(FL_IS_CLANG)
211 #define FL_OPTIMIZATION_LEVEL_O0_BEGIN \
212 _Pragma("clang diagnostic push")
213
214 #define FL_OPTIMIZATION_LEVEL_O0_END _Pragma("clang diagnostic pop")
215
216#elif defined(FL_IS_GCC)
217 #define FL_OPTIMIZATION_LEVEL_O0_BEGIN \
218 _Pragma("GCC push_options") \
219 _Pragma("GCC optimize (\"O0\")")
220
221 #define FL_OPTIMIZATION_LEVEL_O0_END _Pragma("GCC pop_options")
222#else
223 #define FL_OPTIMIZATION_LEVEL_O0_BEGIN /* nothing */
224 #define FL_OPTIMIZATION_LEVEL_O0_END /* nothing */
225#endif
226
227// Optimization for exact timing (cycle-accurate code)
228// Disables optimizations that can affect instruction timing predictability.
229// Default level is O2 for balance between performance and timing predictability.
230// Override with: #define FL_TIMING_OPT_LEVEL 3 before including this header.
231#ifndef FL_TIMING_OPT_LEVEL
232#define FL_TIMING_OPT_LEVEL 2
233#endif
234
235#if defined(FL_IS_GCC) || defined(FL_IS_CLANG)
236 // Note: _Pragma requires a single string literal, so we can't use token pasting
237 // to build the optimization level string. Instead, we use conditional compilation.
238 #if FL_TIMING_OPT_LEVEL == 0
239 #define _FL_TIMING_OPT_PRAGMA _Pragma("GCC optimize(\"O0\")")
240 #elif FL_TIMING_OPT_LEVEL == 1
241 #define _FL_TIMING_OPT_PRAGMA _Pragma("GCC optimize(\"O1\")")
242 #elif FL_TIMING_OPT_LEVEL == 3
243 #define _FL_TIMING_OPT_PRAGMA _Pragma("GCC optimize(\"O3\")")
244 #else
245 // Default to O2 for unrecognized or level 2
246 #define _FL_TIMING_OPT_PRAGMA _Pragma("GCC optimize(\"O2\")")
247 #endif
248
249 #define FL_BEGIN_OPTIMIZE_FOR_EXACT_TIMING \
250 _Pragma("GCC push_options") \
251 _FL_TIMING_OPT_PRAGMA \
252 _Pragma("GCC optimize(\"-fno-lto\")") \
253 _Pragma("GCC optimize(\"-fno-schedule-insns\")") \
254 _Pragma("GCC optimize(\"-fno-schedule-insns2\")") \
255 _Pragma("GCC optimize(\"-fno-reorder-blocks\")") \
256 _Pragma("GCC optimize(\"-fno-tree-vectorize\")") \
257 _Pragma("GCC optimize(\"-fno-tree-reassoc\")") \
258 _Pragma("GCC optimize(\"-fno-unroll-loops\")")
259
260 #define FL_END_OPTIMIZE_FOR_EXACT_TIMING \
261 _Pragma("GCC pop_options")
262#else
263 #define FL_BEGIN_OPTIMIZE_FOR_EXACT_TIMING /* nothing */
264 #define FL_END_OPTIMIZE_FOR_EXACT_TIMING /* nothing */
265#endif
266
267// Function-level optimization attributes
268// GCC supports per-function optimization attributes
269// Clang doesn't support optimize("O3"), but supports hot attribute for aggressive optimization
270#if defined(FL_IS_GCC)
271 #define FL_OPTIMIZE_FUNCTION __attribute__((optimize("O3")))
272 #define FL_OPTIMIZE_O2 __attribute__((optimize("O2")))
273#elif defined(FL_IS_CLANG)
274 #define FL_OPTIMIZE_FUNCTION __attribute__((hot))
275 #define FL_OPTIMIZE_O2 __attribute__((hot))
276#else
277 #define FL_OPTIMIZE_FUNCTION
278 #define FL_OPTIMIZE_O2
279#endif
280
281#ifndef FL_LINK_WEAK
282#define FL_LINK_WEAK __attribute__((weak))
283#endif
284
285// Loop unrolling hint
286// Usage: FL_UNROLL(8) for (int i = 0; i < n; i++) { ... }
287// Note: Some compilers (e.g., AVR-GCC, older GCC versions) may not support
288// this pragma and will emit warnings. The macro expands to nothing on unsupported compilers.
289#if defined(FL_IS_AVR)
290 // AVR-GCC does not support #pragma GCC unroll
291 #define FL_UNROLL(N)
292#elif defined(FL_IS_GCC)
293 // GCC supports #pragma GCC unroll N since GCC 8.0
294 #if FL_GCC_VERSION >= 800
295 #define FL_UNROLL(N) _Pragma(FL_STRINGIFY(GCC unroll N))
296 #else
297 // Older GCC versions don't support #pragma GCC unroll - use no-op
298 #define FL_UNROLL(N)
299 #endif
300#elif defined(FL_IS_CLANG)
301 // Clang supports #pragma unroll N (or #pragma clang loop unroll_count(N))
302 #define FL_UNROLL(N) _Pragma(FL_STRINGIFY(unroll N))
303#else
304 // Unsupported compiler: no-op
305 #define FL_UNROLL(N)
306#endif
307
308// Mark functions/variables as maybe unused (for compile-time test functions)
309#if defined(FL_IS_GCC) || defined(FL_IS_CLANG)
310 #define FL_MAYBE_UNUSED __attribute__((unused))
311#else
312 #define FL_MAYBE_UNUSED
313#endif
314
315// Prevent inlining on AVR to reduce register pressure
316// AVR has only 16 general-purpose registers (r0-r15), and complex functions
317// with divisions can exhaust available registers during LTO optimization.
318// This macro forces functions to be out-of-line on AVR while allowing
319// inlining on other platforms for performance.
320//
321// Usage: FL_NO_INLINE_IF_AVR void myComplexFunction() { ... }
322//
323// Typical use cases:
324// - Functions containing multiple divisions (AVR has no hardware divide)
325// - Template functions with many intermediate variables
326// - Functions called from multiple instantiation sites with LTO enabled
327#if defined(FL_IS_AVR)
328 #define FL_NO_INLINE_IF_AVR __attribute__((noinline))
329#else
330 #define FL_NO_INLINE_IF_AVR
331#endif
332
333// Unconditional "do not inline this function" attribute, portable across
334// GCC/Clang/MSVC. Prefer this over a bare `__attribute__((noinline))` in
335// `src/` — a lint check in `ci/lint_cpp/bare_noinline_checker.py` enforces
336// the use of the macro so the GCC-only syntax doesn't sneak in.
337//
338// Typical use: cold helpers extracted out of a hot path so the compiler
339// can't fold them back via inline expansion. Pair with the lint guard.
340//
341// Usage: FL_NO_INLINE void coldHelper() { ... }
342#if defined(FL_IS_GCC) || defined(FL_IS_CLANG)
343 #define FL_NO_INLINE __attribute__((noinline))
344#elif defined(FL_IS_WIN_MSVC)
345 #define FL_NO_INLINE __declspec(noinline)
346#else
347 #define FL_NO_INLINE
348#endif
349
350// Mark functions to run during C++ static initialization (before main())
351// Used for auto-registration of platform-specific implementations
352#if defined(FL_IS_GCC) || defined(FL_IS_CLANG)
353 #define FL_CONSTRUCTOR __attribute__((constructor))
354#elif defined(FL_IS_WIN_MSVC)
355 // MSVC requires different approach - use #pragma init_seg
356 #define FL_CONSTRUCTOR
357 #pragma message("Warning: FL_CONSTRUCTOR not fully supported on MSVC")
358#else
359 #define FL_CONSTRUCTOR
360#endif
361
362// Prevent linker from discarding symbols (functions/variables)
363// Used to ensure static constructors and other "unused" symbols are retained
364#ifndef FL_KEEP_ALIVE
365 #if defined(FL_IS_WASM)
366 // Emscripten: Use EMSCRIPTEN_KEEPALIVE to export symbols to JavaScript
367 // IWYU pragma: begin_keep
368#include <emscripten.h>
369#include "fl/stl/noexcept.h"
370// IWYU pragma: end_keep
371 #define FL_KEEP_ALIVE EMSCRIPTEN_KEEPALIVE
372 #elif defined(FL_IS_GCC) || defined(FL_IS_CLANG)
373 // GCC/Clang: Mark symbol as used to prevent linker optimization
374 #define FL_KEEP_ALIVE __attribute__((used))
375 #elif defined(FL_IS_WIN_MSVC)
376 // MSVC: Force symbol retention through optimization pragma
377 // Note: MSVC's linker will still strip unreferenced symbols even with this,
378 // so static constructors may need explicit references or /OPT:NOREF linker flag
379 #define FL_KEEP_ALIVE __pragma(optimize("", off)) __pragma(optimize("", on))
380 #else
381 // Fallback for unknown compilers
382 #define FL_KEEP_ALIVE
383 #endif
384#endif
385
386// FL_INIT: Convenient macro for static initialization functions
387// Registers a function to run during C++ static initialization (before main())
388//
389// Usage:
390// namespace detail { void init_my_feature() { /* code */ } }
391// FL_INIT(init_my_feature_wrapper, detail::init_my_feature);
392//
393// The first parameter provides a unique name for the generated wrapper function,
394// ensuring no symbol collisions across the codebase.
395//
396// Implementation details:
397// - GCC/Clang: Uses __attribute__((constructor))
398// - MSVC: Uses .CRT$XCU section to register function pointer at startup
399//
400// MSVC .CRT$XCU mechanism:
401// - MSVC runs function pointers placed in special CRT sections at startup
402// - Sections are ordered lexicographically: .CRT$XCA, .CRT$XCU (user code), .CRT$XCZ
403// - The function pointer must have static linkage to avoid ODR issues
404// - Works in static libraries if object file is linked (use /WHOLEARCHIVE if needed)
407
408#if defined(FL_IS_WIN_MSVC)
409 #pragma section(".CRT$XCU", read)
410
411 #define FL_INIT(wrapper_name, func) \
412 namespace static_init { \
413 static void wrapper_name() { func(); } \
414 __declspec(allocate(".CRT$XCU")) \
415 static void (*wrapper_name##_ptr)(void) = wrapper_name; \
416 }
417
418#elif defined(FL_IS_GCC) || defined(FL_IS_CLANG)
419 #define FL_INIT(wrapper_name, func) \
420 namespace static_init { \
421 FL_CONSTRUCTOR FL_KEEP_ALIVE \
422 void wrapper_name() { func(); } \
423 }
424
425#else
426 #error Unsupported compiler for FL_INIT
427#endif
428
430
431// FL_RUN_ONCE - Ensures a function is called only once (thread-safe on platforms with mutexes)
432//
433// Usage:
434// void initialize_system() { /* code */ }
435// FL_RUN_ONCE(initialize_system());
436//
437// This macro uses a static boolean flag to ensure the wrapped code executes only once,
438// even if called multiple times. The implementation is thread-safe on platforms that
439// support multithreading (via FASTLED_MULTITHREADED).
440//
441// Note: This is a runtime guard (checked each call), unlike FL_INIT which runs at
442// static initialization time. FL_RUN_ONCE is useful for lazy initialization or
443// when the caller doesn't know if initialization has occurred.
444#define FL_RUN_ONCE(code) \
445 do { \
446 static bool fl_run_once_flag = false; \
447 if (!fl_run_once_flag) { \
448 fl_run_once_flag = true; \
449 code; \
450 } \
451 } while(0)
452
453// C linkage macros for compatibility with C++ name mangling
454#ifdef __cplusplus
455 #define FL_EXTERN_C_BEGIN extern "C" {
456 #define FL_EXTERN_C_END }
457 #define FL_EXTERN_C extern "C"
458#else
459 #define FL_EXTERN_C_BEGIN
460 #define FL_EXTERN_C_END
461 #define FL_EXTERN_C
462#endif
463
464// Platform-specific IRAM attribute for functions that must run from IRAM
465// (ESP32/ESP8266: required for ISR handlers and functions called from ISRs)
466//
467// Usage: void FL_IRAM myInterruptHandler() { ... }
468//
469// Platform behavior:
470// ESP32: Places code in internal SRAM with unique section names (.iram1.text.N)
471// ESP8266: Places code in internal SRAM with unique section names (.iram.text.N)
472// STM32: Places code in fast RAM with unique section names (.text_ram.N)
473// Other platforms: No-op (functions execute from normal memory)
474//
475// Each FL_IRAM usage automatically generates a unique section using __COUNTER__
476// for better debugging, linker control, and IRAM usage tracking.
477//
478// Platform-specific definitions are in led_sysdefs_*.h headers.
479// This fallback applies only to platforms that don't define FL_IRAM.
480#ifndef FL_IRAM
481 #define FL_IRAM // No-op on platforms without IRAM support
482#endif
483
484// Inline constexpr macro for C++11/17 compatibility
485// In C++17+, constexpr variables are implicitly inline (external linkage)
486// In C++11/14, we now use enum-based timing types instead of weak symbols
487// (see fl/chipsets/led_timing.h for enum-based TIMING_* definitions)
488#define FL_INLINE_CONSTEXPR inline constexpr
489
490// C++14 constexpr function support
491// C++11 constexpr functions cannot have local variables or multiple return statements.
492// These restrictions were relaxed in C++14. For C++11 compatibility, we use inline
493// functions instead of constexpr for complex functions.
494#if __cplusplus >= 201402L
495#define FL_CONSTEXPR14 constexpr
496#else
497#define FL_CONSTEXPR14 inline
498#endif
499
500// Mark functions whose return value must be used (generates compiler warning if ignored)
501// Preferred over [[nodiscard]] for C++11/14 compatibility
502//
503// Usage: FL_NODISCARD bool try_lock();
504//
505// By default, this generates a WARNING when the return value is ignored.
506// To promote to a compile ERROR, use compiler flags:
507// GCC/Clang: -Werror=unused-result
508// MSVC: /we6031
509//
510// Note: These flags ONLY affect functions explicitly marked with FL_NODISCARD.
511// Regular functions without this attribute can still have their return values
512// ignored without warnings/errors.
513#if __cplusplus >= 201703L
514 // C++17+: Use standard [[nodiscard]] attribute
515 #define FL_NODISCARD [[nodiscard]]
516#elif defined(FL_IS_GCC) || defined(FL_IS_CLANG)
517 // GCC/Clang: Use warn_unused_result attribute
518 #define FL_NODISCARD __attribute__((warn_unused_result))
519#elif defined(FL_IS_WIN_MSVC)
520 // MSVC: Use SAL annotation
521 #define FL_NODISCARD _Check_return_
522#else
523 // Unsupported compiler: no-op
524 #define FL_NODISCARD
525#endif
526
527// Suppress warnings for intentionally unused fall-through in switch statements
528// Used to indicate that fall-through from one case to another is intentional.
529//
530// Usage:
531// switch (state) {
532// case 0:
533// setup();
534// FL_FALLTHROUGH;
535// case 1:
536// process();
537// break;
538// }
539//
540// This macro is portable across C++11/14/17 compilers.
541#if __cplusplus >= 201703L
542 // C++17+: Use standard [[fallthrough]] attribute
543 #define FL_FALLTHROUGH [[fallthrough]]
544#elif defined(FL_IS_CLANG) || (defined(FL_IS_GCC) && FL_GCC_VERSION >= 700)
545 // GCC 7+ / Clang 10+: Use fallthrough attribute
546 #define FL_FALLTHROUGH __attribute__((fallthrough))
547#else
548 // C++11/14 or older compilers: Use empty statement with descriptive comment
549 #define FL_FALLTHROUGH /* fallthrough */
550#endif
551
552// Mark functions that never return (e.g., infinite loops, exit handlers)
553// Enables compiler optimizations and warnings for unreachable code.
554//
555// Usage: FL_NORETURN void abort_handler();
556//
557// This macro is portable across C++11/14/17 compilers.
558#if __cplusplus >= 201103L
559 // C++11+: Use standard [[noreturn]] attribute
560 #define FL_NORETURN [[noreturn]]
561#elif defined(FL_IS_GCC) || defined(FL_IS_CLANG)
562 // GCC/Clang: Use noreturn attribute
563 #define FL_NORETURN __attribute__((noreturn))
564#elif defined(FL_IS_WIN_MSVC)
565 // MSVC: Use __declspec(noreturn)
566 #define FL_NORETURN __declspec(noreturn)
567#else
568 // Unsupported compiler: no-op
569 #define FL_NORETURN
570#endif
571
572// Mark symbols (functions, types, variables) as deprecated
573// Generates compiler warnings when deprecated symbols are used.
574//
575// Usage:
576// FL_DEPRECATED("Use new_function() instead") void old_function();
577// struct FL_DEPRECATED_CLASS("Replaced by NewClass") OldClass { };
578//
579// This macro is portable across C++11/14/17 compilers.
580#if defined(FL_IS_CLANG)
581 // Clang: Support deprecated with message
582 #define FL_DEPRECATED(msg) __attribute__((deprecated(msg)))
583 // Clang: Do not mark classes as deprecated (causes excessive warnings)
584 #define FL_DEPRECATED_CLASS(msg)
585#elif defined(FL_IS_GCC)
586 // GCC: Support deprecated with message (GCC 4.5+)
587 #if FL_GCC_VERSION >= 405
588 #define FL_DEPRECATED(msg) __attribute__((deprecated(msg)))
589 #define FL_DEPRECATED_CLASS(msg) __attribute__((deprecated(msg)))
590 #else
591 #define FL_DEPRECATED(msg) __attribute__((deprecated))
592 #define FL_DEPRECATED_CLASS(msg)
593 #endif
594#elif defined(FL_IS_WIN_MSVC)
595 // MSVC: Use __declspec(deprecated) with message
596 #define FL_DEPRECATED(msg) __declspec(deprecated(msg))
597 #define FL_DEPRECATED_CLASS(msg) __declspec(deprecated(msg))
598#else
599 // Unsupported compiler: no-op
600 #define FL_DEPRECATED(msg)
601 #define FL_DEPRECATED_CLASS(msg)
602#endif
603
604// Legacy macros for compatibility (deprecated, use FL_DEPRECATED instead)
605#define FASTLED_DEPRECATED(msg) FL_DEPRECATED(msg)
606#define FASTLED_DEPRECATED_CLASS(msg) FL_DEPRECATED_CLASS(msg)
607
608// Branch prediction hints for hot paths (C++20 [[likely]] / [[unlikely]])
609// Helps the compiler optimize for the common case.
610//
611// Usage:
612// if FL_LIKELY(ptr != nullptr) { /* common case */ }
613// if FL_UNLIKELY(error_occurred) { /* rare case */ }
614//
615// Note: Use sparingly and only for measurable hot paths. Modern compilers
616// often predict branches well without hints, and incorrect hints can degrade
617// performance.
618#if __cplusplus >= 202002L
619 // C++20+: Use standard [[likely]] / [[unlikely]] attributes
620 #define FL_LIKELY(x) (x) [[likely]]
621 #define FL_UNLIKELY(x) (x) [[unlikely]]
622#elif defined(FL_IS_GCC) || defined(FL_IS_CLANG)
623 // GCC/Clang: Use __builtin_expect
624 #define FL_LIKELY(x) __builtin_expect(!!(x), 1)
625 #define FL_UNLIKELY(x) __builtin_expect(!!(x), 0)
626#else
627 // Unsupported compiler: no-op
628 #define FL_LIKELY(x) (x)
629 #define FL_UNLIKELY(x) (x)
630#endif
631
632// Allows empty base class optimization for member variables (C++20)
633// Enables the compiler to optimize away empty member variables, reducing
634// struct size when possible.
635//
636// Usage:
637// struct MyClass {
638// FL_NO_UNIQUE_ADDRESS EmptyAllocator allocator;
639// int data;
640// };
641//
642// This is particularly useful for policy-based designs where stateless
643// policy classes are common.
644#if __cplusplus >= 202002L
645 // C++20+: Use standard [[no_unique_address]] attribute
646 #define FL_NO_UNIQUE_ADDRESS [[no_unique_address]]
647#elif defined(FL_IS_CLANG) || defined(FL_IS_GCC)
648 // GCC/Clang: May have vendor-specific support (check compiler version)
649 // For now, fall back to no-op since it's not standard in C++17
650 #define FL_NO_UNIQUE_ADDRESS
651#else
652 // Unsupported compiler: no-op
653 #define FL_NO_UNIQUE_ADDRESS
654#endif
655
656// Restrict pointer attribute for better optimization (no aliasing)
657// Tells the compiler that a pointer is the only way to access the underlying
658// object for the scope of that pointer, enabling better optimization.
659//
660// Usage: void process(int* FL_RESTRICT_PARAM a, int* FL_RESTRICT_PARAM b);
661//
662// The restrict keyword indicates that 'a' and 'b' do not alias (point to
663// overlapping memory), allowing the compiler to optimize more aggressively.
664#if defined(__cplusplus)
665 // C++ doesn't have 'restrict' keyword, use compiler-specific extensions
666 #if defined(FL_IS_GCC) || defined(FL_IS_CLANG)
667 #define FL_RESTRICT_PARAM __restrict__
668 #elif defined(FL_IS_WIN_MSVC)
669 #define FL_RESTRICT_PARAM __restrict
670 #else
671 #define FL_RESTRICT_PARAM
672 #endif
673#else
674 // C99 has native 'restrict' keyword
675 #define FL_RESTRICT_PARAM restrict
676#endif
677
678// Pointer alignment hint for the optimizer
679// Tells the compiler that a pointer is aligned to N bytes, enabling better
680// codegen (SIMD loads, wider memory ops). This is a hint only — it does not
681// enforce alignment at the call site.
682//
683// Usage: uint8_t* p = FL_ASSUME_ALIGNED(ptr, 64);
684//
685// Delegates to fl::assume_aligned<N>(ptr) which uses __builtin_assume_aligned
686// on GCC/Clang and falls back to a no-op on other platforms.
687// See fl/assume_aligned.h for the implementation.
688
689#define FL_ASSUME_ALIGNED(ptr, N) (fl::assume_aligned<(N)>(ptr))
690
691// Function name macro for debugging and tracing
692// Provides the current function name in a portable way across compilers.
693//
694// Usage: FL_DBG("Entering " << FL_FUNCTION);
695// FL_SCOPED_TRACE() uses this internally
696//
697// Compiler support:
698// - C++11+: __func__ (standard)
699// - GCC/Clang: __FUNCTION__ (extension, same as __func__ in most cases)
700// - MSVC: __FUNCTION__ (non-standard but widely supported)
701//
702// Note: __PRETTY_FUNCTION__ (GCC/Clang) includes full signature but is too verbose
703//
704// FL_PRETTY_FUNCTION: Full function signature including template parameters.
705// Use when you need a unique string per template instantiation (e.g., singleton keys).
706// - GCC/Clang: __PRETTY_FUNCTION__
707// - MSVC: __FUNCSIG__
708// - Fallback: __func__
709#if defined(FL_IS_GCC) || defined(FL_IS_CLANG)
710 #define FL_PRETTY_FUNCTION __PRETTY_FUNCTION__
711#elif defined(FL_IS_WIN_MSVC)
712 #define FL_PRETTY_FUNCTION __FUNCSIG__
713#else
714 #define FL_PRETTY_FUNCTION __func__
715#endif
716
717// FL_MACRO_CONCAT: Token-paste two macro arguments after expansion.
718// FL_MACRO_CONCAT(foo, __LINE__) on line 42 produces foo42.
719#define FL_MACRO_CONCAT_(a, b) a##b
720#define FL_MACRO_CONCAT(a, b) FL_MACRO_CONCAT_(a, b)
721
722// FL_STRING_CONCAT: Concatenate two string literals (or stringified macros).
723// FL_STRING_CONCAT("file", FL_STRINGIFY(__LINE__)) produces "file42".
724// Both arguments must be string literals or macros that expand to string literals.
725#define FL_STRING_CONCAT(a, b) a b
726
727// FL_UNIQUE_KEY: Compile-time unique string literal per call site.
728// Produces "file:line" (e.g., "src/fl/singleton.h:42").
729// Uses only string literals so the result is a single compile-time constant
730// with zero runtime cost. Identical across DLLs compiling the same source.
731// NOTE: Not suitable for template code — all instantiations from the same
732// header line produce the same key. Use FL_PRETTY_FUNCTION for templates.
733#define FL_UNIQUE_KEY FL_STRING_CONCAT(__FILE__ ":", FL_STRINGIFY(__LINE__))
734
735// FL_UNIQUE_IDENTIFIER: Generate a unique C identifier per line.
736// Produces a token like _fl_uid_42. Useful for variable names in macros
737// that must not collide when the macro is used multiple times in a scope.
738// NOTE: Two invocations on the SAME line produce the same identifier.
739#define FL_UNIQUE_IDENTIFIER FL_MACRO_CONCAT(_fl_uid_, __LINE__)
740
741#if defined(__cplusplus)
742 // C++11 standard: __func__ is a predefined identifier
743 #define FL_FUNCTION __func__
744#elif defined(FL_IS_GCC) || defined(FL_IS_CLANG)
745 // GCC/Clang C: Use __FUNCTION__ extension
746 #define FL_FUNCTION __FUNCTION__
747#elif defined(FL_IS_WIN_MSVC)
748 // MSVC: __FUNCTION__ is supported
749 #define FL_FUNCTION __FUNCTION__
750#else
751 // Fallback: Use __func__ (C99 standard)
752 #define FL_FUNCTION __func__
753#endif
754
755// Sanitizer detection macros
756// Detect if compiling with AddressSanitizer (ASAN includes LSAN - LeakSanitizer)
757// GCC: __SANITIZE_ADDRESS__ is defined when -fsanitize=address is used
758// Clang: __has_feature(address_sanitizer) evaluates to 1
759#if defined(__SANITIZE_ADDRESS__)
760# define FL_HAS_SANITIZER_LSAN 1
761#elif defined(__has_feature)
762# if __has_feature(address_sanitizer)
763# define FL_HAS_SANITIZER_LSAN 1
764# endif
765#endif
766
767#ifndef FL_HAS_SANITIZER_LSAN
768# define FL_HAS_SANITIZER_LSAN 0
769#endif
770
771// ============================================================================
772// Force inline macros (formerly fl/force_inline.h)
773// ============================================================================
774
775// Force inline for member functions (no static keyword)
776#ifdef FASTLED_NO_FORCE_INLINE
777#define FASTLED_FORCE_INLINE inline
778#else
779#define FASTLED_FORCE_INLINE __attribute__((always_inline)) inline
780#endif
781
782// Force inline for static free functions (with static keyword)
783// Replacement for LIB8STATIC_ALWAYS_INLINE
784#ifdef FASTLED_NO_FORCE_INLINE
785#define FL_ALWAYS_INLINE static inline
786#else
787#define FL_ALWAYS_INLINE __attribute__((always_inline)) static inline
788#endif
789
790// ============================================================================
791// Compiler intrinsic memcpy (warning-suppressed)
792// ============================================================================
793// __builtin_memcpy is a compiler intrinsic that always lowers to optimal
794// load/store instructions at the call site. Unlike fl::memcpy (a cross-TU
795// call requiring LTO to inline), this guarantees zero call overhead.
796//
797// Wrapped in always_inline functions instead of pragma-based macros to avoid
798// _Pragma placement errors on newer GCC (e.g. GCC 13+ in ESP-IDF v5.5).
799// The void* parameters naturally suppress -Wclass-memaccess by erasing the
800// source type at the call site.
801#if defined(FL_IS_GCC) || defined(FL_IS_CLANG)
802__attribute__((always_inline))
803static inline void *_fl_builtin_memcpy(void *dest, const void *src,
804 __SIZE_TYPE__ n) FL_NOEXCEPT {
805 return __builtin_memcpy(dest, src, n);
806}
807#define FL_BUILTIN_MEMCPY(dest, src, n) _fl_builtin_memcpy(dest, src, n)
808#else
809// MSVC 2019+ recognizes __builtin_memcpy as an intrinsic; no pragma needed
810#define FL_BUILTIN_MEMCPY(dest, src, n) __builtin_memcpy(dest, src, n)
811#endif
812
813// ============================================================================
814// Compiler intrinsic memset (warning-suppressed)
815// ============================================================================
816// __builtin_memset on non-trivial types (e.g. CRGB) triggers -Wclass-memaccess
817// on GCC. This is safe when the type is trivially copyable / POD-like, which
818// CRGB and CRGB16 are in practice.
819#if defined(FL_IS_GCC) || defined(FL_IS_CLANG)
820__attribute__((always_inline))
821static inline void *_fl_builtin_memset(void *dest, int val, __SIZE_TYPE__ n) FL_NOEXCEPT {
822 return __builtin_memset(dest, val, n);
823}
824#define FL_BUILTIN_MEMSET(dest, val, n) _fl_builtin_memset(dest, val, n)
825#else
826// MSVC 2019+ recognizes __builtin_memset as an intrinsic; no pragma needed
827#define FL_BUILTIN_MEMSET(dest, val, n) __builtin_memset(dest, val, n)
828#endif
829
830// ============================================================================
831// Register keyword compatibility (formerly fl/register.h)
832// ============================================================================
833
843
844#ifndef FASTLED_REGISTER
847#if (defined(_MSVC_LANG) ? _MSVC_LANG : __cplusplus) < 201703L
848 #define FASTLED_REGISTER register
849#else
850 // C++17+: register keyword removed from the language
851 #define FASTLED_REGISTER
852#endif
854#endif
855
856// ============================================================================
857// Unused macros (formerly fl/unused.h)
858// ============================================================================
859
860#ifndef FASTLED_UNUSED
861#define FASTLED_UNUSED(x) (void)(x)
862#endif
863
864#ifndef FL_UNUSED
865#define FL_UNUSED(x) (void)(x)
866#endif
867
868#ifndef FL_UNUSED_FUNCTION
869#define FL_UNUSED_FUNCTION __attribute__((unused))
870#endif
871
872// ============================================================================
873// AVR platform macros (formerly fl/virtual_if_not_avr.h, fl/avr_disallowed.h)
874// ============================================================================
875
876#ifdef FL_IS_AVR
877#define VIRTUAL_IF_NOT_AVR
878#define OVERRIDE_IF_NOT_AVR
879#else
880#define VIRTUAL_IF_NOT_AVR virtual
881#define OVERRIDE_IF_NOT_AVR override
882#endif
883
884#ifdef FL_IS_AVR
885#define AVR_DISALLOWED \
886 [[deprecated("This function or class is deprecated on AVR.")]]
887#else
888#define AVR_DISALLOWED
889#endif
Alignment macros and utilities for FastLED.
#define FL_DISABLE_WARNING_GLOBAL_CONSTRUCTORS
#define FL_DISABLE_WARNING_PUSH
#define FL_DISABLE_WARNING_POP
#define FL_DISABLE_WARNING_DEPRECATED_REGISTER
#define FL_NOEXCEPT