Skip to content
This repository was archived by the owner on Apr 29, 2026. It is now read-only.

Commit c0b6369

Browse files
committed
Refactor cone building logic in pe_synth.h for improved safety and clarity
- Replaced recursive depth-first search with an iterative approach to build the cone in topological order, enhancing cycle safety. - Introduced a new `idx_of` function to streamline node indexing and improve readability. - Utilized a stack-based frame structure to manage node processing, reducing the risk of stack overflow and improving maintainability. - Enhanced error handling for combinational loops and invalid nodes, ensuring robustness in the cone construction process.
1 parent 230ed73 commit c0b6369

2 files changed

Lines changed: 237 additions & 78 deletions

File tree

include/phy_engine/verilog/digital/pe_synth.h

Lines changed: 180 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -8954,45 +8954,97 @@ namespace phy_engine::verilog::digital
89548954
out_index.reserve(gate_count * 2u + 8u);
89558955
bool ok2{true};
89568956

8957-
auto dfs2 = [&](auto&& self, ::phy_engine::model::node_t* n) noexcept -> ::std::uint8_t
8958-
{
8959-
if(!ok2 || n == nullptr)
8960-
{
8961-
ok2 = false;
8962-
return 254u;
8963-
}
8964-
if(auto itc = const_idx.find(n); itc != const_idx.end()) { return itc->second; }
8965-
if(auto itl = leaf_index.find(n); itl != leaf_index.end()) { return itl->second; }
8966-
if(auto ito = out_index.find(n); ito != out_index.end()) { return ito->second; }
8967-
8968-
auto itg = gate_by_out.find(n);
8969-
if(itg == gate_by_out.end())
8970-
{
8971-
ok2 = false;
8972-
return 254u;
8973-
}
8974-
auto const& gg = itg->second;
8975-
8976-
auto const a = self(self, gg.in0);
8977-
auto const b = (gg.k == kind::not_gate) ? static_cast<::std::uint8_t>(254u) : self(self, gg.in1);
8978-
if(!ok2) { return 254u; }
8979-
8980-
if(cone.gate_count >= 64u)
8981-
{
8982-
ok2 = false;
8983-
return 254u;
8984-
}
8985-
auto const gi = static_cast<::std::uint8_t>(cone.gate_count++);
8986-
cone.kind[gi] = enc_kind(gg.k);
8987-
cone.in0[gi] = a;
8988-
cone.in1[gi] = b;
8989-
auto const out = static_cast<::std::uint8_t>(6u + gi);
8990-
out_index.emplace(n, out);
8991-
return out;
8992-
};
8993-
8994-
(void)dfs2(dfs2, root);
8995-
if(!ok2 || cone.gate_count == 0u) { continue; }
8957+
// Build a u64 cone in topological order (iterative; cycle-safe).
8958+
auto idx_of = [&](::phy_engine::model::node_t* n) noexcept -> ::std::optional<::std::uint8_t>
8959+
{
8960+
if(n == nullptr) { return ::std::nullopt; }
8961+
if(auto itc = const_idx.find(n); itc != const_idx.end()) { return itc->second; }
8962+
if(auto itl = leaf_index.find(n); itl != leaf_index.end()) { return itl->second; }
8963+
if(auto ito = out_index.find(n); ito != out_index.end()) { return ito->second; }
8964+
return ::std::nullopt;
8965+
};
8966+
8967+
struct frame
8968+
{
8969+
::phy_engine::model::node_t* n{};
8970+
bool expanded{};
8971+
};
8972+
8973+
::std::unordered_map<::phy_engine::model::node_t*, bool> visiting{};
8974+
visiting.reserve(gate_count * 2u + 8u);
8975+
8976+
::std::vector<frame> st{};
8977+
st.reserve(128);
8978+
st.push_back(frame{root, false});
8979+
while(!st.empty())
8980+
{
8981+
if(!ok2) { break; }
8982+
auto& f = st.back();
8983+
auto* n = f.n;
8984+
if(n == nullptr)
8985+
{
8986+
ok2 = false;
8987+
break;
8988+
}
8989+
if(idx_of(n).has_value())
8990+
{
8991+
st.pop_back();
8992+
continue;
8993+
}
8994+
auto itg = gate_by_out.find(n);
8995+
if(itg == gate_by_out.end())
8996+
{
8997+
ok2 = false;
8998+
break;
8999+
}
9000+
auto const& gg = itg->second;
9001+
9002+
if(!f.expanded)
9003+
{
9004+
if(visiting.contains(n))
9005+
{
9006+
// Combinational loop inside the gate graph.
9007+
ok2 = false;
9008+
break;
9009+
}
9010+
visiting.emplace(n, true);
9011+
f.expanded = true;
9012+
9013+
// Post-order: push children first.
9014+
if(gg.k != kind::not_gate)
9015+
{
9016+
if(!idx_of(gg.in1).has_value()) { st.push_back(frame{gg.in1, false}); }
9017+
}
9018+
if(!idx_of(gg.in0).has_value()) { st.push_back(frame{gg.in0, false}); }
9019+
continue;
9020+
}
9021+
9022+
auto a = idx_of(gg.in0);
9023+
if(!a) { ok2 = false; break; }
9024+
::std::optional<::std::uint8_t> b{};
9025+
if(gg.k == kind::not_gate) { b = static_cast<::std::uint8_t>(254u); }
9026+
else
9027+
{
9028+
b = idx_of(gg.in1);
9029+
}
9030+
if(!b) { ok2 = false; break; }
9031+
9032+
if(cone.gate_count >= 64u)
9033+
{
9034+
ok2 = false;
9035+
break;
9036+
}
9037+
auto const gi = static_cast<::std::uint8_t>(cone.gate_count++);
9038+
cone.kind[gi] = enc_kind(gg.k);
9039+
cone.in0[gi] = *a;
9040+
cone.in1[gi] = *b;
9041+
auto const out = static_cast<::std::uint8_t>(6u + gi);
9042+
out_index.emplace(n, out);
9043+
visiting.erase(n);
9044+
st.pop_back();
9045+
}
9046+
9047+
if(!ok2 || cone.gate_count == 0u) { continue; }
89969048

