diff --git a/src/rng/crand.c b/src/rng/crand.c index 7574a5b..245eac4 100644 --- a/src/rng/crand.c +++ b/src/rng/crand.c @@ -44,8 +44,9 @@ inline static unsigned short* IFUNC_IMPL(_jr_st_resolv, high) (jr_xsub_t* restri return state->xsubi+1; } -inline static __attribute__((gnu_inline, const)) int _resv_is_high() +inline static __attribute__((artificial, always_inline, gnu_inline, const)) int _resv_is_high() { + // This being `static const` initialised makes this function be seen as a proper constant expression. Nothing is leaked on to the stack of the caller and the function is replaced with a single `lea`. static const struct jr_state chk = { .st.xsubl = JR_MAX, }; @@ -62,8 +63,17 @@ inline static unsigned short* IFUNC_RESOLVER(_jr_st_resolv) (jr_xsub_t* restrict // The ifunc `_jr_st_resolv()` is essentially (almost) the same as a symbol-aliased constexpr function. // Ain't that neat? +#ifdef _RNGXX_JR_RESOLV_IFUNC_OLD_STACKDYN_CHECK + // The old, dynamic stack alloc of the struct, instead of putting it in global r/o data struct jr_state chk = {0}; - chk.st.xsubh = JR_MAX; + chk.st.xsubl = JR_MAX; +#else + // The new, static const alloc of the struct, puts the value in at compile-time. Better optimisation opportunities. + // NOTE: Both stratergies make the ifunc resolve its target at compile-time, there is no difference in this ifunc resolver between the two. However, in `_resv_is_high()`, the old (this) stratergy prevents it being recognised as a constant-expression and stack-allocates the useless memory in the function's (macro-expanded) caller. Causing a spill of useless instructions that the new method changes to one `lea'. + static const struct jr_state chk = { + .st.xsubl = JR_MAX, + }; +#endif return chk.st._xsub ? & IFUNC_NAME(_jr_st_resolv, high) : & IFUNC_NAME(_jr_st_resolv, low);