From 7d335ed0d977fa0eb9865a48992dc7f9ecf10f54 Mon Sep 17 00:00:00 2001 From: "Emil J. Tywoniak" Date: Wed, 6 May 2026 12:58:32 +0200 Subject: [PATCH] opt_merge: factor out hashing code across incremental and parallel --- passes/opt/opt_merge.cc | 169 +------------------------- passes/opt/opt_merge_common.h | 218 ++++++++++++++++++++++++++++++++++ passes/opt/opt_merge_inc.cc | 197 ++---------------------------- 3 files changed, 232 insertions(+), 352 deletions(-) create mode 100644 passes/opt/opt_merge_common.h diff --git a/passes/opt/opt_merge.cc b/passes/opt/opt_merge.cc index 275822845..cf12b0d12 100644 --- a/passes/opt/opt_merge.cc +++ b/passes/opt/opt_merge.cc @@ -24,6 +24,7 @@ #include "kernel/celltypes.h" #include "kernel/threading.h" #include "libs/sha1/sha1.h" +#include "passes/opt/opt_merge_common.h" #include #include #include @@ -35,8 +36,7 @@ USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN -template -inline Hasher hash_pair(const T &t, const U &u) { return hash_ops>::hash(t, u); } +using OptMergeCommon::CellHasher; // Some cell and its hash value. struct CellHash @@ -96,178 +96,19 @@ struct FoundDuplicates std::vector duplicates; }; -struct OptMergeThreadWorker +struct OptMergeThreadWorker : public CellHasher { const RTLIL::Module *module; - const SigMap &assign_map; - const FfInitVals &initvals; const CellTypes &ct; int workers; bool mode_share_all; bool mode_keepdc; - static Hasher hash_pmux_in(const SigSpec& sig_s, const SigSpec& sig_b, Hasher h) - { - int s_width = GetSize(sig_s); - int width = GetSize(sig_b) / s_width; - - hashlib::commutative_hash comm; - for (int i = 0; i < s_width; i++) - comm.eat(hash_pair(sig_s[i], sig_b.extract(i*width, width))); - - return comm.hash_into(h); - } - - static void sort_pmux_conn(dict &conn) - { - const SigSpec &sig_s = conn.at(ID::S); - const 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()); - - conn[ID::S] = SigSpec(); - conn[ID::B] = SigSpec(); - - for (auto &it : sb_pairs) { - conn[ID::S].append(it.first); - conn[ID::B].append(it.second); - } - } - - Hasher hash_cell_inputs(const RTLIL::Cell *cell, Hasher h) const - { - // 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_))) { - hashlib::commutative_hash comm; - comm.eat(assign_map(cell->getPort(ID::A))); - comm.eat(assign_map(cell->getPort(ID::B))); - h = comm.hash_into(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_into(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_into(h); - } else if (cell->type == ID($pmux)) { - SigSpec sig_s = assign_map(cell->getPort(ID::S)); - SigSpec sig_b = assign_map(cell->getPort(ID::B)); - h = hash_pmux_in(sig_s, sig_b, h); - h = assign_map(cell->getPort(ID::A)).hash_into(h); - } else { - hashlib::commutative_hash comm; - for (const auto& [port, sig] : cell->connections()) { - if (cell->output(port)) - continue; - comm.eat(hash_pair(port, assign_map(sig))); - } - h = comm.hash_into(h); - if (cell->is_builtin_ff()) - h = initvals(cell->getPort(ID::Q)).hash_into(h); - } - return h; - } - - static Hasher hash_cell_parameters(const RTLIL::Cell *cell, Hasher h) - { - hashlib::commutative_hash comm; - for (const auto& param : cell->parameters) { - comm.eat(param); - } - return comm.hash_into(h); - } - - Hasher hash_cell_function(const RTLIL::Cell *cell, Hasher h) const - { - h.eat(cell->type); - 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) const - { - if (cell1 == cell2) return true; - if (cell1->type != cell2->type) return false; - - if (cell1->parameters != cell2->parameters) - return false; - if (cell1->connections_.size() != cell2->connections_.size()) - return false; - for (const auto &it : cell1->connections_) - if (!cell2->connections_.count(it.first)) - return false; - - decltype(Cell::connections_) conn1, conn2; - conn1.reserve(cell1->connections_.size()); - conn2.reserve(cell1->connections_.size()); - - for (const auto &it : cell1->connections_) { - if (cell1->output(it.first)) { - if (it.first == ID::Q && cell1->is_builtin_ff()) { - // For the 'Q' output of state elements, - // use the (* init *) attribute value - conn1[it.first] = initvals(it.second); - conn2[it.first] = initvals(cell2->getPort(it.first)); - } - else { - conn1[it.first] = RTLIL::SigSpec(); - conn2[it.first] = RTLIL::SigSpec(); - } - } - else { - conn1[it.first] = assign_map(it.second); - conn2[it.first] = assign_map(cell2->getPort(it.first)); - } - } - - if (cell1->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_))) { - if (conn1.at(ID::A) < conn1.at(ID::B)) { - std::swap(conn1[ID::A], conn1[ID::B]); - } - if (conn2.at(ID::A) < conn2.at(ID::B)) { - std::swap(conn2[ID::A], conn2[ID::B]); - } - } else - if (cell1->type.in(ID($reduce_xor), ID($reduce_xnor))) { - conn1[ID::A].sort(); - conn2[ID::A].sort(); - } else - if (cell1->type.in(ID($reduce_and), ID($reduce_or), ID($reduce_bool))) { - conn1[ID::A].sort_and_unify(); - conn2[ID::A].sort_and_unify(); - } else - if (cell1->type == ID($pmux)) { - sort_pmux_conn(conn1); - sort_pmux_conn(conn2); - } - - return conn1 == conn2; - } - - bool has_dont_care_initval(const RTLIL::Cell *cell) const - { - if (!cell->is_builtin_ff()) - return false; - - return !initvals(cell->getPort(ID::Q)).is_fully_def(); - } - OptMergeThreadWorker(const RTLIL::Module *module, const FfInitVals &initvals, const SigMap &assign_map, const CellTypes &ct, int workers, bool mode_share_all, bool mode_keepdc) : - module(module), assign_map(assign_map), initvals(initvals), ct(ct), + CellHasher(assign_map, initvals, /*apply_sigmap=*/true), + module(module), ct(ct), workers(workers), mode_share_all(mode_share_all), mode_keepdc(mode_keepdc) { } diff --git a/passes/opt/opt_merge_common.h b/passes/opt/opt_merge_common.h new file mode 100644 index 000000000..41dfb6b3c --- /dev/null +++ b/passes/opt/opt_merge_common.h @@ -0,0 +1,218 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Claire Xenia Wolf + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#ifndef OPT_MERGE_COMMON_H +#define OPT_MERGE_COMMON_H + +#include "kernel/yosys.h" +#include "kernel/ffinit.h" +#include "kernel/sigtools.h" +#include "kernel/celltypes.h" +#include "kernel/hashlib.h" +#include +#include + +YOSYS_NAMESPACE_BEGIN + +namespace OptMergeCommon { + +template +inline Hasher hash_pair(const T &t, const U &u) { return hash_ops>::hash(t, u); } + +// Shared cell hashing/comparison logic for opt_merge and opt_merge_inc. +// When apply_sigmap is true, signals are run through assign_map before hashing +// and comparison. When false, signals are used as-is (the caller is expected to +// have pre-normalized them, e.g. via design->sigNormalize). +struct CellHasher +{ + const SigMap &assign_map; + const FfInitVals &initvals; + bool apply_sigmap; + + CellHasher(const SigMap &assign_map, const FfInitVals &initvals, bool apply_sigmap) + : assign_map(assign_map), initvals(initvals), apply_sigmap(apply_sigmap) {} + + SigSpec map_sig(const SigSpec &sig) const { + return apply_sigmap ? assign_map(sig) : sig; + } + + static Hasher hash_pmux_in(const SigSpec& sig_s, const SigSpec& sig_b, Hasher h) + { + int s_width = GetSize(sig_s); + int width = GetSize(sig_b) / s_width; + + hashlib::commutative_hash comm; + for (int i = 0; i < s_width; i++) + comm.eat(hash_pair(sig_s[i], sig_b.extract(i*width, width))); + + return comm.hash_into(h); + } + + static void sort_pmux_conn(dict &conn) + { + const SigSpec &sig_s = conn.at(ID::S); + const SigSpec &sig_b = conn.at(ID::B); + + int s_width = GetSize(sig_s); + int width = GetSize(sig_b) / s_width; + + std::vector> sb_pairs; + for (int i = 0; i < s_width; i++) + sb_pairs.push_back(std::pair(sig_s[i], sig_b.extract(i*width, width))); + + std::sort(sb_pairs.begin(), sb_pairs.end()); + + conn[ID::S] = SigSpec(); + conn[ID::B] = SigSpec(); + + for (auto &it : sb_pairs) { + conn[ID::S].append(it.first); + conn[ID::B].append(it.second); + } + } + + Hasher hash_cell_inputs(const RTLIL::Cell *cell, Hasher h) const + { + // 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_))) { + hashlib::commutative_hash comm; + comm.eat(map_sig(cell->getPort(ID::A))); + comm.eat(map_sig(cell->getPort(ID::B))); + h = comm.hash_into(h); + } else if (cell->type.in(ID($reduce_xor), ID($reduce_xnor))) { + SigSpec a = map_sig(cell->getPort(ID::A)); + a.sort(); + h = a.hash_into(h); + } else if (cell->type.in(ID($reduce_and), ID($reduce_or), ID($reduce_bool))) { + SigSpec a = map_sig(cell->getPort(ID::A)); + a.sort_and_unify(); + h = a.hash_into(h); + } else if (cell->type == ID($pmux)) { + SigSpec sig_s = map_sig(cell->getPort(ID::S)); + SigSpec sig_b = map_sig(cell->getPort(ID::B)); + h = hash_pmux_in(sig_s, sig_b, h); + h = map_sig(cell->getPort(ID::A)).hash_into(h); + } else { + hashlib::commutative_hash comm; + for (const auto& [port, sig] : cell->connections()) { + if (cell->output(port)) + continue; + comm.eat(hash_pair(port, map_sig(sig))); + } + h = comm.hash_into(h); + if (cell->is_builtin_ff()) + h = initvals(cell->getPort(ID::Q)).hash_into(h); + } + return h; + } + + static Hasher hash_cell_parameters(const RTLIL::Cell *cell, Hasher h) + { + hashlib::commutative_hash comm; + for (const auto& param : cell->parameters) { + comm.eat(param); + } + return comm.hash_into(h); + } + + Hasher hash_cell_function(const RTLIL::Cell *cell, Hasher h) const + { + h.eat(cell->type); + 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) const + { + if (cell1 == cell2) return true; + if (cell1->type != cell2->type) return false; + + if (cell1->parameters != cell2->parameters) + return false; + if (cell1->connections_.size() != cell2->connections_.size()) + return false; + for (const auto &it : cell1->connections_) + if (!cell2->connections_.count(it.first)) + return false; + + decltype(RTLIL::Cell::connections_) conn1, conn2; + conn1.reserve(cell1->connections_.size()); + conn2.reserve(cell1->connections_.size()); + + for (const auto &it : cell1->connections_) { + if (cell1->output(it.first)) { + if (it.first == ID::Q && cell1->is_builtin_ff()) { + // For the 'Q' output of state elements, + // use the (* init *) attribute value + conn1[it.first] = initvals(it.second); + conn2[it.first] = initvals(cell2->getPort(it.first)); + } + else { + conn1[it.first] = RTLIL::SigSpec(); + conn2[it.first] = RTLIL::SigSpec(); + } + } + else { + conn1[it.first] = map_sig(it.second); + conn2[it.first] = map_sig(cell2->getPort(it.first)); + } + } + + if (cell1->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_))) { + if (conn1.at(ID::A) < conn1.at(ID::B)) { + std::swap(conn1[ID::A], conn1[ID::B]); + } + if (conn2.at(ID::A) < conn2.at(ID::B)) { + std::swap(conn2[ID::A], conn2[ID::B]); + } + } else + if (cell1->type.in(ID($reduce_xor), ID($reduce_xnor))) { + conn1[ID::A].sort(); + conn2[ID::A].sort(); + } else + if (cell1->type.in(ID($reduce_and), ID($reduce_or), ID($reduce_bool))) { + conn1[ID::A].sort_and_unify(); + conn2[ID::A].sort_and_unify(); + } else + if (cell1->type == ID($pmux)) { + sort_pmux_conn(conn1); + sort_pmux_conn(conn2); + } + + return conn1 == conn2; + } + + bool has_dont_care_initval(const RTLIL::Cell *cell) const + { + if (!cell->is_builtin_ff()) + return false; + + return !initvals(cell->getPort(ID::Q)).is_fully_def(); + } +}; + +} // namespace OptMergeCommon + +YOSYS_NAMESPACE_END + +#endif diff --git a/passes/opt/opt_merge_inc.cc b/passes/opt/opt_merge_inc.cc index 4ad24d938..f4a2b7e1a 100644 --- a/passes/opt/opt_merge_inc.cc +++ b/passes/opt/opt_merge_inc.cc @@ -23,6 +23,7 @@ #include "kernel/log.h" #include "kernel/celltypes.h" #include "libs/sha1/sha1.h" +#include "passes/opt/opt_merge_common.h" #include #include #include @@ -34,8 +35,7 @@ USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN -template -inline Hasher hash_pair(const T &t, const U &u) { return hash_ops>::hash(t, u); } +using OptMergeCommon::CellHasher; struct OptMergeIncWorker { @@ -47,168 +47,11 @@ struct OptMergeIncWorker CellTypes ct; int total_count; - - static Hasher hash_pmux_in(const SigSpec& sig_s, const SigSpec& sig_b, Hasher h) - { - int s_width = GetSize(sig_s); - int width = GetSize(sig_b) / s_width; - - hashlib::commutative_hash comm; - for (int i = 0; i < s_width; i++) - comm.eat(hash_pair(sig_s[i], sig_b.extract(i*width, width))); - - return comm.hash_into(h); - } - - static void sort_pmux_conn(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()); - - conn[ID::S] = SigSpec(); - conn[ID::B] = SigSpec(); - - for (auto &it : sb_pairs) { - conn[ID::S].append(it.first); - conn[ID::B].append(it.second); - } - } - - Hasher hash_cell_inputs(const RTLIL::Cell *cell, Hasher h) const - { - // 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_))) { - hashlib::commutative_hash comm; - comm.eat((cell->getPort(ID::A))); - comm.eat((cell->getPort(ID::B))); - h = comm.hash_into(h); - } else if (cell->type.in(ID($reduce_xor), ID($reduce_xnor))) { - SigSpec a = (cell->getPort(ID::A)); - a.sort(); - h = a.hash_into(h); - } else if (cell->type.in(ID($reduce_and), ID($reduce_or), ID($reduce_bool))) { - SigSpec a = (cell->getPort(ID::A)); - a.sort_and_unify(); - h = a.hash_into(h); - } else if (cell->type == ID($pmux)) { - SigSpec sig_s = (cell->getPort(ID::S)); - SigSpec sig_b = (cell->getPort(ID::B)); - h = hash_pmux_in(sig_s, sig_b, h); - h = (cell->getPort(ID::A)).hash_into(h); - } else { - hashlib::commutative_hash comm; - for (const auto& [port, sig] : cell->connections()) { - if (cell->output(port)) - continue; - comm.eat(hash_pair(port, (sig))); - } - h = comm.hash_into(h); - if (cell->is_builtin_ff()) - h = initvals(cell->getPort(ID::Q)).hash_into(h); - } - return h; - } - - static Hasher hash_cell_parameters(const RTLIL::Cell *cell, Hasher h) - { - hashlib::commutative_hash comm; - for (const auto& param : cell->parameters) { - comm.eat(param); - } - return comm.hash_into(h); - } - - Hasher hash_cell_function(const RTLIL::Cell *cell, Hasher h) const - { - h.eat(cell->type); - 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) const - { - if (cell1 == cell2) return true; - if (cell1->type != cell2->type) return false; - - if (cell1->parameters != cell2->parameters) - return false; - - if (cell1->connections_.size() != cell2->connections_.size()) - return false; - for (const auto &it : cell1->connections_) - if (!cell2->connections_.count(it.first)) - return false; - - decltype(Cell::connections_) conn1, conn2; - conn1.reserve(cell1->connections_.size()); - conn2.reserve(cell1->connections_.size()); - - for (const auto &it : cell1->connections_) { - if (cell1->output(it.first)) { - if (it.first == ID::Q && cell1->is_builtin_ff()) { - // For the 'Q' output of state elements, - // use the (* init *) attribute value - conn1[it.first] = initvals(it.second); - conn2[it.first] = initvals(cell2->getPort(it.first)); - } - else { - conn1[it.first] = RTLIL::SigSpec(); - conn2[it.first] = RTLIL::SigSpec(); - } - } - else { - conn1[it.first] = (it.second); - conn2[it.first] = (cell2->getPort(it.first)); - } - } - - if (cell1->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_))) { - if (conn1.at(ID::A) < conn1.at(ID::B)) { - std::swap(conn1[ID::A], conn1[ID::B]); - } - if (conn2.at(ID::A) < conn2.at(ID::B)) { - std::swap(conn2[ID::A], conn2[ID::B]); - } - } else - if (cell1->type.in(ID($reduce_xor), ID($reduce_xnor))) { - conn1[ID::A].sort(); - conn2[ID::A].sort(); - } else - if (cell1->type.in(ID($reduce_and), ID($reduce_or), ID($reduce_bool))) { - conn1[ID::A].sort_and_unify(); - conn2[ID::A].sort_and_unify(); - } else - if (cell1->type == ID($pmux)) { - sort_pmux_conn(conn1); - sort_pmux_conn(conn2); - } - - return conn1 == conn2; - } - - bool has_dont_care_initval(const RTLIL::Cell *cell) - { - if (!cell->is_builtin_ff()) - return false; - - return !initvals(cell->getPort(ID::Q)).is_fully_def(); - } + CellHasher hasher; OptMergeIncWorker(RTLIL::Design *design, RTLIL::Module *module, bool mode_nomux, bool mode_share_all, bool mode_keepdc) : - design(design), module(module), mode_share_all(mode_share_all) + design(design), module(module), mode_share_all(mode_share_all), + hasher(assign_map, initvals, /*apply_sigmap=*/false) { total_count = 0; ct.setup_internals(); @@ -236,28 +79,6 @@ struct OptMergeIncWorker initvals.set(&assign_map, module); - // We keep a set of known cells. They're hashed with our hash_cell_function - // and compared with our compare_cell_parameters_and_connections. - // Both need to capture OptMergeIncWorker to access initvals - struct CellPtrHash { - const OptMergeIncWorker& worker; - CellPtrHash(const OptMergeIncWorker& w) : worker(w) {} - std::size_t operator()(const Cell* c) const { - return (std::size_t)worker.hash_cell_function(c, Hasher()).yield(); - } - }; - struct CellPtrEqual { - const OptMergeIncWorker& worker; - CellPtrEqual(const OptMergeIncWorker& w) : worker(w) {} - bool operator()(const Cell* lhs, const Cell* rhs) const { - return worker.compare_cell_parameters_and_connections(lhs, rhs); - } - }; - // std::unordered_set< - // RTLIL::Cell*, - // CellPtrHash, - // CellPtrEqual> known_cells (0, CellPtrHash(*this), CellPtrEqual(*this)); - dict hashes; dict first_with_hash; dict> more_with_hash; @@ -337,7 +158,7 @@ struct OptMergeIncWorker } if (cell->type == ID($scopeinfo)) continue; - if (mode_keepdc && has_dont_care_initval(cell)) + if (mode_keepdc && hasher.has_dont_care_initval(cell)) continue; if (!cell->known()) continue; @@ -366,7 +187,7 @@ struct OptMergeIncWorker } if (cell->type == ID($scopeinfo)) continue; - if (mode_keepdc && has_dont_care_initval(cell)) + if (mode_keepdc && hasher.has_dont_care_initval(cell)) continue; if (!cell->known()) continue; @@ -391,7 +212,7 @@ struct OptMergeIncWorker pool buckets; for (auto cell : cells) { - uint32_t hash = hash_cell_function(cell, Hasher()).yield(); + uint32_t hash = hasher.hash_cell_function(cell, Hasher()).yield(); if (remember_cell(cell, hash)) buckets.emplace(hash); } @@ -412,7 +233,7 @@ struct OptMergeIncWorker if (removed.count(cell)) break; - if (!compare_cell_parameters_and_connections(cell, other_cell)) + if (!hasher.compare_cell_parameters_and_connections(cell, other_cell)) continue; if (cell->has_keep_attr()) {