89979049
cd.leaves = ::std::move(leaves);
89989050
cd.cone = cone;
@@ -9857,45 +9909,95 @@ namespace phy_engine::verilog::digital
98579909
out_index.reserve(gate_count * 2u + 8u);
98589910
bool ok2{true};
98599911

9860-
auto dfs2 = [&](auto&& self, ::phy_engine::model::node_t* n) noexcept -> ::std::uint8_t
9861-
{
9862-
if(!ok2 || n == nullptr)
9863-
{
9864-
ok2 = false;
9865-
return 254u;
9866-
}
9867-
if(auto itc = const_idx.find(n); itc != const_idx.end()) { return itc->second; }
9868-
if(auto itl = leaf_index.find(n); itl != leaf_index.end()) { return itl->second; }
9869-
if(auto ito = out_index.find(n); ito != out_index.end()) { return ito->second; }
9870-
9871-
auto itg = gate_by_out.find(n);
9872-
if(itg == gate_by_out.end())
9873-
{
9874-
ok2 = false;
9875-
return 254u;
9876-
}
9877-
auto const& gg = itg->second;
9878-
9879-
auto const a = self(self, gg.in0);
9880-
auto const b = (gg.k == kind::not_gate) ? static_cast<::std::uint8_t>(254u) : self(self, gg.in1);
9881-
if(!ok2) { return 254u; }
9882-
9883-
if(cone.gate_count >= 64u)
9884-
{
9885-
ok2 = false;
9886-
return 254u;
9887-
}
9888-
auto const gi = static_cast<::std::uint8_t>(cone.gate_count++);
9889-
cone.kind[gi] = enc_kind(gg.k);
9890-
cone.in0[gi] = a;
9891-
cone.in1[gi] = b;
9892-
auto const out = static_cast<::std::uint8_t>(6u + gi);
9893-
out_index.emplace(n, out);
9894-
return out;
9895-
};
9896-
9897-
(void)dfs2(dfs2, root);
9898-
if(!ok2 || cone.gate_count == 0u) { continue; }
9912+
// Build a u64 cone in topological order (iterative; cycle-safe).
9913+
auto idx_of = [&](::phy_engine::model::node_t* n) noexcept -> ::std::optional<::std::uint8_t>
9914+
{
9915+
if(n == nullptr) { return ::std::nullopt; }
9916+
if(auto itc = const_idx.find(n); itc != const_idx.end()) { return itc->second; }
9917+
if(auto itl = leaf_index.find(n); itl != leaf_index.end()) { return itl->second; }
9918+
if(auto ito = out_index.find(n); ito != out_index.end()) { return ito->second; }
9919+
return ::std::nullopt;
9920+
};
9921+
9922+
struct frame
9923+
{
9924+
::phy_engine::model::node_t* n{};
9925+
bool expanded{};
9926+
};
9927+
9928+
::std::unordered_map<::phy_engine::model::node_t*, bool> visiting{};
9929+
visiting.reserve(gate_count * 2u + 8u);
9930+
9931+
::std::vector<frame> st{};
9932+
st.reserve(128);
9933+
st.push_back(frame{root, false});
9934+
while(!st.empty())
9935+
{
9936+
if(!ok2) { break; }
9937+
auto& f = st.back();
9938+
auto* n = f.n;
9939+
if(n == nullptr)
9940+
{
9941+
ok2 = false;
9942+
break;
9943+
}
9944+
if(idx_of(n).has_value())
9945+
{
9946+
st.pop_back();
9947+
continue;
9948+
}
9949+
auto itg = gate_by_out.find(n);
9950+
if(itg == gate_by_out.end())
9951+
{
9952+
ok2 = false;
9953+
break;
9954+
}
9955+
auto const& gg = itg->second;
9956+
9957+
if(!f.expanded)
9958+
{
9959+
if(visiting.contains(n))
9960+
{
9961+
ok2 = false;
9962+
break;
9963+
}
9964+
visiting.emplace(n, true);
9965+
f.expanded = true;
9966+
9967+
if(gg.k != kind::not_gate)
9968+
{
9969+
if(!idx_of(gg.in1).has_value()) { st.push_back(frame{gg.in1, false}); }
9970+
}
9971+
if(!idx_of(gg.in0).has_value()) { st.push_back(frame{gg.in0, false}); }
9972+
continue;
9973+
}
9974+
9975+
auto a = idx_of(gg.in0);
9976+
if(!a) { ok2 = false; break; }
9977+
::std::optional<::std::uint8_t> b{};
9978+
if(gg.k == kind::not_gate) { b = static_cast<::std::uint8_t>(254u); }
9979+
else
9980+
{
9981+
b = idx_of(gg.in1);
9982+
}
9983+
if(!b) { ok2 = false; break; }
9984+
9985+
if(cone.gate_count >= 64u)
9986+
{
9987+
ok2 = false;
9988+
break;
9989+
}
9990+
auto const gi = static_cast<::std::uint8_t>(cone.gate_count++);
9991+
cone.kind[gi] = enc_kind(gg.k);
9992+
cone.in0[gi] = *a;
9993+
cone.in1[gi] = *b;
9994+
auto const out = static_cast<::std::uint8_t>(6u + gi);
9995+
out_index.emplace(n, out);
9996+
visiting.erase(n);
9997+
st.pop_back();
9998+
}
9999+
10000+
if(!ok2 || cone.gate_count == 0u) { continue; }
989910001

