Fixed unaligned data checksumming in two ways (uncrc32c, flcksum)

Checksumming unaligned data during block compaction is surprisingly
tricky. We don't know if our data will be aligned until after
a potentially unbounded number lookups, we need to write data into our
pcache as we go to avoid unnecessary lookups, but if we end up unaligned
we need to revert our checksum to the checksum of the aligned data.

The way I see it there are 4 options:

1. Calculate the checksum after writing data into the block.

   This is the most expensive option, requiring a full second read of
   the data to calculate the checksum. It is simple though.

2. Do a pass over the btree to figure out alignment before writing.

   This at least only reads metadata twice, so is more efficient than
   the 1st option.

3. Keep track of the aligned checksum on each flush, falling back to the
   last flushed checksum if we need to correct alignment.

   This solution is flexible though requires some extra state to track
   multiple checksums.

4. Leverage the math behind CRCs to run the CRC backwards when we
   truncate for alignment.

   This works, though a bit inefficiently, but is strictly tied to
   CRC-related checksums.

   By inefficient I mean that we would likely be limited to a bit-level
   "uncrc32c". It's possible to create nibble/byte tables for uncrc32c,
   but this adds significant code cost for a relatively uncritical
   function.

   I was hopeful that we could leverage the existing tables in both
   functions, but unfortunately it doesn't work out like that. You could
   scan the crc32c table to find the constant to reverse, but this
   requires ~16*2 or ~256 operations vs "naive" ~8 operations per byte.

This commit implements both 3 and 4, defaulting to 4 unless
LFS_NO_UNCRC32C is defined.

The current lfs_uncrc32c implementation is a simple bit-level
implementation, but does allow for crc32c truncation without any extra
state.

              code          stack
  before:    32044           2880
  uncrc32c:  32108 (+0.2%)   2880 (+0.0%)
  flcksum:   32132 (+0.3%)   2880 (+0.0%)
