diff --git a/include/hal_core/netlist/decorators/netlist_traversal_decorator.h b/include/hal_core/netlist/decorators/netlist_traversal_decorator.h index 5b7225efddc..650cb4cf988 100644 --- a/include/hal_core/netlist/decorators/netlist_traversal_decorator.h +++ b/include/hal_core/netlist/decorators/netlist_traversal_decorator.h @@ -51,6 +51,8 @@ namespace hal * Traverse over gates that do not meet the `target_gate_filter` condition. * Stop traversal if (1) `continue_on_match` is `false` the `target_gate_filter` evaluates to `true`, (2) the `exit_endpoint_filter` evaluates to `false` on a fan-in/out endpoint (i.e., when exiting the current gate during traversal), or (3) the `entry_endpoint_filter` evaluates to `false` on a successor/predecessor endpoint (i.e., when entering the next gate during traversal). * Both the `entry_endpoint_filter` and the `exit_endpoint_filter` may be omitted. + * Provide a cache to speed up traversal when calling this function multiple times on the same netlist using the same forbidden pins. + * Do not use a cache if the filter functions operate on the `current_depth`. * * @param[in] net - Start net. * @param[in] successors - Set `true` to get successors, set `false` to get predecessors. @@ -58,6 +60,7 @@ namespace hal * @param[in] continue_on_match - Set `true` to continue even if `target_gate_filter` evaluated to `true`, `false` otherwise. Defaults to `false`. * @param[in] exit_endpoint_filter - Filter condition that determines whether to stop traversal on a fan-in/out endpoint. * @param[in] entry_endpoint_filter - Filter condition that determines whether to stop traversal on a successor/predecessor endpoint. + * @param[inout] cache - An optional cache that can be used for better performance on repeated calls. Defaults to a `nullptr`. * @returns The next gates fulfilling the target gate filter condition on success, an error otherwise. */ Result> get_next_matching_gates(const Net* net, @@ -65,13 +68,15 @@ namespace hal const std::function& target_gate_filter, bool continue_on_match = false, const std::function& exit_endpoint_filter = nullptr, - const std::function& entry_endpoint_filter = nullptr) const; + const std::function& entry_endpoint_filter = nullptr, + std::unordered_map>* cache = nullptr) const; /** * Starting from the given gate, traverse the netlist and return only the successor/predecessor gates for which the `target_gate_filter` evaluates to `true`. * Traverse over gates that do not meet the `target_gate_filter` condition. * Stop traversal if (1) `continue_on_match` is `false` the `target_gate_filter` evaluates to `true`, (2) the `exit_endpoint_filter` evaluates to `false` on a fan-in/out endpoint (i.e., when exiting the current gate during traversal), or (3) the `entry_endpoint_filter` evaluates to `false` on a successor/predecessor endpoint (i.e., when entering the next gate during traversal). * Both the `entry_endpoint_filter` and the `exit_endpoint_filter` may be omitted. + * Do not use a cache if the filter functions operate on the `current_depth`. * * @param[in] gate - Start gate. * @param[in] successors - Set `true` to get successors, set `false` to get predecessors. @@ -79,6 +84,7 @@ namespace hal * @param[in] continue_on_match - Set `true` to continue even if `target_gate_filter` evaluated to `true`, `false` otherwise. Defaults to `false`. * @param[in] exit_endpoint_filter - Filter condition that determines whether to stop traversal on a fan-in/out endpoint. * @param[in] entry_endpoint_filter - Filter condition that determines whether to stop traversal on a successor/predecessor endpoint. + * @param[inout] cache - An optional cache that can be used for better performance on repeated calls. Defaults to a `nullptr`. * @returns The next gates fulfilling the target gate filter condition on success, an error otherwise. */ Result> get_next_matching_gates(const Gate* gate, @@ -86,7 +92,8 @@ namespace hal const std::function& target_gate_filter, bool continue_on_match = false, const std::function& exit_endpoint_filter = nullptr, - const std::function& entry_endpoint_filter = nullptr) const; + const std::function& entry_endpoint_filter = nullptr, + std::unordered_map>* cache = nullptr) const; /** * Starting from the given net, traverse the netlist and return only the successor/predecessor gates for which the `target_gate_filter` evaluates to `true`. diff --git a/src/netlist/decorators/netlist_traversal_decorator.cpp b/src/netlist/decorators/netlist_traversal_decorator.cpp index 787a97ce64b..bd0a68c7e6c 100644 --- a/src/netlist/decorators/netlist_traversal_decorator.cpp +++ b/src/netlist/decorators/netlist_traversal_decorator.cpp @@ -14,7 +14,8 @@ namespace hal const std::function& target_gate_filter, bool continue_on_match, const std::function& exit_endpoint_filter, - const std::function& entry_endpoint_filter) const + const std::function& entry_endpoint_filter, + std::unordered_map>* cache) const { if (net == nullptr) { @@ -56,11 +57,21 @@ namespace hal continue; } - auto* g = entry_ep->get_gate(); + auto* gate = entry_ep->get_gate(); - if (target_gate_filter(g)) + if (target_gate_filter(gate)) { - res.insert(g); + res.insert(gate); + + // update cache + if (cache) + { + (*cache)[current].insert(gate); + for (const auto* n : previous) + { + (*cache)[n].insert(gate); + } + } if (!continue_on_match) { @@ -68,17 +79,37 @@ namespace hal } } - for (const auto* exit_ep : successors ? g->get_fan_out_endpoints() : g->get_fan_in_endpoints()) + for (const auto* exit_ep : successors ? gate->get_fan_out_endpoints() : gate->get_fan_in_endpoints()) { if (exit_endpoint_filter != nullptr && !exit_endpoint_filter(exit_ep, previous.size() + 1)) { continue; } - const Net* n = exit_ep->get_net(); - if (visited.find(n) == visited.end()) + const Net* exit_net = exit_ep->get_net(); + if (cache) { - stack.push_back(n); + if (const auto it = cache->find(exit_net); it != cache->end()) + { + const auto& cached_gates = std::get<1>(*it); + + // append cached gates to result + res.insert(cached_gates.begin(), cached_gates.end()); + + // update cache + (*cache)[current].insert(cached_gates.begin(), cached_gates.end()); + for (const auto* n : previous) + { + (*cache)[n].insert(cached_gates.begin(), cached_gates.end()); + } + + continue; + } + } + + if (visited.find(exit_net) == visited.end()) + { + stack.push_back(exit_net); added = true; } } @@ -102,7 +133,8 @@ namespace hal const std::function& target_gate_filter, bool continue_on_match, const std::function& exit_endpoint_filter, - const std::function& entry_endpoint_filter) const + const std::function& entry_endpoint_filter, + std::unordered_map>* cache) const { if (gate == nullptr) { @@ -122,7 +154,21 @@ namespace hal continue; } - const auto next_res = this->get_next_matching_gates(exit_ep->get_net(), successors, target_gate_filter, continue_on_match, exit_endpoint_filter, entry_endpoint_filter); + const auto* exit_net = exit_ep->get_net(); + if (cache) + { + if (const auto it = cache->find(exit_net); it != cache->end()) + { + const auto& cached_gates = std::get<1>(*it); + + // append cached gates to result + res.insert(cached_gates.begin(), cached_gates.end()); + + continue; + } + } + + const auto next_res = this->get_next_matching_gates(exit_net, successors, target_gate_filter, continue_on_match, exit_endpoint_filter, entry_endpoint_filter, cache); if (next_res.is_error()) { return ERR(next_res.get_error()); diff --git a/tests/netlist/decorators.cpp b/tests/netlist/decorators.cpp index 068788cec66..3de9aa4307f 100644 --- a/tests/netlist/decorators.cpp +++ b/tests/netlist/decorators.cpp @@ -672,6 +672,15 @@ namespace hal { EXPECT_TRUE(res.is_ok()); EXPECT_EQ(res.get(), std::set({dff4, dff5, dff6})); } + { + std::unordered_map> cache; + const auto res1 = trav_dec.get_next_matching_gates(dff2, true, [](const Gate* g) { return g->get_type()->has_property(GateTypeProperty::ff); }, false, nullptr, nullptr, &cache); + EXPECT_TRUE(res1.is_ok()); + EXPECT_EQ(res1.get(), std::set({dff5, dff6, dff7, dff3})); + const auto res2 = trav_dec.get_next_matching_gates(dff3, true, [](const Gate* g) { return g->get_type()->has_property(GateTypeProperty::ff); }, false, nullptr, nullptr, &cache); + EXPECT_TRUE(res2.is_ok()); + EXPECT_EQ(res2.get(), std::set({dff6, dff7, dff3})); + } // predecessors { @@ -684,6 +693,15 @@ namespace hal { EXPECT_TRUE(res.is_ok()); EXPECT_EQ(res.get(), std::set({dff0, dff1, dff2})); } + { + std::unordered_map> cache; + const auto res1 = trav_dec.get_next_matching_gates(dff6, false, [](const Gate* g) { return g->get_type()->has_property(GateTypeProperty::ff); }, false, nullptr, nullptr, &cache); + EXPECT_TRUE(res1.is_ok()); + EXPECT_EQ(res1.get(), std::set({dff1, dff2, dff3, sff0, sff1})); + const auto res2 = trav_dec.get_next_matching_gates(dff7, false, [](const Gate* g) { return g->get_type()->has_property(GateTypeProperty::ff); }, false, nullptr, nullptr, &cache); + EXPECT_TRUE(res2.is_ok()); + EXPECT_EQ(res2.get(), std::set({dff2, dff3, sff0, sff1})); + } } { // test NetlistModificationDecorator::get_next_matching_gates_until