Merge pull request #117 from Silimate/negopt_revision_1

negopt: fix quadratic blowup by adding index hints and deferring nuse…
This commit is contained in:
Akash Levy 2026-03-02 21:23:22 -08:00 committed by GitHub
commit f126ab94fa
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 72 additions and 45 deletions

View File

@ -85,16 +85,29 @@ struct NegoptPass : public Pass {
for (auto module : design->selected_modules()) {
if (run_pre) {
// manual2sub and sub2neg only need to run once: no downstream
// pre-subpass creates the patterns they match
// separate pm instances so sub2neg sees the $sub cells manual2sub creates.
{
peepopt_pm pm(module);
pm.setup(module->selected_cells());
pm.run_manual2sub();
log_flush();
}
{
peepopt_pm pm(module);
pm.setup(module->selected_cells());
pm.run_sub2neg();
log_flush();
}
// negexpand/negneg/negmux can feed each other.
did_something = true;
for (int iter = 0; iter < max_iterations && did_something; iter++) {
did_something = false;
peepopt_pm pm(module);
pm.setup(module->selected_cells());
pm.run_manual2sub(); // Reduce manual 2's complement to subtraction first
log_flush();
pm.run_sub2neg();
log_flush();
pm.run_negexpand();
log_flush();
pm.run_negneg();

View File

@ -12,9 +12,8 @@ pattern manual2sub
state <SigSpec> minuend subtrahend result_sig root_a root_b
state <bool> is_signed
state <SigSpec> inner_y
state <SigSpec> inner_y inner_port_sig_A root_port_sig inner_port_sig_B
// 1. Match the "root" add (the one that produces the final result)
match root_add
select root_add->type == $add
set root_a port(root_add, \A)
@ -23,8 +22,7 @@ match root_add
set is_signed root_add->getParam(ID::A_SIGNED).as_bool()
endmatch
// 2. Case A: (a + ~b) + 1
// Check if root_add has a constant 1
// Case A: (a + ~b) + 1
code root_add inner_y
{
SigSpec pa = root_a;
@ -51,20 +49,28 @@ code root_add inner_y
}
endcode
// Find the inner add
// inner_y is discovered in code so gating happens in the code block
// Find the inner add whose Y feeds the non-constant port of root_add
match inner_add_A
select inner_add_A->type == $add
index <SigSpec> port(inner_add_A, \Y) === inner_y
filter nusers(port(inner_add_A, \Y)) == 2
endmatch
// Find the NOT gate on one of the ports of inner_add_A
// Branch over both ports of inner_add_A to find the NOT gate
code inner_port_sig_A
inner_port_sig_A = port(inner_add_A, \A);
branch;
inner_port_sig_A = port(inner_add_A, \B);
endcode
match not_gate_A
select not_gate_A->type == $not
index <SigSpec> port(not_gate_A, \Y) === inner_port_sig_A
endmatch
code root_add inner_add_A not_gate_A subtrahend minuend result_sig is_signed
{
// Require consistent signedness on the root add.
// Require consistent signedness across the chain
if (root_add->getParam(ID::B_SIGNED).as_bool() != is_signed)
reject;
@ -73,11 +79,7 @@ code root_add inner_add_A not_gate_A subtrahend minuend result_sig is_signed
if (inner_add_A->getParam(ID::B_SIGNED).as_bool() != is_signed)
reject;
if (port(inner_add_A, \Y) != inner_y)
reject;
if (nusers(port(inner_add_A, \Y)) != 2)
reject;
// Determine which port is the NOT output and which is the minuend
SigSpec not_y = port(not_gate_A, \Y);
SigSpec add_a = port(inner_add_A, \A);
SigSpec add_b = port(inner_add_A, \B);
@ -92,7 +94,6 @@ code root_add inner_add_A not_gate_A subtrahend minuend result_sig is_signed
subtrahend = port(not_gate_A, \A);
// Create the subtraction cell
log("manual2sub in %s: Found (a + ~b) + 1 pattern, creating $sub for %s\n", log_id(module), log_signal(result_sig));
Cell *cell = root_add;
int width = GetSize(result_sig);
@ -100,27 +101,25 @@ code root_add inner_add_A not_gate_A subtrahend minuend result_sig is_signed
// Reject if the +1 wrap boundary is narrower than the final result
if (inner_width < width)
reject;
// Anchor both operands to the inner add width to preserve carry behavior
SigSpec minuend_rs = minuend;
SigSpec subtrahend_rs = subtrahend;
// Anchor both operands to the inner add width to preserve carry behavior
minuend_rs.extend_u0(inner_width, is_signed);
subtrahend_rs.extend_u0(inner_width, is_signed);
SigSpec sub_y = result_sig;
// Extend the sub result back to the root width when needed
if (inner_width != width) {
sub_y = module->addWire(NEW_ID2_SUFFIX("sub_y"), inner_width);
}
Cell *sub = module->addSub(NEW_ID2_SUFFIX("sub"), minuend_rs, subtrahend_rs, sub_y, is_signed);
// Extend the sub result back to root width when inner is wider
if (inner_width != width) {
SigSpec sub_y_rs = sub_y;
sub_y_rs.extend_u0(width, is_signed);
module->connect(result_sig, sub_y_rs);
}
// Let fixup_parameters handle width adjustments
sub->fixup_parameters();
// Remove old cells
autoremove(root_add);
autoremove(inner_add_A);
autoremove(not_gate_A);
@ -130,23 +129,36 @@ code root_add inner_add_A not_gate_A subtrahend minuend result_sig is_signed
}
endcode
// 3. Case B: a + (~b + 1)
// Case B: a + (~b + 1)
// Branch over both ports of root_add to find the inner (~b + 1) add
code root_port_sig
root_port_sig = port(root_add, \A);
branch;
root_port_sig = port(root_add, \B);
endcode
// Find the inner add on either port of root_add
match inner_add_B
select inner_add_B->type == $add
filter port(inner_add_B, \Y) == root_a || port(inner_add_B, \Y) == root_b
select nusers(port(inner_add_B, \Y)) == 2
index <SigSpec> port(inner_add_B, \Y) === root_port_sig
filter nusers(port(inner_add_B, \Y)) == 2
endmatch
// Check if inner_add_B has a constant 1 and a NOT gate
// Branch over both ports of inner_add_B to find the NOT gate
code inner_port_sig_B
inner_port_sig_B = port(inner_add_B, \A);
branch;
inner_port_sig_B = port(inner_add_B, \B);
endcode
match not_gate_B
select not_gate_B->type == $not
index <SigSpec> port(not_gate_B, \Y) === inner_port_sig_B
endmatch
code root_add inner_add_B not_gate_B minuend subtrahend result_sig is_signed
{
// Require consistent signedness on the root add.
// Require consistent signedness across the chain
if (root_add->getParam(ID::B_SIGNED).as_bool() != is_signed)
reject;
@ -155,6 +167,8 @@ code root_add inner_add_B not_gate_B minuend subtrahend result_sig is_signed
if (inner_add_B->getParam(ID::B_SIGNED).as_bool() != is_signed)
reject;
// Verify inner_add_B has the form (~b + 1): one port is constant 1,
// the other is the NOT output
SigSpec pa = inner_add_B->getPort(ID::A);
SigSpec pb = inner_add_B->getPort(ID::B);
SigSpec not_y = port(not_gate_B, \Y);
@ -175,13 +189,13 @@ code root_add inner_add_B not_gate_B minuend subtrahend result_sig is_signed
if (!valid) reject;
// The minuend is whichever root_add port is NOT the inner_add_B output
subtrahend = port(not_gate_B, \A);
if (inner_add_B->getPort(ID::Y) == root_add->getPort(ID::A))
minuend = root_b;
else
minuend = root_a;
// Create the subtraction cell
log("manual2sub in %s: Found a + (~b + 1) pattern, creating $sub for %s\n", log_id(module), log_signal(result_sig));
Cell *cell = root_add;
int width = GetSize(result_sig);
@ -190,32 +204,30 @@ code root_add inner_add_B not_gate_B minuend subtrahend result_sig is_signed
// Reject if the +1 wrap boundary is narrower than the final result
if (inner_width < width)
reject;
// Anchor both operands to the inner add width to preserve carry behavior
SigSpec minuend_rs = minuend;
SigSpec subtrahend_rs = subtrahend;
// Anchor both operands to the inner add width to preserve carry behavior
minuend_rs.extend_u0(inner_width, is_signed);
subtrahend_rs.extend_u0(inner_width, is_signed);
SigSpec sub_y = result_sig;
// Extend the sub result back to the root width when needed
if (inner_width != width) {
sub_y = module->addWire(NEW_ID2_SUFFIX("sub_y"), inner_width);
}
Cell *sub = module->addSub(NEW_ID2_SUFFIX("sub"), minuend_rs, subtrahend_rs, sub_y, is_signed);
// Extend the sub result back to root width when inner is wider
if (inner_width != width) {
SigSpec sub_y_rs = sub_y;
sub_y_rs.extend_u0(width, is_signed);
module->connect(result_sig, sub_y_rs);
}
// Let fixup_parameters handle width adjustments
sub->fixup_parameters();
// Remove old cells
autoremove(root_add);
autoremove(inner_add_B);
autoremove(not_gate_B);

View File

@ -20,7 +20,7 @@ endmatch
match neg_a
select neg_a->type == $neg
select nusers(port(neg_a, \Y)) == 2
filter nusers(port(neg_a, \Y)) == 2
index <SigSpec> port(neg_a, \Y) === mux_a
set neg_a_in port(neg_a, \A)
set neg_a_y port(neg_a, \Y)
@ -29,7 +29,7 @@ endmatch
match neg_b
select neg_b->type == $neg
select nusers(port(neg_b, \Y)) == 2
filter nusers(port(neg_b, \Y)) == 2
index <SigSpec> port(neg_b, \Y) === mux_b
set neg_b_in port(neg_b, \A)
set neg_b_y port(neg_b, \Y)

View File

@ -28,7 +28,7 @@ endcode
match neg
select neg->type == $neg
select nusers(port(neg, \Y)) == 2
filter nusers(port(neg, \Y)) == 2
index <SigSpec> port(neg, \Y) === (neg_on_a ? add_a : add_b)
set neg_a port(neg, \A)
set neg_y port(neg, \Y)

View File

@ -20,7 +20,7 @@ endmatch
match add
select add->type == $add
index <SigSpec> port(add, \Y) === neg_a
select nusers(port(add, \Y)) == 2
filter nusers(port(add, \Y)) == 2
set add_a port(add, \A)
set add_b port(add, \B)
endmatch

View File

@ -18,8 +18,8 @@ match neg
endmatch
match mux
select mux->type == $mux
select nusers(port(mux, \Y)) == 2
select mux->type == $mux
select nusers(port(mux, \Y)) == 2
set mux_a port(mux, \A)
set mux_b port(mux, \B)
set mux_s port(mux, \S)
@ -43,7 +43,7 @@ code neg_a neg_y mux_a mux_b mux_s mux_y a_signed
}
{
// Anchor negations to the original negation width
// Anchor negations to the original neg output width
int width = GetSize(neg_y);
Cell *cell = neg;

View File

@ -18,7 +18,7 @@ endmatch
match neg2
select neg2->type == $neg
index <SigSpec> port(neg2, \Y) === neg1_a
select nusers(port(neg2, \Y)) == 2
filter nusers(port(neg2, \Y)) == 2
set neg2_a port(neg2, \A)
endmatch

View File

@ -39,16 +39,16 @@ code add_a add_b add_y neg1_a neg1_y neg2_a neg2_y add_signed add_b_signed neg1_
if (add_signed != add_b_signed)
reject;
// Require negations to share the add signedness
// Require both negations share the add's signedness
if (neg1_signed != add_signed || neg2_signed != add_signed)
reject;
{
// Avoid matching the same neg cell twice
// Avoid matching the same neg cell for both inputs
if (neg1 == neg2)
reject;
// Require a single wrap boundary for both negations and the sum
// Require a single wrap boundary across both negations and the sum
if (GetSize(neg1_y) != GetSize(neg2_y))
reject;
if (GetSize(add_y) != GetSize(neg1_y))
@ -89,11 +89,13 @@ code add_a add_b add_y neg1_a neg1_y neg2_a neg2_y add_signed add_b_signed neg1_
neg1_a_rs.extend_u0(width, add_signed);
neg2_a_rs.extend_u0(width, add_signed);
// Build -(a + b) to replace (-a) + (-b)
SigSpec sum = module->addWire(NEW_ID2_SUFFIX("sum"), width);
Cell *new_add = module->addAdd(NEW_ID2_SUFFIX("add"), neg1_a_rs, neg2_a_rs, sum, add_signed);
SigSpec neg_out = module->addWire(NEW_ID2_SUFFIX("neg_y"), width);
Cell *new_neg = module->addNeg(NEW_ID2_SUFFIX("neg"), sum, neg_out, add_signed);
// Extend result back to original add output width
SigSpec neg_out_rs = neg_out;
neg_out_rs.extend_u0(GetSize(add_y), add_signed);
module->connect(add_y, neg_out_rs);