FastLED 3.10.9
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//
521// ESP8266 defines it here as well: this header is usually included before
522// led_sysdefs_esp8266.h, so the no-op fallback won and every FL_IRAM
523// function silently landed in flash (the core's irom0 rule takes .text*).
524#if !defined(FL_IRAM) && defined(ESP8266)
525 #define _FL_IRAM_STRINGIFY2(x) #x
526 #define _FL_IRAM_STRINGIFY(x) _FL_IRAM_STRINGIFY2(x)
527 #define _FL_IRAM_SECTION_NAME(counter) ".iram.text." _FL_IRAM_STRINGIFY(counter)
528 #define FL_IRAM __attribute__((section(_FL_IRAM_SECTION_NAME(__COUNTER__))))
529#endif
530#ifndef FL_IRAM
531 #define FL_IRAM // No-op on platforms without IRAM support
532#endif
533
534// Inline constexpr macro for C++11/17 compatibility
535// In C++17+, constexpr variables are implicitly inline (external linkage)
536// In C++11/14, we now use enum-based timing types instead of weak symbols
537// (see fl/chipsets/led_timing.h for enum-based TIMING_* definitions)
538#define FL_INLINE_CONSTEXPR inline constexpr
539
540// C++14 constexpr function support
541// C++11 constexpr functions cannot have local variables or multiple return statements.
542// These restrictions were relaxed in C++14. For C++11 compatibility, we use inline
543// functions instead of constexpr for complex functions.
544#if __cplusplus >= 201402L
545#define FL_CONSTEXPR14 constexpr
546#else
547#define FL_CONSTEXPR14 inline
548#endif
549
550// Mark functions whose return value must be used (generates compiler warning if ignored)
551// Preferred over [[nodiscard]] for C++11/14 compatibility
552//
553// Usage: FL_NO_DISCARD bool try_lock();
554//
555// By default, this generates a WARNING when the return value is ignored.
556// To promote to a compile ERROR, use compiler flags:
557// GCC/Clang: -Werror=unused-result
558// MSVC: /we6031
559//
560// Note: These flags ONLY affect functions explicitly marked with FL_NO_DISCARD.
561// Regular functions without this attribute can still have their return values
562// ignored without warnings/errors.
563#if __cplusplus >= 201703L
564 // C++17+: Use standard [[nodiscard]] attribute
565 #define FL_NO_DISCARD [[nodiscard]]
566#elif defined(FL_IS_GCC) || defined(FL_IS_CLANG)
567 // GCC/Clang: Use warn_unused_result attribute
568 #define FL_NO_DISCARD __attribute__((warn_unused_result))
569#elif defined(FL_IS_WIN_MSVC)
570 // MSVC: Use SAL annotation
571 #define FL_NO_DISCARD _Check_return_
572#else
573 // Unsupported compiler: no-op
574 #define FL_NO_DISCARD
575#endif
576
577// Suppress warnings for intentionally unused fall-through in switch statements
578// Used to indicate that fall-through from one case to another is intentional.
579//
580// Usage:
581// switch (state) {
582// case 0:
583// setup();
584// FL_FALLTHROUGH;
585// case 1:
586// process();
587// break;
588// }
589//
590// This macro is portable across C++11/14/17 compilers.
591#if __cplusplus >= 201703L
592 // C++17+: Use standard [[fallthrough]] attribute
593 #define FL_FALLTHROUGH [[fallthrough]]
594#elif defined(FL_IS_CLANG) || (defined(FL_IS_GCC) && FL_GCC_VERSION >= 700)
595 // GCC 7+ / Clang 10+: Use fallthrough attribute
596 #define FL_FALLTHROUGH __attribute__((fallthrough))
597#else
598 // C++11/14 or older compilers: Use empty statement with descriptive comment
599 #define FL_FALLTHROUGH /* fallthrough */
600#endif
601
602// Mark functions that never return (e.g., infinite loops, exit handlers)
603// Enables compiler optimizations and warnings for unreachable code.
604//
605// Usage: FL_NO_RETURN void abort_handler();
606//
607// This macro is portable across C++11/14/17 compilers.
608#if __cplusplus >= 201103L
609 // C++11+: Use standard [[noreturn]] attribute
610 #define FL_NO_RETURN [[noreturn]]
611#elif defined(FL_IS_GCC) || defined(FL_IS_CLANG)
612 // GCC/Clang: Use noreturn attribute
613 #define FL_NO_RETURN __attribute__((noreturn))
614#elif defined(FL_IS_WIN_MSVC)
615 // MSVC: Use __declspec(noreturn)
616 #define FL_NO_RETURN __declspec(noreturn)
617#else
618 // Unsupported compiler: no-op
619 #define FL_NO_RETURN
620#endif
621
622// Mark symbols (functions, types, variables) as deprecated
623// Generates compiler warnings when deprecated symbols are used.
624//
625// Usage:
626// FL_DEPRECATED("Use new_function() instead") void old_function();
627// struct FL_DEPRECATED_CLASS("Replaced by NewClass") OldClass { };
628//
629// This macro is portable across C++11/14/17 compilers.
630#if defined(FL_IS_CLANG)
631 // Clang: Support deprecated with message
632 #define FL_DEPRECATED(msg) __attribute__((deprecated(msg)))
633 // Clang: Do not mark classes as deprecated (causes excessive warnings)
634 #define FL_DEPRECATED_CLASS(msg)
635#elif defined(FL_IS_GCC)
636 // GCC: Support deprecated with message (GCC 4.5+)
637 #if FL_GCC_VERSION >= 405
638 #define FL_DEPRECATED(msg) __attribute__((deprecated(msg)))
639 #define FL_DEPRECATED_CLASS(msg) __attribute__((deprecated(msg)))
640 #else
641 #define FL_DEPRECATED(msg) __attribute__((deprecated))
642 #define FL_DEPRECATED_CLASS(msg)
643 #endif
644#elif defined(FL_IS_WIN_MSVC)
645 // MSVC: Use __declspec(deprecated) with message
646 #define FL_DEPRECATED(msg) __declspec(deprecated(msg))
647 #define FL_DEPRECATED_CLASS(msg) __declspec(deprecated(msg))
648#else
649 // Unsupported compiler: no-op
650 #define FL_DEPRECATED(msg)
651 #define FL_DEPRECATED_CLASS(msg)
652#endif
653
654// Legacy macros for compatibility (deprecated, use FL_DEPRECATED instead)
655#define FASTLED_DEPRECATED(msg) FL_DEPRECATED(msg)
656#define FASTLED_DEPRECATED_CLASS(msg) FL_DEPRECATED_CLASS(msg)
657
658// Branch prediction hints for hot paths (C++20 [[likely]] / [[unlikely]])
659// Helps the compiler optimize for the common case.
660//
661// Usage:
662// if FL_LIKELY(ptr != nullptr) { /* common case */ }
663// if FL_UNLIKELY(error_occurred) { /* rare case */ }
664//
665// Note: Use sparingly and only for measurable hot paths. Modern compilers
666// often predict branches well without hints, and incorrect hints can degrade
667// performance.
668#if __cplusplus >= 202002L
669 // C++20+: Use standard [[likely]] / [[unlikely]] attributes
670 #define FL_LIKELY(x) (x) [[likely]]
671 #define FL_UNLIKELY(x) (x) [[unlikely]]
672#elif defined(FL_IS_GCC) || defined(FL_IS_CLANG)
673 // GCC/Clang: Use __builtin_expect
674 #define FL_LIKELY(x) __builtin_expect(!!(x), 1)
675 #define FL_UNLIKELY(x) __builtin_expect(!!(x), 0)
676#else
677 // Unsupported compiler: no-op
678 #define FL_LIKELY(x) (x)
679 #define FL_UNLIKELY(x) (x)
680#endif
681
682// Allows empty base class optimization for member variables (C++20)
683// Enables the compiler to optimize away empty member variables, reducing
684// struct size when possible.
685//
686// Usage:
687// struct MyClass {
688// FL_NO_UNIQUE_ADDRESS EmptyAllocator allocator;
689// int data;
690// };
691//
692// This is particularly useful for policy-based designs where stateless
693// policy classes are common.
694#if __cplusplus >= 202002L
695 // C++20+: Use standard [[no_unique_address]] attribute
696 #define FL_NO_UNIQUE_ADDRESS [[no_unique_address]]
697#elif defined(FL_IS_CLANG) || defined(FL_IS_GCC)
698 // GCC/Clang: May have vendor-specific support (check compiler version)
699 // For now, fall back to no-op since it's not standard in C++17
700 #define FL_NO_UNIQUE_ADDRESS
701#else
702 // Unsupported compiler: no-op
703 #define FL_NO_UNIQUE_ADDRESS
704#endif
705
706// Restrict pointer attribute for better optimization (no aliasing)
707// Tells the compiler that a pointer is the only way to access the underlying
708// object for the scope of that pointer, enabling better optimization.
709//
710// Usage: void process(int* FL_RESTRICT_PARAM a, int* FL_RESTRICT_PARAM b);
711//
712// The restrict keyword indicates that 'a' and 'b' do not alias (point to
713// overlapping memory), allowing the compiler to optimize more aggressively.
714#if defined(__cplusplus)
715 // C++ doesn't have 'restrict' keyword, use compiler-specific extensions
716 #if defined(FL_IS_GCC) || defined(FL_IS_CLANG)
717 #define FL_RESTRICT_PARAM __restrict__
718 #elif defined(FL_IS_WIN_MSVC)
719 #define FL_RESTRICT_PARAM __restrict
720 #else
721 #define FL_RESTRICT_PARAM
722 #endif
723#else
724 // C99 has native 'restrict' keyword
725 #define FL_RESTRICT_PARAM restrict
726#endif
727
728// Pointer alignment hint for the optimizer
729// Tells the compiler that a pointer is aligned to N bytes, enabling better
730// codegen (SIMD loads, wider memory ops). This is a hint only — it does not
731// enforce alignment at the call site.
732//
733// Usage: uint8_t* p = FL_ASSUME_ALIGNED(ptr, 64);
734//
735// Delegates to fl::assume_aligned<N>(ptr) which uses __builtin_assume_aligned
736// on GCC/Clang and falls back to a no-op on other platforms.
737// See fl/assume_aligned.h for the implementation.
738
739#define FL_ASSUME_ALIGNED(ptr, N) (fl::assume_aligned<(N)>(ptr))
740
741// Function name macro for debugging and tracing
742// Provides the current function name in a portable way across compilers.
743//
744// Usage: FL_DBG("Entering " << FL_FUNCTION);
745// FL_SCOPED_TRACE() uses this internally
746//
747// Compiler support:
748// - C++11+: __func__ (standard)
749// - GCC/Clang: __FUNCTION__ (extension, same as __func__ in most cases)
750// - MSVC: __FUNCTION__ (non-standard but widely supported)
751//
752// Note: __PRETTY_FUNCTION__ (GCC/Clang) includes full signature but is too verbose
753//
754// FL_PRETTY_FUNCTION: Full function signature including template parameters.
755// Use when you need a unique string per template instantiation (e.g., singleton keys).
756// - GCC/Clang: __PRETTY_FUNCTION__
757// - MSVC: __FUNCSIG__
758// - Fallback: __func__
759#if defined(FL_IS_GCC) || defined(FL_IS_CLANG)
760 #define FL_PRETTY_FUNCTION __PRETTY_FUNCTION__
761#elif defined(FL_IS_WIN_MSVC)
762 #define FL_PRETTY_FUNCTION __FUNCSIG__
763#else
764 #define FL_PRETTY_FUNCTION __func__
765#endif
766
767// FL_MACRO_CONCAT: Token-paste two macro arguments after expansion.
768// FL_MACRO_CONCAT(foo, __LINE__) on line 42 produces foo42.
769#define FL_MACRO_CONCAT_(a, b) a##b
770#define FL_MACRO_CONCAT(a, b) FL_MACRO_CONCAT_(a, b)
771
772// FL_STRING_CONCAT: Concatenate two string literals (or stringified macros).
773// FL_STRING_CONCAT("file", FL_STRINGIFY(__LINE__)) produces "file42".
774// Both arguments must be string literals or macros that expand to string literals.
775#define FL_STRING_CONCAT(a, b) a b
776
777// FL_UNIQUE_KEY: Compile-time unique string literal per call site.
778// Produces "file:line" (e.g., "src/fl/singleton.h:42").
779// Uses only string literals so the result is a single compile-time constant
780// with zero runtime cost. Identical across DLLs compiling the same source.
781// NOTE: Not suitable for template code — all instantiations from the same
782// header line produce the same key. Use FL_PRETTY_FUNCTION for templates.
783#define FL_UNIQUE_KEY FL_STRING_CONCAT(__FILE__ ":", FL_STRINGIFY(__LINE__))
784
785// FL_UNIQUE_IDENTIFIER: Generate a unique C identifier per line.
786// Produces a token like _fl_uid_42. Useful for variable names in macros
787// that must not collide when the macro is used multiple times in a scope.
788// NOTE: Two invocations on the SAME line produce the same identifier.
789#define FL_UNIQUE_IDENTIFIER FL_MACRO_CONCAT(_fl_uid_, __LINE__)
790
791#if defined(__cplusplus)
792 // C++11 standard: __func__ is a predefined identifier
793 #define FL_FUNCTION __func__
794#elif defined(FL_IS_GCC) || defined(FL_IS_CLANG)
795 // GCC/Clang C: Use __FUNCTION__ extension
796 #define FL_FUNCTION __FUNCTION__
797#elif defined(FL_IS_WIN_MSVC)
798 // MSVC: __FUNCTION__ is supported
799 #define FL_FUNCTION __FUNCTION__
800#else
801 // Fallback: Use __func__ (C99 standard)
802 #define FL_FUNCTION __func__
803#endif
804
805// Sanitizer detection macros
806// Detect if compiling with AddressSanitizer (ASAN includes LSAN - LeakSanitizer)
807// GCC: __SANITIZE_ADDRESS__ is defined when -fsanitize=address is used
808// Clang: __has_feature(address_sanitizer) evaluates to 1
809#if defined(__SANITIZE_ADDRESS__)
810# define FL_HAS_SANITIZER_LSAN 1
811#elif defined(__has_feature)
812# if __has_feature(address_sanitizer)
813# define FL_HAS_SANITIZER_LSAN 1
814# endif
815#endif
816
817#ifndef FL_HAS_SANITIZER_LSAN
818# define FL_HAS_SANITIZER_LSAN 0
819#endif
820
821// ============================================================================
822// Force inline macros (formerly fl/force_inline.h)
823// ============================================================================
824
825// Force inline for member functions (no static keyword)
826#ifdef FASTLED_NO_FORCE_INLINE
827#define FASTLED_FORCE_INLINE inline
828#else
829#define FASTLED_FORCE_INLINE __attribute__((always_inline)) inline
830#endif
831
832// Force inline for static free functions (with static keyword)
833// Replacement for LIB8STATIC_ALWAYS_INLINE
834#ifdef FASTLED_NO_FORCE_INLINE
835#define FL_ALWAYS_INLINE static inline
836#else
837#define FL_ALWAYS_INLINE __attribute__((always_inline)) static inline
838#endif
839
840// ============================================================================
841// Compiler intrinsic memcpy (warning-suppressed)
842// ============================================================================
843// __builtin_memcpy is a compiler intrinsic that always lowers to optimal
844// load/store instructions at the call site. Unlike fl::memcpy (a cross-TU
845// call requiring LTO to inline), this guarantees zero call overhead.
846//
847// Wrapped in always_inline functions instead of pragma-based macros to avoid
848// _Pragma placement errors on newer GCC (e.g. GCC 13+ in ESP-IDF v5.5).
849// The void* parameters naturally suppress -Wclass-memaccess by erasing the
850// source type at the call site.
851#if defined(FL_IS_GCC) || defined(FL_IS_CLANG)
852__attribute__((always_inline))
853static inline void *_fl_builtin_memcpy(void *dest, const void *src,
854 __SIZE_TYPE__ n) FL_NO_EXCEPT {
855 return __builtin_memcpy(dest, src, n);
856}
857#define FL_BUILTIN_MEMCPY(dest, src, n) _fl_builtin_memcpy(dest, src, n)
858#else
859// MSVC 2019+ recognizes __builtin_memcpy as an intrinsic; no pragma needed
860#define FL_BUILTIN_MEMCPY(dest, src, n) __builtin_memcpy(dest, src, n)
861#endif
862
863// ============================================================================
864// Compiler intrinsic memset (warning-suppressed)
865// ============================================================================
866// __builtin_memset on non-trivial types (e.g. CRGB) triggers -Wclass-memaccess
867// on GCC. This is safe when the type is trivially copyable / POD-like, which
868// CRGB and CRGB16 are in practice.
869#if defined(FL_IS_GCC) || defined(FL_IS_CLANG)
870__attribute__((always_inline))
871static inline void *_fl_builtin_memset(void *dest, int val, __SIZE_TYPE__ n) FL_NO_EXCEPT {
872 return __builtin_memset(dest, val, n);
873}
874#define FL_BUILTIN_MEMSET(dest, val, n) _fl_builtin_memset(dest, val, n)
875#else
876// MSVC 2019+ recognizes __builtin_memset as an intrinsic; no pragma needed
877#define FL_BUILTIN_MEMSET(dest, val, n) __builtin_memset(dest, val, n)
878#endif
879
880// ============================================================================
881// Register keyword compatibility (formerly fl/register.h)
882// ============================================================================
883
893
894#ifndef FASTLED_REGISTER
897#if (defined(_MSVC_LANG) ? _MSVC_LANG : __cplusplus) < 201703L
898 #define FASTLED_REGISTER register
899#else
900 // C++17+: register keyword removed from the language
901 #define FASTLED_REGISTER
902#endif
904#endif
905
906// ============================================================================
907// Unused macros (formerly fl/unused.h)
908// ============================================================================
909
910#ifndef FASTLED_UNUSED
911#define FASTLED_UNUSED(x) (void)(x)
912#endif
913
914#ifndef FL_UNUSED
915#define FL_UNUSED(x) (void)(x)
916#endif
917
918#ifndef FL_UNUSED_FUNCTION
919#define FL_UNUSED_FUNCTION __attribute__((unused))
920#endif
921
922// ============================================================================
923// AVR platform macros (formerly fl/virtual_if_not_avr.h, fl/avr_disallowed.h)
924// ============================================================================
925
926#ifdef FL_IS_AVR
927#define VIRTUAL_IF_NOT_AVR
928#define OVERRIDE_IF_NOT_AVR
929#else
930#define VIRTUAL_IF_NOT_AVR virtual
931#define OVERRIDE_IF_NOT_AVR override
932#endif
933
934#ifdef FL_IS_AVR
935#define AVR_DISALLOWED \
936 [[deprecated("This function or class is deprecated on AVR.")]]
937#else
938#define AVR_DISALLOWED
939#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