This commit is contained in:
Christopher Haster
2023-12-15 00:58:24 -06:00
parent c2e3a391ff
commit 006d656da2
4 changed files with 208 additions and 86 deletions
+71 -32
View File
@@ -241,7 +241,8 @@ static int lfs_bd_crc32c(lfs_t *lfs,
#ifndef LFS_READONLY
static int lfs_bd_flush(lfs_t *lfs,
lfs_cache_t *pcache, lfs_cache_t *rcache, bool validate) {
lfs_cache_t *pcache, lfs_cache_t *rcache, bool validate,
uint32_t *flcksum_) {
if (pcache->block != LFS_BLOCK_NULL && pcache->block != LFS_BLOCK_INLINE) {
LFS_ASSERT(pcache->block < lfs->cfg->block_count);
lfs_size_t diff = lfs_alignup(pcache->size, lfs->cfg->prog_size);
@@ -267,6 +268,12 @@ static int lfs_bd_flush(lfs_t *lfs,
}
}
// this is when we update the the flushed checksum if requested
if (flcksum_) {
*flcksum_ = lfs_crc32c(*flcksum_,
pcache->buffer, pcache->size);
}
lfs_cache_zero(lfs, pcache);
}
@@ -279,7 +286,7 @@ static int lfs_bd_sync(lfs_t *lfs,
lfs_cache_t *pcache, lfs_cache_t *rcache, bool validate) {
lfs_cache_drop(lfs, rcache);
int err = lfs_bd_flush(lfs, pcache, rcache, validate);
int err = lfs_bd_flush(lfs, pcache, rcache, validate, NULL);
if (err) {
return err;
}
@@ -294,7 +301,8 @@ static int lfs_bd_sync(lfs_t *lfs,
static int lfs_bd_prog(lfs_t *lfs,
lfs_cache_t *pcache, lfs_cache_t *rcache, bool validate,
lfs_block_t block, lfs_size_t off,
const void *buffer, lfs_size_t size) {
const void *buffer, lfs_size_t size,
uint32_t *flcksum_) {
const uint8_t *data = buffer;
LFS_ASSERT(block == LFS_BLOCK_INLINE || block < lfs->cfg->block_count);
LFS_ASSERT(off + size <= lfs->cfg->block_size);
@@ -327,7 +335,8 @@ static int lfs_bd_prog(lfs_t *lfs,
pcache->size = lfs_max(pcache->size, off - pcache->off);
if (pcache->size == lfs->cfg->cache_size) {
// eagerly flush out pcache if we fill up
int err = lfs_bd_flush(lfs, pcache, rcache, validate);
int err = lfs_bd_flush(lfs, pcache, rcache, validate,
flcksum_);
if (err) {
return err;
}
@@ -422,14 +431,15 @@ static lfs_scmp_t lfsr_bd_cmp(lfs_t *lfs,
// program data with optional checksum
static int lfsr_bd_prog(lfs_t *lfs, lfs_block_t block, lfs_size_t off,
const void *buffer, lfs_size_t size,
uint32_t *cksum_) {
uint32_t *cksum_, uint32_t *flcksum_) {
// check for in-bounds
if (off+size > lfs->cfg->block_size) {
return LFS_ERR_RANGE;
}
int err = lfs_bd_prog(lfs, &lfs->pcache, &lfs->rcache, false,
block, off, buffer, size);
block, off, buffer, size,
flcksum_);
if (err) {
return err;
}
@@ -442,8 +452,10 @@ static int lfsr_bd_prog(lfs_t *lfs, lfs_block_t block, lfs_size_t off,
return 0;
}
static int lfsr_bd_flush(lfs_t *lfs) {
return lfs_bd_flush(lfs, &lfs->pcache, &lfs->rcache, false);
static int lfsr_bd_flush(lfs_t *lfs,
uint32_t *flcksum_) {
return lfs_bd_flush(lfs, &lfs->pcache, &lfs->rcache, false,
flcksum_);
}
static int lfsr_bd_sync(lfs_t *lfs) {
@@ -454,7 +466,7 @@ static int lfsr_bd_sync(lfs_t *lfs) {
// be an optional ifdef?
static int lfsr_bd_progvalidate(lfs_t *lfs, lfs_block_t block, lfs_size_t off,
const void *buffer, lfs_size_t size,
uint32_t *cksum_) {
uint32_t *cksum_, uint32_t *flcksum_) {
// check for in-bounds
if (off+size > lfs->cfg->block_size) {
lfs_cache_zero(lfs, &lfs->pcache);
@@ -462,11 +474,13 @@ static int lfsr_bd_progvalidate(lfs_t *lfs, lfs_block_t block, lfs_size_t off,
}
int err = lfs_bd_prog(lfs, &lfs->pcache, &lfs->rcache, true,
block, off, buffer, size);
block, off, buffer, size,
flcksum_);
if (err) {
return err;
}
// optional checksum
if (cksum_) {
*cksum_ = lfs_crc32c(*cksum_, buffer, size);
}
@@ -974,7 +988,8 @@ static lfs_ssize_t lfsr_bd_progtag(lfs_t *lfs,
}
d += d_;
int err = lfsr_bd_prog(lfs, block, off, &tag_buf, d, cksum_);
int err = lfsr_bd_prog(lfs, block, off, &tag_buf, d,
cksum_, NULL);
if (err) {
return err;
}
@@ -1306,7 +1321,7 @@ static lfs_scmp_t lfsr_data_namecmp(lfs_t *lfs, const lfsr_data_t *data,
static int lfsr_bd_progdata_(lfs_t *lfs,
lfs_block_t block, lfs_size_t off, lfsr_data_t data,
uint32_t *cksum_) {
uint32_t *cksum_, uint32_t *flcksum_) {
// on-disk?
if (lfsr_data_ondisk(&data)) {
// TODO byte-level copies have been a pain point, works for prototyping
@@ -1323,7 +1338,7 @@ static int lfsr_bd_progdata_(lfs_t *lfs,
}
err = lfsr_bd_prog(lfs, block, off+i, &dat, 1,
cksum_);
cksum_, flcksum_);
if (err) {
return err;
}
@@ -1333,7 +1348,7 @@ static int lfsr_bd_progdata_(lfs_t *lfs,
} else if (lfsr_data_isbuf(&data)) {
int err = lfsr_bd_prog(lfs, block, off,
data.u.buf.buffer, data.u.buf.size,
cksum_);
cksum_, flcksum_);
if (err) {
return err;
}
@@ -1342,7 +1357,7 @@ static int lfsr_bd_progdata_(lfs_t *lfs,
} else if (lfsr_data_isimm(&data)) {
int err = lfsr_bd_prog(lfs, block, off,
data.u.imm.buf, data.u.imm.size,
cksum_);
cksum_, flcksum_);
if (err) {
return err;
}
@@ -1357,11 +1372,11 @@ static int lfsr_bd_progdata_(lfs_t *lfs,
static int lfsr_bd_progdata(lfs_t *lfs,
lfs_block_t block, lfs_size_t off, lfsr_data_t data,
uint32_t *cksum_) {
uint32_t *cksum_, uint32_t *flcksum_) {
// simple data?
if (!lfsr_data_iscat(&data)) {
int err = lfsr_bd_progdata_(lfs, block, off, data,
cksum_);
cksum_, flcksum_);
if (err) {
return err;
}
@@ -1370,7 +1385,7 @@ static int lfsr_bd_progdata(lfs_t *lfs,
} else {
for (uint8_t i = 0; i < data.u.cat.count; i++) {
int err = lfsr_bd_progdata_(lfs, block, off, data.u.cat.datas[i],
cksum_);
cksum_, flcksum_);
if (err) {
return err;
}
@@ -2363,7 +2378,8 @@ static int lfsr_rbyd_appendrev(lfs_t *lfs, lfsr_rbyd_t *rbyd, uint32_t rev) {
uint8_t rev_buf[sizeof(uint32_t)];
lfs_tole32_(rev, &rev_buf);
int err = lfsr_bd_prog(lfs, rbyd->blocks[0], rbyd->eoff,
&rev_buf, sizeof(uint32_t), &rbyd->cksum);
&rev_buf, sizeof(uint32_t),
&rbyd->cksum, NULL);
if (err) {
return err;
}
@@ -2966,7 +2982,7 @@ leaf:;
// don't forget the data!
err = lfsr_bd_progdata(lfs, rbyd->blocks[0], rbyd->eoff, data,
&rbyd->cksum);
&rbyd->cksum, NULL);
if (err) {
return err;
}
@@ -3053,7 +3069,7 @@ static int lfsr_rbyd_appendcksum(lfs_t *lfs, lfsr_rbyd_t *rbyd) {
rbyd->eoff += d;
err = lfsr_bd_progdata(lfs, rbyd->blocks[0], rbyd->eoff, ecksum_data,
&rbyd->cksum);
&rbyd->cksum, NULL);
if (err) {
return err;
}
@@ -3099,7 +3115,7 @@ static int lfsr_rbyd_appendcksum(lfs_t *lfs, lfsr_rbyd_t *rbyd) {
int err = lfsr_bd_prog(lfs, rbyd->blocks[0], rbyd->eoff,
cksum_buf, 2+1+5+4,
NULL);
NULL, NULL);
if (err) {
return err;
}
@@ -3199,7 +3215,7 @@ static int lfsr_rbyd_appendcompactattr(lfs_t *lfs, lfsr_rbyd_t *rbyd,
// and the data
int err = lfsr_bd_progdata(lfs, rbyd->blocks[0], rbyd->eoff, data,
&rbyd->cksum);
&rbyd->cksum, NULL);
if (err) {
return err;
}
@@ -9934,7 +9950,12 @@ static int lfsr_ftree_flush(lfs_t *lfs,
err = lfsr_bd_prog(lfs, bptr.data.u.disk.block,
bptr.cksize,
&buffer[pos_ - pos], d_,
&bptr.cksum);
#ifndef LFS_NO_UNCRC32C
&bptr.cksum, NULL
#else
NULL, &bptr.cksum
#endif
);
if (err) {
LFS_ASSERT(err != LFS_ERR_RANGE);
return err;
@@ -9990,7 +10011,12 @@ static int lfsr_ftree_flush(lfs_t *lfs,
lfsr_data_slice(bptr_.data,
pos_ - (bid_-(weight_-1)),
d_),
&bptr.cksum);
#ifndef LFS_NO_UNCRC32C
&bptr.cksum, NULL
#else
NULL, &bptr.cksum
#endif
);
if (err) {
LFS_ASSERT(err != LFS_ERR_RANGE);
return err;
@@ -10011,7 +10037,12 @@ static int lfsr_ftree_flush(lfs_t *lfs,
err = lfsr_bd_prog(lfs, bptr.data.u.disk.block,
bptr.cksize + i,
&(uint8_t){0}, 1,
&bptr.cksum);
#ifndef LFS_NO_UNCRC32C
&bptr.cksum, NULL
#else
NULL, &bptr.cksum
#endif
);
if (err) {
LFS_ASSERT(err != LFS_ERR_RANGE);
return err;
@@ -10026,17 +10057,25 @@ static int lfsr_ftree_flush(lfs_t *lfs,
// alignment to avoid padding issues. Doing this retroactively to
// the pcache greatly simplifies the above loop, though we may end
// up reading more than is strictly necessary.
//
// TODO how do we handle checksum truncation???
// TODO lfs_cache_truncate? aligndown?
lfs_ssize_t d = (lfs->pcache.off + lfs->pcache.size)
% lfs->cfg->prog_size;
lfs_ssize_t d = bptr.cksize % lfs->cfg->prog_size;
LFS_ASSERT(d <= lfs->pcache.size);
lfs->pcache.size -= d;
#ifndef LFS_NO_UNCRC32C
bptr.cksum = lfs_uncrc32c(bptr.cksum,
&lfs->pcache.buffer[lfs->pcache.size],
d);
#endif
bptr.cksize -= d;
// TODO validate?
// finalize our write
err = lfsr_bd_flush(lfs);
err = lfsr_bd_flush(lfs,
#ifndef LFS_NO_UNCRC32C
NULL
#else
&bptr.cksum
#endif
);
if (err) {
return err;
}
+22
View File
@@ -195,4 +195,26 @@ uint32_t lfs_crc32c(uint32_t crc, const void *buffer, size_t size) {
return crc;
}
#ifndef LFS_NO_UNCRC32C
// Undoes a crc32c
//
// like crc32c, but backwards
uint32_t lfs_uncrc32c(uint32_t crc, const void *buffer, size_t size) {
// init with 0xffffffff so prefixed zeros affect the crc
const uint8_t *data = buffer;
crc ^= 0xffffffff;
for (size_t i = 0; i < size; i++) {
for (size_t j = 0; j < 8; j++) {
crc = (crc << 1) ^ ((crc & 0x80000000) ? 0x05ec76f1 : 0);
}
crc = crc ^ data[size-1-i];
}
// fini with 0xffffffff to cancel out init when called incrementally
crc ^= 0xffffffff;
return crc;
}
#endif
#endif
+7
View File
@@ -361,6 +361,13 @@ uint32_t lfs_crc(uint32_t crc, const void *buffer, size_t size);
//
uint32_t lfs_crc32c(uint32_t crc, const void *buffer, size_t size);
#ifndef LFS_NO_UNCRC32C
// Undoes a crc32c
//
// like crc32c, but backwards
uint32_t lfs_uncrc32c(uint32_t crc, const void *buffer, size_t size);
#endif
// Allocate memory, only used if buffers are not provided to littlefs
// Note, memory must be 64-bit aligned
+108 -54
View File
@@ -2495,8 +2495,10 @@ code = '''
rbyd = backup_rbyd;
lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0;
lfs_bd_prog(&lfs, &lfs.pcache, &lfs.rcache, false,
rbyd.blocks[0], 0, backup_block, rbyd.eoff) => 0;
lfs_bd_flush(&lfs, &lfs.pcache, &lfs.rcache, false) => 0;
rbyd.blocks[0], 0, backup_block, rbyd.eoff,
NULL) => 0;
lfs_bd_flush(&lfs, &lfs.pcache, &lfs.rcache, false,
NULL) => 0;
// update each tag in permutation order
for (unsigned j = 0; j < N; j++) {
@@ -2783,8 +2785,10 @@ code = '''
rbyd = backup_rbyd;
lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0;
lfs_bd_prog(&lfs, &lfs.pcache, &lfs.rcache, false,
rbyd.blocks[0], 0, backup_block, rbyd.eoff) => 0;
lfs_bd_flush(&lfs, &lfs.pcache, &lfs.rcache, false) => 0;
rbyd.blocks[0], 0, backup_block, rbyd.eoff,
NULL) => 0;
lfs_bd_flush(&lfs, &lfs.pcache, &lfs.rcache, false,
NULL) => 0;
lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS(
LFSR_ATTR(-1, RM(UATTR(j+1)), 0, NULL())))
@@ -2928,8 +2932,10 @@ code = '''
rbyd = backup_rbyd;
lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0;
lfs_bd_prog(&lfs, &lfs.pcache, &lfs.rcache, false,
rbyd.blocks[0], 0, backup_block, rbyd.eoff) => 0;
lfs_bd_flush(&lfs, &lfs.pcache, &lfs.rcache, false) => 0;
rbyd.blocks[0], 0, backup_block, rbyd.eoff,
NULL) => 0;
lfs_bd_flush(&lfs, &lfs.pcache, &lfs.rcache, false,
NULL) => 0;
lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS(
LFSR_ATTR(-1, RM(UATTR(j+1)), 0, NULL()))) => 0;
@@ -3494,8 +3500,10 @@ code = '''
rbyd = backup_rbyd;
lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0;
lfs_bd_prog(&lfs, &lfs.pcache, &lfs.rcache, false,
rbyd.blocks[0], 0, backup_block, rbyd.eoff) => 0;
lfs_bd_flush(&lfs, &lfs.pcache, &lfs.rcache, false) => 0;
rbyd.blocks[0], 0, backup_block, rbyd.eoff,
NULL) => 0;
lfs_bd_flush(&lfs, &lfs.pcache, &lfs.rcache, false,
NULL) => 0;
// remove each tag in permutation order
for (unsigned j = 0; j < N; j++) {
@@ -5978,8 +5986,10 @@ code = '''
rbyd = backup_rbyd;
lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0;
lfs_bd_prog(&lfs, &lfs.pcache, &lfs.rcache, false,
rbyd.blocks[0], 0, backup_block, rbyd.eoff) => 0;
lfs_bd_flush(&lfs, &lfs.pcache, &lfs.rcache, false) => 0;
rbyd.blocks[0], 0, backup_block, rbyd.eoff,
NULL) => 0;
lfs_bd_flush(&lfs, &lfs.pcache, &lfs.rcache, false,
NULL) => 0;
// update each tag in permutation order
for (unsigned j = 0; j < N*M; j++) {
@@ -6121,8 +6131,10 @@ code = '''
rbyd = backup_rbyd;
lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0;
lfs_bd_prog(&lfs, &lfs.pcache, &lfs.rcache, false,
rbyd.blocks[0], 0, backup_block, rbyd.eoff) => 0;
lfs_bd_flush(&lfs, &lfs.pcache, &lfs.rcache, false) => 0;
rbyd.blocks[0], 0, backup_block, rbyd.eoff,
NULL) => 0;
lfs_bd_flush(&lfs, &lfs.pcache, &lfs.rcache, false,
NULL) => 0;
lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS(
LFSR_ATTR(j/M, RM(UATTR((j%M)+1)), 0, NULL()))) => 0;
@@ -6301,8 +6313,10 @@ code = '''
rbyd = backup_rbyd;
lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0;
lfs_bd_prog(&lfs, &lfs.pcache, &lfs.rcache, false,
rbyd.blocks[0], 0, backup_block, rbyd.eoff) => 0;
lfs_bd_flush(&lfs, &lfs.pcache, &lfs.rcache, false) => 0;
rbyd.blocks[0], 0, backup_block, rbyd.eoff,
NULL) => 0;
lfs_bd_flush(&lfs, &lfs.pcache, &lfs.rcache, false,
NULL) => 0;
// remove each tag in permutation order
for (unsigned j = 0; j < N*M; j++) {
@@ -7189,8 +7203,10 @@ code = '''
rbyd = backup_rbyd;
lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0;
lfs_bd_prog(&lfs, &lfs.pcache, &lfs.rcache, false,
rbyd.blocks[0], 0, backup_block, rbyd.eoff) => 0;
lfs_bd_flush(&lfs, &lfs.pcache, &lfs.rcache, false) => 0;
rbyd.blocks[0], 0, backup_block, rbyd.eoff,
NULL) => 0;
lfs_bd_flush(&lfs, &lfs.pcache, &lfs.rcache, false,
NULL) => 0;
lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS(
LFSR_ATTR(j, RM, -1, NULL()))) => 0;
@@ -7349,8 +7365,10 @@ code = '''
rbyd = backup_rbyd;
lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0;
lfs_bd_prog(&lfs, &lfs.pcache, &lfs.rcache, false,
rbyd.blocks[0], 0, backup_block, rbyd.eoff) => 0;
lfs_bd_flush(&lfs, &lfs.pcache, &lfs.rcache, false) => 0;
rbyd.blocks[0], 0, backup_block, rbyd.eoff,
NULL) => 0;
lfs_bd_flush(&lfs, &lfs.pcache, &lfs.rcache, false,
NULL) => 0;
lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS(
LFSR_ATTR(j, RM, -1, NULL()))) => 0;
@@ -7532,8 +7550,10 @@ code = '''
rbyd = backup_rbyd;
lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0;
lfs_bd_prog(&lfs, &lfs.pcache, &lfs.rcache, false,
rbyd.blocks[0], 0, backup_block, rbyd.eoff) => 0;
lfs_bd_flush(&lfs, &lfs.pcache, &lfs.rcache, false) => 0;
rbyd.blocks[0], 0, backup_block, rbyd.eoff,
NULL) => 0;
lfs_bd_flush(&lfs, &lfs.pcache, &lfs.rcache, false,
NULL) => 0;
lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS(
LFSR_ATTR(j, RM, -1, NULL()))) => 0;
@@ -7662,8 +7682,10 @@ code = '''
rbyd = backup_rbyd;
lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0;
lfs_bd_prog(&lfs, &lfs.pcache, &lfs.rcache, false,
rbyd.blocks[0], 0, backup_block, rbyd.eoff) => 0;
lfs_bd_flush(&lfs, &lfs.pcache, &lfs.rcache, false) => 0;
rbyd.blocks[0], 0, backup_block, rbyd.eoff,
NULL) => 0;
lfs_bd_flush(&lfs, &lfs.pcache, &lfs.rcache, false,
NULL) => 0;
lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS(
LFSR_ATTR(j, RM, -1, NULL()))) => 0;
@@ -8030,8 +8052,10 @@ code = '''
rbyd = backup_rbyd;
lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0;
lfs_bd_prog(&lfs, &lfs.pcache, &lfs.rcache, false,
rbyd.blocks[0], 0, backup_block, rbyd.eoff) => 0;
lfs_bd_flush(&lfs, &lfs.pcache, &lfs.rcache, false) => 0;
rbyd.blocks[0], 0, backup_block, rbyd.eoff,
NULL) => 0;
lfs_bd_flush(&lfs, &lfs.pcache, &lfs.rcache, false,
NULL) => 0;
// delete each rid in permutation order
for (unsigned j = 0; j < N; j++) {
@@ -8178,8 +8202,10 @@ code = '''
rbyd = backup_rbyd;
lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0;
lfs_bd_prog(&lfs, &lfs.pcache, &lfs.rcache, false,
rbyd.blocks[0], 0, backup_block, rbyd.eoff) => 0;
lfs_bd_flush(&lfs, &lfs.pcache, &lfs.rcache, false) => 0;
rbyd.blocks[0], 0, backup_block, rbyd.eoff,
NULL) => 0;
lfs_bd_flush(&lfs, &lfs.pcache, &lfs.rcache, false,
NULL) => 0;
// delete each rid in permutation order
for (unsigned j = 0; j < N; j++) {
@@ -10128,8 +10154,10 @@ code = '''
rbyd = backup_rbyd;
lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0;
lfs_bd_prog(&lfs, &lfs.pcache, &lfs.rcache, false,
rbyd.blocks[0], 0, backup_block, rbyd.eoff) => 0;
lfs_bd_flush(&lfs, &lfs.pcache, &lfs.rcache, false) => 0;
rbyd.blocks[0], 0, backup_block, rbyd.eoff,
NULL) => 0;
lfs_bd_flush(&lfs, &lfs.pcache, &lfs.rcache, false,
NULL) => 0;
lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS(
LFSR_ATTR(j*W+W-1, RM, +D, NULL()))) => 0;
@@ -10255,8 +10283,10 @@ code = '''
rbyd = backup_rbyd;
lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0;
lfs_bd_prog(&lfs, &lfs.pcache, &lfs.rcache, false,
rbyd.blocks[0], 0, backup_block, rbyd.eoff) => 0;
lfs_bd_flush(&lfs, &lfs.pcache, &lfs.rcache, false) => 0;
rbyd.blocks[0], 0, backup_block, rbyd.eoff,
NULL) => 0;
lfs_bd_flush(&lfs, &lfs.pcache, &lfs.rcache, false,
NULL) => 0;
lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS(
LFSR_ATTR(j*W+W-1, GROW(REG), +D,
@@ -10391,8 +10421,10 @@ code = '''
rbyd = backup_rbyd;
lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0;
lfs_bd_prog(&lfs, &lfs.pcache, &lfs.rcache, false,
rbyd.blocks[0], 0, backup_block, rbyd.eoff) => 0;
lfs_bd_flush(&lfs, &lfs.pcache, &lfs.rcache, false) => 0;
rbyd.blocks[0], 0, backup_block, rbyd.eoff,
NULL) => 0;
lfs_bd_flush(&lfs, &lfs.pcache, &lfs.rcache, false,
NULL) => 0;
lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS(
LFSR_ATTR(j*W+W-1, GROW(UATTR(1)), +D,
@@ -10539,8 +10571,10 @@ code = '''
rbyd = backup_rbyd;
lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0;
lfs_bd_prog(&lfs, &lfs.pcache, &lfs.rcache, false,
rbyd.blocks[0], 0, backup_block, rbyd.eoff) => 0;
lfs_bd_flush(&lfs, &lfs.pcache, &lfs.rcache, false) => 0;
rbyd.blocks[0], 0, backup_block, rbyd.eoff,
NULL) => 0;
lfs_bd_flush(&lfs, &lfs.pcache, &lfs.rcache, false,
NULL) => 0;
lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS(
LFSR_ATTR(j*W+W-1, GROW, -D, NULL()))) => 0;
@@ -10666,8 +10700,10 @@ code = '''
rbyd = backup_rbyd;
lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0;
lfs_bd_prog(&lfs, &lfs.pcache, &lfs.rcache, false,
rbyd.blocks[0], 0, backup_block, rbyd.eoff) => 0;
lfs_bd_flush(&lfs, &lfs.pcache, &lfs.rcache, false) => 0;
rbyd.blocks[0], 0, backup_block, rbyd.eoff,
NULL) => 0;
lfs_bd_flush(&lfs, &lfs.pcache, &lfs.rcache, false,
NULL) => 0;
lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS(
LFSR_ATTR(j*W+W-1, GROW(REG), -D,
@@ -10802,8 +10838,10 @@ code = '''
rbyd = backup_rbyd;
lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0;
lfs_bd_prog(&lfs, &lfs.pcache, &lfs.rcache, false,
rbyd.blocks[0], 0, backup_block, rbyd.eoff) => 0;
lfs_bd_flush(&lfs, &lfs.pcache, &lfs.rcache, false) => 0;
rbyd.blocks[0], 0, backup_block, rbyd.eoff,
NULL) => 0;
lfs_bd_flush(&lfs, &lfs.pcache, &lfs.rcache, false,
NULL) => 0;
lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS(
LFSR_ATTR(j*W+W-1, GROW(UATTR(1)), -D,
@@ -10949,8 +10987,10 @@ code = '''
rbyd = backup_rbyd;
lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0;
lfs_bd_prog(&lfs, &lfs.pcache, &lfs.rcache, false,
rbyd.blocks[0], 0, backup_block, rbyd.eoff) => 0;
lfs_bd_flush(&lfs, &lfs.pcache, &lfs.rcache, false) => 0;
rbyd.blocks[0], 0, backup_block, rbyd.eoff,
NULL) => 0;
lfs_bd_flush(&lfs, &lfs.pcache, &lfs.rcache, false,
NULL) => 0;
lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS(
LFSR_ATTR(j*W+W-1, RM, -W, NULL()))) => 0;
@@ -11104,8 +11144,10 @@ code = '''
rbyd = backup_rbyd;
lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0;
lfs_bd_prog(&lfs, &lfs.pcache, &lfs.rcache, false,
rbyd.blocks[0], 0, backup_block, rbyd.eoff) => 0;
lfs_bd_flush(&lfs, &lfs.pcache, &lfs.rcache, false) => 0;
rbyd.blocks[0], 0, backup_block, rbyd.eoff,
NULL) => 0;
lfs_bd_flush(&lfs, &lfs.pcache, &lfs.rcache, false,
NULL) => 0;
lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS(
LFSR_ATTR(j*W+W-1, UATTR(1), 0,
@@ -11712,8 +11754,10 @@ code = '''
rbyd = backup_rbyd;
lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0;
lfs_bd_prog(&lfs, &lfs.pcache, &lfs.rcache, false,
rbyd.blocks[0], 0, backup_block, rbyd.eoff) => 0;
lfs_bd_flush(&lfs, &lfs.pcache, &lfs.rcache, false) => 0;
rbyd.blocks[0], 0, backup_block, rbyd.eoff,
NULL) => 0;
lfs_bd_flush(&lfs, &lfs.pcache, &lfs.rcache, false,
NULL) => 0;
// remove with a wide tag
lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS(
@@ -11849,8 +11893,10 @@ code = '''
rbyd = backup_rbyd;
lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0;
lfs_bd_prog(&lfs, &lfs.pcache, &lfs.rcache, false,
rbyd.blocks[0], 0, backup_block, rbyd.eoff) => 0;
lfs_bd_flush(&lfs, &lfs.pcache, &lfs.rcache, false) => 0;
rbyd.blocks[0], 0, backup_block, rbyd.eoff,
NULL) => 0;
lfs_bd_flush(&lfs, &lfs.pcache, &lfs.rcache, false,
NULL) => 0;
// replace with bitwise inverse
lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS(
@@ -12079,8 +12125,10 @@ code = '''
rbyd = backup_rbyd;
lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0;
lfs_bd_prog(&lfs, &lfs.pcache, &lfs.rcache, false,
rbyd.blocks[0], 0, backup_block, rbyd.eoff) => 0;
lfs_bd_flush(&lfs, &lfs.pcache, &lfs.rcache, false) => 0;
rbyd.blocks[0], 0, backup_block, rbyd.eoff,
NULL) => 0;
lfs_bd_flush(&lfs, &lfs.pcache, &lfs.rcache, false,
NULL) => 0;
// remove with a wide tag
lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS(
@@ -12225,8 +12273,10 @@ code = '''
rbyd = backup_rbyd;
lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0;
lfs_bd_prog(&lfs, &lfs.pcache, &lfs.rcache, false,
rbyd.blocks[0], 0, backup_block, rbyd.eoff) => 0;
lfs_bd_flush(&lfs, &lfs.pcache, &lfs.rcache, false) => 0;
rbyd.blocks[0], 0, backup_block, rbyd.eoff,
NULL) => 0;
lfs_bd_flush(&lfs, &lfs.pcache, &lfs.rcache, false,
NULL) => 0;
// replace with bitwise inverse
lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS(
@@ -12456,8 +12506,10 @@ code = '''
rbyd = backup_rbyd;
lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0;
lfs_bd_prog(&lfs, &lfs.pcache, &lfs.rcache, false,
rbyd.blocks[0], 0, backup_block, rbyd.eoff) => 0;
lfs_bd_flush(&lfs, &lfs.pcache, &lfs.rcache, false) => 0;
rbyd.blocks[0], 0, backup_block, rbyd.eoff,
NULL) => 0;
lfs_bd_flush(&lfs, &lfs.pcache, &lfs.rcache, false,
NULL) => 0;
// remove with a wide tag
lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS(
@@ -12595,8 +12647,10 @@ code = '''
rbyd = backup_rbyd;
lfs_bd_erase(&lfs, rbyd.blocks[0]) => 0;
lfs_bd_prog(&lfs, &lfs.pcache, &lfs.rcache, false,
rbyd.blocks[0], 0, backup_block, rbyd.eoff) => 0;
lfs_bd_flush(&lfs, &lfs.pcache, &lfs.rcache, false) => 0;
rbyd.blocks[0], 0, backup_block, rbyd.eoff,
NULL) => 0;
lfs_bd_flush(&lfs, &lfs.pcache, &lfs.rcache, false,
NULL) => 0;
// replace with bitwise inverse
lfsr_rbyd_commit(&lfs, &rbyd, LFSR_ATTRS(