From 07c4a7d4388cdacaa15512dd2f6f0f9e9fcb31f5 Mon Sep 17 00:00:00 2001
From: Bogdan Vukobratovic <bogdan.vukobratovic@gmail.com>
Date: Fri, 26 Jul 2019 11:36:48 +0200
Subject: [PATCH 1/6] Implement opt_share

This pass identifies arithmetic operators that share an operand and whose
results are used in mutually exclusive cases controlled by a multiplexer, and
merges them together by multiplexing the other operands
---
 passes/opt/Makefile.inc         |   2 +-
 passes/opt/opt_share.cc         | 329 ++++++++++++++++++++++++++++++++
 tests/opt/opt_share_cat.v       |  15 ++
 tests/opt/opt_share_cat.ys      |   9 +
 tests/opt/opt_share_mux_tree.v  |  19 ++
 tests/opt/opt_share_mux_tree.ys |  10 +
 6 files changed, 383 insertions(+), 1 deletion(-)
 create mode 100644 passes/opt/opt_share.cc
 create mode 100644 tests/opt/opt_share_cat.v
 create mode 100644 tests/opt/opt_share_cat.ys
 create mode 100644 tests/opt/opt_share_mux_tree.v
 create mode 100644 tests/opt/opt_share_mux_tree.ys

diff --git a/passes/opt/Makefile.inc b/passes/opt/Makefile.inc
index ea3646330..eb07e9452 100644
--- a/passes/opt/Makefile.inc
+++ b/passes/opt/Makefile.inc
@@ -4,6 +4,7 @@ OBJS += passes/opt/opt_merge.o
 OBJS += passes/opt/opt_muxtree.o
 OBJS += passes/opt/opt_reduce.o
 OBJS += passes/opt/opt_rmdff.o
+OBJS += passes/opt/opt_share.o
 OBJS += passes/opt/opt_clean.o
 OBJS += passes/opt/opt_expr.o
 
@@ -16,4 +17,3 @@ OBJS += passes/opt/opt_lut.o
 OBJS += passes/opt/pmux2shiftx.o
 OBJS += passes/opt/muxpack.o
 endif
-
diff --git a/passes/opt/opt_share.cc b/passes/opt/opt_share.cc
new file mode 100644
index 000000000..9f6f59b64
--- /dev/null
+++ b/passes/opt/opt_share.cc
@@ -0,0 +1,329 @@
+/*
+ *  yosys -- Yosys Open SYnthesis Suite
+ *
+ *  Copyright (C) 2012  Clifford Wolf <clifford@clifford.at>
+ *
+ *  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.
+ *
+ */
+
+#include "kernel/log.h"
+#include "kernel/register.h"
+#include "kernel/rtlil.h"
+#include "kernel/sigtools.h"
+#include <algorithm>
+
+#include <stdio.h>
+#include <stdlib.h>
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+SigMap assign_map;
+
+// Helper class that to track whether a SigSpec is signed and whether it is
+// connected to the \\B port of the $sub cell, which makes its sign prefix
+// negative.
+struct ExtSigSpec {
+	RTLIL::SigSpec sig;
+	bool sign;
+	bool is_signed;
+
+	ExtSigSpec() {}
+
+	ExtSigSpec(RTLIL::SigSpec s, bool sign = false, bool is_signed = false) : sig(s), sign(sign), is_signed(is_signed) {}
+
+	ExtSigSpec(RTLIL::Cell *cell, RTLIL::IdString port_name, SigMap *sigmap)
+	{
+		sign = (cell->type == "$sub") && (port_name == "\\B");
+		sig = (*sigmap)(cell->getPort(port_name));
+
+		is_signed = false;
+		if (cell->hasParam(port_name.str() + "_SIGNED")) {
+			is_signed = cell->getParam(port_name.str() + "_SIGNED").as_bool();
+		}
+	}
+
+	bool empty() const { return sig.empty(); }
+
+	bool operator<(const ExtSigSpec &other) const
+	{
+		if (sig != other.sig)
+			return sig < other.sig;
+
+		if (sign != other.sign)
+			return sign < other.sign;
+
+		return is_signed < other.is_signed;
+	}
+
+	bool operator==(const RTLIL::SigSpec &other) const { return sign ? false : sig == other; }
+	bool operator==(const ExtSigSpec &other) const { return is_signed == other.is_signed && sign == other.sign && sig == other.sig; }
+};
+
+void merge_operators(RTLIL::Module *module, RTLIL::Cell *mux, const std::vector<RTLIL::Cell *> &operators, int offset, int width,
+		     const ExtSigSpec &operand)
+{
+
+	std::vector<ExtSigSpec> muxed_operands;
+	int max_width = 0;
+	for (auto op : operators) {
+		for (auto &conn : op->connections()) {
+			if (op->output(conn.first))
+				continue;
+
+			if (conn.second != operand.sig) {
+				auto operand = ExtSigSpec(op, conn.first, &assign_map);
+				if (operand.sig.size() > max_width) {
+					max_width = operand.sig.size();
+				}
+
+				muxed_operands.push_back(operand);
+			}
+		}
+	}
+
+	for (auto &operand : muxed_operands) {
+		operand.sig.extend_u0(max_width, operand.is_signed);
+	}
+
+	auto shared_op = operators[0];
+
+	for (auto op : operators) {
+		if (op == shared_op)
+			continue;
+		module->remove(op);
+	}
+
+	RTLIL::SigSpec mux_out = mux->getPort("\\Y");
+
+	if (muxed_operands[0].sign != muxed_operands[1].sign) {
+		muxed_operands[1] = ExtSigSpec(module->Neg(NEW_ID, muxed_operands[1].sig, muxed_operands[1].is_signed));
+	}
+
+	auto mux_to_oper = module->Mux(NEW_ID, muxed_operands[0].sig, muxed_operands[1].sig, mux->getPort("\\S"));
+
+	shared_op->setPort("\\Y", mux_out.extract(offset, width));
+	shared_op->setParam("\\Y_WIDTH", width);
+
+	auto dummy = module->addWire(NEW_ID, width);
+
+	mux_out.replace(offset, dummy);
+	mux->setPort("\\Y", mux_out);
+
+	if (shared_op->getPort("\\A") == operand.sig) {
+		shared_op->setPort("\\B", mux_to_oper);
+		shared_op->setParam("\\B_WIDTH", max_width);
+	} else {
+		shared_op->setPort("\\A", mux_to_oper);
+		shared_op->setParam("\\A_WIDTH", max_width);
+	}
+}
+
+typedef struct {
+	RTLIL::Cell *mux;
+	std::vector<RTLIL::Cell *> operators;
+	int offset;
+	int width;
+	ExtSigSpec shared_operand;
+} shared_op_t;
+
+bool find_op_res_width(int offset, int &width, RTLIL::SigSpec porta, RTLIL::SigSpec portb,
+		       const dict<RTLIL::SigBit, RTLIL::SigSpec> &op_outbit_to_outsig, const dict<RTLIL::SigBit, int> &op_outbit_user_cnt)
+{
+
+	std::array<RTLIL::SigSpec, 2> op_outsigs{op_outbit_to_outsig.at(porta[offset]), op_outbit_to_outsig.at(portb[offset])};
+
+	width = 0;
+	bool multi_user = false;
+
+	while (true) {
+		for (const auto &op_outsig : op_outsigs)
+			if (op_outbit_user_cnt.at(op_outsig[width]) > 1)
+				multi_user = true;
+
+		++offset;
+		++width;
+
+		if ((offset >= porta.size()) || (width >= op_outsigs[0].size()) || (width >= op_outsigs[1].size()))
+			break;
+
+		if ((porta[offset] != op_outsigs[0][width]) || (portb[offset] != op_outsigs[1][width]))
+			break;
+	}
+
+	if (multi_user)
+		return false;
+
+	for (const auto &outsig : op_outsigs)
+		for (int i = width; i < outsig.size(); i++)
+			if (op_outbit_user_cnt.count(outsig[i]))
+				return false;
+
+	return true;
+}
+
+ExtSigSpec find_shared_operand(const std::vector<RTLIL::Cell *> &operators, const std::map<ExtSigSpec, std::set<RTLIL::Cell *>> &operand_to_users)
+{
+
+	std::set<RTLIL::Cell *> operators_set(operators.begin(), operators.end());
+	ExtSigSpec oper;
+
+	auto op_a = operators[0];
+	for (auto &conn : op_a->connections()) {
+		if (op_a->output(conn.first))
+			continue;
+
+		oper = ExtSigSpec(op_a, conn.first, &assign_map);
+		auto bundle = operand_to_users.at(oper);
+
+		if (std::includes(bundle.begin(), bundle.end(), operators_set.begin(), operators_set.end()))
+			break;
+	}
+
+	return oper;
+}
+
+dict<RTLIL::SigBit, int> find_op_outbit_user_cnt(RTLIL::Module *module, const dict<RTLIL::SigBit, RTLIL::SigSpec> &op_outbit_to_outsig)
+{
+	dict<RTLIL::SigBit, int> op_outbit_user_cnt;
+
+	std::function<void(SigSpec)> update_op_outbit_user_cnt = [&](SigSpec sig) {
+		auto outsig = assign_map(sig);
+		for (auto outbit : outsig)
+			if (op_outbit_to_outsig.count(outbit))
+				op_outbit_user_cnt[outbit]++;
+	};
+
+	for (auto cell : module->cells())
+		for (auto &conn : cell->connections())
+			if (cell->input(conn.first))
+				update_op_outbit_user_cnt(conn.second);
+
+	for (auto w : module->wires()) {
+		if (!w->port_output)
+			continue;
+
+		update_op_outbit_user_cnt(w);
+	}
+
+	return op_outbit_user_cnt;
+}
+
+struct OptRmdffPass : public Pass {
+	OptRmdffPass() : Pass("opt_share", "merge arithmetic operators that share an operand") {}
+	void help() YS_OVERRIDE
+	{
+		//   |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+		log("\n");
+		log("    opt_share [selection]\n");
+		log("\n");
+		log("This pass identifies arithmetic operators that share an operand and whose\n");
+		log("results are used in mutually exclusive cases controlled by a multiplexer,\n");
+		log("and merges them together by multiplexing the other operands.\n");
+		log("\n");
+	}
+	void execute(std::vector<std::string>, RTLIL::Design *design) YS_OVERRIDE
+	{
+
+		log_header(design, "Executing OPT_SHARE pass.\n");
+
+		for (auto module : design->selected_modules()) {
+			assign_map.clear();
+			assign_map.set(module);
+
+			std::map<ExtSigSpec, std::set<RTLIL::Cell *>> operand_to_users;
+			dict<RTLIL::SigSpec, RTLIL::Cell *> outsig_to_operator;
+			dict<RTLIL::SigBit, RTLIL::SigSpec> op_outbit_to_outsig;
+			bool any_shared_operands = false;
+
+			for (auto cell : module->cells()) {
+				if (!cell->type.in("$add", "$sub"))
+					continue;
+
+				for (auto &conn : cell->connections()) {
+					if (cell->output(conn.first)) {
+						auto outsig = assign_map(conn.second);
+						for (auto outbit : outsig)
+							op_outbit_to_outsig[outbit] = outsig;
+
+						outsig_to_operator[outsig] = cell;
+					} else {
+						auto op_insig = ExtSigSpec(cell, conn.first, &assign_map);
+						operand_to_users[op_insig].insert(cell);
+						if (operand_to_users[op_insig].size() > 1)
+							any_shared_operands = true;
+					}
+				}
+			}
+
+			if (!any_shared_operands)
+				continue;
+
+			// Operator outputs need to be exclusively connected to the $mux inputs in order to be mergeable. Hence we count to
+			// how many points are operator output bits connected.
+			dict<RTLIL::SigBit, int> op_outbit_user_cnt = find_op_outbit_user_cnt(module, op_outbit_to_outsig);
+			std::vector<shared_op_t> shared_ops;
+			for (auto cell : module->cells()) {
+				if (!cell->type.in("$mux", "$_MUX_"))
+					continue;
+
+				auto porta = assign_map(cell->getPort("\\A"));
+				auto portb = assign_map(cell->getPort("\\B"));
+
+				// Look through the bits of the $mux inputs and see which of them are connected to the operator
+				// results. Operator results can be concatenated with other signals before led to the $mux.
+				for (int i = 0; i < porta.size(); ++i) {
+					std::array<RTLIL::SigBit, 2> mux_inbits{porta[i], portb[i]};
+
+					// Are the results of an $add or $sub operators connected to both of this $mux inputs?
+					if (!op_outbit_to_outsig.count(mux_inbits[0]) or !op_outbit_to_outsig.count(mux_inbits[1]))
+						continue;
+
+					std::vector<RTLIL::Cell *> operators;
+					for (const auto &b : mux_inbits)
+						operators.push_back(outsig_to_operator.at(op_outbit_to_outsig.at(b)));
+
+					// Do these operators share an operand?
+					auto shared_operand = find_shared_operand(operators, operand_to_users);
+					if (shared_operand.empty())
+						continue;
+
+					// Some bits of the operator results might be unconnected. Calculate the number of conneted
+					// bits.
+					int width;
+
+					if (find_op_res_width(i, width, porta, portb, op_outbit_to_outsig, op_outbit_user_cnt))
+						shared_ops.push_back(shared_op_t{cell, operators, i, width, shared_operand});
+
+					i += width - 1;
+				}
+			}
+
+			for (auto &shared : shared_ops) {
+				log("    Found arithmetic cells that share an operand and can be merged by moving the %s %s in front "
+				    "of "
+				    "them:\n",
+				    log_id(shared.mux->type), log_id(shared.mux));
+				for (auto op : shared.operators)
+					log("        %s\n", log_id(op));
+				log("\n");
+
+				merge_operators(module, shared.mux, shared.operators, shared.offset, shared.width, shared.shared_operand);
+			}
+		}
+	}
+
+} OptRmdffPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/tests/opt/opt_share_cat.v b/tests/opt/opt_share_cat.v
new file mode 100644
index 000000000..c32073360
--- /dev/null
+++ b/tests/opt/opt_share_cat.v
@@ -0,0 +1,15 @@
+module add_sub(
+               input [15:0]  a,
+               input [15:0]  b,
+               input [15:0]  c,
+               input [15:0]  d,
+               input 				 sel,
+               output [63:0] res,
+               );
+
+  reg [31: 0] 							 cat1 = {a+b, c+d};
+  reg [31: 0] 							 cat2 = {a-b, c-d};
+
+  assign res = {b, sel ? cat1 : cat2, a};
+
+endmodule
diff --git a/tests/opt/opt_share_cat.ys b/tests/opt/opt_share_cat.ys
new file mode 100644
index 000000000..c3f2f5a2f
--- /dev/null
+++ b/tests/opt/opt_share_cat.ys
@@ -0,0 +1,9 @@
+read_verilog opt_share_cat.v
+prep -flatten
+opt
+pmuxtree
+opt_share
+opt_clean
+
+select -assert-count 2 t:$sub
+select -assert-count 0 t:$add
diff --git a/tests/opt/opt_share_mux_tree.v b/tests/opt/opt_share_mux_tree.v
new file mode 100644
index 000000000..807ed2978
--- /dev/null
+++ b/tests/opt/opt_share_mux_tree.v
@@ -0,0 +1,19 @@
+module add_sub(
+               input [15:0] 		 a,
+               input [15:0] 		 b,
+               input [15:0] 		 c,
+               input [1:0] 			 sel,
+               output reg [15:0] res
+               );
+
+
+  always @* begin
+    case(sel)
+      0: res = a + b;
+      1: res = a - b;
+      2: res = a + c;
+      default: res = 16'bx;
+    endcase
+  end
+
+endmodule
diff --git a/tests/opt/opt_share_mux_tree.ys b/tests/opt/opt_share_mux_tree.ys
new file mode 100644
index 000000000..94d6aa7d2
--- /dev/null
+++ b/tests/opt/opt_share_mux_tree.ys
@@ -0,0 +1,10 @@
+read_verilog opt_share_mux_tree.v
+prep -flatten
+opt
+pmuxtree
+opt_share;
+opt_share;
+opt_clean
+
+select -assert-count 1 t:$add
+select -assert-count 0 t:$sub

From c075486c59155d16ed278922a3752366a95246ff Mon Sep 17 00:00:00 2001
From: Bogdan Vukobratovic <bogdan.vukobratovic@gmail.com>
Date: Sun, 28 Jul 2019 16:03:54 +0200
Subject: [PATCH 2/6] Reimplement opt_share to work on $alu and $pmux

