diff --git a/experiments/window_rewriting.cpp b/experiments/window_rewriting.cpp new file mode 100644 index 000000000..63452003b --- /dev/null +++ b/experiments/window_rewriting.cpp @@ -0,0 +1,84 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2019 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "experiments.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +int main() +{ + using namespace experiments; + using namespace mockturtle; + + experiment + exp( "window_rewriting", "benchmark", "size_before", "size_after", "runtime", "resubs", "equivalent" ); + + for ( auto const& benchmark : epfl_benchmarks() ) + { + fmt::print( "[i] processing {}\n", benchmark ); + + /* read the benchmark */ + aig_network ntk; + if ( lorina::read_aiger( benchmark_path( benchmark ), aiger_reader( ntk ) ) != lorina::return_code::success ) + { + fmt::print( "[e] could not read {}\n", benchmark ); + continue; + } + + /* optimize benchmark */ + uint64_t const size_before{ntk.num_gates()}; + + fanout_view fntk{ntk}; + depth_view dntk{fntk}; + color_view aig{dntk}; + + window_rewriting_params ps; + ps.cut_size = 6u; + ps.num_levels = 5u; + + window_rewriting_stats st; + window_rewriting( aig, ps, &st ); + ntk = cleanup_dangling( ntk ); + + auto const cec = benchmark != "hyp" ? abc_cec( ntk, benchmark ) : true; + + exp( benchmark, size_before, ntk.num_gates(), to_seconds( st.time_total ), + st.num_substitutions, cec ); + } + + exp.save(); + exp.table(); + + return 0; +} diff --git a/include/mockturtle/algorithms/window_rewriting.hpp b/include/mockturtle/algorithms/window_rewriting.hpp new file mode 100644 index 000000000..8c5931f53 --- /dev/null +++ b/include/mockturtle/algorithms/window_rewriting.hpp @@ -0,0 +1,338 @@ +/* mockturtle: C++ logic network library + * Copyright (C) 2018-2019 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/*! + \file window_rewriting.hpp + \brief Window rewriting + + \author Heinz Riener +*/ + +#include "../utils/index_list.hpp" +#include "../utils/stopwatch.hpp" +#include "../utils/window_utils.hpp" +#include "../views/topo_view.hpp" +#include "../views/window_view.hpp" +#include "../utils/debugging_utils.hpp" + +#include +#include + +#pragma once + +namespace mockturtle +{ + +struct window_rewriting_params +{ + uint64_t cut_size{6}; + uint64_t num_levels{5}; +}; /* window_rewriting_params */ + +struct window_rewriting_stats +{ + /*! \brief Total runtime. */ + stopwatch<>::duration time_total{0}; + + /*! \brief Total number of calls to the resub. engine. */ + uint64_t num_substitutions{0}; +}; /* window_rewriting_stats */ + +namespace detail +{ + +template +class window_rewriting_impl +{ +public: + using node = typename Ntk::node; + using signal = typename Ntk::signal; + +public: + explicit window_rewriting_impl( Ntk& ntk, window_rewriting_params const& ps, window_rewriting_stats& st ) + : ntk( ntk ) + , ps( ps ) + , st( st ) + { + auto const update_level_of_new_node = [&]( const auto& n ) { + ntk.resize_levels(); + update_node_level( n ); + }; + + auto const update_level_of_existing_node = [&]( node const& n, const auto& old_children ) { + (void)old_children; + ntk.resize_levels(); + update_node_level( n ); + }; + + auto const update_level_of_deleted_node = [&]( node const& n ) { + assert( ntk.fanout_size( n ) == 0u ); + ntk.set_level( n, -1 ); + }; + + ntk._events->on_add.emplace_back( update_level_of_new_node ); + ntk._events->on_modified.emplace_back( update_level_of_existing_node ); + ntk._events->on_delete.emplace_back( update_level_of_deleted_node ); + } + + void run() + { + stopwatch t( st.time_total ); + + create_window_impl windowing( ntk ); + uint32_t const size = 3*ntk.size(); + for ( uint32_t n = 0u; n < std::min( size, ntk.size() ); ++n ) + { + if ( ntk.is_constant( n ) || ntk.is_ci( n ) || ntk.is_dead( n ) ) + { + continue; + } + + if ( const auto w = windowing.run( n, ps.cut_size, ps.num_levels ) ) + { + window_view win( ntk, w->inputs, w->outputs, w->nodes ); + topo_view topo_win{win}; + + abc_index_list il; + encode( il, topo_win ); + + auto il_opt = optimize( il ); + if ( !il_opt ) + { + continue; + } + + std::vector signals; + for ( auto const& i : w->inputs ) + { + signals.push_back( ntk.make_signal( i ) ); + } + + std::vector outputs; + topo_win.foreach_co( [&]( signal const& o ){ + outputs.push_back( o ); + }); + + std::vector new_outputs; + uint32_t counter{0}; + bool substitution_failure = false; + + ++st.num_substitutions; + insert( ntk, std::begin( signals ), std::end( signals ), *il_opt, + [&]( signal const& _new ) + { + auto const _old = outputs.at( counter++ ); + if ( substitution_failure ) + { + if ( ntk.fanout_size( ntk.get_node( _new ) ) == 0 ) + { + ntk.take_out_node( ntk.get_node( _new ) ); + } + return true; + } + if ( _old == _new ) + { + return true; + } + else if ( ntk.level( ntk.get_node( _old ) ) >= ntk.level( ntk.get_node( _new ) ) ) + { + auto const updates = substitute_node( ntk.get_node( _old ), topo_win.is_complemented( _old ) ? !_new : _new ); + update_vector( outputs, updates ); + } + else + { + if ( ntk.fanout_size( ntk.get_node( _new ) ) == 0 ) + { + ntk.take_out_node( ntk.get_node( _new ) ); + } + substitution_failure = true; + } + return true; + }); + + /* update internal data structures in windowing */ + windowing.resize( ntk.size() ); + } + } + + assert( count_reachable_dead_nodes( ntk ) == 0u ); + } + +private: + /* optimize an index_list and return the new list */ + std::optional optimize( abc_index_list const& il, bool verbose = false ) + { + int *raw = ABC_CALLOC( int, il.size() + 1u ); + uint64_t i = 0; + for ( auto const& v : il.raw() ) + { + raw[i++] = v; + } + raw[1] = 0; /* fix encoding */ + + abcresub::Abc_ResubPrepareManager( 1 ); + int *new_raw = nullptr; + int num_resubs = 0; + uint64_t new_entries = abcresub::Abc_ResubComputeWindow( raw, ( il.size() / 2u ), 1000, -1, 0, 0, 0, 0, &new_raw, &num_resubs ); + abcresub::Abc_ResubPrepareManager( 0 ); + + if ( verbose ) + { + fmt::print( "Performed resub {} times. Reduced {} nodes.\n", + num_resubs, new_entries > 0 ? ( ( il.size() / 2u ) - new_entries ) : 0 ); + } + + if ( raw ) + { + ABC_FREE( raw ); + } + + if ( new_entries > 0 ) + { + std::vector values; + for ( uint32_t i = 0; i < 2*new_entries; ++i ) + { + values.push_back( new_raw[i] ); + } + values[1u] = 1; /* fix encoding */ + if ( new_raw ) + { + ABC_FREE( new_raw ); + } + return abc_index_list( values, il.num_pis() ); + } + else + { + assert( new_raw == nullptr ); + return std::nullopt; + } + } + + /* substitute the node with a signal and return all strashing updates */ + std::vector> substitute_node( node const& old_node, signal const& new_signal ) + { + std::vector> updates; + std::stack> to_substitute; + to_substitute.push( {old_node, new_signal} ); + while ( !to_substitute.empty() ) + { + const auto [_old, _new] = to_substitute.top(); + to_substitute.pop(); + + auto const p = std::make_pair( _old, _new ); + if ( std::find( std::begin( updates ), std::end( updates ), p ) == std::end( updates ) ) + { + updates.push_back( p ); + } + + for ( auto idx = 1u; idx < ntk._storage->nodes.size(); ++idx ) + { + if ( ntk.is_ci( idx ) || ntk.is_dead( idx ) ) + continue; /* ignore CIs */ + + if ( const auto repl = ntk.replace_in_node( idx, _old, _new ); repl ) + { + to_substitute.push( *repl ); + } + } + + /* check outputs */ + ntk.replace_in_outputs( _old, _new ); + + /* reset fan-in of old node */ + ntk.take_out_node( _old ); + } + + return updates; + } + + void update_vector( std::vector& vs, std::vector> const& updates ) + { + for ( auto it = std::begin( vs ); it != std::end( vs ); ++it ) + { + for ( auto it2 = std::begin( updates ); it2 != std::end( updates ); ++it2 ) + { + if ( ntk.get_node( *it ) == it2->first ) + { + *it = ntk.is_complemented( *it ) ? !it2->second : it2->second; + } + } + } + } + + /* recursively update the node levels and the depth of the critical path */ + void update_node_level( node const& n, bool top_most = true ) + { + uint32_t curr_level = ntk.level( n ); + + uint32_t max_level = 0; + ntk.foreach_fanin( n, [&]( const auto& f ) { + auto const p = ntk.get_node( f ); + auto const fanin_level = ntk.level( p ); + if ( fanin_level > max_level ) + { + max_level = fanin_level; + } + } ); + ++max_level; + + if ( curr_level != max_level ) + { + ntk.set_level( n, max_level ); + if ( max_level >= ntk.depth() ) + { + ntk.set_depth( max_level + 1 ); + } + + /* update only one more level */ + if ( top_most ) + { + ntk.foreach_fanout( n, [&]( const auto& p ) { + update_node_level( p, false ); + } ); + } + } + } + +private: + Ntk& ntk; + window_rewriting_params ps; + window_rewriting_stats& st; +}; /* window_rewriting_impl */ + +} /* detail */ + +template +void window_rewriting( Ntk& ntk, window_rewriting_params const& ps = {}, window_rewriting_stats* pst = nullptr ) +{ + window_rewriting_stats st; + detail::window_rewriting_impl( ntk, ps, st ).run(); + if ( pst ) + { + *pst = st; + } +} + +} /* namespace mockturtle */