Attempted another implementation of crammed lfsr_attr_ts

The idea here was to carve out explicit space for the lfsr_attr_t tag in
lfsr_cat_t, so that when we create lfsr_attr_ts we don't need multiple
references to lfsr_cat_t and can revert back to a macro:

  lfsr_tag_t               lfsr_attr_t
  .---+---.                .---+---+---+---. . . .---+---+---+---.
  |  tag  |-------. .----->|     delta     |     |     delta     |
  '---+---'       | |      +---+---+---+---+     +---+---+---+---+
                  '-|-+--->|  tag  |c|size |     |      cat      |
  lfsr_srid_t       | |    +---+---+---+---+     |               |
  .---+---+---+---. | | .->|      ptr      |     |               |
  |     delta     |-' | |  '---+---+---+---' . . '---+---+---+---'
  '---+---+---+---'   | |
                      | |
  lfsr_cat_t          | |
  .---+---+---+---.   | |
  | (tag) |c|size |---' |
  +---+---+---+---+     |
  |      ptr      |-----'
  '---+---+---+---'

But as a part of creating the lfsr_attr_t we need to mutate the tag in
lfsr_cat_t, which ended up still needing a static inline function, and
ended up making the code/stack size even worse!

           code          stack
  before: 33900           2736
  after:  34486 (+1.7%)   2912 (+6.4%)

