Unified attr-list context into little attr arenas

The idea is for cases where we need to incrementally allocate attrs +
context, to allocate from both sides of a statically allocated attr
array. This keeps all of the attr-list state in one place, simplifying
state allocation:

  .---+---+---+---.
  |      attr     |
  +---+---+---+---+
  |      attr ----------.
  +---+---+---+---+     |
  |      attr --------. |
  +---+---+---+---+   | |
  |       |       |   | |
  |       v       |   | |
  |               |   | |
  |       ^       |   | |
  |       |       |   | |
  +---+---+---+---+   | |
  |      data     | <-' |
  +---+---+---+---+     |
  |  encoded bptr | <---'
  '---+---+---+---'

This is especially useful for the non-terminating tail-recursive
lfsr_btree_commit_, which needs to pass this state through a function
call.

Unfortunately, to make this work we needed to implement more-or-less a
full arena allocator, complete with annoying alignment handling. alignof
isn't even available in C99, so we needed a few more intrinsics:

- LFS_ALIGNOF(t)       - Alignment of type t
- LFS_ALIGNEDSIZEOF(t) - Necessary size to force alignment for t
- LFS_MIN(a, b)        - Compile-time min
- LFS_MAX(a, b)        - Compile-time max

Technically only LFS_ALIGNOF was required, but the others are nice to
have. LFS_MIN/LFS_MAX is also useful anywhere you need to calculate
complicated compile-time sizes.

At least in C11 we get alignof, so we won't need compiler extensions/
hacks for this in the future...

---

Unfortunately this ended up a net-negative. Pushing up the code/stack
cost to near pre-cat levels:

                   code          stack
  before cat:     33856           2824
  before scratch: 33812 (-0.1%)   2800 (-0.8%)
  after:          33844 (-0.0%)   2824 (+0.0%)

I think the two main culprits are 1. the extra logic needed to calculate
alignment, and 2. wasted stack due to aligning scratch space up to the
nearest lfsr_attr_t.
This commit is contained in:
Christopher Haster
2024-05-05 11:50:59 -05:00
parent 88a098c616
commit 8f3036f1e5
2 changed files with 122 additions and 86 deletions
+22 -8
View File
@@ -142,16 +142,16 @@ extern "C"
// toolchain-specific implementations. LFS_NO_INTRINSICS falls back to a more
// expensive basic C implementation for debugging purposes
// Min/max functions for unsigned 32-bit numbers
static inline uint32_t lfs_max(uint32_t a, uint32_t b) {
return (a > b) ? a : b;
}
// Compile time min/max
#define LFS_MIN(a, b) ((a < b) ? a : b)
#define LFS_MAX(a, b) ((a > b) ? a : b)
// Min/max functions for unsigned 32-bit numbers
static inline uint32_t lfs_min(uint32_t a, uint32_t b) {
return (a < b) ? a : b;
}
static inline uint32_t lfs_max32(uint32_t a, uint32_t b) {
static inline uint32_t lfs_max(uint32_t a, uint32_t b) {
return (a > b) ? a : b;
}
@@ -159,7 +159,7 @@ static inline uint32_t lfs_min32(uint32_t a, uint32_t b) {
return (a < b) ? a : b;
}
static inline int32_t lfs_smax32(int32_t a, int32_t b) {
static inline uint32_t lfs_max32(uint32_t a, uint32_t b) {
return (a > b) ? a : b;
}
@@ -167,15 +167,19 @@ static inline int32_t lfs_smin32(int32_t a, int32_t b) {
return (a < b) ? a : b;
}
// TODO other 16-bit ops?
static inline uint16_t lfs_max16(uint16_t a, uint16_t b) {
static inline int32_t lfs_smax32(int32_t a, int32_t b) {
return (a > b) ? a : b;
}
// TODO other 16-bit ops?
static inline uint16_t lfs_min16(uint16_t a, uint16_t b) {
return (a < b) ? a : b;
}
static inline uint16_t lfs_max16(uint16_t a, uint16_t b) {
return (a > b) ? a : b;
}
// Clamp is useful as the logic for min/max when clamping can become confusing
static inline uint32_t lfs_clamp32(uint32_t a, uint32_t min, uint32_t max) {
return lfs_min32(lfs_max32(a, min), max);
@@ -217,6 +221,16 @@ static inline void lfs_sswap32(int32_t *a, int32_t *b) {
*b = t;
}
// Find alignment of a type at compile time
#if !defined(LFS_NO_INTRINSICS)
#define LFS_ALIGNOF(t) __alignof__(t)
#else
#define LFS_ALIGNOF(t) ((size_t)&((struct {char a; t b;}*)0)->b)
#endif
// Find size necessary to align type at compile time
#define LFS_ALIGNEDSIZEOF(t) (sizeof(t) + LFS_ALIGNOF(t)-1)
// Align to nearest multiple of a size
static inline uint32_t lfs_aligndown(uint32_t a, uint32_t alignment) {
return a - (a % alignment);