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)=0 => single in-RAM buffer
// sign(size)=1 => multiple concatenated datas // sign(size)=1 => multiple concatenated datas
union { union {
uint16_t size;
struct { struct {
lfsr_tag_t tag;
uint16_t size; uint16_t size;
const uint8_t *buffer; const uint8_t *buffer;
} buf; } buf;
struct { struct {
lfsr_tag_t tag;
uint16_t size; uint16_t size;
const lfsr_data_t *datas; const lfsr_data_t *datas;
} cat; } cat;
@@ -1452,11 +1453,11 @@ typedef struct lfsr_cat {
// cat helpers // cat helpers
static inline bool lfsr_cat_isbuf(lfsr_cat_t cat) { 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) { 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) { 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) { static inline uint16_t lfsr_cat_count(lfsr_cat_t cat) {
LFS_ASSERT(lfsr_cat_iscat(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) { 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_shrubcommit lfsr_shrubcommit_t;
typedef struct lfsr_attr { typedef struct lfsr_attr {
lfsr_tag_t tag;
uint16_t size;
lfsr_srid_t delta; lfsr_srid_t delta;
// sign(size)=0 => single in-RAM buffer
// sign(size)=1 => multiple concatenated datas
// special tags => other types
union { union {
const uint8_t *buffer; lfsr_tag_t tag;
const lfsr_data_t *datas; lfsr_cat_t cat;
lfsr_grm_t *grm;
const lfsr_mdir_t *mdir;
const lfsr_shrubcommit_t *shrubcommit;
const lfsr_shrub_t *shrub;
} u; } u;
} lfsr_attr_t; } lfsr_attr_t;
#define LFSR_ATTR(_tag, _delta, _cat) \ #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( static inline lfsr_cat_t lfsr_cat_tag(lfsr_cat_t cat, lfsr_tag_t tag) {
lfsr_tag_t tag, lfsr_srid_t delta, lfsr_cat_t cat) { cat.u.buf.tag = tag;
return (lfsr_attr_t){ return cat;
.tag=tag,
.size=cat.u.cat.size,
.delta=delta,
.u.datas=cat.u.cat.datas};
} }
#define LFSR_ATTR_NOOP() \ #define LFSR_ATTR_NOOP() \
((lfsr_attr_t){ \ ((const lfsr_attr_t){ \
.tag=LFSR_TAG_NULL, \ .delta=_delta, \
.size=0, \ .u.cat.u.buf.tag=LFSR_TAG_NULL, \
.delta=0, \ .u.cat.u.buf.size=0, \
.u.datas=NULL}) .u.cat.u.buf.buffer=NULL})
// create an attribute list // create an attribute list
#define LFSR_ATTRS(...) \ #define LFSR_ATTRS(...) \
@@ -1613,25 +1601,25 @@ static inline lfsr_attr_t lfsr_attr(
// a move of all attrs from an mdir entry // a move of all attrs from an mdir entry
#define LFSR_ATTR_MOVE(_tag, _delta, _mdir) \ #define LFSR_ATTR_MOVE(_tag, _delta, _mdir) \
((const lfsr_attr_t){ \ ((const lfsr_attr_t){ \
.tag=_tag, \
.delta=_delta, \ .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 // a grm update, note this is mutable! we may update the grm during
// mdir commits // mdir commits
#define LFSR_ATTR_GRM(_tag, _delta, _grm) \ #define LFSR_ATTR_GRM(_tag, _delta, _grm) \
((const lfsr_attr_t){ \ ((const lfsr_attr_t){ \
.tag=_tag, \
.delta=_delta, \ .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 // writing to an unrelated trunk in the rbyd
#define LFSR_ATTR_SHRUBCOMMIT(_tag, _delta, \ #define LFSR_ATTR_SHRUBCOMMIT(_tag, _delta, \
_shrub, _rid, _attrs, _attr_count) \ _shrub, _rid, _attrs, _attr_count) \
((const lfsr_attr_t){ \ ((const lfsr_attr_t){ \
.tag=_tag, \
.delta=_delta, \ .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, \ .shrub=_shrub, \
.rid=_rid, \ .rid=_rid, \
.attrs=_attrs, \ .attrs=_attrs, \
@@ -1639,23 +1627,35 @@ static inline lfsr_attr_t lfsr_attr(
#define LFSR_ATTR_SHRUBTRUNK(_tag, _delta, _shrub) \ #define LFSR_ATTR_SHRUBTRUNK(_tag, _delta, _shrub) \
((const lfsr_attr_t){ \ ((const lfsr_attr_t){ \
.tag=_tag, \
.delta=_delta, \ .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 // some helpers
static inline bool lfsr_attr_isnoop(const lfsr_attr_t *attr) { 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) { 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) { static inline lfsr_grm_t *lfsr_attr_grm(const lfsr_attr_t *attr) {
return (lfsr_cat_t){ return (lfsr_grm_t*)attr->u.cat.u.buf.buffer;
.u.cat.size=attr->size, }
.u.cat.datas=attr->u.datas};
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) { && (lfs_size_t)(rid + 1) <= (lfs_size_t)end_rid) {
int err = lfsr_rbyd_appendattr(lfs, rbyd, int err = lfsr_rbyd_appendattr(lfs, rbyd,
rid - lfs_smax32(start_rid, 0), 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) { if (err) {
return 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 // ignore any gstate tags here, these need to be handled
// specially by upper-layers // specially by upper-layers
if (attrs[i].tag == LFSR_TAG_GRM) { if (attrs[i].u.tag == LFSR_TAG_GRM) {
// do nothing // do nothing
// move tags copy over any tags associated with the source's rid // move tags copy over any tags associated with the source's rid
// TODO can this be deduplicated with lfsr_mdir_compact__ more? // TODO can this be deduplicated with lfsr_mdir_compact__ more?
// it _really_ wants to be deduplicated // 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 // weighted moves are not supported
LFS_ASSERT(attrs[i].delta == 0); 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 // skip the name tag, this is always replaced by upper layers
lfsr_tag_t tag = LFSR_TAG_STRUCT-1; 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 // shrub tags append a set of attributes to an unrelated trunk
// in our rbyd // in our rbyd
} else if (attrs[i].tag == LFSR_TAG_SHRUBALLOC } else if (attrs[i].u.tag == LFSR_TAG_SHRUBALLOC
|| attrs[i].tag == LFSR_TAG_SHRUBCOMMIT) { || attrs[i].u.tag == LFSR_TAG_SHRUBCOMMIT) {
const lfsr_shrubcommit_t *bshrubcommit const lfsr_shrubcommit_t *bshrubcommit
= attrs[i].u.shrubcommit; = lfsr_attr_shrubcommit(&attrs[i]);
// SHRUBALLOC is roughly the same as SHRUBCOMMIT but also // SHRUBALLOC is roughly the same as SHRUBCOMMIT but also
// resets the shrub, we need to do this here so bshrub root // resets the shrub, we need to do this here so bshrub root
// extensions are atomic // 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->blocks[0] = rbyd_.blocks[0];
bshrubcommit->shrub->trunk = LFSR_RBYD_ISSHRUB | 0; bshrubcommit->shrub->trunk = LFSR_RBYD_ISSHRUB | 0;
bshrubcommit->shrub->weight = 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 preserve mode for all of these?
// TODO should we do the same for sprouts? // TODO should we do the same for sprouts?
} else if (lfsr_tag_key(attrs[i].tag) == LFSR_TAG_SHRUBTRUNK) { } else if (lfsr_tag_key(attrs[i].u.tag) == LFSR_TAG_SHRUBTRUNK) {
const lfsr_shrub_t *shrub = attrs[i].u.shrub; const lfsr_shrub_t *shrub = lfsr_attr_shrubtrunk(&attrs[i]);
int err = lfsr_rbyd_appendattr(lfs, &rbyd_, int err = lfsr_rbyd_appendattr(lfs, &rbyd_,
rid - lfs_smax32(start_rid, 0), 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, attrs[i].delta,
// note we use the staged trunk here // note we use the staged trunk here
LFSR_CAT_SHRUB(shrub)); 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 // write out normal tags normally
} else { } 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_, int err = lfsr_rbyd_appendattr(lfs, &rbyd_,
rid - lfs_smax32(start_rid, 0), rid - lfs_smax32(start_rid, 0),
attrs[i].tag, attrs[i].u.tag, attrs[i].delta, attrs[i].u.cat);
attrs[i].delta,
lfsr_attr_cat(&attrs[i]));
if (err) { if (err) {
return 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 // parse out any pending gstate, these will get automatically
// xored with on-disk gdeltas in lower-level functions // xored with on-disk gdeltas in lower-level functions
for (lfs_size_t i = 0; i < attr_count; i++) { 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 // 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); lfsr_gdelta_xorgrm(lfs, lfs->grm_d, LFSR_GRM_DSIZE, grm);
// xor with our current gstate to find our initial gdelta // 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 // patch any pending grms
for (lfs_size_t i = 0; i < attr_count; i++) { 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 // 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 // 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 // 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) // 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); lfsr_gdelta_xorgrm(lfs, lfs->grm_d, LFSR_GRM_DSIZE, grm);
// patch our 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; lfsr_smid_t mid = mdir->mid;
for (lfs_size_t i = 0; i < attr_count; i++) { for (lfs_size_t i = 0; i < attr_count; i++) {
// update any gstate changes // update any gstate changes
if (attrs[i].tag == LFSR_TAG_GRM) { if (attrs[i].u.tag == LFSR_TAG_GRM) {
lfs->grm = *attrs[i].u.grm; lfs->grm = *lfsr_attr_grm(&attrs[i]);
// keep track of the exact encoding on-disk // keep track of the exact encoding on-disk
lfsr_cat_fromgrm(&lfs->grm, lfs->grm_g); 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) { && o->mdir.mid >= mid) {
// replaced? // replaced?
if (o->mdir.mid == mid - attrs[i].delta 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 o->flags |= LFS_F_ZOMBIE
| LFS_F_UNSYNC | LFS_F_UNSYNC
| LFS_O_DESYNC; | 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; lfs_size_t commit_estimate = 0;
for (lfs_size_t i = 0; i < attr_count; i++) { for (lfs_size_t i = 0; i < attr_count; i++) {
// only include tag overhead if tag is not a grow/rm tag // only include tag overhead if tag is not a grow/rm tag
if (!lfsr_tag_isgrow(attrs[i].tag) if (!lfsr_tag_isgrow(attrs[i].u.tag)
&& !lfsr_tag_isrm(attrs[i].tag)) { && !lfsr_tag_isrm(attrs[i].u.tag)) {
commit_estimate += lfs->attr_estimate; 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 // does our estimate exceed our shrub_size? need to recalculate an