From 643427d9c9532dc19e534f1a34b84eaf6a9a14fa Mon Sep 17 00:00:00 2001 From: tondapusili Date: Tue, 3 Feb 2026 17:09:03 -0800 Subject: [PATCH] Add negopt pass with comprehensive pattern matching MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit introduces the negopt pass with pre/post optimization modes for handling negation patterns in arithmetic circuits. Pre-optimization patterns (expose for tree balancing): - manual2sub: (a + ~b) + 1 → a - b - sub2neg: a - b → a + (-b) - negexpand: -(a + b) → (-a) + (-b) [with output width fix] - negneg: -(-a) → a - negmux: -(s ? a : b) → s ? (-a) : (-b) Post-optimization patterns (cleanup/rebuild): - negrebuild: (-a) + (-b) → -(a + b) - muxneg: s ? (-a) : (-b) → -(s ? a : b) - neg2sub: a + (-b) → a - b All patterns use nusers() for fanout checking (standard Yosys style). Comprehensive test coverage with positive/negative cases and formal verification via equiv_opt. --- passes/silimate/Makefile.inc | 17 +++ passes/silimate/negopt.cc | 119 +++++++++++++++++ passes/silimate/peepopt_manual2sub.pmg | 171 +++++++++++++++++++++++++ passes/silimate/peepopt_muxneg.pmg | 59 +++++++++ passes/silimate/peepopt_neg2sub.pmg | 60 +++++++++ passes/silimate/peepopt_negexpand.pmg | 56 ++++++++ passes/silimate/peepopt_negmux.pmg | 52 ++++++++ passes/silimate/peepopt_negneg.pmg | 35 +++++ passes/silimate/peepopt_negrebuild.pmg | 63 +++++++++ passes/silimate/peepopt_sub2neg.pmg | 42 ++++++ tests/silimate/manual2sub.ys | 73 +++++++++++ tests/silimate/muxneg.ys | 44 +++++++ tests/silimate/neg2sub.ys | 41 ++++++ tests/silimate/negexpand.ys | 65 ++++++++++ tests/silimate/negmux.ys | 69 ++++++++++ tests/silimate/negneg.ys | 60 +++++++++ tests/silimate/negrebuild.ys | 19 +++ tests/silimate/sub2neg.ys | 39 ++++++ 18 files changed, 1084 insertions(+) create mode 100644 passes/silimate/negopt.cc create mode 100644 passes/silimate/peepopt_manual2sub.pmg create mode 100644 passes/silimate/peepopt_muxneg.pmg create mode 100644 passes/silimate/peepopt_neg2sub.pmg create mode 100644 passes/silimate/peepopt_negexpand.pmg create mode 100644 passes/silimate/peepopt_negmux.pmg create mode 100644 passes/silimate/peepopt_negneg.pmg create mode 100644 passes/silimate/peepopt_negrebuild.pmg create mode 100644 passes/silimate/peepopt_sub2neg.pmg create mode 100644 tests/silimate/manual2sub.ys create mode 100644 tests/silimate/muxneg.ys create mode 100644 tests/silimate/neg2sub.ys create mode 100644 tests/silimate/negexpand.ys create mode 100644 tests/silimate/negmux.ys create mode 100644 tests/silimate/negneg.ys create mode 100644 tests/silimate/negrebuild.ys create mode 100644 tests/silimate/sub2neg.ys diff --git a/passes/silimate/Makefile.inc b/passes/silimate/Makefile.inc index 6d40f16e8..98ddf5aa4 100644 --- a/passes/silimate/Makefile.inc +++ b/passes/silimate/Makefile.inc @@ -33,3 +33,20 @@ PEEPOPT_PATTERN += passes/silimate/peepopt_muxinvprop.pmg passes/silimate/peepopt_muxmode.h: passes/pmgen/pmgen.py $(PEEPOPT_PATTERN) $(P) mkdir -p $(dir $@) && $(PYTHON_EXECUTABLE) $< -o $@ -p peepopt $(filter-out $<,$^) + +OBJS += passes/silimate/negopt.o +GENFILES += passes/silimate/peepopt_negopt.h +passes/silimate/negopt.o: passes/silimate/peepopt_negopt.h +$(eval $(call add_extra_objs,passes/silimate/peepopt_negopt.h)) + +PEEPOPT_PATTERN = passes/silimate/peepopt_manual2sub.pmg +PEEPOPT_PATTERN += passes/silimate/peepopt_sub2neg.pmg +PEEPOPT_PATTERN += passes/silimate/peepopt_negexpand.pmg +PEEPOPT_PATTERN += passes/silimate/peepopt_negneg.pmg +PEEPOPT_PATTERN += passes/silimate/peepopt_negmux.pmg +PEEPOPT_PATTERN += passes/silimate/peepopt_negrebuild.pmg +PEEPOPT_PATTERN += passes/silimate/peepopt_muxneg.pmg +PEEPOPT_PATTERN += passes/silimate/peepopt_neg2sub.pmg + +passes/silimate/peepopt_negopt.h: passes/pmgen/pmgen.py $(PEEPOPT_PATTERN) + $(P) mkdir -p $(dir $@) && $(PYTHON_EXECUTABLE) $< -o $@ -p peepopt $(filter-out $<,$^) diff --git a/passes/silimate/negopt.cc b/passes/silimate/negopt.cc new file mode 100644 index 000000000..af5d68496 --- /dev/null +++ b/passes/silimate/negopt.cc @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2025 Silimate, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +// +// Authored by Abhinav Tondapu of Silimate, Inc. +// + +#include "kernel/yosys.h" +#include "kernel/sigtools.h" + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +bool did_something; + +#include "passes/silimate/peepopt_negopt.h" + +struct NegoptPass : public Pass { + NegoptPass() : Pass("negopt", "optimize negation patterns in arithmetic") { } + + void help() override + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" negopt [options] [selection]\n"); + log("\n"); + log("This pass optimizes negation patterns in arithmetic expressions.\n"); + log("\n"); + log(" -pre\n"); + log(" Run pre-optimization transformations:\n" ); + log(" - manual2sub: a + ~b + 1 => a - b\n" ); + log(" - sub2neg: a - b => a + (-b)\n" ); + log(" - negexpand: -(a + b) => (-a) + (-b)\n"); + log(" - negneg: -(-a) => a\n" ); + log(" - negmux: -(s?a:b) => s?(-a):(-b)\n"); + log("\n"); + log(" -post\n"); + log(" Run post-optimization transformations:\n" ); + log(" - negrebuild: (-a)+(-b) => -(a + b)\n"); + log(" - muxneg: s?(-a):(-b) => -(s?a:b)\n"); + log(" - neg2sub: a + (-b) => a - b\n" ); + log("\n"); + log("When called without options, both -pre and -post are executed.\n"); + log("\n"); + } + + void execute(std::vector args, RTLIL::Design *design) override + { + bool run_pre = false; + bool run_post = false; + + log_header(design, "Executing NEGOPT pass (optimize negation patterns).\n"); + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) { + if (args[argidx] == "-pre") { + run_pre = true; + continue; + } + if (args[argidx] == "-post") { + run_post = true; + continue; + } + break; + } + extra_args(args, argidx, design); + + if (!run_pre && !run_post) { + run_pre = true; + run_post = true; + } + + for (auto module : design->selected_modules()) { + if (run_pre) { + did_something = true; + while (did_something) { + did_something = false; + peepopt_pm pm(module); + pm.setup(module->selected_cells()); + + pm.run_manual2sub(); // Reduce manual 2's complement to subtraction first + pm.run_sub2neg(); + pm.run_negexpand(); + pm.run_negneg(); + pm.run_negmux(); + } + } + + if (run_post) { + did_something = true; + while (did_something) { + did_something = false; + peepopt_pm pm(module); + pm.setup(module->selected_cells()); + + pm.run_negrebuild(); + pm.run_muxneg(); + pm.run_neg2sub(); + } + } + } + } +} NegoptPass; + +PRIVATE_NAMESPACE_END diff --git a/passes/silimate/peepopt_manual2sub.pmg b/passes/silimate/peepopt_manual2sub.pmg new file mode 100644 index 000000000..8b456fa76 --- /dev/null +++ b/passes/silimate/peepopt_manual2sub.pmg @@ -0,0 +1,171 @@ +pattern manual2sub +// +// Authored by Abhinav Tondapu of Silimate, Inc. under ISC license. +// +// Canonicalize manual 2's complement subtraction: +// Case A: (a + ~b) + 1 ===> a - b +// Case B: a + (~b + 1) ===> a - b +// +// Note: Fanout checking includes module connections to avoid breaking +// designs where intermediate results are used by output assignments. +// + +state minuend subtrahend result_sig +state is_signed +state inner_y + +// 1. Match the "root" add (the one that produces the final result) +match root_add + select root_add->type == $add + set result_sig port(root_add, \Y) + 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 +code root_add inner_y +{ + SigSpec pa = root_add->getPort(ID::A); + SigSpec pb = root_add->getPort(ID::B); + + auto is_one = [](SigSpec s) { + if (!s.is_fully_const()) return false; + Const c = s.as_const(); + for (int i = 0; i < c.size(); i++) { + if (i == 0 && c[i] != State::S1) return false; + if (i > 0 && c[i] != State::S0) return false; + } + return true; + }; + + if (is_one(pa)) { + inner_y = pb; + } else if (is_one(pb)) { + inner_y = pa; + } else { + branch; + reject; + } +} +endcode + +// Find the inner add +match inner_add_A + select inner_add_A->type == $add + index port(inner_add_A, \Y) === inner_y + select nusers(port(inner_add_A, \Y)) == 2 +endmatch + +// Find the NOT gate on one of the ports of inner_add_A +match not_gate_A + select not_gate_A->type == $not + filter not_gate_A->getPort(ID::Y) == inner_add_A->getPort(ID::A) || not_gate_A->getPort(ID::Y) == inner_add_A->getPort(ID::B) + set subtrahend port(not_gate_A, \A) +endmatch + +code root_add inner_add_A not_gate_A subtrahend minuend result_sig is_signed +{ + + // Require consistent signedness on the root add. + if (root_add->getParam(ID::B_SIGNED).as_bool() != is_signed) + reject; + + if (inner_add_A->getParam(ID::A_SIGNED).as_bool() != is_signed) + reject; + if (inner_add_A->getParam(ID::B_SIGNED).as_bool() != is_signed) + reject; + + if (not_gate_A->getPort(ID::Y) == inner_add_A->getPort(ID::A)) + minuend = inner_add_A->getPort(ID::B); + else + minuend = inner_add_A->getPort(ID::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 *sub = module->addSub(NEW_ID, minuend, subtrahend, result_sig, is_signed); + + // Let fixup_parameters handle width adjustments + sub->fixup_parameters(); + + // Remove old cells + autoremove(root_add); + autoremove(inner_add_A); + autoremove(not_gate_A); + + did_something = true; + accept; +} +endcode + +// 3. Case B: a + (~b + 1) +code root_add + // Just fall through to the next match +endcode + +// Find the inner add on either port of root_add +match inner_add_B + select inner_add_B->type == $add + filter inner_add_B->getPort(ID::Y) == root_add->getPort(ID::A) || inner_add_B->getPort(ID::Y) == root_add->getPort(ID::B) + select nusers(port(inner_add_B, \Y)) == 2 +endmatch + +// Check if inner_add_B has a constant 1 and a NOT gate +match not_gate_B + select not_gate_B->type == $not + filter not_gate_B->getPort(ID::Y) == inner_add_B->getPort(ID::A) || not_gate_B->getPort(ID::Y) == inner_add_B->getPort(ID::B) +endmatch + +code root_add inner_add_B not_gate_B minuend subtrahend result_sig is_signed +{ + + // Require consistent signedness on the root add. + if (root_add->getParam(ID::B_SIGNED).as_bool() != is_signed) + reject; + + if (inner_add_B->getParam(ID::A_SIGNED).as_bool() != is_signed) + reject; + if (inner_add_B->getParam(ID::B_SIGNED).as_bool() != is_signed) + reject; + + SigSpec pa = inner_add_B->getPort(ID::A); + SigSpec pb = inner_add_B->getPort(ID::B); + SigSpec not_y = not_gate_B->getPort(ID::Y); + + auto is_one = [](SigSpec s) { + if (!s.is_fully_const()) return false; + Const c = s.as_const(); + for (int i = 0; i < c.size(); i++) { + if (i == 0 && c[i] != State::S1) return false; + if (i > 0 && c[i] != State::S0) return false; + } + return true; + }; + + bool valid = false; + if (is_one(pa) && pb == not_y) valid = true; + if (is_one(pb) && pa == not_y) valid = true; + + if (!valid) reject; + + subtrahend = not_gate_B->getPort(ID::A); + if (inner_add_B->getPort(ID::Y) == root_add->getPort(ID::A)) + minuend = root_add->getPort(ID::B); + else + minuend = root_add->getPort(ID::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 *sub = module->addSub(NEW_ID, minuend, subtrahend, result_sig, is_signed); + + // Let fixup_parameters handle width adjustments + sub->fixup_parameters(); + + // Remove old cells + autoremove(root_add); + autoremove(inner_add_B); + autoremove(not_gate_B); + + did_something = true; + accept; +} +endcode diff --git a/passes/silimate/peepopt_muxneg.pmg b/passes/silimate/peepopt_muxneg.pmg new file mode 100644 index 000000000..a45a42489 --- /dev/null +++ b/passes/silimate/peepopt_muxneg.pmg @@ -0,0 +1,59 @@ +pattern muxneg +// +// Authored by Abhinav Tondapu of Silimate, Inc. under ISC license. +// +// Factor negation out of mux +// +// s ? (-a) : (-b) ===> -(s ? a : b) +// + +state mux_a mux_b mux_s mux_y neg_a_in neg_b_in +state neg_a_signed neg_b_signed + +match mux + select mux->type == $mux + set mux_a port(mux, \A) + set mux_b port(mux, \B) + set mux_s port(mux, \S) + set mux_y port(mux, \Y) +endmatch + +match neg_a + select neg_a->type == $neg + select nusers(port(neg_a, \Y)) == 2 + index port(neg_a, \Y) === mux_a + set neg_a_in port(neg_a, \A) + set neg_a_signed neg_a->getParam(\A_SIGNED).as_bool() +endmatch + +match neg_b + select neg_b->type == $neg + select nusers(port(neg_b, \Y)) == 2 + index port(neg_b, \Y) === mux_b + set neg_b_in port(neg_b, \A) + set neg_b_signed neg_b->getParam(\A_SIGNED).as_bool() +endmatch + +code mux_a mux_b mux_s mux_y neg_a_in neg_b_in neg_a_signed neg_b_signed + if (neg_a_signed != neg_b_signed) + reject; + + { + int width = GetSize(mux_y); + + SigSpec mux_out = module->addWire(NEW_ID, width); + Cell *new_mux = module->addMux(NEW_ID, neg_a_in, neg_b_in, mux_s, mux_out); + Cell *new_neg = module->addNeg(NEW_ID, mux_out, mux_y, neg_a_signed); + + log("muxneg pattern in %s: mux=%s, neg_a=%s, neg_b=%s\n", + log_id(module), log_id(mux), log_id(neg_a), log_id(neg_b)); + + new_mux->fixup_parameters(); + new_neg->fixup_parameters(); + autoremove(mux); + autoremove(neg_a); + autoremove(neg_b); + did_something = true; + } + accept; +endcode diff --git a/passes/silimate/peepopt_neg2sub.pmg b/passes/silimate/peepopt_neg2sub.pmg new file mode 100644 index 000000000..c811c6785 --- /dev/null +++ b/passes/silimate/peepopt_neg2sub.pmg @@ -0,0 +1,60 @@ +pattern neg2sub +// +// Authored by Abhinav Tondapu of Silimate, Inc. under ISC license. +// +// Convert addition with negation back to subtraction +// +// a + (-b) ===> a - b +// + +state add_a add_b add_y neg_a +state add_a_signed add_b_signed neg_signed +state neg_on_a + +match add + select add->type == $add + set add_a port(add, \A) + set add_b port(add, \B) + set add_y port(add, \Y) + set add_a_signed add->getParam(\A_SIGNED).as_bool() + set add_b_signed add->getParam(\B_SIGNED).as_bool() +endmatch + +code neg_on_a + neg_on_a = true; + branch; + neg_on_a = false; +endcode + +match neg + select neg->type == $neg + select nusers(port(neg, \Y)) == 2 + index port(neg, \Y) === (neg_on_a ? add_a : add_b) + set neg_a port(neg, \A) + set neg_signed neg->getParam(\A_SIGNED).as_bool() +endmatch + +code add_a add_b add_y neg_a neg_on_a add_a_signed add_b_signed neg_signed + if (add_a_signed != add_b_signed) + reject; + + if (neg_signed != (neg_on_a ? add_a_signed : add_b_signed)) + reject; + + { + Cell *new_sub; + if (neg_on_a) + new_sub = module->addSub(NEW_ID, add_b, neg_a, add_y, add_b_signed); + else + new_sub = module->addSub(NEW_ID, add_a, neg_a, add_y, add_a_signed); + + log("neg2sub pattern in %s: add=%s, neg=%s\n", + log_id(module), log_id(add), log_id(neg)); + + new_sub->fixup_parameters(); + autoremove(add); + autoremove(neg); + did_something = true; + } + accept; +endcode diff --git a/passes/silimate/peepopt_negexpand.pmg b/passes/silimate/peepopt_negexpand.pmg new file mode 100644 index 000000000..3fa3c65dd --- /dev/null +++ b/passes/silimate/peepopt_negexpand.pmg @@ -0,0 +1,56 @@ +pattern negexpand +// +// Authored by Abhinav Tondapu of Silimate, Inc. under ISC license. +// +// Expand negation over addition +// +// -(a + b) ===> (-a) + (-b) +// + +state neg_a neg_y add_a add_b +state a_signed + +match neg + select neg->type == $neg + set neg_a port(neg, \A) + set neg_y port(neg, \Y) + set a_signed neg->getParam(\A_SIGNED).as_bool() +endmatch + +match add + select add->type == $add + index port(add, \Y) === neg_a + select nusers(port(add, \Y)) == 2 + set add_a port(add, \A) + set add_b port(add, \B) +endmatch + +code neg_a neg_y add_a add_b a_signed + if (add->getParam(\A_SIGNED).as_bool() != a_signed) + reject; + if (add->getParam(\B_SIGNED).as_bool() != a_signed) + reject; + + { + // Use output width for negations to handle overflow correctly + int width = GetSize(neg_y); + SigSpec neg_add_a = module->addWire(NEW_ID, width); + Cell *neg_a_cell = module->addNeg(NEW_ID, add_a, neg_add_a, a_signed); + + SigSpec neg_add_b = module->addWire(NEW_ID, width); + Cell *neg_b_cell = module->addNeg(NEW_ID, add_b, neg_add_b, a_signed); + + Cell *new_add = module->addAdd(NEW_ID, neg_add_a, neg_add_b, neg_y, a_signed); + + log("negexpand pattern in %s: neg=%s, add=%s\n", + log_id(module), log_id(neg), log_id(add)); + + neg_a_cell->fixup_parameters(); + neg_b_cell->fixup_parameters(); + new_add->fixup_parameters(); + autoremove(neg); + autoremove(add); + did_something = true; + } + accept; +endcode diff --git a/passes/silimate/peepopt_negmux.pmg b/passes/silimate/peepopt_negmux.pmg new file mode 100644 index 000000000..3b6f5bd55 --- /dev/null +++ b/passes/silimate/peepopt_negmux.pmg @@ -0,0 +1,52 @@ +pattern negmux +// +// Authored by Abhinav Tondapu of Silimate, Inc. under ISC license. +// +// Distribute negation over mux +// +// -(s ? a : b) ===> s ? (-a) : (-b) +// + +state neg_a neg_y mux_a mux_b mux_s +state a_signed + +match neg + select neg->type == $neg + set neg_a port(neg, \A) + set neg_y port(neg, \Y) + set a_signed neg->getParam(\A_SIGNED).as_bool() +endmatch + +match mux + select mux->type == $mux + index port(mux, \Y) === neg_a + select nusers(port(mux, \Y)) == 2 + set mux_a port(mux, \A) + set mux_b port(mux, \B) + set mux_s port(mux, \S) +endmatch + +code neg_a neg_y mux_a mux_b mux_s a_signed + { + int width = GetSize(neg_y); + + SigSpec neg_mux_a = module->addWire(NEW_ID, width); + Cell *neg_a_cell = module->addNeg(NEW_ID, mux_a, neg_mux_a, a_signed); + + SigSpec neg_mux_b = module->addWire(NEW_ID, width); + Cell *neg_b_cell = module->addNeg(NEW_ID, mux_b, neg_mux_b, a_signed); + + Cell *new_mux = module->addMux(NEW_ID, neg_mux_a, neg_mux_b, mux_s, neg_y); + + log("negmux pattern in %s: neg=%s, mux=%s\n", + log_id(module), log_id(neg), log_id(mux)); + + neg_a_cell->fixup_parameters(); + neg_b_cell->fixup_parameters(); + new_mux->fixup_parameters(); + autoremove(neg); + autoremove(mux); + did_something = true; + } + accept; +endcode diff --git a/passes/silimate/peepopt_negneg.pmg b/passes/silimate/peepopt_negneg.pmg new file mode 100644 index 000000000..15f61dd04 --- /dev/null +++ b/passes/silimate/peepopt_negneg.pmg @@ -0,0 +1,35 @@ +pattern negneg +// +// Authored by Abhinav Tondapu of Silimate, Inc. under ISC license. +// +// Simplify double negation +// +// -(-a) ===> a +// + +state neg1_a neg1_y neg2_a + +match neg1 + select neg1->type == $neg + set neg1_a port(neg1, \A) + set neg1_y port(neg1, \Y) +endmatch + +match neg2 + select neg2->type == $neg + index port(neg2, \Y) === neg1_a + select nusers(port(neg2, \Y)) == 2 + set neg2_a port(neg2, \A) +endmatch + +code neg1_a neg1_y neg2_a + module->connect(neg1_y, neg2_a); + + log("negneg pattern in %s: neg1=%s, neg2=%s\n", + log_id(module), log_id(neg1), log_id(neg2)); + + autoremove(neg1); + autoremove(neg2); + did_something = true; + accept; +endcode diff --git a/passes/silimate/peepopt_negrebuild.pmg b/passes/silimate/peepopt_negrebuild.pmg new file mode 100644 index 000000000..462943bfe --- /dev/null +++ b/passes/silimate/peepopt_negrebuild.pmg @@ -0,0 +1,63 @@ +pattern negrebuild +// +// Authored by Abhinav Tondapu of Silimate, Inc. under ISC license. +// +// Rebuild addition of negations into negation of addition +// +// (-a) + (-b) ===> -(a + b) +// + +state add_a add_b add_y neg1_a neg2_a +state add_signed add_b_signed neg1_signed neg2_signed + +match add + select add->type == $add + set add_a port(add, \A) + set add_b port(add, \B) + set add_y port(add, \Y) + set add_signed add->getParam(\A_SIGNED).as_bool() + set add_b_signed add->getParam(\B_SIGNED).as_bool() +endmatch + +match neg1 + select neg1->type == $neg + select nusers(port(neg1, \Y)) == 2 + index port(neg1, \Y) === add_a + set neg1_a port(neg1, \A) + set neg1_signed neg1->getParam(\A_SIGNED).as_bool() +endmatch + +match neg2 + select neg2->type == $neg + select nusers(port(neg2, \Y)) == 2 + index port(neg2, \Y) === add_b + set neg2_a port(neg2, \A) + set neg2_signed neg2->getParam(\A_SIGNED).as_bool() +endmatch + +code add_a add_b add_y neg1_a neg2_a add_signed add_b_signed neg1_signed neg2_signed + if (add_signed != add_b_signed) + reject; + + if (neg1_signed != add_signed || neg2_signed != add_signed) + reject; + + { + int width = GetSize(add_y); + + SigSpec sum = module->addWire(NEW_ID, width); + Cell *new_add = module->addAdd(NEW_ID, neg1_a, neg2_a, sum, add_signed); + Cell *new_neg = module->addNeg(NEW_ID, sum, add_y, add_signed); + + log("negrebuild pattern in %s: add=%s, neg1=%s, neg2=%s\n", + log_id(module), log_id(add), log_id(neg1), log_id(neg2)); + + new_add->fixup_parameters(); + new_neg->fixup_parameters(); + autoremove(add); + autoremove(neg1); + autoremove(neg2); + did_something = true; + } + accept; +endcode diff --git a/passes/silimate/peepopt_sub2neg.pmg b/passes/silimate/peepopt_sub2neg.pmg new file mode 100644 index 000000000..ffb273f65 --- /dev/null +++ b/passes/silimate/peepopt_sub2neg.pmg @@ -0,0 +1,42 @@ +pattern sub2neg +// +// Authored by Abhinav Tondapu of Silimate, Inc. under ISC license. +// +// Convert subtraction to addition with negation +// +// a - b ===> a + (-b) +// + +state sub_a sub_b sub_y +state a_signed b_signed + +match sub + select sub->type == $sub + filter !sub->getPort(\B).is_fully_const() + set sub_a port(sub, \A) + set sub_b port(sub, \B) + set sub_y port(sub, \Y) + set a_signed sub->getParam(\A_SIGNED).as_bool() + set b_signed sub->getParam(\B_SIGNED).as_bool() +endmatch + +code sub_a sub_b sub_y a_signed b_signed + if (a_signed != b_signed) + reject; + + { + int width = GetSize(sub_y); + SigSpec neg_y = module->addWire(NEW_ID, width); + Cell *neg = module->addNeg(NEW_ID, sub_b, neg_y, b_signed); + Cell *add = module->addAdd(NEW_ID, sub_a, neg_y, sub_y, a_signed); + + log("sub2neg pattern in %s: sub=%s -> neg=%s, add=%s\n", + log_id(module), log_id(sub), log_id(neg), log_id(add)); + + neg->fixup_parameters(); + add->fixup_parameters(); + autoremove(sub); + did_something = true; + } + accept; +endcode diff --git a/tests/silimate/manual2sub.ys b/tests/silimate/manual2sub.ys new file mode 100644 index 000000000..a93896500 --- /dev/null +++ b/tests/silimate/manual2sub.ys @@ -0,0 +1,73 @@ +log -header "Positive case: (a + ~b) + 1 => a - b => a + (-b)" +log -push +design -reset +read_verilog < a - b => a + (-b)" +log -push +design -reset +read_verilog <