diff --git a/passes/opt/opt_merge.cc b/passes/opt/opt_merge.cc index eb3aa462e01..83518f5bef2 100644 --- a/passes/opt/opt_merge.cc +++ b/passes/opt/opt_merge.cc @@ -42,6 +42,22 @@ struct OptMergeWorker CellTypes ct; int total_count; + static vector> sorted_pmux_in(const dict &conn) + { + SigSpec sig_s = conn.at(ID::S); + SigSpec sig_b = conn.at(ID::B); + + int s_width = GetSize(sig_s); + int width = GetSize(sig_b) / s_width; + + vector> sb_pairs; + for (int i = 0; i < s_width; i++) + sb_pairs.push_back(pair(sig_s[i], sig_b.extract(i*width, width))); + + std::sort(sb_pairs.begin(), sb_pairs.end()); + return sb_pairs; + } + static void sort_pmux_conn(dict &conn) { SigSpec sig_s = conn.at(ID::S); @@ -65,90 +81,73 @@ struct OptMergeWorker } } - std::string int_to_hash_string(unsigned int v) + Hasher hash_cell_inputs(const RTLIL::Cell *cell, Hasher h) { - if (v == 0) - return "0"; - std::string str = ""; - while (v > 0) { - str += 'a' + (v & 15); - v = v >> 4; - } - return str; - } - - uint64_t hash_cell_parameters_and_connections(const RTLIL::Cell *cell) - { - vector hash_conn_strings; - std::string hash_string = cell->type.str() + "\n"; - - const dict *conn = &cell->connections(); - dict alt_conn; - + // TODO: when implemented, use celltypes to match: + // (builtin || stdcell) && (unary || binary) && symmetrical if (cell->type.in(ID($and), ID($or), ID($xor), ID($xnor), ID($add), ID($mul), ID($logic_and), ID($logic_or), ID($_AND_), ID($_OR_), ID($_XOR_))) { - alt_conn = *conn; - if (assign_map(alt_conn.at(ID::A)) < assign_map(alt_conn.at(ID::B))) { - alt_conn[ID::A] = conn->at(ID::B); - alt_conn[ID::B] = conn->at(ID::A); + std::array inputs = { + assign_map(cell->getPort(ID::A)), + assign_map(cell->getPort(ID::B)) + }; + std::sort(inputs.begin(), inputs.end()); + h = hash_ops>::hash_acc(inputs, h); + h = assign_map(cell->getPort(ID::Y)).hash_acc(h); + } else if (cell->type.in(ID($reduce_xor), ID($reduce_xnor))) { + SigSpec a = assign_map(cell->getPort(ID::A)); + a.sort(); + h = a.hash_acc(h); + } else if (cell->type.in(ID($reduce_and), ID($reduce_or), ID($reduce_bool))) { + SigSpec a = assign_map(cell->getPort(ID::A)); + a.sort_and_unify(); + h = a.hash_acc(h); + } else if (cell->type == ID($pmux)) { + dict conn = cell->connections(); + assign_map.apply(conn.at(ID::A)); + assign_map.apply(conn.at(ID::B)); + assign_map.apply(conn.at(ID::S)); + for (const auto& [s_bit, b_chunk] : sorted_pmux_in(conn)) { + h = s_bit.hash_acc(h); + h = b_chunk.hash_acc(h); } - conn = &alt_conn; - } else - if (cell->type.in(ID($reduce_xor), ID($reduce_xnor))) { - alt_conn = *conn; - assign_map.apply(alt_conn.at(ID::A)); - alt_conn.at(ID::A).sort(); - conn = &alt_conn; - } else - if (cell->type.in(ID($reduce_and), ID($reduce_or), ID($reduce_bool))) { - alt_conn = *conn; - assign_map.apply(alt_conn.at(ID::A)); - alt_conn.at(ID::A).sort_and_unify(); - conn = &alt_conn; - } else - if (cell->type == ID($pmux)) { - alt_conn = *conn; - assign_map.apply(alt_conn.at(ID::A)); - assign_map.apply(alt_conn.at(ID::B)); - assign_map.apply(alt_conn.at(ID::S)); - sort_pmux_conn(alt_conn); - conn = &alt_conn; - } - - for (auto &it : *conn) { - RTLIL::SigSpec sig; - if (cell->output(it.first)) { - if (it.first == ID::Q && RTLIL::builtin_ff_cell_types().count(cell->type)) { - // For the 'Q' output of state elements, - // use its (* init *) attribute value - sig = initvals(it.second); - } - else - continue; + h = assign_map(cell->getPort(ID::A)).hash_acc(h); + } else { + std::vector> conns; + for (const auto& conn : cell->connections()) { + conns.push_back(conn); } - else - sig = assign_map(it.second); - string s = "C " + it.first.str() + "="; - for (auto &chunk : sig.chunks()) { - if (chunk.wire) - s += "{" + chunk.wire->name.str() + " " + - int_to_hash_string(chunk.offset) + " " + - int_to_hash_string(chunk.width) + "}"; - else - s += RTLIL::Const(chunk.data).as_string(); + std::sort(conns.begin(), conns.end()); + for (const auto& [port, sig] : conns) { + if (!cell->output(port)) { + h = port.hash_acc(h); + h = sig.hash_acc(h); + } } - hash_conn_strings.push_back(s + "\n"); - } - for (auto &it : cell->parameters) - hash_conn_strings.push_back("P " + it.first.str() + "=" + it.second.as_string() + "\n"); + if (RTLIL::builtin_ff_cell_types().count(cell->type)) + h = initvals(cell->getPort(ID::Q)).hash_acc(h); - std::sort(hash_conn_strings.begin(), hash_conn_strings.end()); + } + return h; + } - for (auto it : hash_conn_strings) - hash_string += it; + static Hasher hash_cell_parameters(const RTLIL::Cell *cell, Hasher h) + { + using Paramvec = std::vector>; + Paramvec params; + for (const auto& param : cell->parameters) { + params.push_back(param); + } + std::sort(params.begin(), params.end()); + return hash_ops::hash_acc(params, h); + } - return std::hash{}(hash_string); + Hasher hash_cell_function(const RTLIL::Cell *cell, Hasher h) + { + h = hash_cell_inputs(cell, h); + h = hash_cell_parameters(cell, h); + return h; } bool compare_cell_parameters_and_connections(const RTLIL::Cell *cell1, const RTLIL::Cell *cell2) @@ -255,18 +254,23 @@ struct OptMergeWorker while (did_something) { std::vector cells; - cells.reserve(module->cells_.size()); - for (auto &it : module->cells_) { - if (!design->selected(module, it.second)) + cells.reserve(module->cells().size()); + for (auto cell : module->cells()) { + if (!design->selected(module, cell)) continue; - if (mode_keepdc && has_dont_care_initval(it.second)) + if (cell->type.in(ID($meminit), ID($meminit_v2), ID($mem), ID($mem_v2))) { + // Ignore those for performance: meminit can have an excessively large port, + // mem can have an excessively large parameter holding the init data + continue; + } + if (mode_keepdc && has_dont_care_initval(cell)) continue; - if (ct.cell_known(it.second->type) || (mode_share_all && it.second->known())) - cells.push_back(it.second); + if (ct.cell_known(cell->type) || (mode_share_all && cell->known())) + cells.push_back(cell); } did_something = false; - dict sharemap; + dict sharemap; for (auto cell : cells) { if ((!mode_share_all && !ct.cell_known(cell->type)) || !cell->known()) @@ -275,7 +279,7 @@ struct OptMergeWorker if (cell->type == ID($scopeinfo)) continue; - uint64_t hash = hash_cell_parameters_and_connections(cell); + Hasher::hash_t hash = hash_cell_function(cell, Hasher()).yield(); auto r = sharemap.insert(std::make_pair(hash, cell)); if (!r.second) { if (compare_cell_parameters_and_connections(cell, r.first->second)) {