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