@@ -88,6 +88,7 @@ Levelize::clear()
8888 delete loop;
8989 loops_.clear ();
9090 loop_edges_.clear ();
91+ latch_d_to_q_edges_.clear ();
9192 max_level_ = 0 ;
9293}
9394
@@ -520,6 +521,18 @@ Levelize::assignLevels(VertexSeq &topo_sorted)
520521// This is because the Q arrival depends on the D arrival and
521522// to find them in parallel they have to be scheduled separately
522523// to avoid a race condition.
524+ // latch_d_to_q_edges_ is kept as a persistent set across incremental
525+ // relevelizations (cleared only by clear() on full re-levelize).
526+ // This is necessary because during incremental relevelize() only the
527+ // D vertices that happen to be visited accumulate edges into
528+ // latch_d_to_q_edges_ via visit(). If Q's clock path is relevelize'd
529+ // (Q.level changes to equal D.level) without touching D's data path,
530+ // D is never visited and its DtoQ edge would be absent from a
531+ // transient set — leaving the D/Q level collision undetected.
532+ // By keeping the set persistent and removing edges only when they are
533+ // deleted (see deleteEdgeBefore), every incremental ensureLatchLevels
534+ // sees all live latch DtoQ edges regardless of which vertices were
535+ // visited in the current run.
523536void
524537Levelize::ensureLatchLevels ()
525538{
@@ -529,7 +542,6 @@ Levelize::ensureLatchLevels()
529542 if (from->level () == to->level ())
530543 setLevel (from, from->level () + level_space_);
531544 }
532- latch_d_to_q_edges_.clear ();
533545}
534546
535547void
0 commit comments