990010002
cd.leaves = ::std::move(leaves);
990110003
cd.cone = cone;
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
#include <cstddef>
2+
#include <cstdint>
3+
#include <vector>
4+
5+
#include <phy_engine/phy_engine.h>
6+
#include <phy_engine/verilog/digital/pe_synth.h>
7+
8+
int main()
9+
{
10+
using namespace phy_engine;
11+
12+
// Regression: resub/sweep cone builders must not recurse infinitely on cyclic gate graphs.
13+
::phy_engine::circult c{};
14+
c.set_analyze_type(::phy_engine::analyze_type::TR);
15+
auto& nl = c.get_netlist();
16+
17+
auto& n0_ref = ::phy_engine::netlist::create_node(nl);
18+
auto& n1_ref = ::phy_engine::netlist::create_node(nl);
19+
auto* n0 = __builtin_addressof(n0_ref);
20+
auto* n1 = __builtin_addressof(n1_ref);
21+
22+
// Create a small combinational loop: n0 = ~n1, n1 = ~n0.
23+
{
24+
auto [m0, pos0] = ::phy_engine::netlist::add_model(nl, ::phy_engine::model::NOT{});
25+
(void)pos0;
26+
if(m0 == nullptr) { return 1; }
27+
if(!::phy_engine::netlist::add_to_node(nl, *m0, 0, *n1) || !::phy_engine::netlist::add_to_node(nl, *m0, 1, *n0)) { return 2; }
28+
}
29+
{
30+
auto [m1, pos1] = ::phy_engine::netlist::add_model(nl, ::phy_engine::model::NOT{});
31+
(void)pos1;
32+
if(m1 == nullptr) { return 3; }
33+
if(!::phy_engine::netlist::add_to_node(nl, *m1, 0, *n0) || !::phy_engine::netlist::add_to_node(nl, *m1, 1, *n1)) { return 4; }
34+
}
35+
36+
// Keep the loop reachable.
37+
{
38+
auto [m, pos] = ::phy_engine::netlist::add_model(nl, ::phy_engine::model::OUTPUT{});
39+
(void)pos;
40+
if(m == nullptr || !::phy_engine::netlist::add_to_node(nl, *m, 0, *n0)) { return 5; }
41+
}
42+
43+
::phy_engine::verilog::digital::pe_synth_options opt{};
44+
opt.assume_binary_inputs = true;
45+
opt.resub_max_vars = 6;
46+
opt.resub_max_gates = 64;
47+
opt.sweep_max_vars = 6;
48+
opt.sweep_max_gates = 64;
49+
50+
std::vector<::phy_engine::model::node_t*> prot{};
51+
52+
(void)::phy_engine::verilog::digital::details::optimize_bounded_resubstitute_in_pe_netlist(nl, prot, opt);
53+
(void)::phy_engine::verilog::digital::details::optimize_bounded_sweep_in_pe_netlist(nl, prot, opt);
54+
55+
return 0;
56+
}
57+

0 commit comments

Comments
 (0)