rbyd-rr: Fixed unreachable red alts not being pruned

It turns out we were never pruning unreachable red alts. I thought we
were because of lfsr_tag_prune2 and related logic, but this was custom
tailored for the specific unreachable patterns created by yellow splits,
and is insufficient for all unreachable alts.

Consider this unreachable altbgt 0x300 (the second one):

  altbgt 0x300 ----------> altbgt 0x300
  altrle 0x200 -----.----> altrgt 0x300 <-- unreachable,
  altbgt 0x300 -----'----> altble 0x200     should have
  null             =>      tag 0x100        been pruned
                 append
                tag 0x100

Our prune logic doesn't catch this because the altbgt is pointing a
different direction than the altrle we end up taking.

This wasn't an issue for earlier range-removal algorithms, since we have
a separate explicit check for diverged pruning to avoid weight ambiguity
issues. Black altas were also not an issue because this logic does catch
unreachable black alts, which are a bit easier. But now that we are
emitting intentionally unreachable red alts with the expectation that
they will be cleaned up by our pruning logic, this is a bit of a
problem...

The solution here is to check for unreachable alts after red flips. This
duplicates quite a bit of code but avoids the logical complexity of
figuring out reachability in all the permutations of red 2-3 nodes.
This commit is contained in:
Christopher Haster
2024-03-28 20:36:53 -05:00
parent a5999c892b
commit 0b6e2b243a
+131 -44
View File
@@ -934,6 +934,23 @@ static inline bool lfsr_tag_prune2(
}
}
static inline bool lfsr_tag_unreachable(
lfsr_tag_t alt, lfsr_rid_t weight,
lfsr_srid_t lower_rid, lfsr_srid_t upper_rid,
lfsr_tag_t lower_tag, lfsr_tag_t upper_tag) {
if (lfsr_tag_isgt(alt)) {
return !lfsr_tag_follow(
alt, weight,
lower_rid, upper_rid,
upper_rid-1, upper_tag-1);
} else {
return !lfsr_tag_follow(
alt, weight,
lower_rid, upper_rid,
lower_rid-1, lower_tag+1);
}
}
static inline void lfsr_tag_flip(
lfsr_tag_t *alt, lfsr_rid_t *weight,
lfsr_srid_t lower, lfsr_srid_t upper) {
@@ -2992,7 +3009,7 @@ again:;
// red alts we can collapse
if (lfsr_tag_isred(p_alts[0])) {
printf("%04x->%04x: rprune 0x%x w%d\n",
printf("%04x->%04x: yrprune 0x%x w%d\n",
branch,
rbyd->eoff,
alt,
@@ -3002,7 +3019,7 @@ again:;
// black alts just become unreachable, if we pruned these
// it would break the coloring of our tree
} else {
printf("%04x->%04x: bprune 0x%x w%d\n",
printf("%04x->%04x: ybprune 0x%x w%d\n",
branch,
rbyd->eoff,
alt,
@@ -3093,52 +3110,122 @@ again:;
}
}
// take black alt? needs a flip
// <b >b
// .-'| => .-'|
// 1 2 1 2 1
if (lfsr_tag_isblack(alt)
&& lfsr_tag_follow2(
alt, weight,
p_alts[0], p_weights[0],
lower_rid, upper_rid,
a_rid, a_tag)) {
lfsr_tag_flip2(&alt, &weight,
p_alts[0], p_weights[0],
lower_rid, upper_rid);
lfs_swap32(&jump, &branch_);
}
// should've taken red alt? needs a flip
// <r >r
// .----'| .-'|
// | <b => | >b
// | .-'| .--|-'|
// 1 2 3 1 2 3 1
if (lfsr_tag_isred(p_alts[0])
&& lfsr_tag_follow(p_alts[0], p_weights[0],
lower_rid, upper_rid,
a_rid, a_tag)) {
lfs_swap16(&p_alts[0], &alt);
lfs_swap32(&p_weights[0], &weight);
lfs_swap32(&p_jumps[0], &jump);
p_alts[0] |= LFSR_TAG_R;
alt &= ~LFSR_TAG_R;
lfsr_tag_flip2(&alt, &weight,
p_alts[0], p_weights[0],
lower_rid, upper_rid);
lfs_swap32(&jump, &branch_);
}
// black alts terminate 2-3-4 nodes
if (lfsr_tag_isblack(alt)) {
// trim alts from our current bounds
lfsr_tag_trim2(
// take black alt? needs a flip
// <b >b
// .-'| => .-'|
// 1 2 1 2 1
if (lfsr_tag_follow2(
alt, weight,
p_alts[0], p_weights[0],
&lower_rid, &upper_rid,
&lower_tag, &upper_tag);
lower_rid, upper_rid,
a_rid, a_tag)) {
lfsr_tag_flip2(&alt, &weight,
p_alts[0], p_weights[0],
lower_rid, upper_rid);
lfs_swap32(&jump, &branch_);
}
if (lfsr_tag_isred(p_alts[0])) {
// should've taken red alt? needs a flip
// <r >r
// .----'| .-'|
// | <b => | >b
// | .-'| .--|-'|
// 1 2 3 1 2 3 1
if (lfsr_tag_follow(p_alts[0], p_weights[0],
lower_rid, upper_rid,
a_rid, a_tag)) {
lfs_swap16(&p_alts[0], &alt);
lfs_swap32(&p_weights[0], &weight);
lfs_swap32(&p_jumps[0], &jump);
p_alts[0] |= LFSR_TAG_R;
alt &= ~LFSR_TAG_R;
lfsr_tag_flip2(&alt, &weight,
p_alts[0], p_weights[0],
lower_rid, upper_rid);
lfs_swap32(&jump, &branch_);
}
// red unreachable? prune
if (lfsr_tag_unreachable(
p_alts[0], p_weights[0],
lower_rid, upper_rid,
lower_tag, upper_tag)) {
printf("%04x->%04x: rprune 0x%x w%d\n",
branch,
rbyd->eoff,
p_alts[0],
p_weights[0]);
lfsr_tag_trim(
p_alts[0], p_weights[0],
&lower_rid, &upper_rid,
&lower_tag, &upper_tag);
lfsr_rbyd_p_pop(p_alts, p_weights, p_jumps);
// trim red bounds
} else {
lfsr_tag_trim(
p_alts[0], p_weights[0],
&lower_rid, &upper_rid,
&lower_tag, &upper_tag);
}
}
// unreachable?
if (lfsr_tag_unreachable(
alt, weight,
lower_rid, upper_rid,
lower_tag, upper_tag)) {
// red unreachable? prune
if (lfsr_tag_isred(p_alts[0])) {
printf("%04x->%04x: brprune 0x%x w%d\n",
branch,
rbyd->eoff,
alt,
weight);
lfsr_tag_trim(
alt, weight,
&lower_rid, &upper_rid,
&lower_tag, &upper_tag);
p_alts[0] &= ~LFSR_TAG_R;
y_branch = branch;
branch = branch_;
continue;
// black unreachable? collapse to alt-nevers, otherwise
// we risk breaking the coloring of our tree
} else {
printf("%04x->%04x: bprune 0x%x w%d\n",
branch,
rbyd->eoff,
alt,
weight);
lfsr_tag_trim(
alt, weight,
&lower_rid, &upper_rid,
&lower_tag, &upper_tag);
alt = LFSR_TAG_ALT(LFSR_TAG_LE, LFSR_TAG_B, 0);
weight = 0;
jump = 0;
}
// trim black bounds
} else {
lfsr_tag_trim(
alt, weight,
&lower_rid, &upper_rid,
&lower_tag, &upper_tag);
}
// // trim alts from our current bounds
// lfsr_tag_trim2(
// alt, weight,
// p_alts[0], p_weights[0],
// &lower_rid, &upper_rid,
// &lower_tag, &upper_tag);
//
// // keep track of last alt on diverged trunk to stitch the
// // trunks together with
// if ((d_state == LFSR_D_DIVERGEDLOWER