Dropped LFS_ASSERT as a compiler hint

I realized the reason asserting on opened/closed file handles added so
much extra code was because our LFS_ASSERT macro doesn't properly
eliminate side-effects.

Consider this assert:

  LFS_ASSERT(lfsr_opened_isopen(lfs, &dir->o));

Expanded:

  ((lfsr_opened_isopen(lfs, &dir->o))
      ? (void)0
      : __builtin_unreachable());

Even though the compiler knows lfsr_opened_isopen must return true
here, it doesn't know what possible side-effects calling
lfsr_opened_isopen may have, and can't eliminate the function call.

This is quite a bit more obvious if you did something like:

  LFS_ASSERT(lfsr_file_sync(&lfs, &file) == 0);

But since lfsr_opened_isopen is a static inline function, it gets a
little bit less clear. Even worse, whether or not the call is eliminated
probably depends if it's actually inlined and other compiler
optimization noise.

---

This commit effectively reverts LFS_ASSERT as a compiler hint, making
LFS_ASSERT an empty string if LFS_NO_ASSERT is defined.

This may not be the optimal solution, but it at least keeps the
programmer's intuition that anything in LFS_ASSERT has zero impact when
asserts are disabled. It would be nice if there was some way to tell the
compiler that an expression should have no side-effects, but as far as
I'm aware this is not currently possible.

Measurements show that just disabling asserts wins in both code and
stack over trying to leverage asserts-as-hints:

                         code          stack
  hint-assert (before): 33904           2584
  no-assert (after):    33626 (-0.8%)   2552 (-1.2%)

At least both of these win over leaving asserts enabled, so
asserts-as-hints does eliminate _most_ code (measured here
with a simple assert-loop, since I assume that would have the smallest
code footprint):

                         code          stack
  loop-assert:          36874           2616
  hint-assert (before): 33904 (-8.1%)   2584 (-1.2%)
  no-assert (after):    33626 (-8.8%)   2552 (-2.4%)

Unfortunately asserts-as-hints was doing quite a bit of heavy lifting
at preventing overzealous GCC warnings. It took quite a few tweaks to
get GCC to shut up, and I'm still not entirely sure the best way to tell
GCC that some functions only return negative values. Currently I just
limit certain error checks to only check for negative values, which is
not great, but at least gets the code compiling again...
This commit is contained in:
Christopher Haster
2024-06-09 22:09:20 -05:00
parent fd41d296db
commit b563050fc8
2 changed files with 27 additions and 26 deletions
-2
View File
@@ -96,8 +96,6 @@ extern "C"
#ifndef LFS_ASSERT
#ifndef LFS_NO_ASSERT
#define LFS_ASSERT(test) assert(test)
#elif !defined(LFS_NO_BUILTINS)
#define LFS_ASSERT(test) ((test) ? (void)0 : __builtin_unreachable())
#else
#define LFS_ASSERT(test)
#endif