Line data Source code
1 : #ifndef HEADER_fd_src_util_fd_util_base_h
2 : #define HEADER_fd_src_util_fd_util_base_h
3 :
4 : /* Base development environment */
5 :
6 : /* Compiler checks ****************************************************/
7 :
8 : #ifdef __cplusplus
9 :
10 : #if __cplusplus<201703L
11 : #error "Firedancer requires C++17 or later"
12 : #endif
13 :
14 : #else
15 :
16 : #if __STDC_VERSION__<201710L
17 : #error "Firedancer requires C Standard version C17 or later"
18 : #endif
19 :
20 : #endif //__cplusplus
21 :
22 : /* Build target capabilities ******************************************/
23 :
24 : /* Different build targets often have different levels of support for
25 : various language and hardware features. The presence of various
26 : features can be tested at preprocessor, compile, or run time via the
27 : below capability macros.
28 :
29 : Code that does not exploit any of these capabilities written within
30 : the base development environment should be broadly portable across a
31 : range of build targets ranging from on-chain virtual machines to
32 : commodity hosts to custom hardware.
33 :
34 : As such, highly portable yet high performance code is possible by
35 : writing generic implementations that do not exploit any of the below
36 : capabilities as a portable fallback along with build target specific
37 : optimized implementations that are invoked when the build target
38 : supports the appropriate capabilities.
39 :
40 : The base development itself provides lots of functionality to help
41 : with implementing portable fallbacks while making very minimal
42 : assumptions about the build targets and zero use of 3rd party
43 : libraries (these might make unknown additional assumptions about the
44 : build target, including availability of a quality implementation of
45 : the library on the build target). */
46 :
47 : /* FD_HAS_HOSTED: If the build target is hosted (e.g. resides on a host
48 : with a POSIX-ish environment ... practically speaking, stdio.h,
49 : stdlib.h, unistd.h, et al more or less behave normally ...
50 : pedantically XOPEN_SOURCE=700), FD_HAS_HOSTED will be 1. It will be
51 : zero otherwise. */
52 :
53 : #ifndef FD_HAS_HOSTED
54 : #define FD_HAS_HOSTED 0
55 : #endif
56 :
57 : /* FD_HAS_ATOMIC: If the build target supports atomic operations
58 : between threads accessing a common memory region (include threads
59 : that reside in different processes on a host communicating via a
60 : shared memory region with potentially different local virtual
61 : mappings). Practically speaking, does atomic compare-and-swap et al
62 : work? */
63 :
64 : #ifndef FD_HAS_ATOMIC
65 : #define FD_HAS_ATOMIC 0
66 : #endif
67 :
68 : /* FD_HAS_THREADS: If the build target supports a POSIX-ish notion of
69 : threads (e.g. practically speaking, global variables declared within
70 : a compile unit are visible to more than one thread of execution,
71 : pthread.h / threading parts of C standard, the atomics parts of the
72 : C standard, ... more or less work normally), FD_HAS_THREADS will be
73 : 1. It will be zero otherwise. FD_HAS_THREADS implies FD_HAS_HOSTED
74 : and FD_HAS_ATOMIC. */
75 :
76 : #ifndef FD_HAS_THREADS
77 : #define FD_HAS_THREADS 0
78 : #endif
79 :
80 : /* FD_HAS_INT128: If the build target supports reasonably efficient
81 : 128-bit wide integer operations, define FD_HAS_INT128 to 1 to enable
82 : use of them in implementations. */
83 :
84 : #ifndef FD_HAS_INT128
85 : #define FD_HAS_INT128 0
86 : #endif
87 :
88 : /* FD_HAS_DOUBLE: If the build target supports reasonably efficient
89 : IEEE 754 64-bit wide double precision floating point operations,
90 : define FD_HAS_DOUBLE to 1 to enable use of them in implementations.
91 : Note that even if the build target does not, va_args handling in the
92 : C / C++ language requires promotion of a float in a va_arg list to a
93 : double. Thus, C / C++ language that support IEEE 754 float also
94 : implies a minimum level of support for double (though not necessarily
95 : efficient or IEEE 754). That is, even if a target does not have
96 : FD_HAS_DOUBLE, there might still be limited use of double in va_arg
97 : list handling. */
98 :
99 : #ifndef FD_HAS_DOUBLE
100 : #define FD_HAS_DOUBLE 0
101 : #endif
102 :
103 : /* FD_HAS_ALLOCA: If the build target supports fast alloca-style
104 : dynamic stack memory allocation (e.g. alloca.h / __builtin_alloca
105 : more or less work normally), define FD_HAS_ALLOCA to 1 to enable use
106 : of it in implementations. */
107 :
108 : #ifndef FD_HAS_ALLOCA
109 : #define FD_HAS_ALLOCA 0
110 : #endif
111 :
112 : /* FD_HAS_X86: If the build target supports x86 specific features and
113 : can benefit from x86 specific optimizations, define FD_HAS_X86. Code
114 : needing more specific target features (Intel / AMD / SSE / AVX2 /
115 : AVX512 / etc) can specialize further as necessary with even more
116 : precise capabilities (that in turn imply FD_HAS_X86). */
117 :
118 : #ifndef FD_HAS_X86
119 : #define FD_HAS_X86 0
120 : #endif
121 :
122 : /* These allow even more precise targeting for X86. */
123 :
124 : /* FD_HAS_SSE indicates the target supports Intel SSE4 style SIMD
125 : (basically do the 128-bit wide parts of "x86intrin.h" work).
126 : Recommend using the simd/fd_sse.h APIs instead of raw Intel
127 : intrinsics for readability and to facilitate portability to non-x86
128 : platforms. Implies FD_HAS_X86. */
129 :
130 : #ifndef FD_HAS_SSE
131 : #define FD_HAS_SSE 0
132 : #endif
133 :
134 : /* FD_HAS_AVX indicates the target supports Intel AVX2 style SIMD
135 : (basically do the 256-bit wide parts of "x86intrin.h" work).
136 : Recommend using the simd/fd_avx.h APIs instead of raw Intel
137 : intrinsics for readability and to facilitate portability to non-x86
138 : platforms. Implies FD_HAS_SSE. */
139 :
140 : #ifndef FD_HAS_AVX
141 : #define FD_HAS_AVX 0
142 : #endif
143 :
144 : /* FD_HAS_AVX512 indicates the target supports Intel AVX-512 style SIMD
145 : (basically do the 512-bit wide parts of "x86intrin.h" work).
146 : Recommend using the simd/fd_avx512.h APIs instead of raw Intel
147 : intrinsics for readability and to facilitate portability to non-x86
148 : platforms. Implies FD_HAS_AVX. */
149 :
150 : #ifndef FD_HAS_AVX512
151 : #define FD_HAS_AVX512 0
152 : #endif
153 :
154 : /* FD_HAS_SHANI indicates that the target supports Intel SHA extensions
155 : which accelerate SHA-1 and SHA-256 computation. This extension is
156 : also called SHA-NI or SHA_NI (Secure Hash Algorithm New
157 : Instructions). Although proposed in 2013, they're only supported on
158 : Intel Ice Lake and AMD Zen CPUs and newer. Implies FD_HAS_AVX. */
159 :
160 : #ifndef FD_HAS_SHANI
161 : #define FD_HAS_SHANI 0
162 : #endif
163 :
164 : /* FD_HAS_GFNI indicates that the target supports Intel Galois Field
165 : extensions, which accelerate operations over binary extension fields,
166 : especially GF(2^8). These instructions are supported on Intel Ice
167 : Lake and newer and AMD Zen4 and newer CPUs. Implies FD_HAS_AVX. */
168 :
169 : #ifndef FD_HAS_GFNI
170 : #define FD_HAS_GFNI 0
171 : #endif
172 :
173 : /* FD_HAS_AESNI indicates that the target supports AES-NI extensions,
174 : which accelerate AES encryption and decryption. While AVX predates
175 : the original AES-NI extension, the combination of AES-NI+AVX adds
176 : additional opcodes (such as vaesenc, a more flexible variant of
177 : aesenc). Thus, implies FD_HAS_AVX. A conservative estimate for
178 : minimum platform support is Intel Haswell or AMD Zen. */
179 :
180 : #ifndef FD_HAS_AESNI
181 : #define FD_HAS_AESNI 0
182 : #endif
183 :
184 : /* FD_HAS_ARM: If the build target supports armv8-a specific features
185 : and can benefit from aarch64 specific optimizations, define
186 : FD_HAS_ARM. */
187 :
188 : #ifndef FD_HAS_ARM
189 : #define FD_HAS_ARM 0
190 : #endif
191 :
192 : #ifndef FD_HAS_RISCV
193 : #define FD_HAS_RISCV 0
194 : #endif
195 :
196 : /* FD_HAS_LZ4 indicates that the target supports LZ4 compression.
197 : Roughly, does "#include <lz4.h>" and the APIs therein work? */
198 :
199 : #ifndef FD_HAS_LZ4
200 0 : #define FD_HAS_LZ4 0
201 : #endif
202 :
203 : /* FD_HAS_COVERAGE indicates that the build target is built with coverage instrumentation. */
204 :
205 : #ifndef FD_HAS_COVERAGE
206 : #define FD_HAS_COVERAGE 0
207 : #endif
208 :
209 : /* FD_HAS_ASAN indicates that the build target is using ASAN. */
210 :
211 : #ifndef FD_HAS_ASAN
212 : #define FD_HAS_ASAN 0
213 : #endif
214 :
215 : /* FD_HAS_UBSAN indicates that the build target is using UBSAN. */
216 :
217 : #ifndef FD_HAS_UBSAN
218 : #define FD_HAS_UBSAN 0
219 : #endif
220 :
221 : /* FD_HAS_DEEPASAN indicates that the build target is using ASAN with manual
222 : memory poisoning for fd_alloc, fd_wksp, and fd_scratch. */
223 :
224 : #ifndef FD_HAS_DEEPASAN
225 : #define FD_HAS_DEEPASAN 0
226 : #endif
227 :
228 : /* Base development environment ***************************************/
229 :
230 : /* The functionality provided by these vanilla headers are always
231 : available within the base development environment. Notably, stdio.h
232 : / stdlib.h / et al are not included here as these make lots of
233 : assumptions about the build target that may not be true (especially
234 : for on-chain and custom hardware use). Code should prefer the fd
235 : util equivalents for such functionality when possible. */
236 :
237 : #include <stdalign.h>
238 : #include <string.h>
239 : #include <limits.h>
240 : #include <float.h>
241 :
242 : /* Work around some library naming irregularities */
243 : /* FIXME: Consider this for FLOAT/FLT, DOUBLE/DBL too? */
244 :
245 0 : #define SHORT_MIN SHRT_MIN
246 0 : #define SHORT_MAX SHRT_MAX
247 814241 : #define USHORT_MAX USHRT_MAX
248 :
249 : /* Primitive types ****************************************************/
250 :
251 : /* These typedefs provide single token regularized names for all the
252 : primitive types in the base development environment:
253 :
254 : char !
255 : schar ! short int long int128 !!
256 : uchar ushort uint ulong uint128 !!
257 : float
258 : double !!!
259 :
260 : ! Does not assume the sign of char. A naked char should be treated
261 : as cstr character and mathematical operations should be avoided on
262 : them. This is less than ideal as the patterns for integer types in
263 : the C/C++ language spec itself are far more consistent with a naked
264 : char naturally being treated as signed (see above). But there are
265 : lots of conflicts between architectures, languages and standard
266 : libraries about this so any use of a naked char shouldn't assume
267 : the sign ... sigh.
268 :
269 : !! Only available if FD_HAS_INT128 is defined
270 :
271 : !!! Should only be used if FD_HAS_DOUBLE is defined but see note in
272 : FD_HAS_DOUBLE about C/C++ silent promotions of float to double in
273 : va_arg lists.
274 :
275 : Note also that these token names more naturally interoperate with
276 : integer constant declarations, type generic code generation
277 : techniques, with printf-style format strings than the stdint.h /
278 : inttypes.h handling.
279 :
280 : To minimize portability issues, unexpected silent type conversion
281 : issues, align with typical developer implicit usage, align with
282 : typical build target usage, ..., assumes char / short / int / long
283 : are 8 / 16 / 32 / 64 twos complement integers and float is IEEE-754
284 : single precision. Further assumes little endian, truncating signed
285 : integer division, sign extending (arithmetic) signed right shift and
286 : signed left shift behaves the same as an unsigned left shift from bit
287 : operations point of view (technically the standard says signed left
288 : shift is undefined if the result would overflow). Also, except for
289 : int128/uint128, assumes that aligned access to these will be
290 : naturally atomic. Lastly assumes that unaligned access to these is
291 : functionally valid but does not assume that unaligned access to these
292 : is efficient or atomic.
293 :
294 : For values meant to be held in registers, code should prefer long /
295 : ulong types (improves asm generation given the prevalence of 64-bit
296 : targets and also to avoid lots of tricky bugs with silent promotions
297 : in the language ... e.g. ushort should ideally only be used for
298 : in-memory representations).
299 :
300 : These are currently not prefixed given how often they are used. If
301 : this becomes problematic prefixes can be added as necessary.
302 : Specifically, C++ allows typedefs to be defined multiple times so
303 : long as they are equivalent. Inequivalent collisions are not
304 : supported but should be rare (e.g. if a 3rd party header thinks
305 : "ulong" should be something other than "unsigned long", the 3rd party
306 : header probably should be nuked from orbit). C11 and forward also
307 : allow multiple equivalent typedefs. C99 and earlier don't but this
308 : is typically only a warning and then only if pedantic warnings are
309 : enabled. Thus, if we want to support users using C99 and earlier who
310 : want to do a strict compile and have a superfluous collision with
311 : these types in other libraries, uncomment the below (or do something
312 : equivalent for the compiler). */
313 :
314 : //#pragma GCC diagnostic push
315 : //#pragma GCC diagnostic ignored "-Wpedantic"
316 :
317 : typedef signed char schar; /* See above note of sadness */
318 :
319 : typedef unsigned char uchar;
320 : typedef unsigned short ushort;
321 : typedef unsigned int uint;
322 : typedef unsigned long ulong;
323 :
324 : #ifdef __SIZEOF_INT128__
325 :
326 : __extension__ typedef __int128 int128;
327 : __extension__ typedef unsigned __int128 uint128;
328 :
329 15787 : #define UINT128_MAX (~(uint128)0)
330 0 : #define INT128_MAX ((int128)(UINT128_MAX>>1))
331 0 : #define INT128_MIN (-INT128_MAX-(int128)1)
332 :
333 : #endif
334 :
335 : //#pragma GCC diagnostic pop
336 :
337 : /* Compiler tricks ****************************************************/
338 :
339 : /* FD_STRINGIFY,FD_CONCAT{2,3,4}: Various macros for token
340 : stringification and pasting. FD_STRINGIFY returns the argument as a
341 : cstr (e.g. FD_STRINGIFY(foo) -> "foo"). FD_CONCAT* pastes the tokens
342 : together into a single token (e.g. FD_CONCAT3(a,b,c) -> abc). The
343 : EXPAND variants first expand their arguments and then do the token
344 : operation (e.g. FD_EXPAND_THEN_STRINGIFY(__LINE__) -> "104" if done
345 : on line 104 of the source code file). */
346 :
347 0 : #define FD_STRINGIFY(x)#x
348 7214 : #define FD_CONCAT2(a,b)a##b
349 0 : #define FD_CONCAT3(a,b,c)a##b##c
350 0 : #define FD_CONCAT4(a,b,c,d)a##b##c##d
351 :
352 : #define FD_EXPAND_THEN_STRINGIFY(x)FD_STRINGIFY(x)
353 7214 : #define FD_EXPAND_THEN_CONCAT2(a,b)FD_CONCAT2(a,b)
354 1384346180 : #define FD_EXPAND_THEN_CONCAT3(a,b,c)FD_CONCAT3(a,b,c)
355 0 : #define FD_EXPAND_THEN_CONCAT4(a,b,c,d)FD_CONCAT4(a,b,c,d)
356 :
357 : /* FD_VA_ARGS_SELECT(__VA_ARGS__,e32,e31,...e1): Macro that expands to
358 : en at compile time where n is number of items in the __VA_ARGS__
359 : list. If __VA_ARGS__ is empty, returns e1. Assumes __VA_ARGS__ has
360 : at most 32 arguments. Useful for making a variadic macro whose
361 : behavior depends on the number of arguments in __VA_ARGS__. */
362 :
363 : #define FD_VA_ARGS_SELECT(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,a,b,c,d,e,f,_,...)_
364 :
365 : /* FD_SRC_LOCATION returns a const cstr holding the line of code where
366 : FD_SRC_LOCATION was used. */
367 :
368 : #define FD_SRC_LOCATION __FILE__ "(" FD_EXPAND_THEN_STRINGIFY(__LINE__) ")"
369 :
370 : /* FD_STATIC_ASSERT tests at compile time if c is non-zero. If not,
371 : it aborts the compile with an error. err itself should be a token
372 : (e.g. not a string, no whitespace, etc). */
373 :
374 : #ifdef __cplusplus
375 : #define FD_STATIC_ASSERT(c,err) static_assert(c, #err)
376 : #else
377 29550 : #define FD_STATIC_ASSERT(c,err) _Static_assert(c, #err)
378 : #endif
379 :
380 : /* FD_ADDRESS_OF_PACKED_MEMBER(x): Linguistically does &(x) but without
381 : recent compiler complaints that &x might be unaligned if x is a
382 : member of a packed datastructure. (Often needed for interfacing with
383 : hardware / packets / etc.) */
384 :
385 0 : #define FD_ADDRESS_OF_PACKED_MEMBER( x ) (__extension__({ \
386 0 : char * _fd_aopm = (char *)&(x); \
387 0 : __asm__( "# FD_ADDRESS_OF_PACKED_MEMBER(" #x ") @" FD_SRC_LOCATION : "+r" (_fd_aopm) :: ); \
388 0 : (__typeof__(&(x)))_fd_aopm; \
389 0 : }))
390 :
391 : /* FD_PROTOTYPES_{BEGIN,END}: Headers that might be included in C++
392 : source should encapsulate the prototypes of code and globals
393 : contained in compilation units compiled as C with a
394 : FD_PROTOTYPES_{BEGIN,END} pair. */
395 :
396 : #ifdef __cplusplus
397 : #define FD_PROTOTYPES_BEGIN extern "C" {
398 : #else
399 : #define FD_PROTOTYPES_BEGIN
400 : #endif
401 :
402 : #ifdef __cplusplus
403 : #define FD_PROTOTYPES_END }
404 : #else
405 : #define FD_PROTOTYPES_END
406 : #endif
407 :
408 : /* FD_ASM_LG_ALIGN(lg_n) expands to an alignment assembler directive
409 : appropriate for the current architecture/ABI. The resulting align
410 : is 2^(lg_n) bytes, i.e. FD_ASM_LG_ALIGN(3) aligns by 8 bytes. */
411 :
412 : #if defined(__aarch64__)
413 : #define FD_ASM_LG_ALIGN(lg_n) ".align " #lg_n "\n"
414 : #elif defined(__x86_64__) || defined(__powerpc64__) || defined(__riscv)
415 : #define FD_ASM_LG_ALIGN(lg_n) ".p2align " #lg_n "\n"
416 : #endif
417 :
418 : /* FD_IMPORT declares a variable name and initializes with the contents
419 : of the file at path (with potentially some assembly directives for
420 : additional footer info). It is equivalent to:
421 :
422 : type const name[] __attribute__((aligned(align))) = {
423 :
424 : ... code that would initialize the contents of name to the
425 : ... raw binary data found in the file at path at compile time
426 : ... (with any appended information as specified by footer)
427 :
428 : };
429 :
430 : ulong const name_sz = ... number of bytes pointed to by name;
431 :
432 : More precisely, this creates a symbol "name" in the object file that
433 : points to a read-only copy of the raw data in the file at "path" as
434 : it was at compile time. 2^lg_align specifies the minimum alignment
435 : required for the copy's first byte as an unsuffixed decimal integer.
436 : footer are assembly commands to permit additional data to be appended
437 : to the copy (use "" for footer if no footer is necessary).
438 :
439 : Then it exposes a pointer to this copy in the current compilation
440 : unit as name and the byte size as name_sz. name_sz covers the first
441 : byte of the included data to the last byte of the footer inclusive.
442 :
443 : The dummy linker symbol _fd_import_name_sz will also be created in
444 : the object file as some under the hood magic to make this work. This
445 : should not be used in any compile unit as some compilers (I'm looking
446 : at you clang-15, but apparently not clang-10) will sometimes mangle
447 : its value from what it was set to in the object file even when marked
448 : as absolute in the object file.
449 :
450 : This should only be used at global scope and should be done at most
451 : once over all object files / libraries used to make a program. If
452 : other compilation units want to make use of an import in a different
453 : compilation unit, they should declare:
454 :
455 : extern type const name[] __attribute__((aligned(align)));
456 :
457 : and/or:
458 :
459 : extern ulong const name_sz;
460 :
461 : as necessary (that is, do the usual to use name and name_sz as shown
462 : for the pseudo code above).
463 :
464 : Important safety tip! gcc -M will generally not detect the
465 : dependency this creates between the importing file and the imported
466 : file. This can cause incremental builds to miss changes to the
467 : imported file. Ideally, we would have FD_IMPORT automatically do
468 : something like:
469 :
470 : _Pragma( "GCC dependency \"" path "\" )
471 :
472 : This doesn't work as is because _Pragma needs some macro expansion
473 : hacks to accept this (this is doable). After that workaround, this
474 : still doesn't work because, due to tooling limitations, the pragma
475 : path is relative to the source file directory and the FD_IMPORT path
476 : is relative to the make directory (working around this would
477 : require a __FILE__-like directive for the source code directory base
478 : path). Even if that did exist, it might still not work because
479 : out-of-tree builds often require some substitutions to the gcc -M
480 : generated dependencies that this might not pick up (at least not
481 : without some build system surgery). And then it still wouldn't work
482 : because gcc -M seems to ignore all of this anyways (which is the
483 : actual show stopper as this pragma does something subtly different
484 : than what the name suggests and there isn't any obvious support for a
485 : "pseudo-include".) Another reminder that make clean and fast builds
486 : are our friend. */
487 :
488 : #if defined(__ELF__)
489 :
490 : #define FD_IMPORT( name, path, type, lg_align, footer ) \
491 : __asm__( ".section .rodata,\"a\",@progbits\n" \
492 : ".type " #name ",@object\n" \
493 : ".globl " #name "\n" \
494 : FD_ASM_LG_ALIGN(lg_align) \
495 : #name ":\n" \
496 : ".incbin \"" path "\"\n" \
497 : footer "\n" \
498 : ".size " #name ",. - " #name "\n" \
499 : "_fd_import_" #name "_sz = . - " #name "\n" \
500 : ".type " #name "_sz,@object\n" \
501 : ".globl " #name "_sz\n" \
502 : FD_ASM_LG_ALIGN(3) \
503 : #name "_sz:\n" \
504 : ".quad _fd_import_" #name "_sz\n" \
505 : ".size " #name "_sz,8\n" \
506 : ".previous\n" ); \
507 : extern type const name[] __attribute__((aligned(1<<(lg_align)))); \
508 : extern ulong const name##_sz
509 :
510 : #elif defined(__MACH__)
511 :
512 : #define FD_IMPORT( name, path, type, lg_align, footer ) \
513 : __asm__( ".section __DATA,__const\n" \
514 : ".globl _" #name "\n" \
515 : FD_ASM_LG_ALIGN(lg_align) \
516 : "_" #name ":\n" \
517 : ".incbin \"" path "\"\n" \
518 : footer "\n" \
519 : "_fd_import_" #name "_sz = . - _" #name "\n" \
520 : ".globl _" #name "_sz\n" \
521 : FD_ASM_LG_ALIGN(3) \
522 : "_" #name "_sz:\n" \
523 : ".quad _fd_import_" #name "_sz\n" \
524 : ".previous\n" ); \
525 : extern type const name[] __attribute__((aligned(1<<(lg_align)))); \
526 : extern ulong const name##_sz
527 :
528 : #endif
529 :
530 : /* FD_IMPORT_{BINARY,CSTR} are common cases for FD_IMPORT.
531 :
532 : In BINARY, the file is imported into the object file and exposed to
533 : the caller as a uchar binary data. name_sz will be the number of
534 : bytes in the file at time of import. name will have 128 byte
535 : alignment.
536 :
537 : In CSTR, the file is imported into the object file with a '\0'
538 : termination appended and exposed to the caller as a cstr. Assuming
539 : the file is text (i.e. has no internal '\0's), strlen(name) will be
540 : the number of bytes in the file and name_sz will be strlen(name)+1.
541 : name can have arbitrary alignment. */
542 :
543 : #ifdef FD_IMPORT
544 : #define FD_IMPORT_BINARY(name, path) FD_IMPORT( name, path, uchar, 7, "" )
545 : #define FD_IMPORT_CSTR( name, path) FD_IMPORT( name, path, char, 1, ".byte 0" )
546 : #endif
547 :
548 : /* Optimizer tricks ***************************************************/
549 :
550 : /* FD_RESTRICT is a pointer modifier to designate a pointer as
551 : restricted. Hoops jumped because C++-17 still doesn't understand
552 : restrict ... sigh */
553 :
554 : #ifndef FD_RESTRICT
555 : #ifdef __cplusplus
556 : #define FD_RESTRICT __restrict
557 : #else
558 : #define FD_RESTRICT restrict
559 : #endif
560 : #endif
561 :
562 : /* fd_type_pun(p), fd_type_pun_const(p): These allow use of type
563 : punning while keeping strict aliasing optimizations enabled (e.g.
564 : some UNIX APIs, like sockaddr related APIs are dependent on type
565 : punning). These allow these API's to be used cleanly while keeping
566 : strict aliasing optimizations enabled and strict alias checking done. */
567 :
568 : static inline void *
569 1709888 : fd_type_pun( void * p ) {
570 1709888 : __asm__( "# fd_type_pun @" FD_SRC_LOCATION : "+r" (p) :: "memory" );
571 1709888 : return p;
572 1709888 : }
573 :
574 : static inline void const *
575 1260215 : fd_type_pun_const( void const * p ) {
576 1260215 : __asm__( "# fd_type_pun_const @" FD_SRC_LOCATION : "+r" (p) :: "memory" );
577 1260215 : return p;
578 1260215 : }
579 :
580 : /* FD_{LIKELY,UNLIKELY}(c): Evaluates c and returns whether it is
581 : logical true/false as long (1L/0L). It also hints to the optimizer
582 : whether it should optimize for the case of c evaluating as
583 : true/false. */
584 :
585 81782789 : #define FD_LIKELY(c) __builtin_expect( !!(c), 1L )
586 691921304 : #define FD_UNLIKELY(c) __builtin_expect( !!(c), 0L )
587 :
588 : /* FD_FN_PURE hints to the optimizer that the function, roughly
589 : speaking, does not have side effects. As such, the compiler can
590 : replace a call to the function with the result of an earlier call to
591 : that function provided the inputs and memory used haven't changed.
592 :
593 : IMPORTANT SAFETY TIP! Recent compilers seem to take an undocumented
594 : and debatable stance that pure functions do no writes to memory.
595 : This is a sufficient condition for the above but not a necessary one.
596 :
597 : Consider, for example, the real world case of an otherwise pure
598 : function that uses pass-by-reference to return more than one value
599 : (an unpleasant practice that is sadly often necessary because C/C++,
600 : compilers and underlying platform ABIs are very bad at helping
601 : developers simply and clearly express their intent to return multiple
602 : values and then generate good assembly for such).
603 :
604 : If called multiple times sequentially, all but the first call to such
605 : a "pure" function could be optimized away because the non-volatile
606 : memory writes done in the all but the 1st call for the
607 : pass-by-reference-returns write the same value to normal memory that
608 : was written on the 1st call. That is, these calls return the same
609 : value for their direct return and do writes that do not have any
610 : visible effect.
611 :
612 : Thus, while it is safe for the compiler to eliminate all but the
613 : first call via techniques like common subexpression elimination, it
614 : is not safe for the compiler to infer that the first call did no
615 : writes.
616 :
617 : But recent compilers seem to do exactly that.
618 :
619 : Sigh ... we can't use FD_FN_PURE on such functions because of all the
620 : above linguistic, compiler, documentation and ABI infinite sadness.
621 :
622 : TL;DR To be safe against the above vagaries, recommend using
623 : FD_FN_PURE to annotate functions that do no memory writes (including
624 : trivial memory writes) and try to design HPC APIs to avoid returning
625 : multiple values as much as possible.
626 :
627 : Followup: FD_FN_PURE expands to nothing by default given additional
628 : confusion between how current languages, compilers, CI, fuzzing, and
629 : developers interpret this function attribute. We keep it around
630 : given it documents the intent of various APIs and so it can be
631 : manually enabled to find implementation surprises during bullet
632 : proofing (e.g. under compiler options like "extra-brutality").
633 : Hopefully someday, pure function attributes will someday be handled
634 : more consistently across the board. */
635 :
636 : #ifndef FD_FN_PURE
637 : #define FD_FN_PURE
638 : #endif
639 :
640 : /* FD_FN_CONST is like pure but also, even stronger, indicates that the
641 : function does not depend on the state of memory. See note above
642 : about why this expands to nothing by default. */
643 :
644 : #ifndef FD_FN_CONST
645 : #define FD_FN_CONST
646 : #endif
647 :
648 : /* FD_FN_UNUSED indicates that it is okay if the function with static
649 : linkage is not used. Allows working around -Winline in header only
650 : APIs where the compiler decides not to actually inline the function.
651 : (This belief, frequently promulgated by anti-macro cults, that "An
652 : Inline Function is As Fast As a Macro" ... an entire section in gcc's
653 : documentation devoted to it in fact ... remains among the biggest
654 : lies in computer science. Yes, an inline function is as fast as a
655 : macro ... when the compiler actually decides to treat the inline
656 : keyword more than just for entertainment purposes only. Which, as
657 : -Winline proves, it frequently doesn't. Sigh ... force_inline like
658 : compiler extensions might be an alternative here but they have their
659 : own portability issues.) */
660 :
661 0 : #define FD_FN_UNUSED __attribute__((unused))
662 :
663 : /* FD_FN_UNSANITIZED tells the compiler to disable AddressSanitizer and
664 : UndefinedBehaviorSanitizer instrumentation. For some functions, this
665 : can improve instrumented compile time by ~30x. */
666 :
667 : #if FD_HAS_MSAN
668 : #define FD_FN_UNSANITIZED __attribute__((no_sanitize("memory")))
669 : #else
670 : #define FD_FN_UNSANITIZED __attribute__((no_sanitize("address", "undefined")))
671 : #endif
672 :
673 : /* FD_FN_SENSITIVE instruments the compiler to sanitize sensitive functions.
674 : https://eprint.iacr.org/2023/1713 (Sec 3.2)
675 : - Clear all registers with __attribute__((zero_call_used_regs("all")))
676 : - Clear stack with __attribute__((strub)), available in gcc 14+ */
677 :
678 : #if __has_attribute(strub)
679 : #define FD_FN_SENSITIVE __attribute__((strub)) __attribute__((zero_call_used_regs("all")))
680 : #elif __has_attribute(zero_call_used_regs)
681 : #define FD_FN_SENSITIVE __attribute__((zero_call_used_regs("all")))
682 : #else
683 : #define FD_FN_SENSITIVE
684 : #endif
685 :
686 : /* FD_PARAM_UNUSED indicates that it is okay if the function parameter is not
687 : used. */
688 :
689 : #define FD_PARAM_UNUSED __attribute__((unused))
690 :
691 : /* FD_TYPE_PACKED indicates that a type is to be packed, resetting its
692 : alignment to 1. */
693 :
694 : #define FD_TYPE_PACKED __attribute__((packed))
695 :
696 : /* FD_WARN_UNUSED tells the compiler the result (from a function) should
697 : be checked. This is useful to force callers to either check the result
698 : or deliberately and explicitly ignore it. Good for result codes and
699 : errors */
700 :
701 : #define FD_WARN_UNUSED __attribute__ ((warn_unused_result))
702 :
703 : /* FD_FALLTHRU tells the compiler that a case in a switch falls through
704 : to the next case. This avoids the compiler complaining, in cases where
705 : it is an intentional fall through.
706 : The "while(0)" avoids a compiler complaint in the event the case
707 : has no statement, example:
708 : switch( return_code ) {
709 : case RETURN_CASE_1: FD_FALLTHRU;
710 : case RETURN_CASE_2: FD_FALLTHRU;
711 : case RETURN_CASE_3:
712 : case_123();
713 : default:
714 : case_other();
715 : }
716 :
717 : See C++17 [[fallthrough]] and gcc __attribute__((fallthrough)) */
718 :
719 : #define FD_FALLTHRU while(0) __attribute__((fallthrough))
720 :
721 : /* FD_COMPILER_FORGET(var): Tells the compiler that it shouldn't use
722 : any knowledge it has about the provided register-compatible variable
723 : var for optimizations going forward (i.e. the variable has changed in
724 : a deterministic but unknown-to-the-compiler way where the actual
725 : change is the identity operation). Useful for inhibiting various
726 : branch nest misoptimizations (compilers unfortunately tend to
727 : radically underestimate the impact in raw average performance and
728 : jitter and the probability of branch mispredicts or the cost to the
729 : CPU of having lots of branches). This is not asm volatile (use
730 : UNPREDICTABLE below for that) and has no clobbers. So if var is not
731 : used after the forget, the compiler can optimize the FORGET away
732 : (along with operations preceding it used to produce var). */
733 :
734 1287187 : #define FD_COMPILER_FORGET(var) __asm__( "# FD_COMPILER_FORGET(" #var ")@" FD_SRC_LOCATION : "+r" (var) )
735 :
736 : /* FD_COMPILER_UNPREDICTABLE(var): Same as FD_COMPILER_FORGET(var) but
737 : the provided variable has changed in a non-deterministic way from the
738 : compiler's POV (e.g. the value in the variable on output should not
739 : be treated as a compile time constant even if it is one
740 : linguistically). Useful for suppressing unwanted
741 : compile-time-const-based optimizations like hoisting operations with
742 : useful CPU side effects out of a critical loop. */
743 :
744 1554 : #define FD_COMPILER_UNPREDICTABLE(var) __asm__ __volatile__( "# FD_COMPILER_UNPREDICTABLE(" #var ")@" FD_SRC_LOCATION : "+m,r" (var) )
745 :
746 : /* Atomic tricks ******************************************************/
747 :
748 : /* FD_COMPILER_MFENCE(): Tells the compiler that it can't move any
749 : memory operations (load or store) from before the MFENCE to after the
750 : MFENCE (and vice versa). The processor itself might still reorder
751 : around the fence though (that requires platform specific fences). */
752 :
753 50555907 : #define FD_COMPILER_MFENCE() __asm__ __volatile__( "# FD_COMPILER_MFENCE()@" FD_SRC_LOCATION ::: "memory" )
754 :
755 : /* FD_HW_MFENCE(): A full hardware memory fence. All prior stores
756 : are globally visible before any subsequent loads execute. Use
757 : when a compiler fence is insufficient, e.g. epoch-based safe
758 : reclamation where a store must be visible to another core before
759 : a dependent load on this core (StoreLoad barrier). */
760 :
761 : #if FD_HAS_X86
762 1213599 : #define FD_HW_MFENCE() __asm__ __volatile__( "lock addl $0, (%%rsp)" ::: "memory", "cc" )
763 0 : #define FD_HW_MFENCE_LD() FD_COMPILER_MFENCE()
764 0 : #define FD_HW_MFENCE_ST() FD_COMPILER_MFENCE()
765 : #elif FD_HAS_ARM
766 : #define FD_HW_MFENCE() __asm__ __volatile__( "dmb ish" ::: "memory" )
767 : #define FD_HW_MFENCE_LD() __asm__ __volatile__( "dmb ishld" ::: "memory" )
768 : #define FD_HW_MFENCE_ST() __asm__ __volatile__( "dmb ishst" ::: "memory" )
769 : #elif FD_HAS_RISCV
770 : #define FD_HW_MFENCE() __asm__ __volatile__( "fence rw,rw" ::: "memory" )
771 : #define FD_HW_MFENCE_LD() __asm__ __volatile__( "fence r,rw" ::: "memory" )
772 : #define FD_HW_MFENCE_ST() __asm__ __volatile__( "fence w,w" ::: "memory" )
773 : #else
774 : #define FD_HW_MFENCE() __sync_synchronize()
775 : #define FD_HW_MFENCE_LD() __sync_synchronize()
776 : #define FD_HW_MFENCE_ST() __sync_synchronize()
777 : #endif
778 :
779 : /* FD_SPIN_PAUSE(): Yields the logical core of the calling thread to
780 : the other logical cores sharing the same underlying physical core for
781 : a few clocks without yielding it to the operating system scheduler.
782 : Typically useful for shared memory spin polling loops, especially if
783 : hyperthreading is in use. IMPORTANT SAFETY TIP! This might act as a
784 : FD_COMPILER_MFENCE on some combinations of toolchains and targets
785 : (e.g. gcc documents that __builtin_ia32_pause also does a compiler
786 : memory fence) but this should not be relied upon for portable code
787 : (consider making this a compiler memory fence on all platforms?) */
788 :
789 : #if FD_HAS_X86
790 293549570 : #define FD_SPIN_PAUSE() __builtin_ia32_pause()
791 : #elif FD_HAS_ARM
792 : #define FD_SPIN_PAUSE() __asm__ __volatile__( "yield" ::: "memory" )
793 : #else
794 : #define FD_SPIN_PAUSE() ((void)0)
795 : #endif
796 :
797 : /* FD_YIELD(): Yields the logical core of the calling thread to the
798 : operating system scheduler if a hosted target and does a spin pause
799 : otherwise. */
800 :
801 : #if FD_HAS_HOSTED
802 1237 : #define FD_YIELD() fd_yield()
803 : #else
804 : #define FD_YIELD() FD_SPIN_PAUSE()
805 : #endif
806 :
807 : /* FD_VOLATILE_CONST(x): Tells the compiler that it is not able to
808 : predict the value obtained by dereferencing x and that dereferencing
809 : x might have other side effects (e.g. maybe another thread could
810 : change the value and the compiler has no way of knowing this).
811 : Generally speaking, the volatile keyword is broken linguistically.
812 : Volatility is not a property of the variable but of the
813 : dereferencing of a variable (e.g. what is volatile from the POV of a
814 : reader of a shared variable is not necessarily volatile from the POV
815 : of a writer of that shared variable in a different thread). */
816 :
817 473987094 : #define FD_VOLATILE_CONST(x) (*((volatile const __typeof__((x)) *)&(x)))
818 :
819 : /* FD_VOLATILE(x): tells the compiler that it is not able to predict the
820 : effect of modifying x and that dereferencing x might have other side
821 : effects (e.g. maybe another thread is spinning on x waiting for its
822 : value to change and the compiler has no way of knowing this). */
823 :
824 119671748 : #define FD_VOLATILE(x) (*((volatile __typeof__((x)) *)&(x)))
825 :
826 : /* FD_ATOMIC_FETCH_AND_{ADD,SUB,OR,AND,XOR}(p,v):
827 :
828 : FD_ATOMIC_FETCH_AND_ADD(p,v) does
829 : f = *p;
830 : *p = f + v
831 : return f;
832 : as a single atomic operation. Similarly for the other variants. */
833 :
834 3632104 : #define FD_ATOMIC_FETCH_AND_ADD(p,v) __sync_fetch_and_add( (p), (v) )
835 4110418 : #define FD_ATOMIC_FETCH_AND_SUB(p,v) __sync_fetch_and_sub( (p), (v) )
836 376965 : #define FD_ATOMIC_FETCH_AND_OR( p,v) __sync_fetch_and_or( (p), (v) )
837 1105820 : #define FD_ATOMIC_FETCH_AND_AND(p,v) __sync_fetch_and_and( (p), (v) )
838 : #define FD_ATOMIC_FETCH_AND_XOR(p,v) __sync_fetch_and_xor( (p), (v) )
839 :
840 : /* FD_ATOMIC_{ADD,SUB,OR,AND,XOR}_AND_FETCH(p,v):
841 :
842 : FD_ATOMIC_{ADD,SUB,OR,AND,XOR}_AND_FETCH(p,v) does
843 : r = *p + v;
844 : *p = r;
845 : return r;
846 : as a single atomic operation. Similarly for the other variants. */
847 :
848 >1844*10^16 : #define FD_ATOMIC_ADD_AND_FETCH(p,v) __sync_add_and_fetch( (p), (v) )
849 0 : #define FD_ATOMIC_SUB_AND_FETCH(p,v) __sync_sub_and_fetch( (p), (v) )
850 : #define FD_ATOMIC_OR_AND_FETCH( p,v) __sync_or_and_fetch( (p), (v) )
851 : #define FD_ATOMIC_AND_AND_FETCH(p,v) __sync_and_and_fetch( (p), (v) )
852 : #define FD_ATOMIC_XOR_AND_FETCH(p,v) __sync_xor_and_fetch( (p), (v) )
853 :
854 : /* FD_ATOMIC_CAS(p,c,s):
855 :
856 : o = FD_ATOMIC_CAS(p,c,s) conceptually does:
857 : o = *p;
858 : if( o==c ) *p = s;
859 : return o
860 : as a single atomic operation. */
861 :
862 1240398 : #define FD_ATOMIC_CAS(p,c,s) __sync_val_compare_and_swap( (p), (c), (s) )
863 :
864 : /* FD_ATOMIC_XCHG(p,v):
865 :
866 : o = FD_ATOMIC_XCHG( p, v ) conceptually does:
867 : o = *p
868 : *p = v
869 : return o
870 : as a single atomic operation. */
871 :
872 0 : #define FD_ATOMIC_XCHG(p,v) __atomic_exchange_n( (p), (v), __ATOMIC_SEQ_CST )
873 :
874 : /* FD_TL: This indicates that the variable should be thread local.
875 :
876 : FD_ONCE_{BEGIN,END}: The block:
877 :
878 : FD_ONCE_BEGIN {
879 : ... code ...
880 : } FD_ONCE_END
881 :
882 : linguistically behaves like:
883 :
884 : do {
885 : ... code ...
886 : } while(0)
887 :
888 : But provides a low overhead guarantee that:
889 : - The block will be executed at most once over all threads
890 : in a process (i.e. the set of threads which share global
891 : variables).
892 : - No thread in a process that encounters the block will continue
893 : past it until it has executed once.
894 :
895 : This implies that caller promises a ONCE block will execute in a
896 : finite time. (Meant for doing simple lightweight initializations.)
897 :
898 : It is okay to nest ONCE blocks. The thread that executes the
899 : outermost will execute all the nested once as part of executing the
900 : outermost.
901 :
902 : A ONCE implicitly provides a compiler memory fence to reduce the risk
903 : that the compiler will assume that operations done in the once block
904 : on another thread have not been done (e.g. propagating pre-once block
905 : variable values into post-once block code). It is up to the user to
906 : provide any necessary hardware fencing (usually not necessary).
907 :
908 : FD_THREAD_ONCE_{BEGIN,END}: The block:
909 :
910 : FD_THREAD_ONCE_BEGIN {
911 : ... code ...
912 : } FD_THREAD_ONCE_END;
913 :
914 : is similar except the guarantee is that the block only covers the
915 : invoking thread and it does not provide any fencing. If a thread
916 : once begin is nested inside a once begin, that thread once begin will
917 : only be executed on the thread that executes the thread once begin.
918 : It is similarly okay to nest ONCE block inside a THREAD_ONCE block.
919 :
920 : FD_TURNSTILE_{BEGIN,BLOCKED,END} implement a turnstile for all
921 : threads in a process. Only one thread can be in the turnstile at a
922 : time. Usage:
923 :
924 : FD_TURNSTILE_BEGIN(blocking) {
925 :
926 : ... At this point, we are the only thread executing this block of
927 : ... code.
928 : ...
929 : ... Do operations that must be done by threads one-at-a-time
930 : ... here.
931 : ...
932 : ... Because compiler memory fences are done just before entering
933 : ... and after exiting this block, there is typically no need to
934 : ... use any atomics / volatile / fencing here. That is, we can
935 : ... just write "normal" code on platforms where writes to memory
936 : ... become visible to other threads in the order in which they
937 : ... were issued in the machine code (e.g. x86) as others will not
938 : ... proceed with this block until they exit it. YMMV for non-x86
939 : ... platforms (probably need additional hardware store fences in
940 : ... these macros).
941 : ...
942 : ... It is safe to use "break" and/or "continue" within this
943 : ... block. The block will exit with the appropriate compiler
944 : ... fencing and unlocking. Execution will resume immediately
945 : ... after FD_TURNSTILE_END.
946 :
947 : ... IMPORTANT SAFETY TIP! DO NOT RETURN FROM THIS BLOCK.
948 :
949 : } FD_TURNSTILE_BLOCKED {
950 :
951 : ... At this point, there was another thread in the turnstile when
952 : ... we tried to enter the turnstile.
953 : ...
954 : ... Handle blocked here.
955 : ...
956 : ... On exiting this block, if blocking was zero, we will resume
957 : ... execution immediately after FD_TURNSTILE_END. If blocking
958 : ... was non-zero, we will resume execution immediately before
959 : ... FD_TURNSTILE_BEGIN (e.g. we will retry again after a short
960 : ... spin pause).
961 : ...
962 : ... It is safe to use "break" and/or "continue" within this
963 : ... block. Both will exit this block and resume execution
964 : ... at the location indicated as per what blocking specified
965 : ... when the turnstile was entered.
966 : ...
967 : ... It is technically safe to return from this block but
968 : ... also extremely gross.
969 :
970 : } FD_TURNSTILE_END; */
971 :
972 : #if FD_HAS_THREADS /* Potentially more than one thread in the process */
973 :
974 : #ifndef FD_TL
975 : #define FD_TL __thread
976 : #endif
977 :
978 2 : #define FD_ONCE_BEGIN do { \
979 2 : FD_COMPILER_MFENCE(); \
980 2 : static volatile int _fd_once_block_state = 0; \
981 2 : for(;;) { \
982 2 : int _fd_once_block_tmp = _fd_once_block_state; \
983 2 : if( FD_LIKELY( _fd_once_block_tmp>0 ) ) break; \
984 2 : if( FD_LIKELY( !_fd_once_block_tmp ) && \
985 1 : FD_LIKELY( !FD_ATOMIC_CAS( &_fd_once_block_state, 0, -1 ) ) ) { \
986 1 : do
987 :
988 : #define FD_ONCE_END \
989 1 : while(0); \
990 1 : FD_COMPILER_MFENCE(); \
991 1 : _fd_once_block_state = 1; \
992 1 : break; \
993 1 : } \
994 1 : FD_YIELD(); \
995 0 : } \
996 2 : } while(0)
997 :
998 0 : #define FD_THREAD_ONCE_BEGIN do { \
999 0 : static FD_TL int _fd_thread_once_block_state = 0; \
1000 0 : if( FD_UNLIKELY( !_fd_thread_once_block_state ) ) { \
1001 0 : do
1002 :
1003 : #define FD_THREAD_ONCE_END \
1004 0 : while(0); \
1005 0 : _fd_thread_once_block_state = 1; \
1006 0 : } \
1007 0 : } while(0)
1008 :
1009 0 : #define FD_TURNSTILE_BEGIN(blocking) do { \
1010 0 : static volatile int _fd_turnstile_state = 0; \
1011 0 : int _fd_turnstile_blocking = (blocking); \
1012 0 : for(;;) { \
1013 0 : int _fd_turnstile_tmp = _fd_turnstile_state; \
1014 0 : if( FD_LIKELY( !_fd_turnstile_tmp ) && \
1015 0 : FD_LIKELY( !FD_ATOMIC_CAS( &_fd_turnstile_state, 0, 1 ) ) ) { \
1016 0 : FD_COMPILER_MFENCE(); \
1017 0 : do
1018 :
1019 : #define FD_TURNSTILE_BLOCKED \
1020 0 : while(0); \
1021 0 : FD_COMPILER_MFENCE(); \
1022 0 : _fd_turnstile_state = 0; \
1023 0 : FD_COMPILER_MFENCE(); \
1024 0 : break; \
1025 0 : } \
1026 0 : FD_COMPILER_MFENCE(); \
1027 0 : do
1028 :
1029 : #define FD_TURNSTILE_END \
1030 0 : while(0); \
1031 0 : FD_COMPILER_MFENCE(); \
1032 0 : if( !_fd_turnstile_blocking ) break; /* likely compile time */ \
1033 0 : FD_SPIN_PAUSE(); \
1034 0 : } \
1035 0 : } while(0)
1036 :
1037 : #else /* Only one thread in the process */
1038 :
1039 : #ifndef FD_TL
1040 : #define FD_TL /**/
1041 : #endif
1042 :
1043 : #define FD_ONCE_BEGIN do { \
1044 : static int _fd_once_block_state = 0; \
1045 : if( FD_UNLIKELY( !_fd_once_block_state ) ) { \
1046 : do
1047 :
1048 : #define FD_ONCE_END \
1049 : while(0); \
1050 : _fd_once_block_state = 1; \
1051 : } \
1052 : } while(0)
1053 :
1054 : #define FD_THREAD_ONCE_BEGIN FD_ONCE_BEGIN
1055 : #define FD_THREAD_ONCE_END FD_ONCE_END
1056 :
1057 : #define FD_TURNSTILE_BEGIN(blocking) do { \
1058 : (void)(blocking); \
1059 : FD_COMPILER_MFENCE(); \
1060 : if( 1 ) { \
1061 : do
1062 :
1063 : #define FD_TURNSTILE_BLOCKED \
1064 : while(0); \
1065 : } else { \
1066 : do
1067 :
1068 : #define FD_TURNSTILE_END \
1069 : while(0); \
1070 : } \
1071 : FD_COMPILER_MFENCE(); \
1072 : } while(0)
1073 :
1074 : #endif
1075 :
1076 : /* An ideal fd_clock_func_t is a function such that:
1077 :
1078 : long dx = clock( args );
1079 : ... stuff ...
1080 : dx = clock( args ) - dx;
1081 :
1082 : yields a strictly positive dx where dx approximates the amount of
1083 : wallclock time elapsed on the caller in some clock specific unit
1084 : (e.g. nanoseconds, CPU ticks, etc) for a reasonable amount of "stuff"
1085 : (including no "stuff"). args allows arbitrary clock specific context
1086 : to be passed to the clock implementation. (clocks that need a
1087 : non-const args can cast away the const in the implementation or cast
1088 : the function pointer as necessary.) */
1089 :
1090 : typedef long (*fd_clock_func_t)( void const * args );
1091 :
1092 : FD_PROTOTYPES_BEGIN
1093 :
1094 : /* fd_memcpy(d,s,sz): On modern x86 in some circumstances, rep mov will
1095 : be faster than memcpy under the hood (basically due to RFO /
1096 : read-for-ownership optimizations in the cache protocol under the hood
1097 : that aren't easily done from the ISA ... see Intel docs on enhanced
1098 : rep mov). Compile time configurable though as this is not always
1099 : true. So application can tune to taste. Hard to beat rep mov for
1100 : code density though (2 bytes) and pretty hard to beat in situations
1101 : needing a completely generic memcpy. But it can be beaten in
1102 : specialized situations for the usual reasons. */
1103 :
1104 : /* FIXME: CONSIDER MEMCMP TOO! */
1105 : /* FIXME: CONSIDER MEMCPY RELATED FUNC ATTRS */
1106 :
1107 : #ifndef FD_USE_ARCH_MEMCPY
1108 : #define FD_USE_ARCH_MEMCPY 0
1109 : #endif
1110 :
1111 : #if FD_HAS_X86 && FD_USE_ARCH_MEMCPY && !defined(CBMC) && !FD_HAS_DEEPASAN && !FD_HAS_MSAN
1112 :
1113 : static inline void *
1114 : fd_memcpy( void * FD_RESTRICT d,
1115 : void const * FD_RESTRICT s,
1116 37580450 : ulong sz ) {
1117 37580450 : void * p = d;
1118 37580450 : __asm__ __volatile__( "rep movsb" : "+D" (p), "+S" (s), "+c" (sz) :: "memory" );
1119 37580450 : return d;
1120 37580450 : }
1121 :
1122 : #elif FD_HAS_MSAN
1123 :
1124 : void * __msan_memcpy( void * dest, void const * src, ulong n );
1125 :
1126 : static inline void *
1127 : fd_memcpy( void * FD_RESTRICT d,
1128 : void const * FD_RESTRICT s,
1129 : ulong sz ) {
1130 : return __msan_memcpy( d, s, sz );
1131 : }
1132 :
1133 : #else
1134 :
1135 : static inline void *
1136 : fd_memcpy( void * FD_RESTRICT d,
1137 : void const * FD_RESTRICT s,
1138 : ulong sz ) {
1139 : #if defined(CBMC) || FD_HAS_ASAN || FD_HAS_UBSAN
1140 : if( FD_UNLIKELY( !sz ) ) return d; /* Standard says sz 0 is UB, uncomment if target is insane and doesn't treat sz 0 as a nop */
1141 : #endif
1142 : return memcpy( d, s, sz );
1143 : }
1144 :
1145 : #endif
1146 :
1147 : /* fd_memset(d,c,sz): architecturally optimized memset. See fd_memcpy
1148 : for considerations. */
1149 :
1150 : /* FIXME: CONSIDER MEMSET RELATED FUNC ATTRS */
1151 :
1152 : #ifndef FD_USE_ARCH_MEMSET
1153 : #define FD_USE_ARCH_MEMSET 0
1154 : #endif
1155 :
1156 : #if FD_HAS_X86 && FD_USE_ARCH_MEMSET && !defined(CBMC) && !FD_HAS_DEEPASAN && !FD_HAS_MSAN
1157 :
1158 : static inline void *
1159 : fd_memset( void * d,
1160 : int c,
1161 773721 : ulong sz ) {
1162 773721 : void * p = d;
1163 773721 : __asm__ __volatile__( "rep stosb" : "+D" (p), "+c" (sz) : "a" (c) : "memory" );
1164 773721 : return d;
1165 773721 : }
1166 :
1167 : #else
1168 :
1169 : static inline void *
1170 : fd_memset( void * d,
1171 : int c,
1172 : ulong sz ) {
1173 : # ifdef CBMC
1174 : if( FD_UNLIKELY( !sz ) ) return d; /* See fd_memcpy note */
1175 : # endif
1176 : return memset( d, c, sz );
1177 : }
1178 :
1179 : #endif
1180 :
1181 : /* Calling fd_memzero_explicit will fill the provided region with zeroes.
1182 : It is guaranteed to not be optimized away. */
1183 : FD_FN_UNUSED static inline void
1184 : fd_memzero_explicit( void * d,
1185 0 : ulong sz ) {
1186 : /* We don't want to depend on explicit_bzero or memset_s, so the simplest
1187 : way to ensure the memset is not optimized away is to use a compiler fence,
1188 : identical to how explicit_bzero is implemented.
1189 : https://elixir.bootlin.com/glibc/glibc-2.40/source/string/explicit_bzero.c#L33 */
1190 0 : memset( d, 0, sz );
1191 0 : __asm__ __volatile__( "" ::: "memory" );
1192 0 : }
1193 :
1194 : /* fd_memeq(s0,s1,sz): Compares two blocks of memory. Returns 1 if
1195 : equal or sz is zero and 0 otherwise. No memory accesses made if sz
1196 : is zero (pointers may be invalid). On x86, uses repe cmpsb which is
1197 : preferable to __builtin_memcmp in some cases. */
1198 :
1199 : #ifndef FD_USE_ARCH_MEMEQ
1200 : #define FD_USE_ARCH_MEMEQ 0
1201 : #endif
1202 :
1203 : #if FD_HAS_X86 && FD_USE_ARCH_MEMEQ && defined(__GCC_ASM_FLAG_OUTPUTS__) && __STDC_VERSION__>=199901L
1204 :
1205 : FD_FN_PURE static inline int
1206 : fd_memeq( void const * s0,
1207 : void const * s1,
1208 : ulong sz ) {
1209 : /* ZF flag is set and exported in two cases:
1210 : a) size is zero (via test)
1211 : b) buffer is equal (via repe cmpsb) */
1212 : int r;
1213 : __asm__( "test %3, %3;"
1214 : "repe cmpsb"
1215 : : "=@cce" (r), "+S" (s0), "+D" (s1), "+c" (sz)
1216 : : "m" (*(char const (*)[sz]) s0), "m" (*(char const (*)[sz]) s1)
1217 : : "cc" );
1218 : return r;
1219 : }
1220 :
1221 : #else
1222 :
1223 : FD_FN_PURE static inline int
1224 : fd_memeq( void const * s1,
1225 : void const * s2,
1226 565710 : ulong sz ) {
1227 565710 : return 0==memcmp( s1, s2, sz );
1228 565710 : }
1229 :
1230 : #endif
1231 :
1232 : /* Returns 1 if all sz bytes starting at s are zero, 0 otherwise. */
1233 : FD_FN_PURE static inline int
1234 : fd_mem_iszero( uchar const * s,
1235 8 : ulong sz ) {
1236 8 : for( ulong i=0UL; i<sz; i++ ) {
1237 8 : if( s[i]!=0 ) return 0;
1238 8 : }
1239 0 : return 1;
1240 8 : }
1241 :
1242 : /* fd_hash(seed,buf,sz), fd_hash_memcpy(seed,d,s,sz): High quality
1243 : (full avalanche) high speed variable length buffer -> 64-bit hash
1244 : function (memcpy_hash is often as fast as plain memcpy). Based on
1245 : the xxhash-r39 (open source BSD licensed) implementation. In-place
1246 : and out-of-place variants provided (out-of-place variant assumes dst
1247 : and src do not overlap). Caller promises valid input arguments,
1248 : cannot fail given valid input arguments. sz==0 is fine. */
1249 :
1250 : FD_FN_PURE ulong
1251 : fd_hash( ulong seed,
1252 : void const * buf,
1253 : ulong sz );
1254 :
1255 : ulong
1256 : fd_hash_memcpy( ulong seed,
1257 : void * FD_RESTRICT d,
1258 : void const * FD_RESTRICT s,
1259 : ulong sz );
1260 :
1261 : #ifndef FD_TICKCOUNT_STYLE
1262 : #if FD_HAS_X86 /* Use RDTSC */
1263 : #define FD_TICKCOUNT_STYLE 1
1264 : #elif FD_HAS_ARM /* Use CNTVCT_EL0 */
1265 : #define FD_TICKCOUNT_STYLE 2
1266 : #elif FD_HAS_RISCV /* Uses shared timebase instead of CPU cycles. */
1267 : #define FD_TICKCOUNT_STYLE 3
1268 : #else /* Use portable fallback */
1269 : #define FD_TICKCOUNT_STYLE 0
1270 : #endif
1271 : #endif
1272 :
1273 : #if FD_TICKCOUNT_STYLE==0 /* Portable fallback (slow). Ticks at 1 ns / tick */
1274 :
1275 : #define fd_tickcount() fd_log_wallclock() /* TODO: fix ugly pre-log usage */
1276 :
1277 : #elif FD_TICKCOUNT_STYLE==1 /* RDTSC (fast) */
1278 :
1279 : /* fd_tickcount: Reads the hardware invariant tickcounter ("RDTSC").
1280 : This monotonically increases at an approximately constant rate
1281 : relative to the system wallclock and is synchronous across all CPUs
1282 : on a host.
1283 :
1284 : The rate this ticks at is not precisely defined (see Intel docs for
1285 : more details) but it is typically in the ballpark of the CPU base
1286 : clock frequency. The relationship to the wallclock is very well
1287 : approximated as linear over short periods of time (i.e. less than a
1288 : fraction of a second) and this should not exhibit any sudden changes
1289 : in its rate relative to the wallclock. Notably, its rate is not
1290 : directly impacted by CPU clock frequency adaptation / Turbo mode (see
1291 : other Intel performance monitoring counters for various CPU cycle
1292 : counters). It can drift over longer periods of time for the usual
1293 : clock synchronization reasons.
1294 :
1295 : This is a reasonably fast O(1) cost (~6-8 ns on recent Intel).
1296 : Because of all compiler options and parallel execution going on in
1297 : modern CPUs cores, other instructions might be reordered around this
1298 : by the compiler and/or CPU. It is up to the user to do lower level
1299 : tricks as necessary when the precise location of this in the
1300 : execution stream and/or when executed by the CPU is needed. (This is
1301 : often unnecessary as such levels of precision are not frequently
1302 : required and often have self-defeating overheads.)
1303 :
1304 : It is worth noting that RDTSC and/or (even more frequently) lower
1305 : level performance counters are often restricted from use in user
1306 : space applications. It is recommended that applications use this
1307 : primarily for debugging / performance tuning on unrestricted hosts
1308 : and/or when the developer is confident that applications using this
1309 : will have appropriate permissions when deployed. */
1310 :
1311 58690 : #define fd_tickcount() ((long)__builtin_ia32_rdtsc())
1312 :
1313 : #elif FD_TICKCOUNT_STYLE==2 /* armv8 (fast) */
1314 :
1315 : /* fd_tickcount (ARM): https://developer.arm.com/documentation/ddi0601/2021-12/AArch64-Registers/CNTVCT-EL0--Counter-timer-Virtual-Count-register
1316 : Approx 24 MHz on Apple M1. */
1317 :
1318 : static inline long
1319 : fd_tickcount( void ) {
1320 : /* consider using 'isb' */
1321 : ulong value;
1322 : __asm__ __volatile__ (
1323 : "mrs %0, cntvct_el0\n"
1324 : "nop"
1325 : : "=r" (value) );
1326 : return (long)value;
1327 : }
1328 :
1329 : #elif FD_TICKCOUNT_STYLE==3 /* RV64 time CSR */
1330 :
1331 : /* The time CSR is a platform timebase shared across harts, which is
1332 : independent of core frequency. Unlike cycle, it is suitable for
1333 : migration between heterogeneous cores. The kernel must permit
1334 : user-mode reads. Select FD_TICKCOUNT_STYLE=0 on systems that do
1335 : not allow it (and trap on rdtime).
1336 :
1337 : We may not assume a frequency or nanoseconds per tick, tempo calibrate
1338 : it against wallclock time. */
1339 :
1340 : #if !defined(__riscv) || (__riscv_xlen!=64)
1341 : #error "FD_TICKCOUNT_STYLE=3 requires RV64"
1342 : #endif
1343 :
1344 : static inline long
1345 : fd_tickcount( void ) {
1346 : ulong value;
1347 : __asm__ __volatile__( "rdtime %0" : "=r" (value) );
1348 : return (long)value;
1349 : }
1350 :
1351 : #else
1352 : #error "Unknown FD_TICKCOUNT_STYLE"
1353 : #endif
1354 :
1355 : long _fd_tickcount( void const * _ ); /* fd_clock_func_t compat */
1356 :
1357 : #if FD_HAS_HOSTED
1358 :
1359 : /* fd_yield yields the calling thread to the operating system scheduler. */
1360 :
1361 : void
1362 : fd_yield( void );
1363 :
1364 : #endif
1365 :
1366 : #if FD_HAS_ARM
1367 :
1368 : /* fd_arm_stp16 stores two ulongs to a 16-byte memory location.
1369 : If LSE2 and p is aligned, is single-copy atomic. */
1370 :
1371 : static inline void
1372 : fd_arm_stp16( ulong * p,
1373 : ulong a,
1374 : ulong b ) {
1375 : __asm__(
1376 : "stp %x[a], %x[b], [%[p]]"
1377 : :
1378 : : [a] "r"(a), [b] "r"(b), [p] "r"(p)
1379 : : "memory"
1380 : );
1381 : }
1382 :
1383 : /* fd_arm_ldp16 loads two ulongs from a 16-byte memory location.
1384 : If LSE2 and p is aligned, is single-copy atomic. */
1385 :
1386 : #define fd_arm_ldp16(p_,a_,b_) \
1387 : __asm__( \
1388 : "ldp %x[a], %x[b], [%[p]]" \
1389 : : [a] "=r"(a_), [b] "=r"(b_) \
1390 : : [p] "r"(p_) \
1391 : : "memory" \
1392 : )
1393 :
1394 : #endif /* FD_HAS_ARM */
1395 :
1396 : FD_PROTOTYPES_END
1397 :
1398 : #endif /* HEADER_fd_src_util_fd_util_base_h */
|