I was hoping to leverage the last-update rule of C99's designated
initializers, but this weird rule didn't quite work how I expected. The
compiler is free to omit earlier struct initializers, even if a later
initializer only partially initializes the struct.
This commit is contained in:
Christopher Haster
2024-05-07 17:56:10 -05:00
parent 2ee6750f2e
commit 7e4d8c1df2
+67 -69
View File
@@ -1416,12 +1416,13 @@ typedef struct lfsr_cat {
// sign(size)=0 => single in-RAM buffer
// sign(size)=1 => multiple concatenated datas
union {
uint16_t size;
struct {
lfsr_tag_t tag;
uint16_t size;
const uint8_t *buffer;
} buf;
struct {
lfsr_tag_t tag;
uint16_t size;
const lfsr_data_t *datas;
} cat;
@@ -1452,11 +1453,11 @@ typedef struct lfsr_cat {
// cat helpers
static inline bool lfsr_cat_isbuf(lfsr_cat_t cat) {
return !(cat.u.size & LFSR_CAT_ISCAT);
return !(cat.u.buf.size & LFSR_CAT_ISCAT);
}
static inline bool lfsr_cat_iscat(lfsr_cat_t cat) {
return cat.u.size & LFSR_CAT_ISCAT;
return cat.u.buf.size & LFSR_CAT_ISCAT;
}
static inline lfsr_data_t lfsr_cat_data(lfsr_cat_t cat) {
@@ -1467,7 +1468,7 @@ static inline lfsr_data_t lfsr_cat_data(lfsr_cat_t cat) {
static inline uint16_t lfsr_cat_count(lfsr_cat_t cat) {
LFS_ASSERT(lfsr_cat_iscat(cat));
return cat.u.size & ~LFSR_CAT_ISCAT;
return cat.u.buf.size & ~LFSR_CAT_ISCAT;
}
static inline lfs_size_t lfsr_cat_size(lfsr_cat_t cat) {
@@ -1566,42 +1567,29 @@ static int lfsr_bd_progcat(lfs_t *lfs,
typedef struct lfsr_shrubcommit lfsr_shrubcommit_t;
typedef struct lfsr_attr {
lfsr_tag_t tag;
uint16_t size;
lfsr_srid_t delta;
// sign(size)=0 => single in-RAM buffer
// sign(size)=1 => multiple concatenated datas
// special tags => other types
union {
const uint8_t *buffer;
const lfsr_data_t *datas;
lfsr_grm_t *grm;
const lfsr_mdir_t *mdir;
const lfsr_shrubcommit_t *shrubcommit;
const lfsr_shrub_t *shrub;
lfsr_tag_t tag;
lfsr_cat_t cat;
} u;
} lfsr_attr_t;
#define LFSR_ATTR(_tag, _delta, _cat) \
lfsr_attr(_tag, _delta, _cat)
((lfsr_attr_t){ \
.delta=_delta, \
.u.cat=lfsr_cat_tag(_cat, _tag)})
static inline lfsr_attr_t lfsr_attr(
lfsr_tag_t tag, lfsr_srid_t delta, lfsr_cat_t cat) {
return (lfsr_attr_t){
.tag=tag,
.size=cat.u.cat.size,
.delta=delta,
.u.datas=cat.u.cat.datas};
static inline lfsr_cat_t lfsr_cat_tag(lfsr_cat_t cat, lfsr_tag_t tag) {
cat.u.buf.tag = tag;
return cat;
}
#define LFSR_ATTR_NOOP() \
((lfsr_attr_t){ \
.tag=LFSR_TAG_NULL, \
.size=0, \
.delta=0, \
.u.datas=NULL})
((const lfsr_attr_t){ \
.delta=_delta, \
.u.cat.u.buf.tag=LFSR_TAG_NULL, \
.u.cat.u.buf.size=0, \
.u.cat.u.buf.buffer=NULL})
// create an attribute list
#define LFSR_ATTRS(...) \
@@ -1613,25 +1601,25 @@ static inline lfsr_attr_t lfsr_attr(
// a move of all attrs from an mdir entry
#define LFSR_ATTR_MOVE(_tag, _delta, _mdir) \
((const lfsr_attr_t){ \
.tag=_tag, \
.delta=_delta, \
.u.mdir=_mdir})
.u.cat.u.buf.tag=_tag, \
.u.cat.u.buf.buffer=(const void*)(const lfsr_mdir_t*){_mdir}})
// a grm update, note this is mutable! we may update the grm during
// mdir commits
#define LFSR_ATTR_GRM(_tag, _delta, _grm) \
((const lfsr_attr_t){ \
.tag=_tag, \
.delta=_delta, \
.u.grm=_grm})
.u.cat.u.buf.tag=_tag, \
.u.cat.u.buf.buffer=(const void*)(const lfsr_grm_t*){_grm}})
// writing to an unrelated trunk in the rbyd
#define LFSR_ATTR_SHRUBCOMMIT(_tag, _delta, \
_shrub, _rid, _attrs, _attr_count) \
((const lfsr_attr_t){ \
.tag=_tag, \
.delta=_delta, \
.u.shrubcommit=&(const lfsr_shrubcommit_t){ \
.u.cat.u.buf.tag=_tag, \
.u.cat.u.buf.buffer=(const void*)&(const lfsr_shrubcommit_t){ \
.shrub=_shrub, \
.rid=_rid, \
.attrs=_attrs, \
@@ -1639,23 +1627,35 @@ static inline lfsr_attr_t lfsr_attr(
#define LFSR_ATTR_SHRUBTRUNK(_tag, _delta, _shrub) \
((const lfsr_attr_t){ \
.tag=_tag, \
.delta=_delta, \
.u.shrub=_shrub})
.u.cat.u.buf.tag=_tag, \
.u.cat.u.buf.buffer=(const void*)(const lfsr_shrub_t*){_shrub}})
// some helpers
static inline bool lfsr_attr_isnoop(const lfsr_attr_t *attr) {
return !attr->tag && attr->delta == 0;
return !attr->u.tag && attr->delta == 0;
}
static inline bool lfsr_attr_isinsert(const lfsr_attr_t *attr) {
return !lfsr_tag_isgrow(attr->tag) && attr->delta > 0;
return !lfsr_tag_isgrow(attr->u.tag) && attr->delta > 0;
}
static inline lfsr_cat_t lfsr_attr_cat(const lfsr_attr_t *attr) {
return (lfsr_cat_t){
.u.cat.size=attr->size,
.u.cat.datas=attr->u.datas};
static inline lfsr_grm_t *lfsr_attr_grm(const lfsr_attr_t *attr) {
return (lfsr_grm_t*)attr->u.cat.u.buf.buffer;
}
static inline const lfsr_mdir_t *lfsr_attr_mdir(const lfsr_attr_t *attr) {
return (lfsr_mdir_t*)attr->u.cat.u.buf.buffer;
}
static inline const lfsr_shrubcommit_t *lfsr_attr_shrubcommit(
const lfsr_attr_t *attr) {
return (const lfsr_shrubcommit_t*)attr->u.cat.u.buf.buffer;
}
static inline const lfsr_shrub_t *lfsr_attr_shrubtrunk(
const lfsr_attr_t *attr) {
return (const lfsr_shrub_t*)attr->u.cat.u.buf.buffer;
}
@@ -3601,7 +3601,7 @@ static int lfsr_rbyd_appendattrs(lfs_t *lfs, lfsr_rbyd_t *rbyd,
&& (lfs_size_t)(rid + 1) <= (lfs_size_t)end_rid) {
int err = lfsr_rbyd_appendattr(lfs, rbyd,
rid - lfs_smax32(start_rid, 0),
attrs[i].tag, attrs[i].delta, lfsr_attr_cat(&attrs[i]));
attrs[i].u.tag, attrs[i].delta, attrs[i].u.cat);
if (err) {
return err;
}
@@ -5984,16 +5984,16 @@ static int lfsr_mdir_commit__(lfs_t *lfs, lfsr_mdir_t *mdir,
// ignore any gstate tags here, these need to be handled
// specially by upper-layers
if (attrs[i].tag == LFSR_TAG_GRM) {
if (attrs[i].u.tag == LFSR_TAG_GRM) {
// do nothing
// move tags copy over any tags associated with the source's rid
// TODO can this be deduplicated with lfsr_mdir_compact__ more?
// it _really_ wants to be deduplicated
} else if (attrs[i].tag == LFSR_TAG_MOVE) {
} else if (attrs[i].u.tag == LFSR_TAG_MOVE) {
// weighted moves are not supported
LFS_ASSERT(attrs[i].delta == 0);
const lfsr_mdir_t *mdir__ = attrs[i].u.mdir;
const lfsr_mdir_t *mdir__ = lfsr_attr_mdir(&attrs[i]);
// skip the name tag, this is always replaced by upper layers
lfsr_tag_t tag = LFSR_TAG_STRUCT-1;
@@ -6112,15 +6112,15 @@ static int lfsr_mdir_commit__(lfs_t *lfs, lfsr_mdir_t *mdir,
// shrub tags append a set of attributes to an unrelated trunk
// in our rbyd
} else if (attrs[i].tag == LFSR_TAG_SHRUBALLOC
|| attrs[i].tag == LFSR_TAG_SHRUBCOMMIT) {
} else if (attrs[i].u.tag == LFSR_TAG_SHRUBALLOC
|| attrs[i].u.tag == LFSR_TAG_SHRUBCOMMIT) {
const lfsr_shrubcommit_t *bshrubcommit
= attrs[i].u.shrubcommit;
= lfsr_attr_shrubcommit(&attrs[i]);
// SHRUBALLOC is roughly the same as SHRUBCOMMIT but also
// resets the shrub, we need to do this here so bshrub root
// extensions are atomic
if (attrs[i].tag == LFSR_TAG_SHRUBALLOC) {
if (attrs[i].u.tag == LFSR_TAG_SHRUBALLOC) {
bshrubcommit->shrub->blocks[0] = rbyd_.blocks[0];
bshrubcommit->shrub->trunk = LFSR_RBYD_ISSHRUB | 0;
bshrubcommit->shrub->weight = 0;
@@ -6140,12 +6140,12 @@ static int lfsr_mdir_commit__(lfs_t *lfs, lfsr_mdir_t *mdir,
//
// TODO should we preserve mode for all of these?
// TODO should we do the same for sprouts?
} else if (lfsr_tag_key(attrs[i].tag) == LFSR_TAG_SHRUBTRUNK) {
const lfsr_shrub_t *shrub = attrs[i].u.shrub;
} else if (lfsr_tag_key(attrs[i].u.tag) == LFSR_TAG_SHRUBTRUNK) {
const lfsr_shrub_t *shrub = lfsr_attr_shrubtrunk(&attrs[i]);
int err = lfsr_rbyd_appendattr(lfs, &rbyd_,
rid - lfs_smax32(start_rid, 0),
lfsr_tag_mode(attrs[i].tag) | LFSR_TAG_BSHRUB,
lfsr_tag_mode(attrs[i].u.tag) | LFSR_TAG_BSHRUB,
attrs[i].delta,
// note we use the staged trunk here
LFSR_CAT_SHRUB(shrub));
@@ -6155,13 +6155,11 @@ static int lfsr_mdir_commit__(lfs_t *lfs, lfsr_mdir_t *mdir,
// write out normal tags normally
} else {
LFS_ASSERT(!lfsr_tag_isinternal(attrs[i].tag));
LFS_ASSERT(!lfsr_tag_isinternal(attrs[i].u.tag));
int err = lfsr_rbyd_appendattr(lfs, &rbyd_,
rid - lfs_smax32(start_rid, 0),
attrs[i].tag,
attrs[i].delta,
lfsr_attr_cat(&attrs[i]));
attrs[i].u.tag, attrs[i].delta, attrs[i].u.cat);
if (err) {
return err;
}
@@ -6615,9 +6613,9 @@ static int lfsr_mdir_commit(lfs_t *lfs, lfsr_mdir_t *mdir,
// parse out any pending gstate, these will get automatically
// xored with on-disk gdeltas in lower-level functions
for (lfs_size_t i = 0; i < attr_count; i++) {
if (attrs[i].tag == LFSR_TAG_GRM) {
if (attrs[i].u.tag == LFSR_TAG_GRM) {
// encode to disk
lfsr_grm_t *grm = attrs[i].u.grm;
lfsr_grm_t *grm = lfsr_attr_grm(&attrs[i]);
lfsr_gdelta_xorgrm(lfs, lfs->grm_d, LFSR_GRM_DSIZE, grm);
// xor with our current gstate to find our initial gdelta
@@ -6892,7 +6890,7 @@ static int lfsr_mdir_commit(lfs_t *lfs, lfsr_mdir_t *mdir,
// patch any pending grms
for (lfs_size_t i = 0; i < attr_count; i++) {
if (attrs[i].tag == LFSR_TAG_GRM) {
if (attrs[i].u.tag == LFSR_TAG_GRM) {
// Assuming we already xored our gdelta with the grm, we first
// need to xor the grm out of the gdelta. We can't just zero
// the gdelta because we may have picked up extra gdelta from
@@ -6900,7 +6898,7 @@ static int lfsr_mdir_commit(lfs_t *lfs, lfsr_mdir_t *mdir,
//
// gd' = gd xor (grm' xor grm)
//
lfsr_grm_t *grm = attrs[i].u.grm;
lfsr_grm_t *grm = lfsr_attr_grm(&attrs[i]);
lfsr_gdelta_xorgrm(lfs, lfs->grm_d, LFSR_GRM_DSIZE, grm);
// patch our grm
@@ -7034,8 +7032,8 @@ static int lfsr_mdir_commit(lfs_t *lfs, lfsr_mdir_t *mdir,
lfsr_smid_t mid = mdir->mid;
for (lfs_size_t i = 0; i < attr_count; i++) {
// update any gstate changes
if (attrs[i].tag == LFSR_TAG_GRM) {
lfs->grm = *attrs[i].u.grm;
if (attrs[i].u.tag == LFSR_TAG_GRM) {
lfs->grm = *lfsr_attr_grm(&attrs[i]);
// keep track of the exact encoding on-disk
lfsr_cat_fromgrm(&lfs->grm, lfs->grm_g);
@@ -7048,7 +7046,7 @@ static int lfsr_mdir_commit(lfs_t *lfs, lfsr_mdir_t *mdir,
&& o->mdir.mid >= mid) {
// replaced?
if (o->mdir.mid == mid - attrs[i].delta
&& lfsr_tag_issup(attrs[i].tag)) {
&& lfsr_tag_issup(attrs[i].u.tag)) {
o->flags |= LFS_F_ZOMBIE
| LFS_F_UNSYNC
| LFS_O_DESYNC;
@@ -10205,11 +10203,11 @@ static int lfsr_bshrub_commit(lfs_t *lfs, lfsr_file_t *file,
lfs_size_t commit_estimate = 0;
for (lfs_size_t i = 0; i < attr_count; i++) {
// only include tag overhead if tag is not a grow/rm tag
if (!lfsr_tag_isgrow(attrs[i].tag)
&& !lfsr_tag_isrm(attrs[i].tag)) {
if (!lfsr_tag_isgrow(attrs[i].u.tag)
&& !lfsr_tag_isrm(attrs[i].u.tag)) {
commit_estimate += lfs->attr_estimate;
}
commit_estimate += lfsr_cat_size(lfsr_attr_cat(&attrs[i]));
commit_estimate += lfsr_cat_size(attrs[i].u.cat);
}
// does our estimate exceed our shrub_size? need to recalculate an