@@ -1914,10 +1914,8 @@ namespace phy_engine::verilog::digital
19141914 ::std::uint64_t const* off_blocks,
19151915 ::std::uint32_t off_words) noexcept;
19161916 extern "C" void phy_engine_pe_synth_cuda_espresso_off_destroy(void* handle) noexcept;
1917- extern "C" bool phy_engine_pe_synth_cuda_espresso_off_hits(void* handle,
1918- cuda_cube_desc const* cubes,
1919- ::std::size_t cube_count,
1920- ::std::uint8_t* out_hits) noexcept;
1917+ extern "C" bool
1918+ phy_engine_pe_synth_cuda_espresso_off_hits(void* handle, cuda_cube_desc const* cubes, ::std::size_t cube_count, ::std::uint8_t* out_hits) noexcept;
19211919#endif
19221920
19231921 [[nodiscard]] inline bool
@@ -2144,14 +2142,15 @@ namespace phy_engine::verilog::digital
21442142
21452143 cuda_espresso_off_raii() noexcept = default;
21462144 cuda_espresso_off_raii(cuda_espresso_off_raii const&) = delete;
2147- cuda_espresso_off_raii& operator=(cuda_espresso_off_raii const&) = delete;
2145+ cuda_espresso_off_raii& operator= (cuda_espresso_off_raii const&) = delete;
21482146
21492147 cuda_espresso_off_raii(cuda_espresso_off_raii&& o) noexcept : handle(o.handle), off_words(o.off_words)
21502148 {
21512149 o.handle = nullptr;
21522150 o.off_words = 0u;
21532151 }
2154- cuda_espresso_off_raii& operator=(cuda_espresso_off_raii&& o) noexcept
2152+
2153+ cuda_espresso_off_raii& operator= (cuda_espresso_off_raii&& o) noexcept
21552154 {
21562155 if(this == &o) { return *this; }
21572156 reset();
@@ -2187,13 +2186,7 @@ namespace phy_engine::verilog::digital
21872186 r.handle = phy_engine_pe_synth_cuda_espresso_off_create(device_mask, var_count, off_blocks, off_words);
21882187 auto const us =
21892188 static_cast<std::size_t>(::std::chrono::duration_cast<::std::chrono::microseconds>(::std::chrono::steady_clock::now() - t0).count());
2190- cuda_trace_add(u8"espresso_off_create",
2191- 0u,
2192- off_words,
2193- static_cast<std::size_t>(off_words) * sizeof(::std::uint64_t),
2194- 0u,
2195- us,
2196- r.handle != nullptr);
2189+ cuda_trace_add(u8"espresso_off_create", 0u, off_words, static_cast<std::size_t>(off_words) * sizeof(::std::uint64_t), 0u, us, r.handle != nullptr);
21972190#else
21982191 (void)device_mask;
21992192 (void)var_count;
@@ -2204,10 +2197,8 @@ namespace phy_engine::verilog::digital
22042197 return r;
22052198 }
22062199
2207- [[nodiscard]] inline bool cuda_espresso_off_hits(cuda_espresso_off_raii const& off,
2208- cuda_cube_desc const* cubes,
2209- ::std::size_t cube_count,
2210- ::std::uint8_t* out_hits) noexcept
2200+ [[nodiscard]] inline bool
2201+ cuda_espresso_off_hits(cuda_espresso_off_raii const& off, cuda_cube_desc const* cubes, ::std::size_t cube_count, ::std::uint8_t* out_hits) noexcept
22112202 {
22122203#if defined(PHY_ENGINE_ENABLE_CUDA_PE_SYNTH)
22132204 if(off.handle == nullptr || cubes == nullptr || out_hits == nullptr || cube_count == 0u) { return false; }
@@ -2216,13 +2207,7 @@ namespace phy_engine::verilog::digital
22162207 auto const us =
22172208 static_cast<std::size_t>(::std::chrono::duration_cast<::std::chrono::microseconds>(::std::chrono::steady_clock::now() - t0).count());
22182209 // OFF-set already resident, so H2D is just the cubes; D2H is the hits vector.
2219- cuda_trace_add(u8"espresso_hits_off",
2220- cube_count,
2221- off.off_words,
2222- cube_count * sizeof(cuda_cube_desc),
2223- cube_count * sizeof(::std::uint8_t),
2224- us,
2225- ok);
2210+ cuda_trace_add(u8"espresso_hits_off", cube_count, off.off_words, cube_count * sizeof(cuda_cube_desc), cube_count * sizeof(::std::uint8_t), us, ok);
22262211 return ok;
22272212#else
22282213 (void)off;
@@ -10616,7 +10601,10 @@ namespace phy_engine::verilog::digital
1061610601 bool use_off_gpu{};
1061710602 if(opt.cuda_enable && blocksU != 0u && var_count <= 16u)
1061810603 {
10619- off_gpu = cuda_espresso_off_create(opt.cuda_device_mask, static_cast<::std::uint32_t>(var_count), off_bits.data(), static_cast<::std::uint32_t>(blocksU));
10604+ off_gpu = cuda_espresso_off_create(opt.cuda_device_mask,
10605+ static_cast<::std::uint32_t>(var_count),
10606+ off_bits.data(),
10607+ static_cast<::std::uint32_t>(blocksU));
1062010608 use_off_gpu = (off_gpu.handle != nullptr);
1062110609 }
1062210610
@@ -10638,10 +10626,7 @@ namespace phy_engine::verilog::digital
1063810626 desc[i].value = cubes[i].value;
1063910627 desc[i].mask = cubes[i].mask;
1064010628 }
10641- if(use_off_gpu)
10642- {
10643- used_cuda = cuda_espresso_off_hits(off_gpu, desc.data(), desc.size(), out_hits.data());
10644- }
10629+ if(use_off_gpu) { used_cuda = cuda_espresso_off_hits(off_gpu, desc.data(), desc.size(), out_hits.data()); }
1064510630 else
1064610631 {
1064710632 used_cuda = cuda_espresso_cube_hits_off(opt.cuda_device_mask,
@@ -10736,7 +10721,7 @@ namespace phy_engine::verilog::digital
1073610721 }
1073710722 if(bits.empty()) { continue; }
1073810723
10739- for(auto const b1 : bits)
10724+ for(auto const b1: bits)
1074010725 {
1074110726 qm_implicant cand = c0;
1074210727 cand.mask = static_cast<::std::uint16_t>(cand.mask | b1);
@@ -16011,7 +15996,9 @@ namespace phy_engine::verilog::digital
1601115996 bool const do_strash = (lvl >= 1);
1601215997 bool const do_dce = (lvl >= 1);
1601315998 bool const do_factoring = (lvl >= 2);
16014- bool const do_qm = (lvl >= 4);
15999+ // O3 should be meaningfully stronger than O2, but still "fast tier".
16000+ // Enable a bounded 2-level minimization starting at O3 (with O3-specific caps applied below).
16001+ bool const do_qm = (lvl >= 3) && opt.assume_binary_inputs;
1601516002 bool const do_input_inv_map = (lvl >= 2);
1601616003 bool const do_xor_rewrite = (lvl >= 2);
1601716004 bool const do_double_not = (lvl >= 1);
@@ -16025,7 +16012,9 @@ namespace phy_engine::verilog::digital
1602516012 bool const do_resub = (lvl >= 3) && opt.assume_binary_inputs;
1602616013 bool const do_sweep = (lvl >= 3) && opt.assume_binary_inputs;
1602716014 bool const do_techmap = (lvl >= 4) && opt.assume_binary_inputs && opt.techmap_enable;
16028- bool const do_decompose = (lvl >= 4) && opt.assume_binary_inputs && opt.decompose_large_functions;
16015+ // Allow limited BDD decomposition starting at O3; it is one of the few "structural" transforms
16016+ // that can materially change the optimization landscape without full O4 fixpoints.
16017+ bool const do_decompose = (lvl >= 3) && opt.assume_binary_inputs && opt.decompose_large_functions;
1602916018
1603016019 auto run_once = [&](::phy_engine::netlist::netlist& net, pe_synth_options const& opt_run, pe_synth_report* rep_run) noexcept -> void
1603116020 {
@@ -16135,6 +16124,17 @@ namespace phy_engine::verilog::digital
1613516124 {
1613616125 // AIG rewrite can be the most expensive "fast tier" pass; cap it by default unless user already did.
1613716126 if(tuned_opt.rewrite_max_candidates == 0u) { tuned_opt.rewrite_max_candidates = 4096u; }
16127+
16128+ // Keep O3 predictably fast: cap the "structural" heavy hitters while still enabling them.
16129+ // Users who want deeper search should use O4/O5 and/or explicit knobs.
16130+ // Heuristic: only auto-cap when the user appears to be using defaults; if they changed the knob, respect it.
16131+ if(opt.qm_max_vars == 10u && tuned_opt.qm_max_vars > 8u) { tuned_opt.qm_max_vars = 8u; }
16132+ if(opt.qm_max_primes == 4096u && tuned_opt.qm_max_primes > 2048u) { tuned_opt.qm_max_primes = 2048u; }
16133+ if(opt.qm_max_gates == 64u && tuned_opt.qm_max_gates > 64u) { tuned_opt.qm_max_gates = 64u; }
16134+
16135+ if(opt.decomp_min_vars == 11u && tuned_opt.decomp_min_vars < 13u) { tuned_opt.decomp_min_vars = 13u; }
16136+ if(opt.decomp_max_vars == 16u && tuned_opt.decomp_max_vars > 12u) { tuned_opt.decomp_max_vars = 12u; }
16137+ if(opt.decomp_bdd_node_limit == 4096u && tuned_opt.decomp_bdd_node_limit > 2048u) { tuned_opt.decomp_bdd_node_limit = 2048u; }
1613816138 }
1613916139
1614016140 if(!is_omax) { run_o34_fixpoint(nl, tuned_opt, rep); }
0 commit comments