@@ -51,6 +51,27 @@ struct window_rewriting_params
5151 uint64_t cut_size{6 };
5252 uint64_t num_levels{5 };
5353
54+ /* Level information guides the windowing construction and as such impacts QoR:
55+ -- dont_update: fastest, but levels are wrong (QoR degrades)
56+ -- eager: fast, some levels are wrong
57+ -- precise: fast, all levels are correct (best QoR)
58+ -- recompute: slow, same as precise (used only for debugging)
59+ */
60+ enum
61+ {
62+ /* do not update any levels */
63+ dont_update,
64+ /* eagerly update the levels of changed nodes but avoid
65+ topological sorting (some levels will be wrong) */
66+ eager,
67+ /* precisely update the levels of changed nodes bottom-to-top and
68+ in topological order */
69+ precise,
70+ /* recompute all levels (also precise, but more expensive to
71+ compute) */
72+ recompute,
73+ } level_update_strategy = dont_update;
74+
5475 bool filter_cyclic_substitutions{false };
5576}; /* window_rewriting_params */
5677
@@ -77,6 +98,9 @@ struct window_rewriting_stats
7798 /* ! \brief Time for encoding index_list. */
7899 stopwatch<>::duration time_encode{0 };
79100
101+ /* ! \brief Time for detecting cycles. */
102+ stopwatch<>::duration time_cycle{0 };
103+
80104 /* ! \brief Total number of calls to the resub. engine. */
81105 uint64_t num_substitutions{0 };
82106 uint64_t num_restrashes{0 };
@@ -93,6 +117,7 @@ struct window_rewriting_stats
93117 time_topo_sort += other.time_topo_sort ;
94118 time_encode += other.time_encode ;
95119 num_substitutions += other.num_substitutions ;
120+ num_restrashes += other.num_restrashes ;
96121 num_windows += other.num_windows ;
97122 gain += other.gain ;
98123 return *this ;
@@ -177,20 +202,24 @@ class window_rewriting_impl
177202 : ntk( ntk )
178203 , ps( ps )
179204 , st( st )
205+ /* initialize levels to network depth */
206+ , levels( ntk.depth() )
180207 {
181208 auto const update_level_of_new_node = [&]( const auto & n ) {
182- ntk. resize_levels ( );
183- update_node_level ( n );
209+ stopwatch t ( st. time_total );
210+ update_levels ( n );
184211 };
185212
186213 auto const update_level_of_existing_node = [&]( node const & n, const auto & old_children ) {
187214 (void )old_children;
188- ntk. resize_levels ( );
189- update_node_level ( n );
215+ stopwatch t ( st. time_total );
216+ update_levels ( n );
190217 };
191218
192219 auto const update_level_of_deleted_node = [&]( node const & n ) {
220+ stopwatch t ( st.time_total );
193221 assert ( ntk.fanout_size ( n ) == 0u );
222+ assert ( ntk.is_dead ( n ) );
194223 ntk.set_level ( n, -1 );
195224 };
196225
@@ -205,7 +234,7 @@ class window_rewriting_impl
205234
206235 create_window_impl windowing ( ntk );
207236 uint32_t const size = ntk.size ();
208- for ( uint32_t n = 0u ; n < std::min ( size, ntk. size () ) ; ++n )
237+ for ( uint32_t n = 0u ; n < size; ++n )
209238 {
210239 if ( ntk.is_constant ( n ) || ntk.is_ci ( n ) || ntk.is_dead ( n ) )
211240 {
@@ -247,6 +276,9 @@ class window_rewriting_impl
247276 uint32_t counter{0 };
248277 ++st.num_substitutions ;
249278
279+ /* ensure that no dead nodes are reachable */
280+ assert ( count_reachable_dead_nodes ( ntk ) == 0u );
281+
250282 std::list<std::pair<node, signal>> substitutions;
251283 insert ( ntk, std::begin ( signals ), std::end ( signals ), *il_opt,
252284 [&]( signal const & _new )
@@ -260,34 +292,52 @@ class window_rewriting_impl
260292
261293 /* ensure that _old is not in the TFI of _new */
262294 // assert( !is_contained_in_tfi( ntk, ntk.get_node( _new ), ntk.get_node( _old ) ) );
263- if ( ps.filter_cyclic_substitutions && is_contained_in_tfi ( ntk, ntk.get_node ( _new ), ntk.get_node ( _old ) ) )
295+ if ( ps.filter_cyclic_substitutions &&
296+ call_with_stopwatch ( st.time_window , [&](){ return is_contained_in_tfi ( ntk, ntk.get_node ( _new ), ntk.get_node ( _old ) ); }) )
264297 {
265298 std::cout << " undo resubstitution " << ntk.get_node ( _old ) << std::endl;
266- if ( ntk.fanout_size ( ntk.get_node ( _new ) ) == 0u )
299+ substitutions.emplace_back ( std::make_pair ( ntk.get_node ( _old ), ntk.is_complemented ( _old ) ? !_new : _new ) );
300+ for ( auto it = std::rbegin ( substitutions ); it != std::rend ( substitutions ); ++it )
267301 {
268- ntk.take_out_node ( ntk.get_node ( _new ) );
302+ if ( ntk.fanout_size ( ntk.get_node ( it->second ) ) == 0u )
303+ {
304+ ntk.take_out_node ( ntk.get_node ( it->second ) );
305+ }
269306 }
307+ substitutions.clear ();
270308 return false ;
271309 }
272310
273311 substitutions.emplace_back ( std::make_pair ( ntk.get_node ( _old ), ntk.is_complemented ( _old ) ? !_new : _new ) );
274312 return true ;
275313 });
276314
315+ /* ensure that no dead nodes are reachable */
316+ assert ( count_reachable_dead_nodes ( ntk ) == 0u );
277317 substitute_nodes ( substitutions );
278318
279319 /* recompute levels and depth */
280- // call_with_stopwatch( st.time_levels, [&]() { ntk.update_levels(); } );
281- update_depth ();
320+ if ( ps.level_update_strategy == window_rewriting_params::recompute )
321+ {
322+ call_with_stopwatch ( st.time_levels , [&]() { ntk.update_levels (); } );
323+ }
324+ if ( ps.level_update_strategy != window_rewriting_params::dont_update )
325+ {
326+ update_depth ();
327+ }
282328
283329 /* ensure that no dead nodes are reachable */
284330 assert ( count_reachable_dead_nodes ( ntk ) == 0u );
285331
286332 /* ensure that the network structure is still acyclic */
287333 assert ( network_is_acylic ( ntk ) );
288334
289- /* ensure that the levels and depth is correct */
290- assert ( check_network_levels ( ntk ) );
335+ if ( ps.level_update_strategy == window_rewriting_params::precise ||
336+ ps.level_update_strategy == window_rewriting_params::recompute )
337+ {
338+ /* ensure that the levels and depth is correct */
339+ assert ( check_network_levels ( ntk ) );
340+ }
291341
292342 /* update internal data structures in windowing */
293343 windowing.resize ( ntk.size () );
@@ -370,7 +420,7 @@ class window_rewriting_impl
370420 {
371421 ntk.decr_fanout_size ( nn );
372422 }
373- /* remove the node if it's fanout_size becomes 0 */
423+ /* remove the node if its fanout_size becomes 0 */
374424 if ( ntk.fanout_size ( nn ) == 0 )
375425 {
376426 ntk.take_out_node ( nn );
@@ -399,24 +449,27 @@ class window_rewriting_impl
399449 auto const [old_node, new_signal] = substitutions.front ();
400450 substitutions.pop_front ();
401451
402- // for ( auto index = 1u; index < _storage->nodes.size(); ++index )
403- const auto parents = ntk.fanout ( old_node );
404- for ( auto index : parents )
452+ for ( auto index : ntk.fanout ( old_node ) )
405453 {
406454 /* skip CIs and dead nodes */
407455 if ( ntk.is_dead ( index ) )
456+ {
408457 continue ;
458+ }
409459
410460 /* skip nodes that will be deleted */
411461 if ( std::find_if ( std::begin ( substitutions ), std::end ( substitutions ),
412462 [&index]( auto s ){ return s.first == index; } ) != std::end ( substitutions ) )
463+ {
413464 continue ;
465+ }
414466
415467 /* replace in node */
416468 if ( const auto repl = ntk.replace_in_node ( index, old_node, new_signal ); repl )
417469 {
418470 ntk.incr_fanout_size ( ntk.get_node ( repl->second ) );
419471 substitutions.emplace_back ( *repl );
472+ ++st.num_restrashes ;
420473 }
421474 }
422475
@@ -442,16 +495,102 @@ class window_rewriting_impl
442495
443496 /* decrement fanout_size when released from substitution list */
444497 ntk.decr_fanout_size ( ntk.get_node ( new_signal ) );
498+ if ( ntk.fanout_size ( ntk.get_node ( new_signal ) ) == 0 )
499+ {
500+ ntk.take_out_node ( ntk.get_node ( new_signal ) );
501+ }
445502 }
446503
447504 ntk._events ->on_delete .pop_back ();
448505 }
449506
450- /* recursively update the node levels and the depth of the critical path */
451- void update_node_level ( node const & n )
507+ void update_levels ( node const & n )
452508 {
453- uint32_t const curr_level = ntk.level ( n );
509+ ntk.resize_levels ();
510+ if ( ps.level_update_strategy == window_rewriting_params::precise )
511+ {
512+ call_with_stopwatch ( st.time_levels , [&]() { update_node_level_precise ( n ); } );
513+ }
514+ else if ( ps.level_update_strategy == window_rewriting_params::eager )
515+ {
516+ call_with_stopwatch ( st.time_levels , [&]() { update_node_level_eager ( n ); } );
517+ }
518+
519+ /* levels can be wrong until substitute_nodes has finished */
520+ // assert( check_network_levels( ntk ) );
521+ }
522+
523+ /* precisely update node levels using an iterative topological sorting approach */
524+ void update_node_level_precise ( node const & n )
525+ {
526+ assert ( count_reachable_dead_nodes_from_node ( ntk, n ) == 0u );
527+ // assert( count_nodes_with_dead_fanins( ntk, n ) == 0u );
528+
529+ /* compute level of current node */
530+ uint32_t level_offset{0 };
531+ ntk.foreach_fanin ( n, [&]( signal const & fi ){
532+ level_offset = std::max ( ntk.level ( ntk.get_node ( fi ) ), level_offset );
533+ });
534+ ++level_offset;
535+
536+ /* add node into levels */
537+ if ( levels.size () < 1u )
538+ {
539+ levels.resize ( 1u );
540+ }
541+ levels[0 ].emplace_back ( n );
542+
543+ for ( uint32_t level_index = 0u ; level_index < levels.size (); ++level_index )
544+ {
545+ if ( levels[level_index].empty () )
546+ continue ;
547+
548+ for ( uint32_t node_index = 0u ; node_index < levels[level_index].size (); ++node_index )
549+ {
550+ node const p = levels[level_index][node_index];
551+
552+ /* recompute level of this node */
553+ uint32_t lvl{0 };
554+ ntk.foreach_fanin ( p, [&]( signal const & fi ){
555+ if ( ntk.is_dead ( ntk.get_node ( fi ) ) )
556+ return ;
557+
558+ lvl = std::max ( ntk.level ( ntk.get_node ( fi ) ), lvl );
559+ return ;
560+ });
561+ ++lvl;
562+ assert ( lvl > 0 );
454563
564+ /* update level and add fanouts to levels[.] if the recomputed
565+ level is different from the current level */
566+ if ( lvl != ntk.level ( p ) )
567+ {
568+ ntk.set_level ( p, lvl );
569+ ntk.foreach_fanout ( p, [&]( node const & fo ){
570+ assert ( std::max ( ntk.level ( fo ), lvl + 1 ) >= level_offset );
571+ uint32_t const pos = std::max ( ntk.level ( fo ), lvl + 1 ) - level_offset;
572+ assert ( pos >= 0u );
573+ assert ( pos >= level_index );
574+ if ( levels.size () <= pos )
575+ {
576+ levels.resize ( std::max ( uint32_t ( levels.size () << 1 ), pos + 1 ) );
577+ }
578+ levels[pos].emplace_back ( fo );
579+ });
580+ }
581+ }
582+
583+ /* clean the level */
584+ levels[level_index].clear ();
585+ }
586+ levels.clear ();
587+ }
588+
589+ /* eagerly update the node levels without topologically sorting (may
590+ stack-overflow if the network is deep)*/
591+ void update_node_level_eager ( node const & n )
592+ {
593+ uint32_t const curr_level = ntk.level ( n );
455594 uint32_t max_level = 0 ;
456595 ntk.foreach_fanin ( n, [&]( const auto & f ) {
457596 auto const p = ntk.get_node ( f );
@@ -469,21 +608,21 @@ class window_rewriting_impl
469608 ntk.foreach_fanout ( n, [&]( const auto & p ) {
470609 if ( !ntk.is_dead ( p ) )
471610 {
472- update_node_level ( p );
611+ update_node_level_eager ( p );
473612 }
474613 } );
475614 }
476615 }
477616
617+ /* update network depth (needs level information!) */
478618 void update_depth ()
479619 {
620+ stopwatch t ( st.time_levels );
621+
480622 uint32_t max_level{0 };
481- ntk.foreach_co ( [&]( signal s ){
623+ ntk.foreach_co ( [&]( signal const & s ){
482624 assert ( !ntk.is_dead ( ntk.get_node ( s ) ) );
483- if ( ntk.level ( ntk.get_node ( s ) ) > max_level )
484- {
485- max_level = ntk.level ( ntk.get_node ( s ) );
486- }
625+ max_level = std::max ( ntk.level ( ntk.get_node ( s ) ), max_level );
487626 });
488627
489628 if ( ntk.depth () != max_level )
@@ -496,9 +635,11 @@ class window_rewriting_impl
496635 Ntk& ntk;
497636 window_rewriting_params ps;
498637 window_rewriting_stats& st;
499- }; /* window_rewriting_impl */
500638
501- } /* detail */
639+ std::vector<std::vector<node>> levels;
640+ };
641+
642+ } /* namespace detail */
502643
503644template <class Ntk >
504645void window_rewriting ( Ntk& ntk, window_rewriting_params const & ps = {}, window_rewriting_stats* pst = nullptr )
0 commit comments