|
8 | 8 | #include <memory> |
9 | 9 | #include <string> |
10 | 10 | #include <string_view> |
| 11 | +#include <utility> |
11 | 12 |
|
12 | 13 | #include "spdlog/fmt/fmt.h" |
13 | 14 | #include "sta/NetworkClass.hh" |
@@ -66,52 +67,23 @@ InstanceSeq dbSdcNetwork::findInstancesMatching( |
66 | 67 | void dbSdcNetwork::findInstancesMatching1(const PatternMatch* pattern, |
67 | 68 | InstanceSeq& insts) const |
68 | 69 | { |
69 | | - // If the pattern is a literal (no wildcards and not a regex), return |
70 | | - // immediately as the literal lookup has already failed in the caller. |
| 70 | + // Literal pattern: serve from the precomputed pathological-path map. |
| 71 | + // Most designs contribute zero entries to this map, so the lookup is |
| 72 | + // O(1) and the miss path simply falls through. |
71 | 73 | if (!pattern->isRegexp() && !pattern->hasWildcards()) { |
| 74 | + const SdcPathToInstMap& map = sdcPathToInstMap(); |
| 75 | + auto it = map.find(pattern->pattern()); |
| 76 | + if (it != map.end()) { |
| 77 | + insts.push_back(it->second); |
| 78 | + } |
72 | 79 | return; |
73 | 80 | } |
74 | | - // A recursive lambda to traverse the design hierarchy with a depth-first |
75 | | - // search (DFS). |
76 | | - // It builds the hierarchical path incrementally using fmt::memory_buffer to |
77 | | - // avoid expensive std::string allocations and copies at each step. |
78 | | - std::function<void(Instance*, fmt::memory_buffer&)> dfs_search |
79 | | - = [&, this](Instance* instance, fmt::memory_buffer& path_buffer) -> void { |
80 | | - // Iterate over the children of the current instance. |
81 | | - std::unique_ptr<InstanceChildIterator> child_iter{childIterator(instance)}; |
82 | | - while (child_iter->hasNext()) { |
83 | | - Instance* child = child_iter->next(); |
84 | | - |
85 | | - // Save the current size of the buffer to restore it later. |
86 | | - const size_t original_size = path_buffer.size(); |
87 | | - |
88 | | - // Build the child's full path name incrementally. |
89 | | - if (original_size > 0) { |
90 | | - path_buffer.push_back(pathDivider()); |
91 | | - } |
92 | | - path_buffer.append(std::string_view(name(child))); |
93 | | - |
94 | | - // Check if the child instance name matches the pattern. |
95 | | - // Add a null terminator for C-style string compatibility. |
96 | | - path_buffer.push_back('\0'); |
97 | | - if (pattern->match(staToSdc(path_buffer.data()))) { |
98 | | - insts.push_back(child); |
99 | | - } |
100 | | - path_buffer.resize(path_buffer.size() - 1); // Remove the null terminator |
101 | | - |
102 | | - // Recurse into the child's hierarchy if it's not a leaf. |
103 | | - if (!isLeaf(child)) { |
104 | | - dfs_search(child, path_buffer); |
105 | | - } |
106 | | - |
107 | | - // Restore the buffer to its original state for the next sibling. |
108 | | - path_buffer.resize(original_size); |
109 | | - } |
110 | | - }; |
111 | | - |
112 | | - // Start the search from the top-level instance. |
113 | | - fmt::memory_buffer path_buffer; |
114 | | - dfs_search(topInstance(), path_buffer); |
| 81 | + visitAllInstancesSdcPath( |
| 82 | + [&](Instance* child, const std::string& sdc_path, bool /*any_div*/) { |
| 83 | + if (pattern->match(sdc_path)) { |
| 84 | + insts.push_back(child); |
| 85 | + } |
| 86 | + }); |
115 | 87 | } |
116 | 88 |
|
117 | 89 | NetSeq dbSdcNetwork::findNetsMatching(const Instance*, |
@@ -243,4 +215,119 @@ Pin* dbSdcNetwork::findPin(std::string_view path_name) const |
243 | 215 | return pin; |
244 | 216 | } |
245 | 217 |
|
| 218 | +void dbSdcNetwork::visitAllInstancesSdcPath(const SdcPathVisitor& visitor) const |
| 219 | +{ |
| 220 | + // Build paths incrementally in a fmt::memory_buffer to avoid the |
| 221 | + // per-step std::string allocations a naive DFS would incur. Using a |
| 222 | + // generic recursive lambda (auto& self) instead of a std::function |
| 223 | + // skips type erasure and the heap allocation it can incur. |
| 224 | + // The any_div flag propagates down so the visitor can cheaply tell |
| 225 | + // whether sdc_path's recursive splitter resolution would fail. |
| 226 | + auto rec = [&](auto& self, |
| 227 | + Instance* parent, |
| 228 | + fmt::memory_buffer& buf, |
| 229 | + bool any_div) -> void { |
| 230 | + std::unique_ptr<InstanceChildIterator> it{childIterator(parent)}; |
| 231 | + while (it->hasNext()) { |
| 232 | + Instance* child = it->next(); |
| 233 | + const size_t orig_size = buf.size(); |
| 234 | + if (orig_size > 0) { |
| 235 | + buf.push_back(pathDivider()); |
| 236 | + } |
| 237 | + const std::string leaf = name(child); |
| 238 | + // Short-circuit: once a subtree is pathological, descendants |
| 239 | + // inherit that flag without re-scanning the leaf string. |
| 240 | + const bool subtree_any_div |
| 241 | + = any_div || leaf.find(pathDivider()) != std::string::npos; |
| 242 | + buf.append(std::string_view(leaf)); |
| 243 | + buf.push_back('\0'); // null-terminate for staToSdc's C-string input |
| 244 | + visitor(child, staToSdc(buf.data()), subtree_any_div); |
| 245 | + buf.resize(buf.size() - 1); |
| 246 | + if (!isLeaf(child)) { |
| 247 | + self(self, child, buf, subtree_any_div); |
| 248 | + } |
| 249 | + buf.resize(orig_size); |
| 250 | + } |
| 251 | + }; |
| 252 | + fmt::memory_buffer buf; |
| 253 | + rec(rec, topInstance(), buf, false); |
| 254 | +} |
| 255 | + |
| 256 | +const dbSdcNetwork::SdcPathToInstMap& dbSdcNetwork::sdcPathToInstMap() const |
| 257 | +{ |
| 258 | + if (!cache_built_) { |
| 259 | + visitAllInstancesSdcPath( |
| 260 | + [&](Instance* inst, std::string sdc_path, bool any_div) { |
| 261 | + // Only pathological entries (those the recursive splitter in |
| 262 | + // findInstance cannot resolve) need to live in the cache. |
| 263 | + if (any_div) { |
| 264 | + inst_to_sdc_path_.emplace(inst, sdc_path); |
| 265 | + sdc_path_to_inst_.emplace(std::move(sdc_path), inst); |
| 266 | + } |
| 267 | + }); |
| 268 | + cache_built_ = true; |
| 269 | + } |
| 270 | + return sdc_path_to_inst_; |
| 271 | +} |
| 272 | + |
| 273 | +bool dbSdcNetwork::hasPathologicalPath(const Instance* inst) const |
| 274 | +{ |
| 275 | + const Instance* top = topInstance(); |
| 276 | + for (const Instance* a = inst; a && a != top; a = network_->parent(a)) { |
| 277 | + if (name(a).find(pathDivider()) != std::string::npos) { |
| 278 | + return true; |
| 279 | + } |
| 280 | + } |
| 281 | + return false; |
| 282 | +} |
| 283 | + |
| 284 | +void dbSdcNetwork::insertEntry(Instance* inst) const |
| 285 | +{ |
| 286 | + std::string sdc_path = SdcNetwork::pathName(inst); |
| 287 | + inst_to_sdc_path_.emplace(inst, sdc_path); |
| 288 | + sdc_path_to_inst_.emplace(std::move(sdc_path), inst); |
| 289 | +} |
| 290 | + |
| 291 | +void dbSdcNetwork::eraseEntry(const Instance* inst) const |
| 292 | +{ |
| 293 | + auto rev_it = inst_to_sdc_path_.find(inst); |
| 294 | + if (rev_it == inst_to_sdc_path_.end()) { |
| 295 | + return; |
| 296 | + } |
| 297 | + sdc_path_to_inst_.erase(rev_it->second); |
| 298 | + inst_to_sdc_path_.erase(rev_it); |
| 299 | +} |
| 300 | + |
| 301 | +void dbSdcNetwork::onInstCreated(Instance* inst) |
| 302 | +{ |
| 303 | + if (!cache_built_) { |
| 304 | + return; |
| 305 | + } |
| 306 | + if (!hasPathologicalPath(inst)) { |
| 307 | + return; |
| 308 | + } |
| 309 | + insertEntry(inst); |
| 310 | +} |
| 311 | + |
| 312 | +void dbSdcNetwork::onInstDestroyed(Instance* inst) |
| 313 | +{ |
| 314 | + if (!cache_built_) { |
| 315 | + return; |
| 316 | + } |
| 317 | + eraseEntry(inst); |
| 318 | +} |
| 319 | + |
| 320 | +void dbSdcNetwork::onInstRenamed(Instance* inst) |
| 321 | +{ |
| 322 | + if (!cache_built_) { |
| 323 | + return; |
| 324 | + } |
| 325 | + // Erase by Instance* (uses the reverse map) — no need to reconstruct |
| 326 | + // the pre-rename path. Re-insert if still pathological. |
| 327 | + eraseEntry(inst); |
| 328 | + if (hasPathologicalPath(inst)) { |
| 329 | + insertEntry(inst); |
| 330 | + } |
| 331 | +} |
| 332 | + |
246 | 333 | } // namespace sta |
0 commit comments