---
 passes/opt/opt_share.cc                       | 322 ++++++++++++------
 tests/opt/opt_share_add_sub.v                 |  10 +
 tests/opt/opt_share_add_sub.ys                |  13 +
 tests/opt/opt_share_cat.v                     |   2 +-
 tests/opt/opt_share_cat.ys                    |  18 +-
 tests/opt/opt_share_cat_multiuser.v           |  22 ++
 tests/opt/opt_share_cat_multiuser.ys          |  13 +
 tests/opt/opt_share_diff_port_widths.v        |  21 ++
 tests/opt/opt_share_diff_port_widths.ys       |  13 +
 tests/opt/opt_share_extend.v                  |  19 ++
 tests/opt/opt_share_extend.ys                 |  13 +
 tests/opt/opt_share_large_pmux_cat.v          |  22 ++
 tests/opt/opt_share_large_pmux_cat.ys         |  13 +
 .../opt/opt_share_large_pmux_cat_multipart.v  |  25 ++
 .../opt/opt_share_large_pmux_cat_multipart.ys |  15 +
 tests/opt/opt_share_large_pmux_multipart.v    |  24 ++
 tests/opt/opt_share_large_pmux_multipart.ys   |  13 +
 tests/opt/opt_share_large_pmux_part.v         |  22 ++
 tests/opt/opt_share_large_pmux_part.ys        |  13 +
 tests/opt/opt_share_mux_tree.v                |   2 +-
 tests/opt/opt_share_mux_tree.ys               |  19 +-
 21 files changed, 521 insertions(+), 113 deletions(-)
 create mode 100644 tests/opt/opt_share_add_sub.v
 create mode 100644 tests/opt/opt_share_add_sub.ys
 create mode 100644 tests/opt/opt_share_cat_multiuser.v
 create mode 100644 tests/opt/opt_share_cat_multiuser.ys
 create mode 100644 tests/opt/opt_share_diff_port_widths.v
 create mode 100644 tests/opt/opt_share_diff_port_widths.ys
 create mode 100644 tests/opt/opt_share_extend.v
 create mode 100644 tests/opt/opt_share_extend.ys
 create mode 100644 tests/opt/opt_share_large_pmux_cat.v
 create mode 100644 tests/opt/opt_share_large_pmux_cat.ys
 create mode 100644 tests/opt/opt_share_large_pmux_cat_multipart.v
 create mode 100644 tests/opt/opt_share_large_pmux_cat_multipart.ys
 create mode 100644 tests/opt/opt_share_large_pmux_multipart.v
 create mode 100644 tests/opt/opt_share_large_pmux_multipart.ys
 create mode 100644 tests/opt/opt_share_large_pmux_part.v
 create mode 100644 tests/opt/opt_share_large_pmux_part.ys

diff --git a/passes/opt/opt_share.cc b/passes/opt/opt_share.cc
index 9f6f59b64..e9a2f05f9 100644
--- a/passes/opt/opt_share.cc
+++ b/passes/opt/opt_share.cc
@@ -31,12 +31,21 @@ PRIVATE_NAMESPACE_BEGIN
 
 SigMap assign_map;
 
+struct InPort {
+	RTLIL::SigSpec sig;
+	RTLIL::Cell *pmux;
+	int port_id;
+	RTLIL::Cell *alu;
+
+	InPort(RTLIL::SigSpec s, RTLIL::Cell *c, int p, RTLIL::Cell *a = NULL) : sig(s), pmux(c), port_id(p), alu(a) {}
+};
+
 // Helper class that to track whether a SigSpec is signed and whether it is
 // connected to the \\B port of the $sub cell, which makes its sign prefix
 // negative.
 struct ExtSigSpec {
 	RTLIL::SigSpec sig;
-	bool sign;
+	RTLIL::SigSpec sign;
 	bool is_signed;
 
 	ExtSigSpec() {}
@@ -45,7 +54,7 @@ struct ExtSigSpec {
 
 	ExtSigSpec(RTLIL::Cell *cell, RTLIL::IdString port_name, SigMap *sigmap)
 	{
-		sign = (cell->type == "$sub") && (port_name == "\\B");
+		sign = (port_name == "\\B") ? cell->getPort("\\BI") : RTLIL::Const(0, 1);
 		sig = (*sigmap)(cell->getPort(port_name));
 
 		is_signed = false;
@@ -67,23 +76,22 @@ struct ExtSigSpec {
 		return is_signed < other.is_signed;
 	}
 
-	bool operator==(const RTLIL::SigSpec &other) const { return sign ? false : sig == other; }
+	bool operator==(const RTLIL::SigSpec &other) const { return (sign != RTLIL::Const(0, 1)) ? false : sig == other; }
 	bool operator==(const ExtSigSpec &other) const { return is_signed == other.is_signed && sign == other.sign && sig == other.sig; }
 };
 
-void merge_operators(RTLIL::Module *module, RTLIL::Cell *mux, const std::vector<RTLIL::Cell *> &operators, int offset, int width,
+void merge_operators(RTLIL::Module *module, RTLIL::Cell *mux, const std::vector<InPort> &ports, int offset, int width,
 		     const ExtSigSpec &operand)
 {
 
 	std::vector<ExtSigSpec> muxed_operands;
 	int max_width = 0;
-	for (auto op : operators) {
-		for (auto &conn : op->connections()) {
-			if (op->output(conn.first))
-				continue;
+	for (const auto& p : ports) {
+		auto op = p.alu;
 
-			if (conn.second != operand.sig) {
-				auto operand = ExtSigSpec(op, conn.first, &assign_map);
+		for (RTLIL::IdString port_name : {"\\A", "\\B"}) {
+			if (op->getPort(port_name) != operand.sig) {
+				auto operand = ExtSigSpec(op, port_name, &assign_map);
 				if (operand.sig.size() > max_width) {
 					max_width = operand.sig.size();
 				}
@@ -97,30 +105,61 @@ void merge_operators(RTLIL::Module *module, RTLIL::Cell *mux, const std::vector<
 		operand.sig.extend_u0(max_width, operand.is_signed);
 	}
 
-	auto shared_op = operators[0];
+	auto shared_op = ports[0].alu;
 
-	for (auto op : operators) {
+	for (const auto& p : ports) {
+		auto op = p.alu;
 		if (op == shared_op)
 			continue;
 		module->remove(op);
 	}
 
-	RTLIL::SigSpec mux_out = mux->getPort("\\Y");
-
-	if (muxed_operands[0].sign != muxed_operands[1].sign) {
-		muxed_operands[1] = ExtSigSpec(module->Neg(NEW_ID, muxed_operands[1].sig, muxed_operands[1].is_signed));
+	for (auto &muxed_op : muxed_operands) {
+		if (muxed_op.sign != muxed_operands[0].sign) {
+			muxed_op = ExtSigSpec(module->Neg(NEW_ID, muxed_op.sig, muxed_op.is_signed));
+		}
 	}
 
-	auto mux_to_oper = module->Mux(NEW_ID, muxed_operands[0].sig, muxed_operands[1].sig, mux->getPort("\\S"));
+	RTLIL::SigSpec mux_y = mux->getPort("\\Y");
+	RTLIL::SigSpec mux_a = mux->getPort("\\A");
+	RTLIL::SigSpec mux_b = mux->getPort("\\B");
+	RTLIL::SigSpec mux_s = mux->getPort("\\S");
 
-	shared_op->setPort("\\Y", mux_out.extract(offset, width));
+	RTLIL::SigSpec alu_x = shared_op->getPort("\\X");
+	RTLIL::SigSpec alu_co = shared_op->getPort("\\CO");
+
+	RTLIL::SigSpec shared_pmux_a = RTLIL::Const(RTLIL::State::Sx, max_width);
+	RTLIL::SigSpec shared_pmux_b;
+	RTLIL::SigSpec shared_pmux_s;
+
+	shared_op->setPort("\\Y", shared_op->getPort("\\Y").extract(0, width));
+
+	if (mux->type == "$pmux") {
+		shared_pmux_s = RTLIL::SigSpec();
+
+		for (const auto&p: ports) {
+			shared_pmux_s.append(mux_s[p.port_id]);
+			mux_b.replace(p.port_id * mux_a.size() + offset, shared_op->getPort("\\Y"));
+		}
+	} else {
+		shared_pmux_s = RTLIL::SigSpec{mux_s, module->Not(NEW_ID, mux_s)};
+		mux_a.replace(offset, shared_op->getPort("\\Y"));
+		mux_b.replace(offset, shared_op->getPort("\\Y"));
+	}
+
+	mux->setPort("\\Y", mux_y);
+	mux->setPort("\\S", mux_s);
+	mux->setPort("\\B", mux_b);
+
+	for (const auto &op : muxed_operands)
+		shared_pmux_b.append(op.sig);
+
+	auto mux_to_oper = module->Pmux(NEW_ID, shared_pmux_a, shared_pmux_b, shared_pmux_s);
+
+	shared_op->setPort("\\X", alu_x.extract(0, width));
+	shared_op->setPort("\\CO", alu_co.extract(0, width));
 	shared_op->setParam("\\Y_WIDTH", width);
 
-	auto dummy = module->addWire(NEW_ID, width);
-
-	mux_out.replace(offset, dummy);
-	mux->setPort("\\Y", mux_out);
-
 	if (shared_op->getPort("\\A") == operand.sig) {
 		shared_op->setPort("\\B", mux_to_oper);
 		shared_op->setParam("\\B_WIDTH", max_width);
@@ -128,81 +167,132 @@ void merge_operators(RTLIL::Module *module, RTLIL::Cell *mux, const std::vector<
 		shared_op->setPort("\\A", mux_to_oper);
 		shared_op->setParam("\\A_WIDTH", max_width);
 	}
+
 }
 
 typedef struct {
 	RTLIL::Cell *mux;
-	std::vector<RTLIL::Cell *> operators;
+	std::vector<InPort> ports;
 	int offset;
 	int width;
 	ExtSigSpec shared_operand;
 } shared_op_t;
 
-bool find_op_res_width(int offset, int &width, RTLIL::SigSpec porta, RTLIL::SigSpec portb,
-		       const dict<RTLIL::SigBit, RTLIL::SigSpec> &op_outbit_to_outsig, const dict<RTLIL::SigBit, int> &op_outbit_user_cnt)
+
+template <typename T> void remove_val(std::vector<T> &v, const std::vector<T> &vals)
+{
+	auto val_iter = vals.rbegin();
+	for (auto i = v.rbegin(); i != v.rend(); ++i)
+		if ((val_iter != vals.rend()) && (*i == *val_iter)) {
+			v.erase(i.base() - 1);
+			++val_iter;
+		}
+}
+
+bool find_op_res_width(int offset, int &width, std::vector<InPort*>& ports, const dict<RTLIL::SigBit, RTLIL::SigSpec> &op_outbit_to_outsig)
 {
 
-	std::array<RTLIL::SigSpec, 2> op_outsigs{op_outbit_to_outsig.at(porta[offset]), op_outbit_to_outsig.at(portb[offset])};
+	std::vector<RTLIL::SigSpec> op_outsigs;
+	dict<int, std::set<InPort*>> op_outsig_span;
+
+	std::transform(ports.begin(), ports.end(), std::back_inserter(op_outsigs), [&](InPort *p) { return op_outbit_to_outsig.at(p->sig[offset]); });
+
+	std::vector<bool> finished(ports.size(), false);
 
 	width = 0;
-	bool multi_user = false;
 
-	while (true) {
-		for (const auto &op_outsig : op_outsigs)
-			if (op_outbit_user_cnt.at(op_outsig[width]) > 1)
-				multi_user = true;
+	std::function<bool()> all_finished = [&] { return std::find(std::begin(finished), std::end(finished), false) == end(finished);};
 
+	while (!all_finished())
+	{
 		++offset;
 		++width;
 
-		if ((offset >= porta.size()) || (width >= op_outsigs[0].size()) || (width >= op_outsigs[1].size()))
-			break;
+		if (offset >= ports[0]->sig.size()) {
+			for (size_t i = 0; i < op_outsigs.size(); ++i) {
+				if (finished[i])
+					continue;
+
+				op_outsig_span[width].insert(ports[i]);
+				finished[i] = true;
+			}
 
-		if ((porta[offset] != op_outsigs[0][width]) || (portb[offset] != op_outsigs[1][width]))
 			break;
+		}
+
+		for (size_t i = 0; i < op_outsigs.size(); ++i) {
+			if (finished[i])
+				continue;
+
+			if ((width >= op_outsigs[i].size()) || (ports[i]->sig[offset] != op_outsigs[i][width])) {
+				op_outsig_span[width].insert(ports[i]);
+				finished[i] = true;
+			}
+		}
 	}
 
-	if (multi_user)
-		return false;
+	for (auto w: op_outsig_span) {
+		if (w.second.size() > 1) {
+			width = w.first;
 
-	for (const auto &outsig : op_outsigs)
-		for (int i = width; i < outsig.size(); i++)
-			if (op_outbit_user_cnt.count(outsig[i]))
-				return false;
+			ports.erase(std::remove_if(ports.begin(), ports.end(), [&](InPort *p) { return !w.second.count(p); }), ports.end());
 
-	return true;
+			return true;
+		}
+	}
+
+	return false;
 }
 
-ExtSigSpec find_shared_operand(const std::vector<RTLIL::Cell *> &operators, const std::map<ExtSigSpec, std::set<RTLIL::Cell *>> &operand_to_users)
+ExtSigSpec find_shared_operand(InPort* seed, std::vector<InPort *> &ports, const std::map<ExtSigSpec, std::set<RTLIL::Cell *>> &operand_to_users)
 {
+	std::set<RTLIL::Cell *> alus_using_operand;
+	std::set<RTLIL::Cell *> alus_set;
+	for(const auto& p: ports)
+		alus_set.insert(p->alu);
 
-	std::set<RTLIL::Cell *> operators_set(operators.begin(), operators.end());
 	ExtSigSpec oper;
 
-	auto op_a = operators[0];
-	for (auto &conn : op_a->connections()) {
-		if (op_a->output(conn.first))
+	auto op_a = seed->alu;
+
+	for (RTLIL::IdString port_name : {"\\A", "\\B"}) {
+		oper = ExtSigSpec(op_a, port_name, &assign_map);
+		auto operand_users = operand_to_users.at(oper);
+
+		if (operand_users.size() == 1)
 			continue;
 
-		oper = ExtSigSpec(op_a, conn.first, &assign_map);
-		auto bundle = operand_to_users.at(oper);
+		alus_using_operand.clear();
+		std::set_intersection(operand_users.begin(), operand_users.end(), alus_set.begin(), alus_set.end(),
+				      std::inserter(alus_using_operand, alus_using_operand.begin()));
 
-		if (std::includes(bundle.begin(), bundle.end(), operators_set.begin(), operators_set.end()))
-			break;
+		if (alus_using_operand.size() > 1) {
+			ports.erase(std::remove_if(ports.begin(), ports.end(), [&](InPort *p) { return !alus_using_operand.count(p->alu); }),
+				    ports.end());
+			return oper;
+		}
 	}
 
-	return oper;
+	return ExtSigSpec();
 }
 
-dict<RTLIL::SigBit, int> find_op_outbit_user_cnt(RTLIL::Module *module, const dict<RTLIL::SigBit, RTLIL::SigSpec> &op_outbit_to_outsig)
+void remove_multi_user_outbits(RTLIL::Module *module, dict<RTLIL::SigBit, RTLIL::SigSpec> &op_outbit_to_outsig)
 {
 	dict<RTLIL::SigBit, int> op_outbit_user_cnt;
 
 	std::function<void(SigSpec)> update_op_outbit_user_cnt = [&](SigSpec sig) {
 		auto outsig = assign_map(sig);
-		for (auto outbit : outsig)
-			if (op_outbit_to_outsig.count(outbit))
-				op_outbit_user_cnt[outbit]++;
+		for (auto outbit : outsig) {
+			if (!op_outbit_to_outsig.count(outbit))
+				continue;
+
+			if (++op_outbit_user_cnt[outbit] > 1) {
+				auto alu_outsig = op_outbit_to_outsig.at(outbit);
+
+				for (auto outbit : alu_outsig)
+					op_outbit_to_outsig.erase(outbit);
+			}
+		}
 	};
 
 	for (auto cell : module->cells())
@@ -216,8 +306,6 @@ dict<RTLIL::SigBit, int> find_op_outbit_user_cnt(RTLIL::Module *module, const di
 
 		update_op_outbit_user_cnt(w);
 	}
-
-	return op_outbit_user_cnt;
 }
 
 struct OptRmdffPass : public Pass {
@@ -246,24 +334,31 @@ struct OptRmdffPass : public Pass {
 			dict<RTLIL::SigSpec, RTLIL::Cell *> outsig_to_operator;
 			dict<RTLIL::SigBit, RTLIL::SigSpec> op_outbit_to_outsig;
 			bool any_shared_operands = false;
+			std::vector<ExtSigSpec> op_insigs;
 
 			for (auto cell : module->cells()) {
-				if (!cell->type.in("$add", "$sub"))
+				if (!cell->type.in("$alu"))
 					continue;
 
-				for (auto &conn : cell->connections()) {
-					if (cell->output(conn.first)) {
-						auto outsig = assign_map(conn.second);
-						for (auto outbit : outsig)
-							op_outbit_to_outsig[outbit] = outsig;
+				RTLIL::SigSpec sig_bi = cell->getPort("\\BI");
+				RTLIL::SigSpec sig_ci = cell->getPort("\\CI");
 
-						outsig_to_operator[outsig] = cell;
-					} else {
-						auto op_insig = ExtSigSpec(cell, conn.first, &assign_map);
-						operand_to_users[op_insig].insert(cell);
-						if (operand_to_users[op_insig].size() > 1)
-							any_shared_operands = true;
-					}
+				if ((!sig_bi.is_fully_const()) || (!sig_ci.is_fully_const()) || (sig_bi != sig_ci))
+					continue;
+
+				RTLIL::SigSpec sig_y = cell->getPort("\\A");
+
+				auto outsig = assign_map(cell->getPort("\\Y"));
+				outsig_to_operator[outsig] = cell;
+				for (auto outbit : outsig)
+					op_outbit_to_outsig[outbit] = outsig;
+
+				for (RTLIL::IdString port_name : {"\\A", "\\B"}) {
+					auto op_insig = ExtSigSpec(cell, port_name, &assign_map);
+					op_insigs.push_back(op_insig);
+					operand_to_users[op_insig].insert(cell);
+					if (operand_to_users[op_insig].size() > 1)
+						any_shared_operands = true;
 				}
 			}
 
@@ -272,42 +367,77 @@ struct OptRmdffPass : public Pass {
 
 			// Operator outputs need to be exclusively connected to the $mux inputs in order to be mergeable. Hence we count to
 			// how many points are operator output bits connected.
-			dict<RTLIL::SigBit, int> op_outbit_user_cnt = find_op_outbit_user_cnt(module, op_outbit_to_outsig);
+			remove_multi_user_outbits(module, op_outbit_to_outsig);
+
 			std::vector<shared_op_t> shared_ops;
 			for (auto cell : module->cells()) {
-				if (!cell->type.in("$mux", "$_MUX_"))
+				if (!cell->type.in("$mux", "$_MUX_", "$pmux"))
 					continue;
 
-				auto porta = assign_map(cell->getPort("\\A"));
-				auto portb = assign_map(cell->getPort("\\B"));
+				RTLIL::SigSpec sig_a = cell->getPort("\\A");
+				RTLIL::SigSpec sig_b = cell->getPort("\\B");
+				RTLIL::SigSpec sig_s = cell->getPort("\\S");
+
+				std::vector<InPort> ports;
+
+				if (cell->type.in("$mux", "$_MUX_")) {
+					ports.push_back(InPort(assign_map(sig_a), cell, 0));
+					ports.push_back(InPort(assign_map(sig_b), cell, 1));
+				} else {
+					RTLIL::SigSpec sig_s = cell->getPort("\\S");
+					for (int i = 0; i < sig_s.size(); i++) {
+						auto inp = sig_b.extract(i * sig_a.size(), sig_a.size());
+						ports.push_back(InPort(assign_map(inp), cell, i));
+					}
+				}
 
 				// Look through the bits of the $mux inputs and see which of them are connected to the operator
 				// results. Operator results can be concatenated with other signals before led to the $mux.
-				for (int i = 0; i < porta.size(); ++i) {
-					std::array<RTLIL::SigBit, 2> mux_inbits{porta[i], portb[i]};
+				for (int i = 0; i < sig_a.size(); ++i) {
+					std::vector<InPort*> alu_ports;
+					for (auto& p: ports)
+						if (op_outbit_to_outsig.count(p.sig[i])) {
+							p.alu = outsig_to_operator.at(op_outbit_to_outsig.at(p.sig[i]));
+							alu_ports.push_back(&p);
+						}
 
-					// Are the results of an $add or $sub operators connected to both of this $mux inputs?
-					if (!op_outbit_to_outsig.count(mux_inbits[0]) or !op_outbit_to_outsig.count(mux_inbits[1]))
-						continue;
+					int alu_port_width = 0;
 
-					std::vector<RTLIL::Cell *> operators;
-					for (const auto &b : mux_inbits)
-						operators.push_back(outsig_to_operator.at(op_outbit_to_outsig.at(b)));
+					while (alu_ports.size() > 1) {
+						std::vector<InPort*> shared_ports(alu_ports);
 
-					// Do these operators share an operand?
-					auto shared_operand = find_shared_operand(operators, operand_to_users);
-					if (shared_operand.empty())
-						continue;
+						auto seed = alu_ports[0];
+						alu_ports.erase(alu_ports.begin());
 
-					// Some bits of the operator results might be unconnected. Calculate the number of conneted
-					// bits.
-					int width;
+						// Find ports whose $alu-s share an operand with $alu connected to the seed port
+						auto shared_operand = find_shared_operand(seed, shared_ports, operand_to_users);
 
-					if (find_op_res_width(i, width, porta, portb, op_outbit_to_outsig, op_outbit_user_cnt))
-						shared_ops.push_back(shared_op_t{cell, operators, i, width, shared_operand});
+						if (shared_operand.empty())
+							continue;
 
-					i += width - 1;
+						// Some bits of the operator results might be unconnected. Calculate the number of conneted
+						// bits.
+						if (!find_op_res_width(i, alu_port_width, shared_ports, op_outbit_to_outsig))
+							break;
+
+						if (shared_ports.size() < 2)
+							break;
+
+						// Remember the combination for the merger
+						std::vector<InPort> shared_p;
+						for (auto p: shared_ports)
+							shared_p.push_back(*p);
+
+						shared_ops.push_back(shared_op_t{cell, shared_p, i, alu_port_width, shared_operand});
+
+						// Remove merged ports from the list and try to find other mergers for the mux
+						remove_val(alu_ports, shared_ports);
+					}
+
+					if (alu_port_width)
+						i += alu_port_width - 1;
 				}
+
 			}
 
 			for (auto &shared : shared_ops) {
@@ -315,11 +445,11 @@ struct OptRmdffPass : public Pass {
 				    "of "
 				    "them:\n",
 				    log_id(shared.mux->type), log_id(shared.mux));
-				for (auto op : shared.operators)
-					log("        %s\n", log_id(op));
+				for (const auto& op : shared.ports)
+					log("        %s\n", log_id(op.alu));
 				log("\n");
 
-				merge_operators(module, shared.mux, shared.operators, shared.offset, shared.width, shared.shared_operand);
+				merge_operators(module, shared.mux, shared.ports, shared.offset, shared.width, shared.shared_operand);
 			}
 		}
 	}
diff --git a/tests/opt/opt_share_add_sub.v b/tests/opt/opt_share_add_sub.v
new file mode 100644
index 000000000..30e093a39
--- /dev/null
+++ b/tests/opt/opt_share_add_sub.v
@@ -0,0 +1,10 @@
+module opt_share_test(
+               input [15:0]  a,
+               input [15:0]  b,
+               input 				 sel,
+               output [15:0] res,
+               );
+
+  assign res = {sel ? a + b : a - b};
+
+endmodule
diff --git a/tests/opt/opt_share_add_sub.ys b/tests/opt/opt_share_add_sub.ys
new file mode 100644
index 000000000..4a5406791
--- /dev/null
+++ b/tests/opt/opt_share_add_sub.ys
@@ -0,0 +1,13 @@
+read_verilog opt_share_add_sub.v
+proc;;
+copy opt_share_test merged
+
+alumacc merged
+opt merged
+opt_share merged
+opt_clean merged
+
+miter -equiv -flatten -ignore_gold_x -make_outputs -make_outcmp opt_share_test merged miter
+sat -set-def-inputs -verify -prove trigger 0 -show-inputs -show-outputs miter
+
+select -assert-count 1 -module merged t:$alu
diff --git a/tests/opt/opt_share_cat.v b/tests/opt/opt_share_cat.v
index c32073360..605dcfe59 100644
--- a/tests/opt/opt_share_cat.v
+++ b/tests/opt/opt_share_cat.v
@@ -1,4 +1,4 @@
-module add_sub(
+module opt_share_test(
                input [15:0]  a,
                input [15:0]  b,
                input [15:0]  c,
diff --git a/tests/opt/opt_share_cat.ys b/tests/opt/opt_share_cat.ys
index c3f2f5a2f..7de69bfde 100644
--- a/tests/opt/opt_share_cat.ys
+++ b/tests/opt/opt_share_cat.ys
@@ -1,9 +1,13 @@
 read_verilog opt_share_cat.v
-prep -flatten
-opt
-pmuxtree
-opt_share
-opt_clean
+proc;;
+copy opt_share_test merged
 
-select -assert-count 2 t:$sub
-select -assert-count 0 t:$add
+alumacc merged
+opt merged
+opt_share merged
+opt_clean merged
+
+miter -equiv -flatten -ignore_gold_x -make_outputs -make_outcmp opt_share_test merged miter
+sat -set-def-inputs -verify -prove trigger 0 -show-inputs -show-outputs miter
+
+select -assert-count 2 -module merged t:$alu
diff --git a/tests/opt/opt_share_cat_multiuser.v b/tests/opt/opt_share_cat_multiuser.v
new file mode 100644
index 000000000..9ac0ceec8
--- /dev/null
+++ b/tests/opt/opt_share_cat_multiuser.v
@@ -0,0 +1,22 @@
+module opt_share_test(
+               input [15:0] 		 a,
+               input [15:0] 		 b,
+               input [15:0] 		 c,
+               input [15:0] 		 d,
+               input 						 sel,
+               output reg [47:0] res,
+               );
+
+  wire [15:0] 									 add_res = a+b;
+  wire [15:0] 									 sub_res = a-b;
+  wire [31: 0] 									 cat1 = {add_res, c+d};
+  wire [31: 0] 									 cat2 = {sub_res, c-d};
+
+  always @* begin
+    case(sel)
+      0: res = {cat1, add_res};
+      1: res = {cat2, add_res};
+    endcase
+  end
+
+endmodule
diff --git a/tests/opt/opt_share_cat_multiuser.ys b/tests/opt/opt_share_cat_multiuser.ys
new file mode 100644
index 000000000..6a82fbd79
--- /dev/null
+++ b/tests/opt/opt_share_cat_multiuser.ys
@@ -0,0 +1,13 @@
+read_verilog opt_share_cat_multiuser.v
+proc;;
+copy opt_share_test merged
+
+alumacc merged
+opt merged
+opt_share merged
+opt_clean merged
+
+miter -equiv -flatten -ignore_gold_x -make_outputs -make_outcmp opt_share_test merged miter
+sat -set-def-inputs -verify -prove trigger 0 -show-inputs -show-outputs miter
+
+select -assert-count 3 -module merged t:$alu
diff --git a/tests/opt/opt_share_diff_port_widths.v b/tests/opt/opt_share_diff_port_widths.v
new file mode 100644
index 000000000..5e2971e30
--- /dev/null
+++ b/tests/opt/opt_share_diff_port_widths.v
@@ -0,0 +1,21 @@
+module opt_share_test(
+               input [15:0] 		 a,
+               input [15:0] 		 b,
+							 input [15:0] 		 c,
+							 input [1:0] 			 sel,
+							 output reg [15:0] res
+               );
+
+  wire [15:0] 							 add0_res = a+b;
+  wire [15:0] 							 add1_res = a+c;
+
+  always @* begin
+    case(sel)
+      0: res = add0_res[10:0];
+      1: res = add1_res[10:0];
+      2: res = a - b;
+      default: res = 32'bx;
+    endcase
+  end
+
+endmodule
diff --git a/tests/opt/opt_share_diff_port_widths.ys b/tests/opt/opt_share_diff_port_widths.ys
new file mode 100644
index 000000000..ec5e9f7b0
--- /dev/null
+++ b/tests/opt/opt_share_diff_port_widths.ys
@@ -0,0 +1,13 @@
+read_verilog opt_share_diff_port_widths.v
+proc;;
+copy opt_share_test merged
+
+alumacc merged
+opt merged
+opt_share merged
+opt_clean merged
+
+miter -equiv -flatten -ignore_gold_x -make_outputs -make_outcmp opt_share_test merged miter
+sat -set-def-inputs -verify -prove trigger 0 -show-inputs -show-outputs miter
+
+select -assert-count 2 -module merged t:$alu
diff --git a/tests/opt/opt_share_extend.v b/tests/opt/opt_share_extend.v
new file mode 100644
index 000000000..5ed6bde6f
--- /dev/null
+++ b/tests/opt/opt_share_extend.v
@@ -0,0 +1,19 @@
+module opt_share_test(
+											input signed [7:0] 			 a,
+											input signed [10:0] 		 b,
+											input signed [15:0] 		 c,
+											input [1:0] 						 sel,
+											output reg signed [15:0] res
+											);
+
+
+  always @* begin
+    case(sel)
+      0: res = a + b;
+      1: res = a - b;
+      2: res = a + c;
+      default: res = 16'bx;
+    endcase
+  end
+
+endmodule
diff --git a/tests/opt/opt_share_extend.ys b/tests/opt/opt_share_extend.ys
new file mode 100644
index 000000000..c553ee0fb
--- /dev/null
+++ b/tests/opt/opt_share_extend.ys
@@ -0,0 +1,13 @@
+read_verilog opt_share_extend.v
+proc;;
+copy opt_share_test merged
+
+alumacc merged
+opt merged
+opt_share merged
+opt_clean merged
+
+miter -equiv -flatten -ignore_gold_x -make_outputs -make_outcmp opt_share_test merged miter
+sat -set-def-inputs -verify -prove trigger 0 -show-inputs -show-outputs miter
+
+select -assert-count 1 -module merged t:$alu
diff --git a/tests/opt/opt_share_large_pmux_cat.v b/tests/opt/opt_share_large_pmux_cat.v
new file mode 100644
index 000000000..6208c796b
--- /dev/null
+++ b/tests/opt/opt_share_large_pmux_cat.v
@@ -0,0 +1,22 @@
+module opt_share_test(
+											input [15:0] 			a,
+											input [15:0] 			b,
+											input [15:0] 			c,
+											input [2:0] 			sel,
+											output reg [31:0] res
+											);
+
+
+  always @* begin
+    case(sel)
+      0: res = {a + b, a};
+      1: res = {a - b, b};
+      2: res = {a + c, c};
+      3: res = {a - c, a};
+      4: res = {b, b};
+      5: res = {c, c};
+      default: res = 32'bx;
+    endcase
+  end
+
+endmodule
diff --git a/tests/opt/opt_share_large_pmux_cat.ys b/tests/opt/opt_share_large_pmux_cat.ys
new file mode 100644
index 000000000..4186ca52e
--- /dev/null
+++ b/tests/opt/opt_share_large_pmux_cat.ys
@@ -0,0 +1,13 @@
+read_verilog opt_share_large_pmux_cat.v
+proc;;
+copy opt_share_test merged
+
+alumacc merged
+opt merged
+opt_share merged
+opt_clean merged
+
+miter -equiv -flatten -ignore_gold_x -make_outputs -make_outcmp opt_share_test merged miter
+sat -set-def-inputs -verify -prove trigger 0 -show-inputs -show-outputs miter
+
+select -assert-count 1 -module merged t:$alu
diff --git a/tests/opt/opt_share_large_pmux_cat_multipart.v b/tests/opt/opt_share_large_pmux_cat_multipart.v
new file mode 100644
index 000000000..f97971bf6
--- /dev/null
+++ b/tests/opt/opt_share_large_pmux_cat_multipart.v
@@ -0,0 +1,25 @@
+module opt_share_test(
+											input [15:0] 			a,
+											input [15:0] 			b,
+											input [15:0] 			c,
+											input [15:0] 			d,
+											input [2:0] 			sel,
+											output reg [31:0] res
+											);
+
+  wire [15:0] 													add0_res = a+d;
+
+  always @* begin
+    case(sel)
+      0: res = {add0_res, a};
+      1: res = {a - b, add0_res[7], 15'b0};
+      2: res = {b-a, b};
+      3: res = {d, b - c};
+      4: res = {d, b - a};
+      5: res = {c, d};
+      6: res = {a - c, b-d};
+      default: res = 32'bx;
+    endcase
+  end
+
+endmodule
diff --git a/tests/opt/opt_share_large_pmux_cat_multipart.ys b/tests/opt/opt_share_large_pmux_cat_multipart.ys
new file mode 100644
index 000000000..54d200dc7
--- /dev/null
+++ b/tests/opt/opt_share_large_pmux_cat_multipart.ys
@@ -0,0 +1,15 @@
+read_verilog opt_share_large_pmux_cat_multipart.v
+proc;;
+copy opt_share_test merged
+
+alumacc merged
+opt merged
+
+opt_share merged
+opt_clean merged
+opt -full
+
+miter -equiv -flatten -ignore_gold_x -make_outputs -make_outcmp opt_share_test merged miter
+sat -set-def-inputs -verify -prove trigger 0 -show-inputs -show-outputs miter
+
+select -assert-count 4 -module merged t:$alu
diff --git a/tests/opt/opt_share_large_pmux_multipart.v b/tests/opt/opt_share_large_pmux_multipart.v
new file mode 100644
index 000000000..e7ba318ef
--- /dev/null
+++ b/tests/opt/opt_share_large_pmux_multipart.v
@@ -0,0 +1,24 @@
+module opt_share_test(
+											input [15:0] 			a,
+											input [15:0] 			b,
+											input [15:0] 			c,
+											input [15:0] 			d,
+											input [2:0] 			sel,
+											output reg [15:0] res
+											);
+
+
+  always @* begin
+    case(sel)
+      0: res = a + d;
+      1: res = a - b;
+      2: res = b;
+      3: res = b - c;
+      4: res = b - a;
+      5: res = c;
+      6: res = a - c;
+      default: res = 16'bx;
+    endcase
+  end
+
+endmodule
diff --git a/tests/opt/opt_share_large_pmux_multipart.ys b/tests/opt/opt_share_large_pmux_multipart.ys
new file mode 100644
index 000000000..11182df1a
--- /dev/null
+++ b/tests/opt/opt_share_large_pmux_multipart.ys
@@ -0,0 +1,13 @@
+read_verilog opt_share_large_pmux_multipart.v
+proc;;
+copy opt_share_test merged
+
+alumacc merged
+opt merged
+opt_share merged
+opt_clean merged
+
+miter -equiv -flatten -ignore_gold_x -make_outputs -make_outcmp opt_share_test merged miter
+sat -set-def-inputs -verify -prove trigger 0 -show-inputs -show-outputs miter
+
+select -assert-count 2 -module merged t:$alu
diff --git a/tests/opt/opt_share_large_pmux_part.v b/tests/opt/opt_share_large_pmux_part.v
new file mode 100644
index 000000000..138be0cd6
--- /dev/null
+++ b/tests/opt/opt_share_large_pmux_part.v
@@ -0,0 +1,22 @@
+module opt_share_test(
+											input [15:0] 			a,
+											input [15:0] 			b,
+											input [15:0] 			c,
+											input [2:0] 			sel,
+											output reg [15:0] res
+											);
+
+
+  always @* begin
+    case(sel)
+      0: res = a + b;
+      1: res = a - b;
+      2: res = a + c;
+      3: res = a - c;
+      4: res = b;
+      5: res = c;
+      default: res = 16'bx;
+    endcase
+  end
+
+endmodule
diff --git a/tests/opt/opt_share_large_pmux_part.ys b/tests/opt/opt_share_large_pmux_part.ys
new file mode 100644
index 000000000..6b594a3d6
--- /dev/null
+++ b/tests/opt/opt_share_large_pmux_part.ys
@@ -0,0 +1,13 @@
+read_verilog opt_share_large_pmux_part.v
+proc;;
+copy opt_share_test merged
+
+alumacc merged
+opt merged
+opt_share merged
+opt_clean merged
+
+miter -equiv -flatten -ignore_gold_x -make_outputs -make_outcmp opt_share_test merged miter
+sat -set-def-inputs -verify -prove trigger 0 -show-inputs -show-outputs miter
+
+select -assert-count 1 -module merged t:$alu
diff --git a/tests/opt/opt_share_mux_tree.v b/tests/opt/opt_share_mux_tree.v
index 807ed2978..c90826204 100644
--- a/tests/opt/opt_share_mux_tree.v
+++ b/tests/opt/opt_share_mux_tree.v
@@ -1,4 +1,4 @@
-module add_sub(
+module opt_share_test(
                input [15:0] 		 a,
                input [15:0] 		 b,
                input [15:0] 		 c,
diff --git a/tests/opt/opt_share_mux_tree.ys b/tests/opt/opt_share_mux_tree.ys
index 94d6aa7d2..58473039f 100644
--- a/tests/opt/opt_share_mux_tree.ys
+++ b/tests/opt/opt_share_mux_tree.ys
@@ -1,10 +1,13 @@
 read_verilog opt_share_mux_tree.v
-prep -flatten
-opt
-pmuxtree
-opt_share;
-opt_share;
-opt_clean
+proc;;
+copy opt_share_test merged
 
-select -assert-count 1 t:$add
-select -assert-count 0 t:$sub
+alumacc merged
+opt merged
+opt_share merged
+opt_clean merged
+
+miter -equiv -flatten -ignore_gold_x -make_outputs -make_outcmp opt_share_test merged miter
+sat -set-def-inputs -verify -prove trigger 0 -show-inputs -show-outputs miter
+
+select -assert-count 1 -module merged t:$alu

From 280c4e7794543e99244aafffc62a2dd4454bcb06 Mon Sep 17 00:00:00 2001
From: Bogdan Vukobratovic <bogdan.vukobratovic@gmail.com>
Date: Sat, 3 Aug 2019 12:28:46 +0200
Subject: [PATCH 3/6] Fix spacing in opt_share tests, change wording in
 opt_share help

---
 passes/opt/opt_share.cc                       | 16 +++++---
 tests/opt/opt_share_add_sub.v                 | 12 +++---
 tests/opt/opt_share_cat.v                     | 20 +++++-----
 tests/opt/opt_share_cat_multiuser.v           | 34 ++++++++--------
 tests/opt/opt_share_diff_port_widths.v        | 32 +++++++--------
 tests/opt/opt_share_extend.v                  | 29 +++++++-------
 tests/opt/opt_share_large_pmux_cat.v          | 35 ++++++++--------
 .../opt/opt_share_large_pmux_cat_multipart.v  | 40 +++++++++----------
 tests/opt/opt_share_large_pmux_multipart.v    | 39 +++++++++---------
 tests/opt/opt_share_large_pmux_part.v         | 35 ++++++++--------
 tests/opt/opt_share_mux_tree.v                | 29 +++++++-------
 11 files changed, 160 insertions(+), 161 deletions(-)

diff --git a/passes/opt/opt_share.cc b/passes/opt/opt_share.cc
index e9a2f05f9..25b07cbbd 100644
--- a/passes/opt/opt_share.cc
+++ b/passes/opt/opt_share.cc
@@ -2,6 +2,7 @@
  *  yosys -- Yosys Open SYnthesis Suite
  *
  *  Copyright (C) 2012  Clifford Wolf <clifford@clifford.at>
+ *                2019  Bogdan Vukobratovic <bogdan.vukobratovic@gmail.com>
  *
  *  Permission to use, copy, modify, and/or distribute this software for any
  *  purpose with or without fee is hereby granted, provided that the above
@@ -308,17 +309,20 @@ void remove_multi_user_outbits(RTLIL::Module *module, dict<RTLIL::SigBit, RTLIL:
 	}
 }
 
-struct OptRmdffPass : public Pass {
-	OptRmdffPass() : Pass("opt_share", "merge arithmetic operators that share an operand") {}
+struct OptSharePass : public Pass {
+	OptSharePass() : Pass("opt_share", "merge arithmetic operators that share an operand") {}
 	void help() YS_OVERRIDE
 	{
 		//   |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
 		log("\n");
 		log("    opt_share [selection]\n");
 		log("\n");
-		log("This pass identifies arithmetic operators that share an operand and whose\n");
-		log("results are used in mutually exclusive cases controlled by a multiplexer,\n");
-		log("and merges them together by multiplexing the other operands.\n");
+
+		log("This pass identifies mutually exclusive $alu arithmetic cells that:\n");
+		log("    (a) share an input operand\n");
+		log("    (b) drive the same $mux, $_MUX_, or $pmux multiplexing cell allowing\n");
+		log("        the $alu cell to be merged and the multiplexer to be moved from\n");
+		log("        multiplexing its output to multiplexing the non-shared input operands.\n");
 		log("\n");
 	}
 	void execute(std::vector<std::string>, RTLIL::Design *design) YS_OVERRIDE
@@ -454,6 +458,6 @@ struct OptRmdffPass : public Pass {
 		}
 	}
 
-} OptRmdffPass;
+} OptSharePass;
 
 PRIVATE_NAMESPACE_END
diff --git a/tests/opt/opt_share_add_sub.v b/tests/opt/opt_share_add_sub.v
index 30e093a39..1c2665cf0 100644
--- a/tests/opt/opt_share_add_sub.v
+++ b/tests/opt/opt_share_add_sub.v
@@ -1,10 +1,10 @@
 module opt_share_test(
-               input [15:0]  a,
-               input [15:0]  b,
-               input 				 sel,
-               output [15:0] res,
-               );
+	input [15:0] 	a,
+	input [15:0] 	b,
+	input 				sel,
+	output [15:0] res,
+	);
 
-  assign res = {sel ? a + b : a - b};
+	assign res = {sel ? a + b : a - b};
 
 endmodule
diff --git a/tests/opt/opt_share_cat.v b/tests/opt/opt_share_cat.v
index 605dcfe59..7b6f626b9 100644
--- a/tests/opt/opt_share_cat.v
+++ b/tests/opt/opt_share_cat.v
@@ -1,15 +1,15 @@
 module opt_share_test(
-               input [15:0]  a,
-               input [15:0]  b,
-               input [15:0]  c,
-               input [15:0]  d,
-               input 				 sel,
-               output [63:0] res,
-               );
+	input [15:0] 	a,
+	input [15:0] 	b,
+	input [15:0] 	c,
+	input [15:0] 	d,
+	input 				sel,
+	output [63:0] res,
+	);
 
-  reg [31: 0] 							 cat1 = {a+b, c+d};
-  reg [31: 0] 							 cat2 = {a-b, c-d};
+	reg [31: 0] 	cat1 = {a+b, c+d};
+	reg [31: 0] 	cat2 = {a-b, c-d};
 
-  assign res = {b, sel ? cat1 : cat2, a};
+	assign res = {b, sel ? cat1 : cat2, a};
 
 endmodule
diff --git a/tests/opt/opt_share_cat_multiuser.v b/tests/opt/opt_share_cat_multiuser.v
index 9ac0ceec8..f77f912e9 100644
--- a/tests/opt/opt_share_cat_multiuser.v
+++ b/tests/opt/opt_share_cat_multiuser.v
@@ -1,22 +1,22 @@
 module opt_share_test(
-               input [15:0] 		 a,
-               input [15:0] 		 b,
-               input [15:0] 		 c,
-               input [15:0] 		 d,
-               input 						 sel,
-               output reg [47:0] res,
-               );
+	input [15:0] 			a,
+	input [15:0] 			b,
+	input [15:0] 			c,
+	input [15:0] 			d,
+	input 						sel,
+	output reg [47:0] res,
+	);
 
-  wire [15:0] 									 add_res = a+b;
-  wire [15:0] 									 sub_res = a-b;
-  wire [31: 0] 									 cat1 = {add_res, c+d};
-  wire [31: 0] 									 cat2 = {sub_res, c-d};
+	wire [15:0] 			add_res = a+b;
+	wire [15:0] 			sub_res = a-b;
+	wire [31: 0] 			cat1 = {add_res, c+d};
+	wire [31: 0] 			cat2 = {sub_res, c-d};
 
-  always @* begin
-    case(sel)
-      0: res = {cat1, add_res};
-      1: res = {cat2, add_res};
-    endcase
-  end
+	always @* begin
+		case(sel)
+			0: res = {cat1, add_res};
+			1: res = {cat2, add_res};
+		endcase
+	end
 
 endmodule
diff --git a/tests/opt/opt_share_diff_port_widths.v b/tests/opt/opt_share_diff_port_widths.v
index 5e2971e30..e57ab7a83 100644
--- a/tests/opt/opt_share_diff_port_widths.v
+++ b/tests/opt/opt_share_diff_port_widths.v
@@ -1,21 +1,21 @@
 module opt_share_test(
-               input [15:0] 		 a,
-               input [15:0] 		 b,
-							 input [15:0] 		 c,
-							 input [1:0] 			 sel,
-							 output reg [15:0] res
-               );
+	input [15:0]			 a,
+	input [15:0]			 b,
+	input [15:0]			 c,
+	input [1:0]			 sel,
+	output reg [15:0] res
+	);
 
-  wire [15:0] 							 add0_res = a+b;
-  wire [15:0] 							 add1_res = a+c;
+	wire [15:0] 			add0_res = a+b;
+	wire [15:0] 			add1_res = a+c;
 
-  always @* begin
-    case(sel)
-      0: res = add0_res[10:0];
-      1: res = add1_res[10:0];
-      2: res = a - b;
-      default: res = 32'bx;
-    endcase
-  end
+	always @* begin
+		case(sel)
+			0: res = add0_res[10:0];
+			1: res = add1_res[10:0];
+			2: res = a - b;
+			default: res = 32'bx;
+		endcase
+	end
 
 endmodule
diff --git a/tests/opt/opt_share_extend.v b/tests/opt/opt_share_extend.v
index 5ed6bde6f..60ce1a2f3 100644
--- a/tests/opt/opt_share_extend.v
+++ b/tests/opt/opt_share_extend.v
@@ -1,19 +1,18 @@
 module opt_share_test(
-											input signed [7:0] 			 a,
-											input signed [10:0] 		 b,
-											input signed [15:0] 		 c,
-											input [1:0] 						 sel,
-											output reg signed [15:0] res
-											);
+	input signed [7:0]			 a,
+	input signed [10:0]			 b,
+	input signed [15:0]			 c,
+	input [1:0]							 sel,
+	output reg signed [15:0] res
+	);
 
-
-  always @* begin
-    case(sel)
-      0: res = a + b;
-      1: res = a - b;
-      2: res = a + c;
-      default: res = 16'bx;
-    endcase
-  end
+	always @* begin
+		case(sel)
+			0: res = a + b;
+			1: res = a - b;
+			2: res = a + c;
+			default: res = 16'bx;
+		endcase
+	end
 
 endmodule
diff --git a/tests/opt/opt_share_large_pmux_cat.v b/tests/opt/opt_share_large_pmux_cat.v
index 6208c796b..0667e6080 100644
--- a/tests/opt/opt_share_large_pmux_cat.v
+++ b/tests/opt/opt_share_large_pmux_cat.v
@@ -1,22 +1,21 @@
 module opt_share_test(
-											input [15:0] 			a,
-											input [15:0] 			b,
-											input [15:0] 			c,
-											input [2:0] 			sel,
-											output reg [31:0] res
-											);
+	input [15:0]			a,
+	input [15:0]			b,
+	input [15:0]			c,
+	input [2:0]				sel,
+	output reg [31:0] res
+	);
 
-
-  always @* begin
-    case(sel)
-      0: res = {a + b, a};
-      1: res = {a - b, b};
-      2: res = {a + c, c};
-      3: res = {a - c, a};
-      4: res = {b, b};
-      5: res = {c, c};
-      default: res = 32'bx;
-    endcase
-  end
+	always @* begin
+		case(sel)
+			0: res = {a + b, a};
+			1: res = {a - b, b};
+			2: res = {a + c, c};
+			3: res = {a - c, a};
+			4: res = {b, b};
+			5: res = {c, c};
+			default: res = 32'bx;
+		endcase
+	end
 
 endmodule
diff --git a/tests/opt/opt_share_large_pmux_cat_multipart.v b/tests/opt/opt_share_large_pmux_cat_multipart.v
index f97971bf6..f26505d3a 100644
--- a/tests/opt/opt_share_large_pmux_cat_multipart.v
+++ b/tests/opt/opt_share_large_pmux_cat_multipart.v
@@ -1,25 +1,25 @@
 module opt_share_test(
-											input [15:0] 			a,
-											input [15:0] 			b,
-											input [15:0] 			c,
-											input [15:0] 			d,
-											input [2:0] 			sel,
-											output reg [31:0] res
-											);
+	input [15:0]			a,
+	input [15:0]			b,
+	input [15:0]			c,
+	input [15:0]			d,
+	input [2:0]				sel,
+	output reg [31:0] res
+	);
 
-  wire [15:0] 													add0_res = a+d;
+	wire [15:0] 			add0_res = a+d;
 
-  always @* begin
-    case(sel)
-      0: res = {add0_res, a};
-      1: res = {a - b, add0_res[7], 15'b0};
-      2: res = {b-a, b};
-      3: res = {d, b - c};
-      4: res = {d, b - a};
-      5: res = {c, d};
-      6: res = {a - c, b-d};
-      default: res = 32'bx;
-    endcase
-  end
+	always @* begin
+		case(sel)
+			0: res = {add0_res, a};
+			1: res = {a - b, add0_res[7], 15'b0};
+			2: res = {b-a, b};
+			3: res = {d, b - c};
+			4: res = {d, b - a};
+			5: res = {c, d};
+			6: res = {a - c, b-d};
+			default: res = 32'bx;
+		endcase
+	end
 
 endmodule
diff --git a/tests/opt/opt_share_large_pmux_multipart.v b/tests/opt/opt_share_large_pmux_multipart.v
index e7ba318ef..1c460292f 100644
--- a/tests/opt/opt_share_large_pmux_multipart.v
+++ b/tests/opt/opt_share_large_pmux_multipart.v
@@ -1,24 +1,23 @@
 module opt_share_test(
-											input [15:0] 			a,
-											input [15:0] 			b,
-											input [15:0] 			c,
-											input [15:0] 			d,
-											input [2:0] 			sel,
-											output reg [15:0] res
-											);
+	input [15:0]			a,
+	input [15:0]			b,
+	input [15:0]			c,
+	input [15:0]			d,
+	input [2:0]				sel,
+	output reg [15:0] res
+	);
 
-
-  always @* begin
-    case(sel)
-      0: res = a + d;
-      1: res = a - b;
-      2: res = b;
-      3: res = b - c;
-      4: res = b - a;
-      5: res = c;
-      6: res = a - c;
-      default: res = 16'bx;
-    endcase
-  end
+	always @* begin
+		case(sel)
+			0: res = a + d;
+			1: res = a - b;
+			2: res = b;
+			3: res = b - c;
+			4: res = b - a;
+			5: res = c;
+			6: res = a - c;
+			default: res = 16'bx;
+		endcase
+	end
 
 endmodule
diff --git a/tests/opt/opt_share_large_pmux_part.v b/tests/opt/opt_share_large_pmux_part.v
index 138be0cd6..f9dd17446 100644
--- a/tests/opt/opt_share_large_pmux_part.v
+++ b/tests/opt/opt_share_large_pmux_part.v
@@ -1,22 +1,21 @@
 module opt_share_test(
-											input [15:0] 			a,
-											input [15:0] 			b,
-											input [15:0] 			c,
-											input [2:0] 			sel,
-											output reg [15:0] res
-											);
+	input [15:0]			a,
+	input [15:0]			b,
+	input [15:0]			c,
+	input [2:0]				sel,
+	output reg [15:0] res
+	);
 
-
-  always @* begin
-    case(sel)
-      0: res = a + b;
-      1: res = a - b;
-      2: res = a + c;
-      3: res = a - c;
-      4: res = b;
-      5: res = c;
-      default: res = 16'bx;
-    endcase
-  end
+	always @* begin
+		case(sel)
+			0: res = a + b;
+			1: res = a - b;
+			2: res = a + c;
+			3: res = a - c;
+			4: res = b;
+			5: res = c;
+			default: res = 16'bx;
+		endcase
+	end
 
 endmodule
diff --git a/tests/opt/opt_share_mux_tree.v b/tests/opt/opt_share_mux_tree.v
index c90826204..4a26afb46 100644
--- a/tests/opt/opt_share_mux_tree.v
+++ b/tests/opt/opt_share_mux_tree.v
@@ -1,19 +1,18 @@
 module opt_share_test(
-               input [15:0] 		 a,
-               input [15:0] 		 b,
-               input [15:0] 		 c,
-               input [1:0] 			 sel,
-               output reg [15:0] res
-               );
+	input [15:0] 			a,
+	input [15:0] 			b,
+	input [15:0] 			c,
+	input [1:0] 			sel,
+	output reg [15:0] res
+	);
 
-
-  always @* begin
-    case(sel)
-      0: res = a + b;
-      1: res = a - b;
-      2: res = a + c;
-      default: res = 16'bx;
-    endcase
-  end
+	always @* begin
+		case(sel)
+			0: res = a + b;
+			1: res = a - b;
+			2: res = a + c;
+			default: res = 16'bx;
+		endcase
+	end
 
 endmodule

From d8be5ce6ba11ec78d0f7925d488fad09a3eaba2c Mon Sep 17 00:00:00 2001
From: Bogdan Vukobratovic <bogdan.vukobratovic@gmail.com>
Date: Sat, 3 Aug 2019 12:35:46 +0200
Subject: [PATCH 4/6] Tabs to spaces in opt_share examples

---
 tests/opt/opt_share_add_sub.v                 | 12 +++---
 tests/opt/opt_share_cat.v                     | 20 +++++-----
 tests/opt/opt_share_cat_multiuser.v           | 34 ++++++++--------
 tests/opt/opt_share_diff_port_widths.v        | 32 +++++++--------
 tests/opt/opt_share_extend.v                  | 28 ++++++-------
 tests/opt/opt_share_large_pmux_cat.v          | 34 ++++++++--------
 .../opt/opt_share_large_pmux_cat_multipart.v  | 40 +++++++++----------
 tests/opt/opt_share_large_pmux_multipart.v    | 38 +++++++++---------
 tests/opt/opt_share_large_pmux_part.v         | 34 ++++++++--------
 tests/opt/opt_share_mux_tree.v                | 28 ++++++-------
 10 files changed, 150 insertions(+), 150 deletions(-)

diff --git a/tests/opt/opt_share_add_sub.v b/tests/opt/opt_share_add_sub.v
index 1c2665cf0..d918f27cc 100644
--- a/tests/opt/opt_share_add_sub.v
+++ b/tests/opt/opt_share_add_sub.v
@@ -1,10 +1,10 @@
 module opt_share_test(
-	input [15:0] 	a,
-	input [15:0] 	b,
-	input 				sel,
-	output [15:0] res,
-	);
+  input [15:0]  a,
+  input [15:0]  b,
+  input         sel,
+  output [15:0] res,
+  );
 
-	assign res = {sel ? a + b : a - b};
+  assign res = {sel ? a + b : a - b};
 
 endmodule
diff --git a/tests/opt/opt_share_cat.v b/tests/opt/opt_share_cat.v
index 7b6f626b9..7fb97fef5 100644
--- a/tests/opt/opt_share_cat.v
+++ b/tests/opt/opt_share_cat.v
@@ -1,15 +1,15 @@
 module opt_share_test(
-	input [15:0] 	a,
-	input [15:0] 	b,
-	input [15:0] 	c,
-	input [15:0] 	d,
-	input 				sel,
-	output [63:0] res,
-	);
+  input [15:0]  a,
+  input [15:0]  b,
+  input [15:0]  c,
+  input [15:0]  d,
+  input         sel,
+  output [63:0] res,
+  );
 
-	reg [31: 0] 	cat1 = {a+b, c+d};
-	reg [31: 0] 	cat2 = {a-b, c-d};
+  reg [31: 0]   cat1 = {a+b, c+d};
+  reg [31: 0]   cat2 = {a-b, c-d};
 
-	assign res = {b, sel ? cat1 : cat2, a};
+  assign res = {b, sel ? cat1 : cat2, a};
 
 endmodule
diff --git a/tests/opt/opt_share_cat_multiuser.v b/tests/opt/opt_share_cat_multiuser.v
index f77f912e9..b250689d9 100644
--- a/tests/opt/opt_share_cat_multiuser.v
+++ b/tests/opt/opt_share_cat_multiuser.v
@@ -1,22 +1,22 @@
 module opt_share_test(
-	input [15:0] 			a,
-	input [15:0] 			b,
-	input [15:0] 			c,
-	input [15:0] 			d,
-	input 						sel,
-	output reg [47:0] res,
-	);
+  input [15:0]      a,
+  input [15:0]      b,
+  input [15:0]      c,
+  input [15:0]      d,
+  input             sel,
+  output reg [47:0] res,
+  );
 
-	wire [15:0] 			add_res = a+b;
-	wire [15:0] 			sub_res = a-b;
-	wire [31: 0] 			cat1 = {add_res, c+d};
-	wire [31: 0] 			cat2 = {sub_res, c-d};
+  wire [15:0]       add_res = a+b;
+  wire [15:0]       sub_res = a-b;
+  wire [31: 0]      cat1 = {add_res, c+d};
+  wire [31: 0]      cat2 = {sub_res, c-d};
 
-	always @* begin
-		case(sel)
-			0: res = {cat1, add_res};
-			1: res = {cat2, add_res};
-		endcase
-	end
+  always @* begin
+    case(sel)
+      0: res = {cat1, add_res};
+      1: res = {cat2, add_res};
+    endcase
+  end
 
 endmodule
diff --git a/tests/opt/opt_share_diff_port_widths.v b/tests/opt/opt_share_diff_port_widths.v
index e57ab7a83..1a37c80a6 100644
--- a/tests/opt/opt_share_diff_port_widths.v
+++ b/tests/opt/opt_share_diff_port_widths.v
@@ -1,21 +1,21 @@
 module opt_share_test(
-	input [15:0]			 a,
-	input [15:0]			 b,
-	input [15:0]			 c,
-	input [1:0]			 sel,
-	output reg [15:0] res
-	);
+  input [15:0]       a,
+  input [15:0]       b,
+  input [15:0]       c,
+  input [1:0]      sel,
+  output reg [15:0] res
+  );
 
-	wire [15:0] 			add0_res = a+b;
-	wire [15:0] 			add1_res = a+c;
+  wire [15:0]       add0_res = a+b;
+  wire [15:0]       add1_res = a+c;
 
-	always @* begin
-		case(sel)
-			0: res = add0_res[10:0];
-			1: res = add1_res[10:0];
-			2: res = a - b;
-			default: res = 32'bx;
-		endcase
-	end
+  always @* begin
+    case(sel)
+      0: res = add0_res[10:0];
+      1: res = add1_res[10:0];
+      2: res = a - b;
+      default: res = 32'bx;
+    endcase
+  end
 
 endmodule
diff --git a/tests/opt/opt_share_extend.v b/tests/opt/opt_share_extend.v
index 60ce1a2f3..d39f19069 100644
--- a/tests/opt/opt_share_extend.v
+++ b/tests/opt/opt_share_extend.v
@@ -1,18 +1,18 @@
 module opt_share_test(
-	input signed [7:0]			 a,
-	input signed [10:0]			 b,
-	input signed [15:0]			 c,
-	input [1:0]							 sel,
-	output reg signed [15:0] res
-	);
+  input signed [7:0]       a,
+  input signed [10:0]      b,
+  input signed [15:0]      c,
+  input [1:0]              sel,
+  output reg signed [15:0] res
+  );
 
-	always @* begin
-		case(sel)
-			0: res = a + b;
-			1: res = a - b;
-			2: res = a + c;
-			default: res = 16'bx;
-		endcase
-	end
+  always @* begin
+    case(sel)
+      0: res = a + b;
+      1: res = a - b;
+      2: res = a + c;
+      default: res = 16'bx;
+    endcase
+  end
 
 endmodule
diff --git a/tests/opt/opt_share_large_pmux_cat.v b/tests/opt/opt_share_large_pmux_cat.v
index 0667e6080..416ba3766 100644
--- a/tests/opt/opt_share_large_pmux_cat.v
+++ b/tests/opt/opt_share_large_pmux_cat.v
@@ -1,21 +1,21 @@
 module opt_share_test(
-	input [15:0]			a,
-	input [15:0]			b,
-	input [15:0]			c,
-	input [2:0]				sel,
-	output reg [31:0] res
-	);
+  input [15:0]      a,
+  input [15:0]      b,
+  input [15:0]      c,
+  input [2:0]       sel,
+  output reg [31:0] res
+  );
 
-	always @* begin
-		case(sel)
-			0: res = {a + b, a};
-			1: res = {a - b, b};
-			2: res = {a + c, c};
-			3: res = {a - c, a};
-			4: res = {b, b};
-			5: res = {c, c};
-			default: res = 32'bx;
-		endcase
-	end
+  always @* begin
+    case(sel)
+      0: res = {a + b, a};
+      1: res = {a - b, b};
+      2: res = {a + c, c};
+      3: res = {a - c, a};
+      4: res = {b, b};
+      5: res = {c, c};
+      default: res = 32'bx;
+    endcase
+  end
 
 endmodule
diff --git a/tests/opt/opt_share_large_pmux_cat_multipart.v b/tests/opt/opt_share_large_pmux_cat_multipart.v
index f26505d3a..34d2bd9a8 100644
--- a/tests/opt/opt_share_large_pmux_cat_multipart.v
+++ b/tests/opt/opt_share_large_pmux_cat_multipart.v
@@ -1,25 +1,25 @@
 module opt_share_test(
-	input [15:0]			a,
-	input [15:0]			b,
-	input [15:0]			c,
-	input [15:0]			d,
-	input [2:0]				sel,
-	output reg [31:0] res
-	);
+  input [15:0]      a,
+  input [15:0]      b,
+  input [15:0]      c,
+  input [15:0]      d,
+  input [2:0]       sel,
+  output reg [31:0] res
+  );
 
-	wire [15:0] 			add0_res = a+d;
+  wire [15:0]       add0_res = a+d;
 
-	always @* begin
-		case(sel)
-			0: res = {add0_res, a};
-			1: res = {a - b, add0_res[7], 15'b0};
-			2: res = {b-a, b};
-			3: res = {d, b - c};
-			4: res = {d, b - a};
-			5: res = {c, d};
-			6: res = {a - c, b-d};
-			default: res = 32'bx;
-		endcase
-	end
+  always @* begin
+    case(sel)
+      0: res = {add0_res, a};
+      1: res = {a - b, add0_res[7], 15'b0};
+      2: res = {b-a, b};
+      3: res = {d, b - c};
+      4: res = {d, b - a};
+      5: res = {c, d};
+      6: res = {a - c, b-d};
+      default: res = 32'bx;
+    endcase
+  end
 
 endmodule
diff --git a/tests/opt/opt_share_large_pmux_multipart.v b/tests/opt/opt_share_large_pmux_multipart.v
index 1c460292f..535adf96f 100644
--- a/tests/opt/opt_share_large_pmux_multipart.v
+++ b/tests/opt/opt_share_large_pmux_multipart.v
@@ -1,23 +1,23 @@
 module opt_share_test(
-	input [15:0]			a,
-	input [15:0]			b,
-	input [15:0]			c,
-	input [15:0]			d,
-	input [2:0]				sel,
-	output reg [15:0] res
-	);
+  input [15:0]      a,
+  input [15:0]      b,
+  input [15:0]      c,
+  input [15:0]      d,
+  input [2:0]       sel,
+  output reg [15:0] res
+  );
 
-	always @* begin
-		case(sel)
-			0: res = a + d;
-			1: res = a - b;
-			2: res = b;
-			3: res = b - c;
-			4: res = b - a;
-			5: res = c;
-			6: res = a - c;
-			default: res = 16'bx;
-		endcase
-	end
+  always @* begin
+    case(sel)
+      0: res = a + d;
+      1: res = a - b;
+      2: res = b;
+      3: res = b - c;
+      4: res = b - a;
+      5: res = c;
+      6: res = a - c;
+      default: res = 16'bx;
+    endcase
+  end
 
 endmodule
diff --git a/tests/opt/opt_share_large_pmux_part.v b/tests/opt/opt_share_large_pmux_part.v
index f9dd17446..a9008fb5a 100644
--- a/tests/opt/opt_share_large_pmux_part.v
+++ b/tests/opt/opt_share_large_pmux_part.v
@@ -1,21 +1,21 @@
 module opt_share_test(
-	input [15:0]			a,
-	input [15:0]			b,
-	input [15:0]			c,
-	input [2:0]				sel,
-	output reg [15:0] res
-	);
+  input [15:0]      a,
+  input [15:0]      b,
+  input [15:0]      c,
+  input [2:0]       sel,
+  output reg [15:0] res
+  );
 
-	always @* begin
-		case(sel)
-			0: res = a + b;
-			1: res = a - b;
-			2: res = a + c;
-			3: res = a - c;
-			4: res = b;
-			5: res = c;
-			default: res = 16'bx;
-		endcase
-	end
+  always @* begin
+    case(sel)
+      0: res = a + b;
+      1: res = a - b;
+      2: res = a + c;
+      3: res = a - c;
+      4: res = b;
+      5: res = c;
+      default: res = 16'bx;
+    endcase
+  end
 
 endmodule
diff --git a/tests/opt/opt_share_mux_tree.v b/tests/opt/opt_share_mux_tree.v
index 4a26afb46..cc5ae4eb9 100644
--- a/tests/opt/opt_share_mux_tree.v
+++ b/tests/opt/opt_share_mux_tree.v
@@ -1,18 +1,18 @@
 module opt_share_test(
-	input [15:0] 			a,
-	input [15:0] 			b,
-	input [15:0] 			c,
-	input [1:0] 			sel,
-	output reg [15:0] res
-	);
+  input [15:0]      a,
+  input [15:0]      b,
+  input [15:0]      c,
+  input [1:0]       sel,
+  output reg [15:0] res
+  );
 
-	always @* begin
-		case(sel)
-			0: res = a + b;
-			1: res = a - b;
-			2: res = a + c;
-			default: res = 16'bx;
-		endcase
-	end
+  always @* begin
+    case(sel)
+      0: res = a + b;
+      1: res = a - b;
+      2: res = a + c;
+      default: res = 16'bx;
+    endcase
+  end
 
 endmodule

From 6a796accc09bc2c8ef98c068185de13d3e01890a Mon Sep 17 00:00:00 2001
From: Bogdan Vukobratovic <bogdan.vukobratovic@gmail.com>
Date: Sun, 4 Aug 2019 19:06:38 +0200
Subject: [PATCH 5/6] Support various binary operators in opt_share

---
 Makefile                    |   1 +
 passes/opt/opt_share.cc     | 610 ++++++++++++++++++++++++------------
 tests/opt_share/.gitignore  |   1 +
 tests/opt_share/generate.py |  86 +++++
 tests/opt_share/run-test.sh |  39 +++
 5 files changed, 531 insertions(+), 206 deletions(-)
 create mode 100644 tests/opt_share/.gitignore
 create mode 100644 tests/opt_share/generate.py
 create mode 100755 tests/opt_share/run-test.sh

diff --git a/Makefile b/Makefile
index 3bc119800..d06c7ab3d 100644
--- a/Makefile
+++ b/Makefile
@@ -678,6 +678,7 @@ test: $(TARGETS) $(EXTRA_TARGETS)
 	+cd tests/asicworld && bash run-test.sh $(SEEDOPT)
 	# +cd tests/realmath && bash run-test.sh $(SEEDOPT)
 	+cd tests/share && bash run-test.sh $(SEEDOPT)
+	+cd tests/opt_share && bash run-test.sh $(SEEDOPT)
 	+cd tests/fsm && bash run-test.sh $(SEEDOPT)
 	+cd tests/techmap && bash run-test.sh
 	+cd tests/memories && bash run-test.sh $(ABCOPT) $(SEEDOPT)
diff --git a/passes/opt/opt_share.cc b/passes/opt/opt_share.cc
index 25b07cbbd..e8f44749a 100644
--- a/passes/opt/opt_share.cc
+++ b/passes/opt/opt_share.cc
@@ -32,37 +32,36 @@ PRIVATE_NAMESPACE_BEGIN
 
 SigMap assign_map;
 
-struct InPort {
+struct OpMuxConn {
 	RTLIL::SigSpec sig;
-	RTLIL::Cell *pmux;
-	int port_id;
-	RTLIL::Cell *alu;
+	RTLIL::Cell *mux;
+	RTLIL::Cell *op;
+	int mux_port_id;
+	int mux_port_offset;
+	int op_outsig_offset;
 
-	InPort(RTLIL::SigSpec s, RTLIL::Cell *c, int p, RTLIL::Cell *a = NULL) : sig(s), pmux(c), port_id(p), alu(a) {}
+	bool operator<(const OpMuxConn &other) const
+	{
+		if (mux != other.mux)
+			return mux < other.mux;
+
+		if (mux_port_id != other.mux_port_id)
+			return mux_port_id < other.mux_port_id;
+
+		return mux_port_offset < other.mux_port_offset;
+	}
 };
 
-// Helper class that to track whether a SigSpec is signed and whether it is
-// connected to the \\B port of the $sub cell, which makes its sign prefix
-// negative.
+// Helper class to track additiona information about a SigSpec, like whether it is signed and the semantics of the port it is connected to
 struct ExtSigSpec {
 	RTLIL::SigSpec sig;
 	RTLIL::SigSpec sign;
 	bool is_signed;
+	RTLIL::IdString semantics;
 
 	ExtSigSpec() {}
 
-	ExtSigSpec(RTLIL::SigSpec s, bool sign = false, bool is_signed = false) : sig(s), sign(sign), is_signed(is_signed) {}
-
-	ExtSigSpec(RTLIL::Cell *cell, RTLIL::IdString port_name, SigMap *sigmap)
-	{
-		sign = (port_name == "\\B") ? cell->getPort("\\BI") : RTLIL::Const(0, 1);
-		sig = (*sigmap)(cell->getPort(port_name));
-
-		is_signed = false;
-		if (cell->hasParam(port_name.str() + "_SIGNED")) {
-			is_signed = cell->getParam(port_name.str() + "_SIGNED").as_bool();
-		}
-	}
+	ExtSigSpec(RTLIL::SigSpec s, RTLIL::SigSpec sign = RTLIL::Const(0, 1), bool is_signed = false, RTLIL::IdString semantics = RTLIL::IdString()) : sig(s), sign(sign), is_signed(is_signed), semantics(semantics) {}
 
 	bool empty() const { return sig.empty(); }
 
@@ -74,42 +73,136 @@ struct ExtSigSpec {
 		if (sign != other.sign)
 			return sign < other.sign;
 
-		return is_signed < other.is_signed;
+		if (is_signed != other.is_signed)
+			return is_signed < other.is_signed;
+
+		return semantics < other.semantics;
 	}
 
 	bool operator==(const RTLIL::SigSpec &other) const { return (sign != RTLIL::Const(0, 1)) ? false : sig == other; }
-	bool operator==(const ExtSigSpec &other) const { return is_signed == other.is_signed && sign == other.sign && sig == other.sig; }
+	bool operator==(const ExtSigSpec &other) const { return is_signed == other.is_signed && sign == other.sign && sig == other.sig && semantics == other.semantics; }
 };
 
-void merge_operators(RTLIL::Module *module, RTLIL::Cell *mux, const std::vector<InPort> &ports, int offset, int width,
-		     const ExtSigSpec &operand)
+#define BITWISE_OPS "$_AND_", "$_NAND_", "$_OR_", "$_NOR_", "$_XOR_", "$_XNOR_", "$_ANDNOT_", "$_ORNOT_", "$and", "$or", "$xor", "$xnor"
+
+#define REDUCTION_OPS "$reduce_and", "$reduce_or", "$reduce_xor", "$reduce_xnor", "$reduce_bool", "$reduce_nand"
+
+#define LOGICAL_OPS "$logic_and", "$logic_or"
+
+#define SHIFT_OPS "$shl", "$shr", "$sshl", "$sshr", "$shift", "$shiftx"
+
+#define RELATIONAL_OPS "$lt", "$le", "$eq", "$ne", "$eqx", "$nex", "$ge", "$gt"
+
+bool cell_supported(RTLIL::Cell *cell)
+{
+
+	if (cell->type.in("$alu")) {
+		RTLIL::SigSpec sig_bi = cell->getPort("\\BI");
+		RTLIL::SigSpec sig_ci = cell->getPort("\\CI");
+
+		if (sig_bi.is_fully_const() && sig_ci.is_fully_const() && sig_bi == sig_ci)
+			return true;
+	} else if (cell->type.in(LOGICAL_OPS, SHIFT_OPS, BITWISE_OPS, RELATIONAL_OPS, "$add", "$sub", "$mul", "$div", "$mod", "$concat")) {
+		return true;
+	}
+
+	return false;
+}
+
+std::map<std::string, std::string> mergeable_type_map{
+  {"$sub", "$add"},
+};
+
+bool mergeable(RTLIL::Cell *a, RTLIL::Cell *b)
+{
+	auto a_type = a->type;
+	if (mergeable_type_map.count(a_type.str()))
+		a_type = mergeable_type_map.at(a_type.str());
+
+	auto b_type = b->type;
+	if (mergeable_type_map.count(b_type.str()))
+		b_type = mergeable_type_map.at(b_type.str());
+
+	return a_type == b_type;
+}
+
+RTLIL::IdString decode_port_semantics(RTLIL::Cell *cell, RTLIL::IdString port_name)
+{
+	if (cell->type.in("$lt", "$le", "$ge", "$gt", "$div", "$mod", "$concat", SHIFT_OPS) && port_name == "\\B")
+		return port_name;
+
+	return "";
+}
+
+RTLIL::SigSpec decode_port_sign(RTLIL::Cell *cell, RTLIL::IdString port_name) {
+
+	if (cell->type == "$alu" && port_name == "\\B")
+		return cell->getPort("\\BI");
+	else if	(cell->type == "$sub" && port_name == "\\B")
+		return RTLIL::Const(1, 1);
+
+	return RTLIL::Const(0, 1);
+}
+
+bool decode_port_signed(RTLIL::Cell *cell, RTLIL::IdString port_name)
+{
+	if (cell->type.in(BITWISE_OPS, LOGICAL_OPS))
+		return false;
+
+	if (cell->hasParam(port_name.str() + "_SIGNED"))
+		return cell->getParam(port_name.str() + "_SIGNED").as_bool();
+
+	return false;
+
+}
+
+ExtSigSpec decode_port(RTLIL::Cell *cell, RTLIL::IdString port_name, SigMap *sigmap)
+{
+	auto sig = (*sigmap)(cell->getPort(port_name));
+
+	RTLIL::SigSpec sign = decode_port_sign(cell, port_name);
+	RTLIL::IdString semantics = decode_port_semantics(cell, port_name);
+
+	bool is_signed = decode_port_signed(cell, port_name);
+
+	return ExtSigSpec(sig, sign, is_signed, semantics);
+}
+
+void merge_operators(RTLIL::Module *module, RTLIL::Cell *mux, const std::vector<OpMuxConn> &ports, const ExtSigSpec &operand)
 {
 
 	std::vector<ExtSigSpec> muxed_operands;
 	int max_width = 0;
 	for (const auto& p : ports) {
-		auto op = p.alu;
+		auto op = p.op;
 
-		for (RTLIL::IdString port_name : {"\\A", "\\B"}) {
-			if (op->getPort(port_name) != operand.sig) {
-				auto operand = ExtSigSpec(op, port_name, &assign_map);
-				if (operand.sig.size() > max_width) {
-					max_width = operand.sig.size();
-				}
-
-				muxed_operands.push_back(operand);
-			}
+		RTLIL::IdString muxed_port_name = "\\A";
+		if (op->getPort("\\A") == operand.sig) {
+			muxed_port_name = "\\B";
 		}
+
+		auto operand = decode_port(op, muxed_port_name, &assign_map);
+		if (operand.sig.size() > max_width) {
+			max_width = operand.sig.size();
+		}
+
+		muxed_operands.push_back(operand);
 	}
 
+	auto shared_op = ports[0].op;
+
+	if (std::any_of(muxed_operands.begin(), muxed_operands.end(), [&](ExtSigSpec &op) { return op.sign != muxed_operands[0].sign; }))
+		if (max_width < shared_op->getParam("\\Y_WIDTH").as_int())
+			max_width = shared_op->getParam("\\Y_WIDTH").as_int();
+
+
 	for (auto &operand : muxed_operands) {
 		operand.sig.extend_u0(max_width, operand.is_signed);
 	}
 
-	auto shared_op = ports[0].alu;
 
 	for (const auto& p : ports) {
-		auto op = p.alu;
+		auto op = p.op;
 		if (op == shared_op)
 			continue;
 		module->remove(op);
@@ -126,40 +219,47 @@ void merge_operators(RTLIL::Module *module, RTLIL::Cell *mux, const std::vector<
 	RTLIL::SigSpec mux_b = mux->getPort("\\B");
 	RTLIL::SigSpec mux_s = mux->getPort("\\S");
 
-	RTLIL::SigSpec alu_x = shared_op->getPort("\\X");
-	RTLIL::SigSpec alu_co = shared_op->getPort("\\CO");
-
 	RTLIL::SigSpec shared_pmux_a = RTLIL::Const(RTLIL::State::Sx, max_width);
 	RTLIL::SigSpec shared_pmux_b;
 	RTLIL::SigSpec shared_pmux_s;
 
-	shared_op->setPort("\\Y", shared_op->getPort("\\Y").extract(0, width));
+	int conn_width = ports[0].sig.size();
+	int conn_offset = ports[0].mux_port_offset;
+
+	shared_op->setPort("\\Y", shared_op->getPort("\\Y").extract(0, conn_width));
 
 	if (mux->type == "$pmux") {
 		shared_pmux_s = RTLIL::SigSpec();
 
-		for (const auto&p: ports) {
-			shared_pmux_s.append(mux_s[p.port_id]);
-			mux_b.replace(p.port_id * mux_a.size() + offset, shared_op->getPort("\\Y"));
+		for (const auto &p : ports) {
+			shared_pmux_s.append(mux_s[p.mux_port_id]);
+			mux_b.replace(p.mux_port_id * mux_a.size() + conn_offset, shared_op->getPort("\\Y"));
 		}
 	} else {
 		shared_pmux_s = RTLIL::SigSpec{mux_s, module->Not(NEW_ID, mux_s)};
-		mux_a.replace(offset, shared_op->getPort("\\Y"));
-		mux_b.replace(offset, shared_op->getPort("\\Y"));
+		mux_a.replace(conn_offset, shared_op->getPort("\\Y"));
+		mux_b.replace(conn_offset, shared_op->getPort("\\Y"));
 	}
 
+	mux->setPort("\\A", mux_a);
+	mux->setPort("\\B", mux_b);
 	mux->setPort("\\Y", mux_y);
 	mux->setPort("\\S", mux_s);
-	mux->setPort("\\B", mux_b);
 
 	for (const auto &op : muxed_operands)
 		shared_pmux_b.append(op.sig);
 
 	auto mux_to_oper = module->Pmux(NEW_ID, shared_pmux_a, shared_pmux_b, shared_pmux_s);
 
-	shared_op->setPort("\\X", alu_x.extract(0, width));
-	shared_op->setPort("\\CO", alu_co.extract(0, width));
-	shared_op->setParam("\\Y_WIDTH", width);
+	if (shared_op->type.in("$alu")) {
+		RTLIL::SigSpec alu_x = shared_op->getPort("\\X");
+		RTLIL::SigSpec alu_co = shared_op->getPort("\\CO");
+
+		shared_op->setPort("\\X", alu_x.extract(0, conn_width));
+		shared_op->setPort("\\CO", alu_co.extract(0, conn_width));
+	}
+
+	shared_op->setParam("\\Y_WIDTH", conn_width);
 
 	if (shared_op->getPort("\\A") == operand.sig) {
 		shared_op->setPort("\\B", mux_to_oper);
@@ -173,11 +273,9 @@ void merge_operators(RTLIL::Module *module, RTLIL::Cell *mux, const std::vector<
 
 typedef struct {
 	RTLIL::Cell *mux;
-	std::vector<InPort> ports;
-	int offset;
-	int width;
+	std::vector<OpMuxConn> ports;
 	ExtSigSpec shared_operand;
-} shared_op_t;
+} merged_op_t;
 
 
 template <typename T> void remove_val(std::vector<T> &v, const std::vector<T> &vals)
@@ -190,86 +288,60 @@ template <typename T> void remove_val(std::vector<T> &v, const std::vector<T> &v
 		}
 }
 
-bool find_op_res_width(int offset, int &width, std::vector<InPort*>& ports, const dict<RTLIL::SigBit, RTLIL::SigSpec> &op_outbit_to_outsig)
+void check_muxed_operands(std::vector<const OpMuxConn *> &ports, const ExtSigSpec &shared_operand)
 {
 
-	std::vector<RTLIL::SigSpec> op_outsigs;
-	dict<int, std::set<InPort*>> op_outsig_span;
+	auto it = ports.begin();
+	ExtSigSpec seed;
 
-	std::transform(ports.begin(), ports.end(), std::back_inserter(op_outsigs), [&](InPort *p) { return op_outbit_to_outsig.at(p->sig[offset]); });
+	while (it != ports.end()) {
+		auto p = *it;
+		auto op = p->op;
 
-	std::vector<bool> finished(ports.size(), false);
-
-	width = 0;
-
-	std::function<bool()> all_finished = [&] { return std::find(std::begin(finished), std::end(finished), false) == end(finished);};
-
-	while (!all_finished())
-	{
-		++offset;
-		++width;
-
-		if (offset >= ports[0]->sig.size()) {
-			for (size_t i = 0; i < op_outsigs.size(); ++i) {
-				if (finished[i])
-					continue;
-
-				op_outsig_span[width].insert(ports[i]);
-				finished[i] = true;
-			}
-
-			break;
+		RTLIL::IdString muxed_port_name = "\\A";
+		if (op->getPort("\\A") == shared_operand.sig) {
+			muxed_port_name = "\\B";
 		}
 
-		for (size_t i = 0; i < op_outsigs.size(); ++i) {
-			if (finished[i])
-				continue;
+		auto operand = decode_port(op, muxed_port_name, &assign_map);
 
-			if ((width >= op_outsigs[i].size()) || (ports[i]->sig[offset] != op_outsigs[i][width])) {
-				op_outsig_span[width].insert(ports[i]);
-				finished[i] = true;
-			}
+		if (seed.empty())
+			seed = operand;
+
+		if (operand.is_signed != seed.is_signed) {
+			ports.erase(it);
+		} else {
+			++it;
 		}
 	}
-
-	for (auto w: op_outsig_span) {
-		if (w.second.size() > 1) {
-			width = w.first;
-
-			ports.erase(std::remove_if(ports.begin(), ports.end(), [&](InPort *p) { return !w.second.count(p); }), ports.end());
-
-			return true;
-		}
-	}
-
-	return false;
 }
 
-ExtSigSpec find_shared_operand(InPort* seed, std::vector<InPort *> &ports, const std::map<ExtSigSpec, std::set<RTLIL::Cell *>> &operand_to_users)
+ExtSigSpec find_shared_operand(const OpMuxConn* seed, std::vector<const OpMuxConn *> &ports, const std::map<ExtSigSpec, std::set<RTLIL::Cell *>> &operand_to_users)
 {
-	std::set<RTLIL::Cell *> alus_using_operand;
-	std::set<RTLIL::Cell *> alus_set;
+	std::set<RTLIL::Cell *> ops_using_operand;
+	std::set<RTLIL::Cell *> ops_set;
 	for(const auto& p: ports)
-		alus_set.insert(p->alu);
+		ops_set.insert(p->op);
 
 	ExtSigSpec oper;
 
-	auto op_a = seed->alu;
+	auto op_a = seed->op;
 
 	for (RTLIL::IdString port_name : {"\\A", "\\B"}) {
-		oper = ExtSigSpec(op_a, port_name, &assign_map);
+		oper = decode_port(op_a, port_name, &assign_map);
 		auto operand_users = operand_to_users.at(oper);
 
 		if (operand_users.size() == 1)
 			continue;
 
-		alus_using_operand.clear();
-		std::set_intersection(operand_users.begin(), operand_users.end(), alus_set.begin(), alus_set.end(),
-				      std::inserter(alus_using_operand, alus_using_operand.begin()));
+		ops_using_operand.clear();
+		for (auto mux_ops: ops_set)
+			if (operand_users.count(mux_ops))
+				ops_using_operand.insert(mux_ops);
 
-		if (alus_using_operand.size() > 1) {
-			ports.erase(std::remove_if(ports.begin(), ports.end(), [&](InPort *p) { return !alus_using_operand.count(p->alu); }),
-				    ports.end());
+		if (ops_using_operand.size() > 1) {
+			ports.erase(std::remove_if(ports.begin(), ports.end(), [&](const OpMuxConn *p) { return !ops_using_operand.count(p->op); }),
+						ports.end());
 			return oper;
 		}
 	}
@@ -277,40 +349,135 @@ ExtSigSpec find_shared_operand(InPort* seed, std::vector<InPort *> &ports, const
 	return ExtSigSpec();
 }
 
-void remove_multi_user_outbits(RTLIL::Module *module, dict<RTLIL::SigBit, RTLIL::SigSpec> &op_outbit_to_outsig)
+dict<RTLIL::SigSpec, OpMuxConn> find_valid_op_mux_conns(RTLIL::Module *module, dict<RTLIL::SigBit, RTLIL::SigSpec> &op_outbit_to_outsig,
+							dict<RTLIL::SigSpec, RTLIL::Cell *> outsig_to_operator,
+							dict<RTLIL::SigBit, RTLIL::SigSpec> &op_aux_to_outsig)
 {
-	dict<RTLIL::SigBit, int> op_outbit_user_cnt;
+	dict<RTLIL::SigSpec, int> op_outsig_user_track;
+	dict<RTLIL::SigSpec, OpMuxConn> op_mux_conn_map;
+
+	std::function<void(RTLIL::SigSpec)> remove_outsig = [&](RTLIL::SigSpec outsig) {
+		for (auto op_outbit : outsig)
+			op_outbit_to_outsig.erase(op_outbit);
+
+		if (op_mux_conn_map.count(outsig))
+			op_mux_conn_map.erase(outsig);
+	};
+
+	std::function<void(RTLIL::SigBit)> remove_outsig_from_aux_bit = [&](RTLIL::SigBit auxbit) {
+		auto aux_outsig = op_aux_to_outsig.at(auxbit);
+		auto op = outsig_to_operator.at(aux_outsig);
+		auto op_outsig = assign_map(op->getPort("\\Y"));
+		remove_outsig(op_outsig);
+
+		for (auto aux_outbit : aux_outsig)
+			op_aux_to_outsig.erase(aux_outbit);
+	};
+
+	std::function<void(RTLIL::Cell *)>
+	  find_op_mux_conns = [&](RTLIL::Cell *mux) {
+		  RTLIL::SigSpec sig;
+		  int mux_port_size;
+
+		  if (mux->type.in("$mux", "$_MUX_")) {
+			  mux_port_size = mux->getPort("\\A").size();
+			  sig = RTLIL::SigSpec{mux->getPort("\\B"), mux->getPort("\\A")};
+		  } else {
+			  mux_port_size = mux->getPort("\\A").size();
+			  sig = mux->getPort("\\B");
+		  }
+
+		  auto mux_insig = assign_map(sig);
+
+		  for (int i = 0; i < mux_insig.size(); ++i) {
+			  if (op_aux_to_outsig.count(mux_insig[i])) {
+				  remove_outsig_from_aux_bit(mux_insig[i]);
+				  continue;
+			  }
+
+			  if (!op_outbit_to_outsig.count(mux_insig[i]))
+				  continue;
+
+			  auto op_outsig = op_outbit_to_outsig.at(mux_insig[i]);
+
+			  if (op_mux_conn_map.count(op_outsig)) {
+					remove_outsig(op_outsig);
+				  continue;
+			  }
+
+			  int mux_port_id = i / mux_port_size;
+			  int mux_port_offset = i % mux_port_size;
+
+			  int op_outsig_offset;
+			  for (op_outsig_offset = 0; op_outsig[op_outsig_offset] != mux_insig[i]; ++op_outsig_offset)
+				  ;
+
+			  int j = op_outsig_offset;
+			  do {
+				  if (!op_outbit_to_outsig.count(mux_insig[i]))
+					  break;
+
+				  if (op_outbit_to_outsig.at(mux_insig[i]) != op_outsig)
+					  break;
+
+				  ++i;
+				  ++j;
+			  } while ((i / mux_port_size == mux_port_id) && (j < op_outsig.size()));
+
+			  int op_conn_width = j - op_outsig_offset;
+			  OpMuxConn inp = {
+			    op_outsig.extract(op_outsig_offset, op_conn_width),
+			    mux,
+			    outsig_to_operator.at(op_outsig),
+			    mux_port_id,
+			    mux_port_offset,
+			    op_outsig_offset,
+			  };
+
+			  op_mux_conn_map[op_outsig] = inp;
+
+			  --i;
+		  }
+	  };
+
+	std::function<void(RTLIL::SigSpec)> remove_connected_ops = [&](RTLIL::SigSpec sig) {
+		auto mux_insig = assign_map(sig);
+		for (auto outbit : mux_insig) {
+			if (op_aux_to_outsig.count(outbit)) {
+				remove_outsig_from_aux_bit(outbit);
+				continue;
+			}
 
-	std::function<void(SigSpec)> update_op_outbit_user_cnt = [&](SigSpec sig) {
-		auto outsig = assign_map(sig);
-		for (auto outbit : outsig) {
 			if (!op_outbit_to_outsig.count(outbit))
 				continue;
 
-			if (++op_outbit_user_cnt[outbit] > 1) {
-				auto alu_outsig = op_outbit_to_outsig.at(outbit);
-
-				for (auto outbit : alu_outsig)
-					op_outbit_to_outsig.erase(outbit);
-			}
+			remove_outsig(op_outbit_to_outsig.at(outbit));
 		}
 	};
 
-	for (auto cell : module->cells())
-		for (auto &conn : cell->connections())
-			if (cell->input(conn.first))
-				update_op_outbit_user_cnt(conn.second);
+	for (auto cell : module->cells()) {
+		if (cell->type.in("$mux", "$_MUX_", "$pmux")) {
+			remove_connected_ops(cell->getPort("\\S"));
+			find_op_mux_conns(cell);
+		} else {
+			for (auto &conn : cell->connections())
+				if (cell->input(conn.first))
+					remove_connected_ops(conn.second);
+		}
+	}
 
 	for (auto w : module->wires()) {
 		if (!w->port_output)
 			continue;
 
-		update_op_outbit_user_cnt(w);
+		remove_connected_ops(w);
 	}
+
+	return op_mux_conn_map;
 }
 
 struct OptSharePass : public Pass {
-	OptSharePass() : Pass("opt_share", "merge arithmetic operators that share an operand") {}
+	OptSharePass() : Pass("opt_share", "merge mutually exclusive cells of the same type that share an input signal") {}
 	void help() YS_OVERRIDE
 	{
 		//   |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
@@ -318,18 +485,19 @@ struct OptSharePass : public Pass {
 		log("    opt_share [selection]\n");
 		log("\n");
 
-		log("This pass identifies mutually exclusive $alu arithmetic cells that:\n");
-		log("    (a) share an input operand\n");
+		log("This pass identifies mutually exclusive cells of the same type that:\n");
+		log("    (a) share an input signal\n");
 		log("    (b) drive the same $mux, $_MUX_, or $pmux multiplexing cell allowing\n");
-		log("        the $alu cell to be merged and the multiplexer to be moved from\n");
-		log("        multiplexing its output to multiplexing the non-shared input operands.\n");
+		log("        the cell to be merged and the multiplexer to be moved from\n");
+		log("        multiplexing its output to multiplexing the non-shared input signals.\n");
 		log("\n");
 	}
-	void execute(std::vector<std::string>, RTLIL::Design *design) YS_OVERRIDE
+	void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
 	{
 
 		log_header(design, "Executing OPT_SHARE pass.\n");
 
+		extra_args(args, 1, design);
 		for (auto module : design->selected_modules()) {
 			assign_map.clear();
 			assign_map.set(module);
@@ -337,28 +505,30 @@ struct OptSharePass : public Pass {
 			std::map<ExtSigSpec, std::set<RTLIL::Cell *>> operand_to_users;
 			dict<RTLIL::SigSpec, RTLIL::Cell *> outsig_to_operator;
 			dict<RTLIL::SigBit, RTLIL::SigSpec> op_outbit_to_outsig;
+			dict<RTLIL::SigBit, RTLIL::SigSpec> op_aux_to_outsig;
 			bool any_shared_operands = false;
 			std::vector<ExtSigSpec> op_insigs;
 
 			for (auto cell : module->cells()) {
-				if (!cell->type.in("$alu"))
+				if (!cell_supported(cell))
 					continue;
 
-				RTLIL::SigSpec sig_bi = cell->getPort("\\BI");
-				RTLIL::SigSpec sig_ci = cell->getPort("\\CI");
+				if (cell->type == "$alu") {
+					for (RTLIL::IdString port_name : {"\\X", "\\CO"}) {
+						auto mux_insig = assign_map(cell->getPort(port_name));
+						outsig_to_operator[mux_insig] = cell;
+						for (auto outbit : mux_insig)
+							op_aux_to_outsig[outbit] = mux_insig;
+					}
+				}
 
-				if ((!sig_bi.is_fully_const()) || (!sig_ci.is_fully_const()) || (sig_bi != sig_ci))
-					continue;
-
-				RTLIL::SigSpec sig_y = cell->getPort("\\A");
-
-				auto outsig = assign_map(cell->getPort("\\Y"));
-				outsig_to_operator[outsig] = cell;
-				for (auto outbit : outsig)
-					op_outbit_to_outsig[outbit] = outsig;
+				auto mux_insig = assign_map(cell->getPort("\\Y"));
+				outsig_to_operator[mux_insig] = cell;
+				for (auto outbit : mux_insig)
+					op_outbit_to_outsig[outbit] = mux_insig;
 
 				for (RTLIL::IdString port_name : {"\\A", "\\B"}) {
-					auto op_insig = ExtSigSpec(cell, port_name, &assign_map);
+					auto op_insig = decode_port(cell, port_name, &assign_map);
 					op_insigs.push_back(op_insig);
 					operand_to_users[op_insig].insert(cell);
 					if (operand_to_users[op_insig].size() > 1)
@@ -371,89 +541,117 @@ struct OptSharePass : public Pass {
 
 			// Operator outputs need to be exclusively connected to the $mux inputs in order to be mergeable. Hence we count to
 			// how many points are operator output bits connected.
-			remove_multi_user_outbits(module, op_outbit_to_outsig);
+			dict<RTLIL::SigSpec, OpMuxConn> op_mux_conn_map =
+			  find_valid_op_mux_conns(module, op_outbit_to_outsig, outsig_to_operator, op_aux_to_outsig);
 
-			std::vector<shared_op_t> shared_ops;
-			for (auto cell : module->cells()) {
-				if (!cell->type.in("$mux", "$_MUX_", "$pmux"))
-					continue;
+			// Group op connections connected to same ports of the same $mux. Sort them in ascending order of their port offset
+			dict<RTLIL::Cell*, std::vector<std::set<OpMuxConn>>> mux_port_op_conns;
+			for (auto& val: op_mux_conn_map) {
+				OpMuxConn p = val.second;
+				auto& mux_port_conns = mux_port_op_conns[p.mux];
 
-				RTLIL::SigSpec sig_a = cell->getPort("\\A");
-				RTLIL::SigSpec sig_b = cell->getPort("\\B");
-				RTLIL::SigSpec sig_s = cell->getPort("\\S");
+				if (mux_port_conns.size() == 0) {
+					int mux_port_num;
 
-				std::vector<InPort> ports;
+					if (p.mux->type.in("$mux", "$_MUX_"))
+						mux_port_num = 2;
+					else
+						mux_port_num = p.mux->getPort("\\S").size();
 
-				if (cell->type.in("$mux", "$_MUX_")) {
-					ports.push_back(InPort(assign_map(sig_a), cell, 0));
-					ports.push_back(InPort(assign_map(sig_b), cell, 1));
-				} else {
-					RTLIL::SigSpec sig_s = cell->getPort("\\S");
-					for (int i = 0; i < sig_s.size(); i++) {
-						auto inp = sig_b.extract(i * sig_a.size(), sig_a.size());
-						ports.push_back(InPort(assign_map(inp), cell, i));
-					}
+					mux_port_conns.resize(mux_port_num);
 				}
 
+				mux_port_conns[p.mux_port_id].insert(p);
+			}
+
+			std::vector<merged_op_t> merged_ops;
+			for (auto& val: mux_port_op_conns) {
+
+				RTLIL::Cell* cell = val.first;
+				auto &mux_port_conns = val.second;
+
+				const OpMuxConn *seed = NULL;
+
 				// Look through the bits of the $mux inputs and see which of them are connected to the operator
 				// results. Operator results can be concatenated with other signals before led to the $mux.
-				for (int i = 0; i < sig_a.size(); ++i) {
-					std::vector<InPort*> alu_ports;
-					for (auto& p: ports)
-						if (op_outbit_to_outsig.count(p.sig[i])) {
-							p.alu = outsig_to_operator.at(op_outbit_to_outsig.at(p.sig[i]));
-							alu_ports.push_back(&p);
-						}
+				while (true) {
 
-					int alu_port_width = 0;
-
-					while (alu_ports.size() > 1) {
-						std::vector<InPort*> shared_ports(alu_ports);
-
-						auto seed = alu_ports[0];
-						alu_ports.erase(alu_ports.begin());
-
-						// Find ports whose $alu-s share an operand with $alu connected to the seed port
-						auto shared_operand = find_shared_operand(seed, shared_ports, operand_to_users);
-
-						if (shared_operand.empty())
-							continue;
-
-						// Some bits of the operator results might be unconnected. Calculate the number of conneted
-						// bits.
-						if (!find_op_res_width(i, alu_port_width, shared_ports, op_outbit_to_outsig))
-							break;
-
-						if (shared_ports.size() < 2)
-							break;
-
-						// Remember the combination for the merger
-						std::vector<InPort> shared_p;
-						for (auto p: shared_ports)
-							shared_p.push_back(*p);
-
-						shared_ops.push_back(shared_op_t{cell, shared_p, i, alu_port_width, shared_operand});
-
-						// Remove merged ports from the list and try to find other mergers for the mux
-						remove_val(alu_ports, shared_ports);
+					// Remove either the merged ports from the last iteration or the seed that failed to yield a merger
+					if (seed != NULL) {
+						mux_port_conns[seed->mux_port_id].erase(*seed);
+						seed = NULL;
 					}
 
-					if (alu_port_width)
-						i += alu_port_width - 1;
+					// For a new merger, find the seed op connection that starts at lowest port offset among port connections
+					for (auto &port_conns : mux_port_conns) {
+						if (!port_conns.size())
+							continue;
+
+						const OpMuxConn *next_p = &(*port_conns.begin());
+
+						if ((seed == NULL) || (seed->mux_port_offset > next_p->mux_port_offset))
+							seed = next_p;
+					}
+
+					// Cannot find the seed -> nothing to do for this $mux anymore
+					if (seed == NULL)
+						break;
+
+					// Find all other op connections that start from the same port offset, and whose ops can be merged with the seed op
+					std::vector<const OpMuxConn *> mergeable_conns;
+					for (auto &port_conns : mux_port_conns) {
+						if (!port_conns.size())
+							continue;
+
+						const OpMuxConn *next_p = &(*port_conns.begin());
+
+						if ((next_p->op_outsig_offset == seed->op_outsig_offset) &&
+						    (next_p->mux_port_offset == seed->mux_port_offset) && mergeable(next_p->op, seed->op) &&
+						    next_p->sig.size() == seed->sig.size())
+							mergeable_conns.push_back(next_p);
+					}
+
+					// We need at least two mergeable connections for the merger
+					if (mergeable_conns.size() < 2)
+						continue;
+
+					// Filter mergeable connections whose ops share an operand with seed connection's op
+					auto shared_operand = find_shared_operand(seed, mergeable_conns, operand_to_users);
+
+					if (shared_operand.empty())
+						continue;
+
+					check_muxed_operands(mergeable_conns, shared_operand);
+
+					if (mergeable_conns.size() < 2)
+						continue;
+
+					// Remember the combination for the merger
+					std::vector<OpMuxConn> merged_ports;
+					for (auto p : mergeable_conns) {
+						merged_ports.push_back(*p);
+						mux_port_conns[p->mux_port_id].erase(*p);
+					}
+
+					seed = NULL;
+
+					merged_ops.push_back(merged_op_t{cell, merged_ports, shared_operand});
+
+					design->scratchpad_set_bool("opt.did_something", true);
 				}
 
 			}
 
-			for (auto &shared : shared_ops) {
-				log("    Found arithmetic cells that share an operand and can be merged by moving the %s %s in front "
+			for (auto &shared : merged_ops) {
+				log("    Found cells that share an operand and can be merged by moving the %s %s in front "
 				    "of "
 				    "them:\n",
 				    log_id(shared.mux->type), log_id(shared.mux));
 				for (const auto& op : shared.ports)
-					log("        %s\n", log_id(op.alu));
+					log("        %s\n", log_id(op.op));
 				log("\n");
 
-				merge_operators(module, shared.mux, shared.ports, shared.offset, shared.width, shared.shared_operand);
+				merge_operators(module, shared.mux, shared.ports, shared.shared_operand);
 			}
 		}
 	}
diff --git a/tests/opt_share/.gitignore b/tests/opt_share/.gitignore
new file mode 100644
index 000000000..9c595a6fb
--- /dev/null
+++ b/tests/opt_share/.gitignore
@@ -0,0 +1 @@
+temp
diff --git a/tests/opt_share/generate.py b/tests/opt_share/generate.py
new file mode 100644
index 000000000..2ec92f7de
--- /dev/null
+++ b/tests/opt_share/generate.py
@@ -0,0 +1,86 @@
+#!/usr/bin/env python3
+
+import argparse
+import sys
+import random
+from contextlib import contextmanager
+
+
+@contextmanager
+def redirect_stdout(new_target):
+    old_target, sys.stdout = sys.stdout, new_target
+    try:
+        yield new_target
+    finally:
+        sys.stdout = old_target
+
+
+def random_plus_x():
+    return "%s x" % random.choice(['+', '+', '+', '-', '-', '|', '&', '^'])
+
+
+def maybe_plus_x(expr):
+    if random.randint(0, 4) == 0:
+        return "(%s %s)" % (expr, random_plus_x())
+    else:
+        return expr
+
+
+parser = argparse.ArgumentParser(
+    formatter_class=argparse.ArgumentDefaultsHelpFormatter)
+parser.add_argument('-S', '--seed', type=int, help='seed for PRNG')
+parser.add_argument('-c',
+                    '--count',
+                    type=int,
+                    default=100,
+                    help='number of test cases to generate')
+args = parser.parse_args()
+
+if args.seed is not None:
+    print("PRNG seed: %d" % args.seed)
+    random.seed(args.seed)
+
+for idx in range(args.count):
+    with open('temp/uut_%05d.v' % idx, 'w') as f:
+        with redirect_stdout(f):
+            print('module uut_%05d(a, b, c, s, y);' % (idx))
+            op = random.choice([
+                random.choice(['+', '-', '*', '/', '%']),
+                random.choice(['<', '<=', '==', '!=', '===', '!==', '>=',
+                               '>']),
+                random.choice(['<<', '>>', '<<<', '>>>']),
+                random.choice(['|', '&', '^', '~^', '||', '&&']),
+            ])
+            print('  input%s [%d:0] a;' % (random.choice(['', ' signed']), 8))
+            print('  input%s [%d:0] b;' % (random.choice(['', ' signed']), 8))
+            print('  input%s [%d:0] c;' % (random.choice(['', ' signed']), 8))
+            print('  input s;')
+            print('  output [%d:0] y;' % 8)
+            ops1 = ['a', 'b']
+            ops2 = ['a', 'c']
+            random.shuffle(ops1)
+            random.shuffle(ops2)
+            cast1 = random.choice(['', '$signed', '$unsigned'])
+            cast2 = random.choice(['', '$signed', '$unsigned'])
+            print('  assign y = (s ? %s(%s %s %s) : %s(%s %s %s));' %
+                  (cast1, ops1[0], op, ops1[1],
+                   cast2, ops2[0], op, ops2[1]))
+            print('endmodule')
+
+    with open('temp/uut_%05d.ys' % idx, 'w') as f:
+        with redirect_stdout(f):
+            print('read_verilog temp/uut_%05d.v' % idx)
+            print('proc;;')
+            print('copy uut_%05d gold' % idx)
+            print('rename uut_%05d gate' % idx)
+            print('tee -a temp/all_share_log.txt log')
+            print('tee -a temp/all_share_log.txt log #job# uut_%05d' % idx)
+            print('tee -a temp/all_share_log.txt opt gate')
+            print('tee -a temp/all_share_log.txt opt_share gate')
+            print('tee -a temp/all_share_log.txt opt_clean gate')
+            print(
+                'miter -equiv -flatten -ignore_gold_x -make_outputs -make_outcmp gold gate miter'
+            )
+            print(
+                'sat -set-def-inputs -verify -prove trigger 0 -show-inputs -show-outputs miter'
+            )
diff --git a/tests/opt_share/run-test.sh b/tests/opt_share/run-test.sh
new file mode 100755
index 000000000..e01552646
--- /dev/null
+++ b/tests/opt_share/run-test.sh
@@ -0,0 +1,39 @@
+#!/bin/bash
+
+# run this test many times:
+# time bash -c 'for ((i=0; i<100; i++)); do echo "-- $i --"; bash run-test.sh || exit 1; done'
+
+set -e
+
+OPTIND=1
+count=100
+seed=""    # default to no seed specified
+while getopts "c:S:" opt
+do
+  case "$opt" in
+		c) count="$OPTARG" ;;
+		S) seed="-S $OPTARG" ;;
+  esac
+done
+shift "$((OPTIND-1))"
+
+rm -rf temp
+mkdir -p temp
+echo "generating tests.."
+python3 generate.py -c $count $seed
+
+echo "running tests.."
+for i in $( ls temp/*.ys | sed 's,[^0-9],,g; s,^0*\(.\),\1,g;' ); do
+	echo -n "[$i]"
+	idx=$( printf "%05d" $i )
+	../../yosys -ql temp/uut_${idx}.log temp/uut_${idx}.ys
+done
+echo
+
+failed_share=$( echo $( gawk '/^#job#/ { j=$2; db[j]=0; } /^Removing [246] cells/ { delete db[j]; } END { for (j in db) print(j); }' temp/all_share_log.txt ) )
+if [ -n "$failed_share" ]; then
+	echo "Resource sharing failed for the following test cases: $failed_share"
+	false
+fi
+
+exit 0

From 067b44938c1fd3e24fc9478b96a47bac7152c111 Mon Sep 17 00:00:00 2001
From: Bogdan Vukobratovic <bogdan.vukobratovic@gmail.com>
Date: Wed, 7 Aug 2019 09:30:58 +0200
Subject: [PATCH 6/6] Fix wrong results when opt_share called before opt_clean

---
 passes/opt/opt_share.cc                       | 32 ++++++++-----------
 .../opt/opt_share_large_pmux_cat_multipart.ys |  1 -
 2 files changed, 14 insertions(+), 19 deletions(-)

diff --git a/passes/opt/opt_share.cc b/passes/opt/opt_share.cc
index e8f44749a..a2ec9cc37 100644
--- a/passes/opt/opt_share.cc
+++ b/passes/opt/opt_share.cc
@@ -138,7 +138,7 @@ RTLIL::SigSpec decode_port_sign(RTLIL::Cell *cell, RTLIL::IdString port_name) {
 
 	if (cell->type == "$alu" && port_name == "\\B")
 		return cell->getPort("\\BI");
-	else if	(cell->type == "$sub" && port_name == "\\B")
+	else if (cell->type == "$sub" && port_name == "\\B")
 		return RTLIL::Const(1, 1);
 
 	return RTLIL::Const(0, 1);
@@ -177,14 +177,12 @@ void merge_operators(RTLIL::Module *module, RTLIL::Cell *mux, const std::vector<
 		auto op = p.op;
 
 		RTLIL::IdString muxed_port_name = "\\A";
-		if (op->getPort("\\A") == operand.sig) {
+		if (decode_port(op, "\\A", &assign_map) == operand)
 			muxed_port_name = "\\B";
-		}
 
 		auto operand = decode_port(op, muxed_port_name, &assign_map);
-		if (operand.sig.size() > max_width) {
+		if (operand.sig.size() > max_width)
 			max_width = operand.sig.size();
-		}
 
 		muxed_operands.push_back(operand);
 	}
@@ -196,10 +194,8 @@ void merge_operators(RTLIL::Module *module, RTLIL::Cell *mux, const std::vector<
 			max_width = shared_op->getParam("\\Y_WIDTH").as_int();
 
 
-	for (auto &operand : muxed_operands) {
+	for (auto &operand : muxed_operands)
 		operand.sig.extend_u0(max_width, operand.is_signed);
-	}
-
 
 	for (const auto& p : ports) {
 		auto op = p.op;
@@ -208,11 +204,10 @@ void merge_operators(RTLIL::Module *module, RTLIL::Cell *mux, const std::vector<
 		module->remove(op);
 	}
 
-	for (auto &muxed_op : muxed_operands) {
-		if (muxed_op.sign != muxed_operands[0].sign) {
+	for (auto &muxed_op : muxed_operands)
+		if (muxed_op.sign != muxed_operands[0].sign)
 			muxed_op = ExtSigSpec(module->Neg(NEW_ID, muxed_op.sig, muxed_op.is_signed));
-		}
-	}
+
 
 	RTLIL::SigSpec mux_y = mux->getPort("\\Y");
 	RTLIL::SigSpec mux_a = mux->getPort("\\A");
@@ -261,7 +256,7 @@ void merge_operators(RTLIL::Module *module, RTLIL::Cell *mux, const std::vector<
 
 	shared_op->setParam("\\Y_WIDTH", conn_width);
 
-	if (shared_op->getPort("\\A") == operand.sig) {
+	if (decode_port(shared_op, "\\A", &assign_map) == operand) {
 		shared_op->setPort("\\B", mux_to_oper);
 		shared_op->setParam("\\B_WIDTH", max_width);
 	} else {
@@ -299,7 +294,7 @@ void check_muxed_operands(std::vector<const OpMuxConn *> &ports, const ExtSigSpe
 		auto op = p->op;
 
 		RTLIL::IdString muxed_port_name = "\\A";
-		if (op->getPort("\\A") == shared_operand.sig) {
+		if (decode_port(op, "\\A", &assign_map) == shared_operand) {
 			muxed_port_name = "\\B";
 		}
 
@@ -486,10 +481,11 @@ struct OptSharePass : public Pass {
 		log("\n");
 
 		log("This pass identifies mutually exclusive cells of the same type that:\n");
-		log("    (a) share an input signal\n");
-		log("    (b) drive the same $mux, $_MUX_, or $pmux multiplexing cell allowing\n");
-		log("        the cell to be merged and the multiplexer to be moved from\n");
-		log("        multiplexing its output to multiplexing the non-shared input signals.\n");
+		log("    (a) share an input signal,\n");
+		log("    (b) drive the same $mux, $_MUX_, or $pmux multiplexing cell,\n");
+		log("\n");
+		log("allowing the cell to be merged and the multiplexer to be moved from\n");
+		log("multiplexing its output to multiplexing the non-shared input signals.\n");
 		log("\n");
 	}
 	void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
diff --git a/tests/opt/opt_share_large_pmux_cat_multipart.ys b/tests/opt/opt_share_large_pmux_cat_multipart.ys
index 54d200dc7..610bb8c6c 100644
--- a/tests/opt/opt_share_large_pmux_cat_multipart.ys
+++ b/tests/opt/opt_share_large_pmux_cat_multipart.ys
@@ -7,7 +7,6 @@ opt merged
 
 opt_share merged
 opt_clean merged
-opt -full
 
 miter -equiv -flatten -ignore_gold_x -make_outputs -make_outcmp opt_share_test merged miter
 sat -set-def-inputs -verify -prove trigger 0 -show-inputs -show-outputs miter