diff --git a/Makefile b/Makefile
index 249c1d0ee..b51ffd4c8 100644
--- a/Makefile
+++ b/Makefile
@@ -294,7 +294,7 @@ endif
 PY_WRAPPER_FILE = kernel/python_wrappers
 OBJS += $(PY_WRAPPER_FILE).o
 PY_GEN_SCRIPT= py_wrap_generator
-PY_WRAP_INCLUDES := $(shell python$(PYTHON_VERSION) -c "import $(PY_GEN_SCRIPT); $(PY_GEN_SCRIPT).print_includes()")
+PY_WRAP_INCLUDES := $(shell python$(PYTHON_VERSION) -c "from misc import $(PY_GEN_SCRIPT); $(PY_GEN_SCRIPT).print_includes()")
 endif
 
 ifeq ($(ENABLE_READLINE),1)
@@ -550,9 +550,9 @@ libyosys.so: $(filter-out kernel/driver.o,$(OBJS))
 	$(Q) mkdir -p $(dir $@)
 	$(P) cat $< | grep -E -v "#[ ]*(include|error)" | $(LD) -x c++ -o $@ -E -P -
 
-$(PY_WRAPPER_FILE).cc: $(PY_GEN_SCRIPT).py $(PY_WRAP_INCLUDES)
+$(PY_WRAPPER_FILE).cc: misc/$(PY_GEN_SCRIPT).py $(PY_WRAP_INCLUDES)
 	$(Q) mkdir -p $(dir $@)
-	$(P) python$(PYTHON_VERSION) -c "import $(PY_GEN_SCRIPT); $(PY_GEN_SCRIPT).gen_wrappers(\"$(PY_WRAPPER_FILE).cc\")"
+	$(P) python$(PYTHON_VERSION) -c "from misc import $(PY_GEN_SCRIPT); $(PY_GEN_SCRIPT).gen_wrappers(\"$(PY_WRAPPER_FILE).cc\")"
 
 %.o: %.cpp
 	$(Q) mkdir -p $(dir $@)
@@ -685,7 +685,7 @@ ifeq ($(ENABLE_LIBYOSYS),1)
 ifeq ($(ENABLE_PYOSYS),1)
 	$(INSTALL_SUDO) mkdir -p $(PYTHON_DESTDIR)/pyosys
 	$(INSTALL_SUDO) cp libyosys.so $(PYTHON_DESTDIR)/pyosys
-	$(INSTALL_SUDO) cp __init__.py $(PYTHON_DESTDIR)/pyosys
+	$(INSTALL_SUDO) cp misc/__init__.py $(PYTHON_DESTDIR)/pyosys
 endif
 endif
 
diff --git a/backends/firrtl/firrtl.cc b/backends/firrtl/firrtl.cc
index ed6e9f8ee..9feff71c6 100644
--- a/backends/firrtl/firrtl.cc
+++ b/backends/firrtl/firrtl.cc
@@ -163,31 +163,61 @@ struct FirrtlWorker
 		}
 	};
 	/* Memories defined within this module. */
-	 struct memory {
-		 string name;					// memory name
-		 int abits;						// number of address bits
-		 int size;						// size (in units) of the memory
-		 int width;						// size (in bits) of each element
-		 int read_latency;
-		 int write_latency;
-		 vector<read_port> read_ports;
-		 vector<write_port> write_ports;
-		 std::string init_file;
-		 std::string init_file_srcFileSpec;
-		 memory(string name, int abits, int size, int width) : name(name), abits(abits), size(size), width(width), read_latency(0), write_latency(1), init_file(""), init_file_srcFileSpec("") {}
-		 memory() : read_latency(0), write_latency(1), init_file(""), init_file_srcFileSpec(""){}
-		 void add_memory_read_port(read_port &rp) {
-			 read_ports.push_back(rp);
-		 }
-		 void add_memory_write_port(write_port &wp) {
-			 write_ports.push_back(wp);
-		 }
-		 void add_memory_file(std::string init_file, std::string init_file_srcFileSpec) {
-			 this->init_file = init_file;
-			 this->init_file_srcFileSpec = init_file_srcFileSpec;
+	struct memory {
+		Cell *pCell;					// for error reporting
+		string name;					// memory name
+		int abits;						// number of address bits
+		int size;						// size (in units) of the memory
+		int width;						// size (in bits) of each element
+		int read_latency;
+		int write_latency;
+		vector<read_port> read_ports;
+		vector<write_port> write_ports;
+		std::string init_file;
+		std::string init_file_srcFileSpec;
+		string srcLine;
+		memory(Cell *pCell, string name, int abits, int size, int width) : pCell(pCell), name(name), abits(abits), size(size), width(width), read_latency(0), write_latency(1), init_file(""), init_file_srcFileSpec("") {
+			// Provide defaults for abits or size if one (but not the other) is specified.
+			if (this->abits == 0 && this->size != 0) {
+				this->abits = ceil_log2(this->size);
+			} else if (this->abits != 0 && this->size == 0) {
+				this->size = 1 << this->abits;
+			}
+			// Sanity-check this construction.
+			if (this->name == "") {
+				log_error("Nameless memory%s\n", this->atLine());
+			}
+			if (this->abits == 0 && this->size == 0) {
+				log_error("Memory %s has zero address bits and size%s\n", this->name.c_str(), this->atLine());
+			}
+			if (this->width == 0) {
+				log_error("Memory %s has zero width%s\n", this->name.c_str(), this->atLine());
+			}
 		 }
+		// We need a default constructor for the dict insert.
+	   memory() : pCell(0), read_latency(0), write_latency(1), init_file(""), init_file_srcFileSpec(""){}
 
-	 };
+		const char *atLine() {
+			if (srcLine == "") {
+				if (pCell) {
+					auto p = pCell->attributes.find("\\src");
+					srcLine = " at " + p->second.decode_string();
+				}
+			}
+			return srcLine.c_str();
+		}
+		void add_memory_read_port(read_port &rp) {
+			read_ports.push_back(rp);
+		}
+		void add_memory_write_port(write_port &wp) {
+			write_ports.push_back(wp);
+		}
+		void add_memory_file(std::string init_file, std::string init_file_srcFileSpec) {
+			this->init_file = init_file;
+			this->init_file_srcFileSpec = init_file_srcFileSpec;
+		}
+
+	};
 	dict<string, memory> memories;
 
 	void register_memory(memory &m)
@@ -604,7 +634,7 @@ struct FirrtlWorker
 				int abits = cell->parameters.at("\\ABITS").as_int();
 				int width = cell->parameters.at("\\WIDTH").as_int();
 				int size = cell->parameters.at("\\SIZE").as_int();
-				memory m(mem_id, abits, size, width);
+				memory m(cell, mem_id, abits, size, width);
 				int rd_ports = cell->parameters.at("\\RD_PORTS").as_int();
 				int wr_ports = cell->parameters.at("\\WR_PORTS").as_int();
 
@@ -681,6 +711,8 @@ struct FirrtlWorker
 			{
 				std::string cell_type = fid(cell->type);
 				std::string mem_id = make_id(cell->parameters["\\MEMID"].decode_string());
+				int abits = cell->parameters.at("\\ABITS").as_int();
+				int width = cell->parameters.at("\\WIDTH").as_int();
 				memory *mp = nullptr;
 				if (cell->type == "$meminit" ) {
 					log_error("$meminit (%s.%s.%s) currently unsupported\n", log_id(module), log_id(cell), mem_id.c_str());
@@ -693,6 +725,11 @@ struct FirrtlWorker
 					Const clk_enable = cell->parameters.at("\\CLK_ENABLE");
 					Const clk_polarity = cell->parameters.at("\\CLK_POLARITY");
 
+					// Do we already have an entry for this memory?
+					if (memories.count(mem_id) == 0) {
+						memory m(cell, mem_id, abits, 0, width);
+						register_memory(m);
+					}
 					mp = &memories.at(mem_id);
 					int portNum = 0;
 					bool transparency = false;
@@ -890,7 +927,7 @@ struct FirrtlWorker
 
 		// If we have any memory definitions, output them.
 		for (auto kv : memories) {
-			memory m = kv.second;
+			memory &m = kv.second;
 			f << stringf("    mem %s:\n", m.name.c_str());
 			f << stringf("      data-type => UInt<%d>\n", m.width);
 			f << stringf("      depth => %d\n", m.size);
diff --git a/frontends/ast/genrtlil.cc b/frontends/ast/genrtlil.cc
index 48bd466e6..92205b7ae 100644
--- a/frontends/ast/genrtlil.cc
+++ b/frontends/ast/genrtlil.cc
@@ -645,6 +645,8 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun
 			if (!id_ast->children[0]->range_valid)
 				log_file_error(filename, linenum, "Failed to detect width of memory access `%s'!\n", str.c_str());
 			this_width = id_ast->children[0]->range_left - id_ast->children[0]->range_right + 1;
+			if (children.size() > 1)
+				range = children[1];
 		} else
 			log_file_error(filename, linenum, "Failed to detect width for identifier %s!\n", str.c_str());
 		if (range) {
diff --git a/frontends/ast/simplify.cc b/frontends/ast/simplify.cc
index 3e453bd7f..d6561682a 100644
--- a/frontends/ast/simplify.cc
+++ b/frontends/ast/simplify.cc
@@ -1172,6 +1172,15 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
 			varbuf->children[0] = buf;
 		}
 
+#if 0
+		if (type == AST_FOR) {
+			AstNode *buf = next_ast->clone();
+			delete buf->children[1];
+			buf->children[1] = varbuf->children[0]->clone();
+			current_block->children.insert(current_block->children.begin() + current_block_idx++, buf);
+		}
+#endif
+
 		current_scope[varbuf->str] = backup_scope_varbuf;
 		delete varbuf;
 		delete_children();
@@ -1598,6 +1607,7 @@ skip_dynamic_range_lvalue_expansion:;
 			current_scope[wire_tmp->str] = wire_tmp;
 			wire_tmp->attributes["\\nosync"] = AstNode::mkconst_int(1, false);
 			while (wire_tmp->simplify(true, false, false, 1, -1, false, false)) { }
+			wire_tmp->is_logic = true;
 
 			AstNode *wire_tmp_id = new AstNode(AST_IDENTIFIER);
 			wire_tmp_id->str = wire_tmp->str;
diff --git a/frontends/verilog/verilog_frontend.cc b/frontends/verilog/verilog_frontend.cc
index 8202ab9d7..01e589efb 100644
--- a/frontends/verilog/verilog_frontend.cc
+++ b/frontends/verilog/verilog_frontend.cc
@@ -246,8 +246,6 @@ struct VerilogFrontend : public Frontend {
 		specify_mode = false;
 		default_nettype_wire = true;
 
-		log_header(design, "Executing Verilog-2005 frontend.\n");
-
 		args.insert(args.begin()+1, verilog_defaults.begin(), verilog_defaults.end());
 
 		size_t argidx;
@@ -423,6 +421,8 @@ struct VerilogFrontend : public Frontend {
 		}
 		extra_args(f, filename, args, argidx);
 
+		log_header(design, "Executing Verilog-2005 frontend: %s\n", filename.c_str());
+
 		log("Parsing %s%s input from `%s' to AST representation.\n",
 				formal_mode ? "formal " : "", sv_mode ? "SystemVerilog" : "Verilog", filename.c_str());
 
diff --git a/kernel/driver.cc b/kernel/driver.cc
index 1bc7a5935..f273057dd 100644
--- a/kernel/driver.cc
+++ b/kernel/driver.cc
@@ -529,13 +529,13 @@ int main(int argc, char **argv)
 			log_error("Can't open dependencies file for writing: %s\n", strerror(errno));
 		bool first = true;
 		for (auto fn : yosys_output_files) {
-			fprintf(f, "%s%s", first ? "" : " ", fn.c_str());
+			fprintf(f, "%s%s", first ? "" : " ", escape_filename_spaces(fn).c_str());
 			first = false;
 		}
 		fprintf(f, ":");
 		for (auto fn : yosys_input_files) {
 			if (yosys_output_files.count(fn) == 0)
-				fprintf(f, " %s", fn.c_str());
+				fprintf(f, " %s", escape_filename_spaces(fn).c_str());
 		}
 		fprintf(f, "\n");
 	}
diff --git a/kernel/rtlil.cc b/kernel/rtlil.cc
index 040644c47..147d378e5 100644
--- a/kernel/rtlil.cc
+++ b/kernel/rtlil.cc
@@ -3496,7 +3496,7 @@ bool RTLIL::SigSpec::operator ==(const RTLIL::SigSpec &other) const
 	pack();
 	other.pack();
 
-	if (chunks_.size() != chunks_.size())
+	if (chunks_.size() != other.chunks_.size())
 		return false;
 
 	updhash();
diff --git a/kernel/yosys.cc b/kernel/yosys.cc
index a12355f1d..20d972150 100644
--- a/kernel/yosys.cc
+++ b/kernel/yosys.cc
@@ -482,6 +482,20 @@ void remove_directory(std::string dirname)
 #endif
 }
 
+std::string escape_filename_spaces(const std::string& filename)
+{
+	std::string out;
+	out.reserve(filename.size());
+	for (auto c : filename)
+	{
+		if (c == ' ')
+			out += "\\ ";
+		else
+			out.push_back(c);
+	}
+	return out;
+}
+
 int GetSize(RTLIL::Wire *wire)
 {
 	return wire->width;
diff --git a/kernel/yosys.h b/kernel/yosys.h
index 2cf6188b4..82eb069ab 100644
--- a/kernel/yosys.h
+++ b/kernel/yosys.h
@@ -257,6 +257,7 @@ std::string make_temp_dir(std::string template_str = "/tmp/yosys_XXXXXX");
 bool check_file_exists(std::string filename, bool is_exec = false);
 bool is_absolute_path(std::string filename);
 void remove_directory(std::string dirname);
+std::string escape_filename_spaces(const std::string& filename);
 
 template<typename T> int GetSize(const T &obj) { return obj.size(); }
 int GetSize(RTLIL::Wire *wire);
diff --git a/__init__.py b/misc/__init__.py
similarity index 100%
rename from __init__.py
rename to misc/__init__.py
diff --git a/py_wrap_generator.py b/misc/py_wrap_generator.py
similarity index 100%
rename from py_wrap_generator.py
rename to misc/py_wrap_generator.py
diff --git a/passes/cmds/qwp.cc b/passes/cmds/qwp.cc
index 1c64a7b77..adbe89e31 100644
--- a/passes/cmds/qwp.cc
+++ b/passes/cmds/qwp.cc
@@ -291,7 +291,7 @@ struct QwpWorker
 		// gaussian elimination
 		for (int i = 0; i < N; i++)
 		{
-			if (config.verbose && ((i+1) % (N/15)) == 0)
+			if (config.verbose && N > 15 && ((i+1) % (N/15)) == 0)
 				log("> Solved %d%%: %d/%d\n", (100*(i+1))/N, i+1, N);
 
 			// find best row
diff --git a/passes/equiv/equiv_opt.cc b/passes/equiv/equiv_opt.cc
index e5dda9c24..3596dfd7b 100644
--- a/passes/equiv/equiv_opt.cc
+++ b/passes/equiv/equiv_opt.cc
@@ -44,7 +44,10 @@ struct EquivOptPass:public ScriptPass
 		log("        useful for handling architecture-specific primitives.\n");
 		log("\n");
 		log("    -assert\n");
-		log("        produce an error if the circuits are not equivalent\n");
+		log("        produce an error if the circuits are not equivalent.\n");
+		log("\n");
+		log("    -undef\n");
+		log("        enable modelling of undef states during equiv_induct.\n");
 		log("\n");
 		log("The following commands are executed by this verification command:\n");
 		help_script();
@@ -52,13 +55,14 @@ struct EquivOptPass:public ScriptPass
 	}
 
 	std::string command, techmap_opts;
-	bool assert;
+	bool assert, undef;
 
 	void clear_flags() YS_OVERRIDE
 	{
 		command = "";
 		techmap_opts = "";
 		assert = false;
+		undef = false;
 	}
 
 	void execute(std::vector < std::string > args, RTLIL::Design * design) YS_OVERRIDE
@@ -84,6 +88,10 @@ struct EquivOptPass:public ScriptPass
 				assert = true;
 				continue;
 			}
+			if (args[argidx] == "-undef") {
+				undef = true;
+				continue;
+			}
 			break;
 		}
 
@@ -139,7 +147,12 @@ struct EquivOptPass:public ScriptPass
 
 		if (check_label("prove")) {
 			run("equiv_make gold gate equiv");
-			run("equiv_induct equiv");
+			if (help_mode)
+				run("equiv_induct [-undef] equiv");
+			else if (undef)
+				run("equiv_induct -undef equiv");
+			else
+				run("equiv_induct equiv");
 			if (help_mode)
 				run("equiv_status [-assert] equiv");
 			else if (assert)
diff --git a/passes/opt/opt_clean.cc b/passes/opt/opt_clean.cc
index 3e131d2af..2f7995071 100644
--- a/passes/opt/opt_clean.cc
+++ b/passes/opt/opt_clean.cc
@@ -274,37 +274,53 @@ void rmunused_module_signals(RTLIL::Module *module, bool purge_mode, bool verbos
 	std::vector<RTLIL::Wire*> maybe_del_wires;
 	for (auto wire : module->wires())
 	{
+		SigSpec s1 = SigSpec(wire), s2 = assign_map(s1);
+		log_assert(GetSize(s1) == GetSize(s2));
+
+		bool maybe_del = false;
 		if ((!purge_mode && check_public_name(wire->name)) || wire->port_id != 0 || wire->get_bool_attribute("\\keep") || wire->attributes.count("\\init")) {
-			RTLIL::SigSpec s1 = RTLIL::SigSpec(wire), s2 = s1;
-			assign_map.apply(s2);
-			if (!used_signals.check_any(s2) && wire->port_id == 0 && !wire->get_bool_attribute("\\keep")) {
-				maybe_del_wires.push_back(wire);
-			} else {
-				log_assert(GetSize(s1) == GetSize(s2));
-				RTLIL::SigSig new_conn;
-				for (int i = 0; i < GetSize(s1); i++)
-					if (s1[i] != s2[i]) {
-						new_conn.first.append_bit(s1[i]);
-						new_conn.second.append_bit(s2[i]);
-					}
-				if (new_conn.first.size() > 0) {
-					used_signals.add(new_conn.first);
-					used_signals.add(new_conn.second);
-					module->connect(new_conn);
-				}
-			}
+			if (!used_signals.check_any(s2) && wire->port_id == 0 && !wire->get_bool_attribute("\\keep"))
+				maybe_del = true;
 		} else {
-			if (!used_signals.check_any(RTLIL::SigSpec(wire)))
-				maybe_del_wires.push_back(wire);
+			if (!used_signals.check_any(s2))
+				maybe_del = true;
 		}
 
-		RTLIL::SigSpec sig = assign_map(RTLIL::SigSpec(wire));
-		if (!used_signals_nodrivers.check_any(sig)) {
+		if (maybe_del) {
+			maybe_del_wires.push_back(wire);
+		} else {
+			Const initval;
+			if (wire->attributes.count("\\init"))
+				initval = wire->attributes.at("\\init");
+			if (GetSize(initval) != GetSize(wire))
+				initval.bits.resize(GetSize(wire), State::Sx);
+			RTLIL::SigSig new_conn;
+			for (int i = 0; i < GetSize(s1); i++)
+				if (s1[i] != s2[i]) {
+					if (s2[i] == State::Sx && (initval[i] == State::S0 || initval[i] == State::S1)) {
+						s2[i] = initval[i];
+						initval[i] = State::Sx;
+					}
+					new_conn.first.append_bit(s1[i]);
+					new_conn.second.append_bit(s2[i]);
+				}
+			if (new_conn.first.size() > 0) {
+				if (initval.is_fully_undef())
+					wire->attributes.erase("\\init");
+				else
+					wire->attributes.at("\\init") = initval;
+				used_signals.add(new_conn.first);
+				used_signals.add(new_conn.second);
+				module->connect(new_conn);
+			}
+		}
+
+		if (!used_signals_nodrivers.check_all(s2)) {
 			std::string unused_bits;
-			for (int i = 0; i < GetSize(sig); i++) {
-				if (sig[i].wire == NULL)
+			for (int i = 0; i < GetSize(s2); i++) {
+				if (s2[i].wire == NULL)
 					continue;
-				if (!used_signals_nodrivers.check(sig[i])) {
+				if (!used_signals_nodrivers.check(s2[i])) {
 					if (!unused_bits.empty())
 						unused_bits += " ";
 					unused_bits += stringf("%d", i);
@@ -323,14 +339,40 @@ void rmunused_module_signals(RTLIL::Module *module, bool purge_mode, bool verbos
 	pool<RTLIL::Wire*> del_wires;
 
 	int del_wires_count = 0;
-	for (auto wire : maybe_del_wires)
-		if (!used_signals.check_any(RTLIL::SigSpec(wire))) {
-			if (check_public_name(wire->name) && verbose) {
+	for (auto wire : maybe_del_wires) {
+		SigSpec s1 = SigSpec(wire);
+		if (used_signals.check_any(s1)) {
+			SigSpec s2 = assign_map(s1);
+			Const initval;
+			if (wire->attributes.count("\\init"))
+				initval = wire->attributes.at("\\init");
+			if (GetSize(initval) != GetSize(wire))
+				initval.bits.resize(GetSize(wire), State::Sx);
+			RTLIL::SigSig new_conn;
+			for (int i = 0; i < GetSize(s1); i++)
+				if (s1[i] != s2[i]) {
+					if (s2[i] == State::Sx && (initval[i] == State::S0 || initval[i] == State::S1)) {
+						s2[i] = initval[i];
+						initval[i] = State::Sx;
+					}
+					new_conn.first.append_bit(s1[i]);
+					new_conn.second.append_bit(s2[i]);
+				}
+			if (new_conn.first.size() > 0) {
+				if (initval.is_fully_undef())
+					wire->attributes.erase("\\init");
+				else
+					wire->attributes.at("\\init") = initval;
+				module->connect(new_conn);
+			}
+		} else {
+			if (ys_debug() || (check_public_name(wire->name) && verbose)) {
 				log_debug("  removing unused non-port wire %s.\n", wire->name.c_str());
 			}
 			del_wires.insert(wire);
 			del_wires_count++;
 		}
+	}
 
 	module->remove(del_wires);
 	count_rm_wires += del_wires.size();
@@ -483,6 +525,9 @@ struct OptCleanPass : public Pass {
 
 		ct_all.setup(design);
 
+		count_rm_cells = 0;
+		count_rm_wires = 0;
+
 		for (auto module : design->selected_whole_modules_warn()) {
 			if (module->has_processes_warn())
 				continue;
@@ -548,7 +593,7 @@ struct CleanPass : public Pass {
 		for (auto module : design->selected_whole_modules()) {
 			if (module->has_processes())
 				continue;
-			rmunused_module(module, purge_mode, false, false);
+			rmunused_module(module, purge_mode, ys_debug(), false);
 		}
 
 		log_suppressed();
diff --git a/passes/opt/opt_expr.cc b/passes/opt/opt_expr.cc
index af6d352af..512ef0cbf 100644
--- a/passes/opt/opt_expr.cc
+++ b/passes/opt/opt_expr.cc
@@ -39,6 +39,9 @@ void replace_undriven(RTLIL::Design *design, RTLIL::Module *module)
 	SigPool used_signals;
 	SigPool all_signals;
 
+	dict<SigBit, pair<Wire*, State>> initbits;
+	pool<Wire*> revisit_initwires;
+
 	for (auto cell : module->cells())
 	for (auto &conn : cell->connections()) {
 		if (!ct.cell_known(cell->type) || ct.cell_output(cell->type, conn.first))
@@ -48,9 +51,17 @@ void replace_undriven(RTLIL::Design *design, RTLIL::Module *module)
 	}
 
 	for (auto wire : module->wires()) {
+		if (wire->attributes.count("\\init")) {
+			SigSpec sig = sigmap(wire);
+			Const initval = wire->attributes.at("\\init");
+			for (int i = 0; i < GetSize(initval) && i < GetSize(wire); i++) {
+				if (initval[i] == State::S0 || initval[i] == State::S1)
+					initbits[sig[i]] = make_pair(wire, initval[i]);
+			}
+		}
 		if (wire->port_input)
 			driven_signals.add(sigmap(wire));
-		if (wire->port_output)
+		if (wire->port_output || wire->get_bool_attribute("\\keep"))
 			used_signals.add(sigmap(wire));
 		all_signals.add(sigmap(wire));
 	}
@@ -67,10 +78,43 @@ void replace_undriven(RTLIL::Design *design, RTLIL::Module *module)
 		if (sig.size() == 0)
 			continue;
 
-		log_debug("Setting undriven signal in %s to undef: %s\n", RTLIL::id2cstr(module->name), log_signal(c));
-		module->connect(RTLIL::SigSig(c, RTLIL::SigSpec(RTLIL::State::Sx, c.width)));
+		Const val(RTLIL::State::Sx, GetSize(sig));
+		for (int i = 0; i < GetSize(sig); i++) {
+			SigBit bit = sigmap(sig[i]);
+			auto cursor = initbits.find(bit);
+			if (cursor != initbits.end()) {
+				revisit_initwires.insert(cursor->second.first);
+				val[i] = cursor->second.second;
+			}
+		}
+
+		log_debug("Setting undriven signal in %s to constant: %s = %s\n", log_id(module), log_signal(sig), log_signal(val));
+		module->connect(sig, val);
 		did_something = true;
 	}
+
+	if (!revisit_initwires.empty())
+	{
+		SigMap sm2(module);
+
+		for (auto wire : revisit_initwires) {
+			SigSpec sig = sm2(wire);
+			Const initval = wire->attributes.at("\\init");
+			for (int i = 0; i < GetSize(initval) && i < GetSize(wire); i++) {
+				if (SigBit(initval[i]) == sig[i])
+					initval[i] = State::Sx;
+			}
+			if (initval.is_fully_undef()) {
+				log_debug("Removing init attribute from %s/%s.\n", log_id(module), log_id(wire));
+				wire->attributes.erase("\\init");
+				did_something = true;
+			} else if (initval != wire->attributes.at("\\init")) {
+				log_debug("Updating init attribute on %s/%s: %s\n", log_id(module), log_id(wire), log_signal(initval));
+				wire->attributes["\\init"] = initval;
+				did_something = true;
+			}
+		}
+	}
 }
 
 void replace_cell(SigMap &assign_map, RTLIL::Module *module, RTLIL::Cell *cell, std::string info, std::string out_port, RTLIL::SigSpec out_val)
diff --git a/passes/opt/wreduce.cc b/passes/opt/wreduce.cc
index 68e077cf9..58c6e4b4b 100644
--- a/passes/opt/wreduce.cc
+++ b/passes/opt/wreduce.cc
@@ -180,6 +180,8 @@ struct WreduceWorker
 			}
 
 			auto info = mi.query(sig_q[i]);
+			if (info == nullptr)
+				return;
 			if (!info->is_output && GetSize(info->ports) == 1 && !keep_bits.count(mi.sigmap(sig_q[i]))) {
 				remove_init_bits.insert(sig_q[i]);
 				sig_d.remove(i);
@@ -529,6 +531,42 @@ struct WreducePass : public Pass {
 						module->connect(sig, Const(0, GetSize(sig)));
 					}
 				}
+
+				if (c->type.in("$div", "$mod", "$pow"))
+				{
+					SigSpec A = c->getPort("\\A");
+					int original_a_width = GetSize(A);
+					if (c->getParam("\\A_SIGNED").as_bool()) {
+						while (GetSize(A) > 1 && A[GetSize(A)-1] == State::S0 && A[GetSize(A)-2] == State::S0)
+							A.remove(GetSize(A)-1, 1);
+					} else {
+						while (GetSize(A) > 0 && A[GetSize(A)-1] == State::S0)
+							A.remove(GetSize(A)-1, 1);
+					}
+					if (original_a_width != GetSize(A)) {
+						log("Removed top %d bits (of %d) from port A of cell %s.%s (%s).\n",
+								original_a_width-GetSize(A), original_a_width, log_id(module), log_id(c), log_id(c->type));
+						c->setPort("\\A", A);
+						c->setParam("\\A_WIDTH", GetSize(A));
+					}
+
+					SigSpec B = c->getPort("\\B");
+					int original_b_width = GetSize(B);
+					if (c->getParam("\\B_SIGNED").as_bool()) {
+						while (GetSize(B) > 1 && B[GetSize(B)-1] == State::S0 && B[GetSize(B)-2] == State::S0)
+							B.remove(GetSize(B)-1, 1);
+					} else {
+						while (GetSize(B) > 0 && B[GetSize(B)-1] == State::S0)
+							B.remove(GetSize(B)-1, 1);
+					}
+					if (original_b_width != GetSize(B)) {
+						log("Removed top %d bits (of %d) from port B of cell %s.%s (%s).\n",
+								original_b_width-GetSize(B), original_b_width, log_id(module), log_id(c), log_id(c->type));
+						c->setPort("\\B", B);
+						c->setParam("\\B_WIDTH", GetSize(B));
+					}
+				}
+
 				if (!opt_memx && c->type.in("$memrd", "$memwr", "$meminit")) {
 					IdString memid = c->getParam("\\MEMID").decode_string();
 					RTLIL::Memory *mem = module->memories.at(memid);
diff --git a/passes/pmgen/.gitignore b/passes/pmgen/.gitignore
index c9263057e..0ad36ea2c 100644
--- a/passes/pmgen/.gitignore
+++ b/passes/pmgen/.gitignore
@@ -1 +1,2 @@
 /ice40_dsp_pm.h
+/peepopt_pm.h
diff --git a/passes/pmgen/Makefile.inc b/passes/pmgen/Makefile.inc
index e0609d9ba..7911132db 100644
--- a/passes/pmgen/Makefile.inc
+++ b/passes/pmgen/Makefile.inc
@@ -1,8 +1,23 @@
 OBJS += passes/pmgen/ice40_dsp.o
+OBJS += passes/pmgen/peepopt.o
+
+# --------------------------------------
 
 passes/pmgen/ice40_dsp.o: passes/pmgen/ice40_dsp_pm.h
 EXTRA_OBJS += passes/pmgen/ice40_dsp_pm.h
 .SECONDARY: passes/pmgen/ice40_dsp_pm.h
 
 passes/pmgen/ice40_dsp_pm.h: passes/pmgen/pmgen.py passes/pmgen/ice40_dsp.pmg
-	$(P) mkdir -p passes/pmgen && python3 $^ $@
+	$(P) mkdir -p passes/pmgen && python3 $< -o $@ -p ice40_dsp $(filter-out $<,$^)
+
+# --------------------------------------
+
+passes/pmgen/peepopt.o: passes/pmgen/peepopt_pm.h
+EXTRA_OBJS += passes/pmgen/peepopt_pm.h
+.SECONDARY: passes/pmgen/peepopt_pm.h
+
+PEEPOPT_PATTERN  = passes/pmgen/peepopt_shiftmul.pmg
+PEEPOPT_PATTERN += passes/pmgen/peepopt_muldiv.pmg
+
+passes/pmgen/peepopt_pm.h: passes/pmgen/pmgen.py $(PEEPOPT_PATTERN)
+	$(P) mkdir -p passes/pmgen && python3 $< -o $@ -p peepopt $(filter-out $<,$^)
diff --git a/passes/pmgen/README.md b/passes/pmgen/README.md
index 7a46558b1..2f0b1fd5a 100644
--- a/passes/pmgen/README.md
+++ b/passes/pmgen/README.md
@@ -29,19 +29,25 @@ up in any future matches:
 
     pm.blacklist(some_cell);
 
-The `.run(callback_function)` method searches for all matches and calls the
-callback function for each found match:
+The `.run_<pattern_name>(callback_function)` method searches for all matches
+for the pattern`<pattern_name>` and calls the callback function for each found
+match:
 
-    pm.run([&](){
+    pm.run_foobar([&](){
         log("found matching 'foo' cell: %s\n", log_id(pm.st.foo));
         log("          with 'bar' cell: %s\n", log_id(pm.st.bar));
     });
 
 The `.pmg` file declares matcher state variables that are accessible via the
-`.st.<state_name>` members. (The `.st` member is of type `foobar_pm::state_t`.)
+`.st_<pattern_name>.<state_name>` members. (The `.st_<pattern_name>` member is
+of type `foobar_pm::state_<pattern_name>_t`.)
 
 Similarly the `.pmg` file declares user data variables that become members of
-`.ud`, a struct of type `foobar_pm::udata_t`.
+`.ud_<pattern_name>`, a struct of type `foobar_pm::udata_<pattern_name>_t`.
+
+There are four versions of the `run_<pattern_name>()` method: Without callback,
+callback without arguments, callback with reference to `pm`, and callback with
+reference to `pm.st_<pattern_name>`.
 
 
 The .pmg File Format
@@ -52,6 +58,12 @@ lines consist of whitespace-separated tokens.
 
 Lines in `.pmg` files starting with `//` are comments.
 
+Declaring a pattern
+-------------------
+
+A `.pmg` file contains one or more patterns. Each pattern starts with a line
+with the `pattern` keyword followed by the name of the pattern.
+
 Declaring state variables
 -------------------------
 
@@ -66,7 +78,7 @@ State variables are automatically managed by the generated backtracking algorith
 and saved and restored as needed.
 
 They are automatically initialized to the default constructed value of their type
-when `.run(callback_function)` is called.
+when `.run_<pattern_name>(callback_function)` is called.
 
 Declaring udata variables
 -------------------------
@@ -220,5 +232,5 @@ But in some cases it is more natural to utilize the implicit branch statement:
         portAB = \B;
     endcode
 
-There is an implicit `code..endcode` block at the end of each `.pgm` file
+There is an implicit `code..endcode` block at the end of each `.pmg` file
 that just accepts everything that gets all the way there.
diff --git a/passes/pmgen/ice40_dsp.cc b/passes/pmgen/ice40_dsp.cc
index 3a054a463..39d033a04 100644
--- a/passes/pmgen/ice40_dsp.cc
+++ b/passes/pmgen/ice40_dsp.cc
@@ -19,47 +19,50 @@
 
 #include "kernel/yosys.h"
 #include "kernel/sigtools.h"
-#include "passes/pmgen/ice40_dsp_pm.h"
 
 USING_YOSYS_NAMESPACE
 PRIVATE_NAMESPACE_BEGIN
 
+#include "passes/pmgen/ice40_dsp_pm.h"
+
 void create_ice40_dsp(ice40_dsp_pm &pm)
 {
+	auto &st = pm.st_ice40_dsp;
+
 #if 0
 	log("\n");
-	log("ffA:   %s\n", log_id(pm.st.ffA, "--"));
-	log("ffB:   %s\n", log_id(pm.st.ffB, "--"));
-	log("mul:   %s\n", log_id(pm.st.mul, "--"));
-	log("ffY:   %s\n", log_id(pm.st.ffY, "--"));
-	log("addAB: %s\n", log_id(pm.st.addAB, "--"));
-	log("muxAB: %s\n", log_id(pm.st.muxAB, "--"));
-	log("ffS:   %s\n", log_id(pm.st.ffS, "--"));
+	log("ffA:   %s\n", log_id(st.ffA, "--"));
+	log("ffB:   %s\n", log_id(st.ffB, "--"));
+	log("mul:   %s\n", log_id(st.mul, "--"));
+	log("ffY:   %s\n", log_id(st.ffY, "--"));
+	log("addAB: %s\n", log_id(st.addAB, "--"));
+	log("muxAB: %s\n", log_id(st.muxAB, "--"));
+	log("ffS:   %s\n", log_id(st.ffS, "--"));
 #endif
 
-	log("Checking %s.%s for iCE40 DSP inference.\n", log_id(pm.module), log_id(pm.st.mul));
+	log("Checking %s.%s for iCE40 DSP inference.\n", log_id(pm.module), log_id(st.mul));
 
-	if (GetSize(pm.st.sigA) > 16) {
-		log("  input A (%s) is too large (%d > 16).\n", log_signal(pm.st.sigA), GetSize(pm.st.sigA));
+	if (GetSize(st.sigA) > 16) {
+		log("  input A (%s) is too large (%d > 16).\n", log_signal(st.sigA), GetSize(st.sigA));
 		return;
 	}
 
-	if (GetSize(pm.st.sigB) > 16) {
-		log("  input B (%s) is too large (%d > 16).\n", log_signal(pm.st.sigB), GetSize(pm.st.sigB));
+	if (GetSize(st.sigB) > 16) {
+		log("  input B (%s) is too large (%d > 16).\n", log_signal(st.sigB), GetSize(st.sigB));
 		return;
 	}
 
-	if (GetSize(pm.st.sigS) > 32) {
-		log("  accumulator (%s) is too large (%d > 32).\n", log_signal(pm.st.sigS), GetSize(pm.st.sigS));
+	if (GetSize(st.sigS) > 32) {
+		log("  accumulator (%s) is too large (%d > 32).\n", log_signal(st.sigS), GetSize(st.sigS));
 		return;
 	}
 
-	if (GetSize(pm.st.sigY) > 32) {
-		log("  output (%s) is too large (%d > 32).\n", log_signal(pm.st.sigY), GetSize(pm.st.sigY));
+	if (GetSize(st.sigY) > 32) {
+		log("  output (%s) is too large (%d > 32).\n", log_signal(st.sigY), GetSize(st.sigY));
 		return;
 	}
 
-	bool mul_signed = pm.st.mul->getParam("\\A_SIGNED").as_bool();
+	bool mul_signed = st.mul->getParam("\\A_SIGNED").as_bool();
 
 	if (mul_signed) {
 		log("  inference of signed iCE40 DSP arithmetic is currently not supported.\n");
@@ -69,21 +72,21 @@ void create_ice40_dsp(ice40_dsp_pm &pm)
 	log("  replacing $mul with SB_MAC16 cell.\n");
 
 	Cell *cell = pm.module->addCell(NEW_ID, "\\SB_MAC16");
-	pm.module->swap_names(cell, pm.st.mul);
+	pm.module->swap_names(cell, st.mul);
 
 	// SB_MAC16 Input Interface
 
-	SigSpec A = pm.st.sigA;
+	SigSpec A = st.sigA;
 	A.extend_u0(16, mul_signed);
 
-	SigSpec B = pm.st.sigB;
+	SigSpec B = st.sigB;
 	B.extend_u0(16, mul_signed);
 
 	SigSpec CD;
-	if (pm.st.muxA)
-		CD = pm.st.muxA->getPort("\\B");
-	if (pm.st.muxB)
-		CD = pm.st.muxB->getPort("\\A");
+	if (st.muxA)
+		CD = st.muxA->getPort("\\B");
+	if (st.muxB)
+		CD = st.muxB->getPort("\\A");
 	CD.extend_u0(32, mul_signed);
 
 	cell->setPort("\\A", A);
@@ -91,8 +94,8 @@ void create_ice40_dsp(ice40_dsp_pm &pm)
 	cell->setPort("\\C", CD.extract(0, 16));
 	cell->setPort("\\D", CD.extract(16, 16));
 
-	cell->setParam("\\A_REG", pm.st.ffA ? State::S1 : State::S0);
-	cell->setParam("\\B_REG", pm.st.ffB ? State::S1 : State::S0);
+	cell->setParam("\\A_REG", st.ffA ? State::S1 : State::S0);
+	cell->setParam("\\B_REG", st.ffB ? State::S1 : State::S0);
 
 	cell->setPort("\\AHOLD", State::S0);
 	cell->setPort("\\BHOLD", State::S0);
@@ -102,25 +105,25 @@ void create_ice40_dsp(ice40_dsp_pm &pm)
 	cell->setPort("\\IRSTTOP", State::S0);
 	cell->setPort("\\IRSTBOT", State::S0);
 
-	if (pm.st.clock_vld)
+	if (st.clock_vld)
 	{
-		cell->setPort("\\CLK", pm.st.clock);
+		cell->setPort("\\CLK", st.clock);
 		cell->setPort("\\CE", State::S1);
-		cell->setParam("\\NEG_TRIGGER", pm.st.clock_pol ? State::S0 : State::S1);
+		cell->setParam("\\NEG_TRIGGER", st.clock_pol ? State::S0 : State::S1);
 
-		log("  clock: %s (%s)", log_signal(pm.st.clock), pm.st.clock_pol ? "posedge" : "negedge");
+		log("  clock: %s (%s)", log_signal(st.clock), st.clock_pol ? "posedge" : "negedge");
 
-		if (pm.st.ffA)
-			log(" ffA:%s", log_id(pm.st.ffA));
+		if (st.ffA)
+			log(" ffA:%s", log_id(st.ffA));
 
-		if (pm.st.ffB)
-			log(" ffB:%s", log_id(pm.st.ffB));
+		if (st.ffB)
+			log(" ffB:%s", log_id(st.ffB));
 
-		if (pm.st.ffY)
-			log(" ffY:%s", log_id(pm.st.ffY));
+		if (st.ffY)
+			log(" ffY:%s", log_id(st.ffY));
 
-		if (pm.st.ffS)
-			log(" ffS:%s", log_id(pm.st.ffS));
+		if (st.ffS)
+			log(" ffS:%s", log_id(st.ffS));
 
 		log("\n");
 	}
@@ -144,16 +147,16 @@ void create_ice40_dsp(ice40_dsp_pm &pm)
 
 	// SB_MAC16 Output Interface
 
-	SigSpec O = pm.st.ffS ? pm.st.sigS : pm.st.sigY;
+	SigSpec O = st.ffS ? st.sigS : st.sigY;
 	if (GetSize(O) < 32)
 		O.append(pm.module->addWire(NEW_ID, 32-GetSize(O)));
 
 	cell->setPort("\\O", O);
 
-	if (pm.st.addAB) {
-		log("  accumulator %s (%s)\n", log_id(pm.st.addAB), log_id(pm.st.addAB->type));
-		cell->setPort("\\ADDSUBTOP", pm.st.addAB->type == "$add" ? State::S0 : State::S1);
-		cell->setPort("\\ADDSUBBOT", pm.st.addAB->type == "$add" ? State::S0 : State::S1);
+	if (st.addAB) {
+		log("  accumulator %s (%s)\n", log_id(st.addAB), log_id(st.addAB->type));
+		cell->setPort("\\ADDSUBTOP", st.addAB->type == "$add" ? State::S0 : State::S1);
+		cell->setPort("\\ADDSUBBOT", st.addAB->type == "$add" ? State::S0 : State::S1);
 	} else {
 		cell->setPort("\\ADDSUBTOP", State::S0);
 		cell->setPort("\\ADDSUBBOT", State::S0);
@@ -166,10 +169,10 @@ void create_ice40_dsp(ice40_dsp_pm &pm)
 	cell->setPort("\\OHOLDBOT", State::S0);
 
 	SigSpec acc_reset = State::S0;
-	if (pm.st.muxA)
-		acc_reset = pm.st.muxA->getPort("\\S");
-	if (pm.st.muxB)
-		acc_reset = pm.module->Not(NEW_ID, pm.st.muxB->getPort("\\S"));
+	if (st.muxA)
+		acc_reset = st.muxA->getPort("\\S");
+	if (st.muxB)
+		acc_reset = pm.module->Not(NEW_ID, st.muxB->getPort("\\S"));
 
 	cell->setPort("\\OLOADTOP", acc_reset);
 	cell->setPort("\\OLOADBOT", acc_reset);
@@ -179,17 +182,17 @@ void create_ice40_dsp(ice40_dsp_pm &pm)
 	cell->setParam("\\C_REG", State::S0);
 	cell->setParam("\\D_REG", State::S0);
 
-	cell->setParam("\\TOP_8x8_MULT_REG", pm.st.ffY ? State::S1 : State::S0);
-	cell->setParam("\\BOT_8x8_MULT_REG", pm.st.ffY ? State::S1 : State::S0);
-	cell->setParam("\\PIPELINE_16x16_MULT_REG1", pm.st.ffY ? State::S1 : State::S0);
+	cell->setParam("\\TOP_8x8_MULT_REG", st.ffY ? State::S1 : State::S0);
+	cell->setParam("\\BOT_8x8_MULT_REG", st.ffY ? State::S1 : State::S0);
+	cell->setParam("\\PIPELINE_16x16_MULT_REG1", st.ffY ? State::S1 : State::S0);
 	cell->setParam("\\PIPELINE_16x16_MULT_REG2", State::S0);
 
-	cell->setParam("\\TOPOUTPUT_SELECT", Const(pm.st.ffS ? 1 : 3, 2));
+	cell->setParam("\\TOPOUTPUT_SELECT", Const(st.ffS ? 1 : 3, 2));
 	cell->setParam("\\TOPADDSUB_LOWERINPUT", Const(2, 2));
 	cell->setParam("\\TOPADDSUB_UPPERINPUT", State::S0);
 	cell->setParam("\\TOPADDSUB_CARRYSELECT", Const(3, 2));
 
-	cell->setParam("\\BOTOUTPUT_SELECT", Const(pm.st.ffS ? 1 : 3, 2));
+	cell->setParam("\\BOTOUTPUT_SELECT", Const(st.ffS ? 1 : 3, 2));
 	cell->setParam("\\BOTADDSUB_LOWERINPUT", Const(2, 2));
 	cell->setParam("\\BOTADDSUB_UPPERINPUT", State::S0);
 	cell->setParam("\\BOTADDSUB_CARRYSELECT", Const(0, 2));
@@ -198,9 +201,9 @@ void create_ice40_dsp(ice40_dsp_pm &pm)
 	cell->setParam("\\A_SIGNED", mul_signed ? State::S1 : State::S0);
 	cell->setParam("\\B_SIGNED", mul_signed ? State::S1 : State::S0);
 
-	pm.autoremove(pm.st.mul);
-	pm.autoremove(pm.st.ffY);
-	pm.autoremove(pm.st.ffS);
+	pm.autoremove(st.mul);
+	pm.autoremove(st.ffY);
+	pm.autoremove(st.ffS);
 }
 
 struct Ice40DspPass : public Pass {
@@ -230,7 +233,7 @@ struct Ice40DspPass : public Pass {
 		extra_args(args, argidx, design);
 
 		for (auto module : design->selected_modules())
-			ice40_dsp_pm(module, module->selected_cells()).run(create_ice40_dsp);
+			ice40_dsp_pm(module, module->selected_cells()).run_ice40_dsp(create_ice40_dsp);
 	}
 } Ice40DspPass;
 
diff --git a/passes/pmgen/ice40_dsp.pmg b/passes/pmgen/ice40_dsp.pmg
index 96c62e313..1f3590d4e 100644
--- a/passes/pmgen/ice40_dsp.pmg
+++ b/passes/pmgen/ice40_dsp.pmg
@@ -1,3 +1,5 @@
+pattern ice40_dsp
+
 state <SigBit> clock
 state <bool> clock_pol clock_vld
 state <SigSpec> sigA sigB sigY sigS
diff --git a/passes/pmgen/peepopt.cc b/passes/pmgen/peepopt.cc
new file mode 100644
index 000000000..e7f95cf85
--- /dev/null
+++ b/passes/pmgen/peepopt.cc
@@ -0,0 +1,68 @@
+/*
+ *  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/yosys.h"
+#include "kernel/sigtools.h"
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+bool did_something;
+
+#include "passes/pmgen/peepopt_pm.h"
+
+struct PeepoptPass : public Pass {
+	PeepoptPass() : Pass("peepopt", "collection of peephole optimizers") { }
+	void help() YS_OVERRIDE
+	{
+		//   |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+		log("\n");
+		log("    peepopt [options] [selection]\n");
+		log("\n");
+		log("This pass applies a collection of peephole optimizers to the current design.\n");
+		log("\n");
+	}
+	void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
+	{
+		log_header(design, "Executing PEEPOPT pass (run peephole optimizers).\n");
+
+		size_t argidx;
+		for (argidx = 1; argidx < args.size(); argidx++)
+		{
+			// if (args[argidx] == "-singleton") {
+			// 	singleton_mode = true;
+			// 	continue;
+			// }
+			break;
+		}
+		extra_args(args, argidx, design);
+
+		for (auto module : design->selected_modules()) {
+			did_something = true;
+			while (did_something) {
+				did_something = false;
+				peepopt_pm pm(module, module->selected_cells());
+				pm.run_shiftmul();
+				pm.run_muldiv();
+			}
+		}
+	}
+} PeepoptPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/pmgen/peepopt_muldiv.pmg b/passes/pmgen/peepopt_muldiv.pmg
new file mode 100644
index 000000000..06c275834
--- /dev/null
+++ b/passes/pmgen/peepopt_muldiv.pmg
@@ -0,0 +1,36 @@
+pattern muldiv
+
+state <SigSpec> t x y
+
+match mul
+	select mul->type == $mul
+	select GetSize(port(mul, \A)) + GetSize(port(mul, \B)) <= GetSize(port(mul, \Y))
+endmatch
+
+code t x y
+	t = port(mul, \Y);
+	x = port(mul, \A);
+	y = port(mul, \B);
+	branch;
+	std::swap(x, y);
+endcode
+
+match div
+	select div->type.in($div)
+	index <SigSpec> port(div, \A) === t
+	index <SigSpec> port(div, \B) === x
+endmatch
+
+code
+	SigSpec div_y = port(div, \Y);
+	SigSpec val_y = y;
+
+	if (GetSize(div_y) != GetSize(val_y))
+		val_y.extend_u0(GetSize(div_y), param(div, \A_SIGNED).as_bool());
+
+	did_something = true;
+	log("muldiv pattern in %s: mul=%s, div=%s\n", log_id(module), log_id(mul), log_id(div));
+	module->connect(div_y, val_y);
+	autoremove(div);
+	reject;
+endcode
diff --git a/passes/pmgen/peepopt_shiftmul.pmg b/passes/pmgen/peepopt_shiftmul.pmg
new file mode 100644
index 000000000..1f9b3c2b9
--- /dev/null
+++ b/passes/pmgen/peepopt_shiftmul.pmg
@@ -0,0 +1,83 @@
+pattern shiftmul
+
+state <SigSpec> shamt
+
+match shift
+	select shift->type.in($shift, $shiftx, $shr)
+endmatch
+
+code shamt
+	shamt = port(shift, \B);
+	if (shamt[GetSize(shamt)-1] == State::S0) {
+		do {
+			shamt.remove(GetSize(shamt)-1);
+		} while (shamt[GetSize(shamt)-1] == State::S0);
+	} else
+	if (shift->type.in($shift, $shiftx) && param(shift, \B_SIGNED).as_bool()) {
+		reject;
+	}
+	if (GetSize(shamt) > 20)
+		reject;
+endcode
+
+match mul
+	select mul->type.in($mul)
+	select port(mul, \A).is_fully_const() || port(mul, \B).is_fully_const()
+	index <SigSpec> port(mul, \Y) === shamt
+endmatch
+
+code
+	IdString const_factor_port = port(mul, \A).is_fully_const() ? \A : \B;
+	IdString const_factor_signed = const_factor_port == \A ? \A_SIGNED : \B_SIGNED;
+	Const const_factor_cnst = port(mul, const_factor_port).as_const();
+	int const_factor = const_factor_cnst.as_int();
+
+	if (GetSize(const_factor_cnst) == 0)
+		reject;
+
+	if (const_factor_cnst.bits[GetSize(const_factor_cnst)-1] != State::S0 &&
+			param(mul, const_factor_signed).as_bool())
+		reject;
+
+	if (GetSize(const_factor_cnst) > 20)
+		reject;
+
+	if (GetSize(port(shift, \Y)) > const_factor)
+		reject;
+
+	did_something = true;
+	log("shiftmul pattern in %s: shift=%s, mul=%s\n", log_id(module), log_id(shift), log_id(mul));
+
+	int new_const_factor_log2 = ceil_log2(const_factor);
+	int new_const_factor = 1 << new_const_factor_log2;
+
+	SigSpec padding(State::Sx, new_const_factor-const_factor);
+	SigSpec old_a = port(shift, \A), new_a;
+	int trunc = 0;
+
+	if (GetSize(old_a) % const_factor != 0) {
+		trunc = const_factor - GetSize(old_a) % const_factor;
+		old_a.append(SigSpec(State::Sx, trunc));
+	}
+
+	for (int i = 0; i*const_factor < GetSize(old_a); i++) {
+		SigSpec slice = old_a.extract(i*const_factor, const_factor);
+		new_a.append(slice);
+		new_a.append(padding);
+	}
+
+	if (trunc > 0)
+		new_a.remove(GetSize(new_a)-trunc, trunc);
+
+	SigSpec new_b = {port(mul, const_factor_port == \A ? \B : \A), SigSpec(State::S0, new_const_factor_log2)};
+	if (param(shift, \B_SIGNED).as_bool())
+		new_b.append(State::S0);
+
+	shift->setPort(\A, new_a);
+	shift->setParam(\A_WIDTH, GetSize(new_a));
+	shift->setPort(\B, new_b);
+	shift->setParam(\B_WIDTH, GetSize(new_b));
+
+	blacklist(shift);
+	reject;
+endcode
diff --git a/passes/pmgen/pmgen.py b/passes/pmgen/pmgen.py
index d9747b065..81052afce 100644
--- a/passes/pmgen/pmgen.py
+++ b/passes/pmgen/pmgen.py
@@ -3,15 +3,42 @@
 import re
 import sys
 import pprint
+import getopt
 
 pp = pprint.PrettyPrinter(indent=4)
 
-pmgfile = sys.argv[1]
-assert pmgfile.endswith(".pmg")
-prefix = pmgfile[0:-4]
-prefix = prefix.split('/')[-1]
-outfile = sys.argv[2]
+prefix = None
+pmgfiles = list()
+outfile = None
+debug = False
+genhdr = False
 
+opts, args = getopt.getopt(sys.argv[1:], "p:o:dg")
+
+for o, a in opts:
+    if o == "-p":
+        prefix = a
+    elif o == "-o":
+        outfile = a
+    elif o == "-d":
+        debug = True
+    elif o == "-g":
+        genhdr = True
+
+if outfile is None:
+    outfile = "/dev/stdout"
+
+for a in args:
+    assert a.endswith(".pmg")
+    if prefix is None and len(args) == 1:
+        prefix = a[0:-4]
+        prefix = prefix.split('/')[-1]
+    pmgfiles.append(a)
+
+assert prefix is not None
+
+current_pattern = None
+patterns = dict()
 state_types = dict()
 udata_types = dict()
 blocks = list()
@@ -77,7 +104,8 @@ def rewrite_cpp(s):
 
     return "".join(t)
 
-with open(pmgfile, "r") as f:
+def process_pmgfile(f):
+    global current_pattern
     while True:
         line = f.readline()
         if line == "": break
@@ -87,14 +115,31 @@ with open(pmgfile, "r") as f:
         if len(cmd) == 0 or cmd[0].startswith("//"): continue
         cmd = cmd[0]
 
+        if cmd == "pattern":
+            if current_pattern is not None:
+                block = dict()
+                block["type"] = "final"
+                block["pattern"] = current_pattern
+                blocks.append(block)
+            line = line.split()
+            assert len(line) == 2
+            assert line[1] not in patterns
+            current_pattern = line[1]
+            patterns[current_pattern] = len(blocks)
+            state_types[current_pattern] = dict()
+            udata_types[current_pattern] = dict()
+            continue
+
+        assert current_pattern is not None
+
         if cmd == "state":
             m = re.match(r"^state\s+<(.*?)>\s+(([A-Za-z_][A-Za-z_0-9]*\s+)*[A-Za-z_][A-Za-z_0-9]*)\s*$", line)
             assert m
             type_str = m.group(1)
             states_str = m.group(2)
             for s in re.split(r"\s+", states_str):
-                assert s not in state_types
-                state_types[s] = type_str
+                assert s not in state_types[current_pattern]
+                state_types[current_pattern][s] = type_str
             continue
 
         if cmd == "udata":
@@ -103,19 +148,20 @@ with open(pmgfile, "r") as f:
             type_str = m.group(1)
             udatas_str = m.group(2)
             for s in re.split(r"\s+", udatas_str):
-                assert s not in udata_types
-                udata_types[s] = type_str
+                assert s not in udata_types[current_pattern]
+                udata_types[current_pattern][s] = type_str
             continue
 
         if cmd == "match":
             block = dict()
             block["type"] = "match"
+            block["pattern"] = current_pattern
 
             line = line.split()
             assert len(line) == 2
-            assert line[1] not in state_types
+            assert line[1] not in state_types[current_pattern]
             block["cell"] = line[1]
-            state_types[line[1]] = "Cell*";
+            state_types[current_pattern][line[1]] = "Cell*";
 
             block["if"] = list()
             block["select"] = list()
@@ -158,15 +204,18 @@ with open(pmgfile, "r") as f:
                 assert False
 
             blocks.append(block)
+            continue
 
         if cmd == "code":
             block = dict()
             block["type"] = "code"
+            block["pattern"] = current_pattern
+
             block["code"] = list()
             block["states"] = set()
 
             for s in line.split()[1:]:
-                assert s in state_types
+                assert s in state_types[current_pattern]
                 block["states"].add(s)
 
             while True:
@@ -179,17 +228,36 @@ with open(pmgfile, "r") as f:
                 block["code"].append(rewrite_cpp(l.rstrip()))
 
             blocks.append(block)
+            continue
+
+        assert False
+
+for fn in pmgfiles:
+    with open(fn, "r") as f:
+        process_pmgfile(f)
+
+if current_pattern is not None:
+    block = dict()
+    block["type"] = "final"
+    block["pattern"] = current_pattern
+    blocks.append(block)
+
+current_pattern = None
+
+if debug:
+    pp.pprint(blocks)
 
 with open(outfile, "w") as f:
-    print("// Generated by pmgen.py from {}.pgm".format(prefix), file=f)
+    for fn in pmgfiles:
+        print("// Generated by pmgen.py from {}".format(fn), file=f)
     print("", file=f)
 
-    print("#include \"kernel/yosys.h\"", file=f)
-    print("#include \"kernel/sigtools.h\"", file=f)
-    print("", file=f)
-
-    print("YOSYS_NAMESPACE_BEGIN", file=f)
-    print("", file=f)
+    if genhdr:
+        print("#include \"kernel/yosys.h\"", file=f)
+        print("#include \"kernel/sigtools.h\"", file=f)
+        print("", file=f)
+        print("YOSYS_NAMESPACE_BEGIN", file=f)
+        print("", file=f)
 
     print("struct {}_pm {{".format(prefix), file=f)
     print("  Module *module;", file=f)
@@ -212,17 +280,19 @@ with open(outfile, "w") as f:
     print("  int rollback;", file=f)
     print("", file=f)
 
-    print("  struct state_t {", file=f)
-    for s, t in sorted(state_types.items()):
-        print("    {} {};".format(t, s), file=f)
-    print("  } st;", file=f)
-    print("", file=f)
+    for current_pattern in sorted(patterns.keys()):
+        print("  struct state_{}_t {{".format(current_pattern), file=f)
+        for s, t in sorted(state_types[current_pattern].items()):
+            print("    {} {};".format(t, s), file=f)
+        print("  }} st_{};".format(current_pattern), file=f)
+        print("", file=f)
 
-    print("  struct udata_t {", file=f)
-    for s, t in sorted(udata_types.items()):
-        print("    {} {};".format(t, s), file=f)
-    print("  } ud;", file=f)
-    print("", file=f)
+        print("  struct udata_{}_t {{".format(current_pattern), file=f)
+        for s, t in sorted(udata_types[current_pattern].items()):
+            print("    {} {};".format(t, s), file=f)
+        print("  }} ud_{};".format(current_pattern), file=f)
+        print("", file=f)
+    current_pattern = None
 
     for v, n in sorted(ids.items()):
         if n[0] == "\\":
@@ -258,20 +328,24 @@ with open(outfile, "w") as f:
     print("  }", file=f)
     print("", file=f)
 
-    print("  void check_blacklist() {", file=f)
-    print("    if (!blacklist_dirty)", file=f)
-    print("      return;", file=f)
-    print("    blacklist_dirty = false;", file=f)
-    for index in range(len(blocks)):
-        block = blocks[index]
-        if block["type"] == "match":
-            print("    if (st.{} != nullptr && blacklist_cells.count(st.{})) {{".format(block["cell"], block["cell"]), file=f)
-            print("      rollback = {};".format(index+1), file=f)
-            print("      return;", file=f)
-            print("    }", file=f)
-    print("    rollback = 0;", file=f)
-    print("  }", file=f)
-    print("", file=f)
+    for current_pattern in sorted(patterns.keys()):
+        print("  void check_blacklist_{}() {{".format(current_pattern), file=f)
+        print("    if (!blacklist_dirty)", file=f)
+        print("      return;", file=f)
+        print("    blacklist_dirty = false;", file=f)
+        for index in range(len(blocks)):
+            block = blocks[index]
+            if block["pattern"] != current_pattern:
+                continue
+            if block["type"] == "match":
+                print("    if (st_{}.{} != nullptr && blacklist_cells.count(st_{}.{})) {{".format(current_pattern, block["cell"], current_pattern, block["cell"]), file=f)
+                print("      rollback = {};".format(index+1), file=f)
+                print("      return;", file=f)
+                print("    }", file=f)
+        print("    rollback = 0;", file=f)
+        print("  }", file=f)
+        print("", file=f)
+    current_pattern = None
 
     print("  SigSpec port(Cell *cell, IdString portname) {", file=f)
     print("    return sigmap(cell->getPort(portname));", file=f)
@@ -294,11 +368,13 @@ with open(outfile, "w") as f:
 
     print("  {}_pm(Module *module, const vector<Cell*> &cells) :".format(prefix), file=f)
     print("      module(module), sigmap(module) {", file=f)
-    for s, t in sorted(udata_types.items()):
-        if t.endswith("*"):
-            print("    ud.{} = nullptr;".format(s), file=f)
-        else:
-            print("    ud.{} = {}();".format(s, t), file=f)
+    for current_pattern in sorted(patterns.keys()):
+        for s, t in sorted(udata_types[current_pattern].items()):
+            if t.endswith("*"):
+                print("    ud_{}.{} = nullptr;".format(current_pattern,s), file=f)
+            else:
+                print("    ud_{}.{} = {}();".format(current_pattern, s, t), file=f)
+    current_pattern = None
     print("    for (auto cell : module->cells()) {", file=f)
     print("      for (auto &conn : cell->connections())", file=f)
     print("        add_siguser(conn.second, cell);", file=f)
@@ -328,34 +404,52 @@ with open(outfile, "w") as f:
     print("  }", file=f)
     print("", file=f)
 
-    print("  void run(std::function<void()> on_accept_f) {", file=f)
-    print("    on_accept = on_accept_f;", file=f)
-    print("    rollback = 0;", file=f)
-    print("    blacklist_dirty = false;", file=f)
-    for s, t in sorted(state_types.items()):
-        if t.endswith("*"):
-            print("    st.{} = nullptr;".format(s), file=f)
-        else:
-            print("    st.{} = {}();".format(s, t), file=f)
-    print("    block_0();", file=f)
-    print("  }", file=f)
-    print("", file=f)
-
-    print("  void run(std::function<void({}_pm&)> on_accept_f) {{".format(prefix), file=f)
-    print("    run([&](){on_accept_f(*this);});", file=f)
-    print("  }", file=f)
-    print("", file=f)
+    for current_pattern in sorted(patterns.keys()):
+        print("  void run_{}(std::function<void()> on_accept_f) {{".format(current_pattern), file=f)
+        print("    on_accept = on_accept_f;", file=f)
+        print("    rollback = 0;", file=f)
+        print("    blacklist_dirty = false;", file=f)
+        for s, t in sorted(state_types[current_pattern].items()):
+            if t.endswith("*"):
+                print("    st_{}.{} = nullptr;".format(current_pattern, s), file=f)
+            else:
+                print("    st_{}.{} = {}();".format(current_pattern, s, t), file=f)
+        print("    block_{}();".format(patterns[current_pattern]), file=f)
+        print("  }", file=f)
+        print("", file=f)
+        print("  void run_{}(std::function<void({}_pm&)> on_accept_f) {{".format(current_pattern, prefix), file=f)
+        print("    run_{}([&](){{on_accept_f(*this);}});".format(current_pattern), file=f)
+        print("  }", file=f)
+        print("", file=f)
+        print("  void run_{}(std::function<void(state_{}_t&)> on_accept_f) {{".format(current_pattern, current_pattern), file=f)
+        print("    run_{}([&](){{on_accept_f(st_{});}});".format(current_pattern, current_pattern), file=f)
+        print("  }", file=f)
+        print("", file=f)
+        print("  void run_{}() {{".format(current_pattern), file=f)
+        print("    run_{}([](){{}});".format(current_pattern, current_pattern), file=f)
+        print("  }", file=f)
+        print("", file=f)
+    current_pattern = None
 
     for index in range(len(blocks)):
         block = blocks[index]
 
         print("  void block_{}() {{".format(index), file=f)
+        current_pattern = block["pattern"]
+
+        if block["type"] == "final":
+            print("    on_accept();", file=f)
+            print("    check_blacklist_{}();".format(current_pattern), file=f)
+            print("  }", file=f)
+            if index+1 != len(blocks):
+                print("", file=f)
+            continue
 
         const_st = set()
         nonconst_st = set()
         restore_st = set()
 
-        for i in range(index):
+        for i in range(patterns[current_pattern], index):
             if blocks[i]["type"] == "code":
                 for s in blocks[i]["states"]:
                     const_st.add(s)
@@ -378,27 +472,27 @@ with open(outfile, "w") as f:
             assert False
 
         for s in sorted(const_st):
-            t = state_types[s]
+            t = state_types[current_pattern][s]
             if t.endswith("*"):
-                print("    {} const &{} YS_ATTRIBUTE(unused) = st.{};".format(t, s, s), file=f)
+                print("    {} const &{} YS_ATTRIBUTE(unused) = st_{}.{};".format(t, s, current_pattern, s), file=f)
             else:
-                print("    const {} &{} YS_ATTRIBUTE(unused) = st.{};".format(t, s, s), file=f)
+                print("    const {} &{} YS_ATTRIBUTE(unused) = st_{}.{};".format(t, s, current_pattern, s), file=f)
 
         for s in sorted(nonconst_st):
-            t = state_types[s]
-            print("    {} &{} YS_ATTRIBUTE(unused) = st.{};".format(t, s, s), file=f)
+            t = state_types[current_pattern][s]
+            print("    {} &{} YS_ATTRIBUTE(unused) = st_{}.{};".format(t, s, current_pattern, s), file=f)
 
         if len(restore_st):
             print("", file=f)
             for s in sorted(restore_st):
-                t = state_types[s]
+                t = state_types[current_pattern][s]
                 print("    {} backup_{} = {};".format(t, s, s), file=f)
 
         if block["type"] == "code":
             print("", file=f)
             print("    do {", file=f)
-            print("#define reject do { check_blacklist(); goto rollback_label; } while(0)", file=f)
-            print("#define accept do { on_accept(); check_blacklist(); if (rollback) goto rollback_label; } while(0)", file=f)
+            print("#define reject do {{ check_blacklist_{}(); goto rollback_label; }} while(0)".format(current_pattern), file=f)
+            print("#define accept do {{ on_accept(); check_blacklist_{}(); if (rollback) goto rollback_label; }} while(0)".format(current_pattern), file=f)
             print("#define branch do {{ block_{}(); if (rollback) goto rollback_label; }} while(0)".format(index+1), file=f)
 
             for line in block["code"]:
@@ -417,11 +511,11 @@ with open(outfile, "w") as f:
             if len(restore_st) or len(nonconst_st):
                 print("", file=f)
                 for s in sorted(restore_st):
-                    t = state_types[s]
+                    t = state_types[current_pattern][s]
                     print("    {} = backup_{};".format(s, s), file=f)
                 for s in sorted(nonconst_st):
                     if s not in restore_st:
-                        t = state_types[s]
+                        t = state_types[current_pattern][s]
                         if t.endswith("*"):
                             print("    {} = nullptr;".format(s), file=f)
                         else:
@@ -470,17 +564,12 @@ with open(outfile, "w") as f:
         else:
             assert False
 
-
+        current_pattern = None
         print("  }", file=f)
         print("", file=f)
 
-    print("  void block_{}() {{".format(len(blocks)), file=f)
-    print("    on_accept();", file=f)
-    print("    check_blacklist();", file=f)
-    print("  }", file=f)
     print("};", file=f)
 
-    print("", file=f)
-    print("YOSYS_NAMESPACE_END", file=f)
-
-# pp.pprint(blocks)
+    if genhdr:
+        print("", file=f)
+        print("YOSYS_NAMESPACE_END", file=f)
diff --git a/passes/sat/sat.cc b/passes/sat/sat.cc
index 695a03e15..cbba738f0 100644
--- a/passes/sat/sat.cc
+++ b/passes/sat/sat.cc
@@ -1169,6 +1169,7 @@ struct SatPass : public Pass {
 			if (args[argidx] == "-tempinduct-def") {
 				tempinduct = true;
 				tempinduct_def = true;
+				enable_undef = true;
 				continue;
 			}
 			if (args[argidx] == "-tempinduct-baseonly") {
diff --git a/passes/techmap/dffinit.cc b/passes/techmap/dffinit.cc
index 48390488e..0ad33dc0e 100644
--- a/passes/techmap/dffinit.cc
+++ b/passes/techmap/dffinit.cc
@@ -102,7 +102,8 @@ struct DffinitPass : public Pass {
 				if (wire->attributes.count("\\init")) {
 					Const value = wire->attributes.at("\\init");
 					for (int i = 0; i < min(GetSize(value), GetSize(wire)); i++)
-						init_bits[sigmap(SigBit(wire, i))] = value[i];
+						if (value[i] != State::Sx)
+							init_bits[sigmap(SigBit(wire, i))] = value[i];
 				}
 				if (wire->port_output)
 					for (auto bit : sigmap(wire))
diff --git a/passes/techmap/shregmap.cc b/passes/techmap/shregmap.cc
index a541b33be..75eedfbcc 100644
--- a/passes/techmap/shregmap.cc
+++ b/passes/techmap/shregmap.cc
@@ -178,7 +178,17 @@ struct ShregmapTechXilinx7 : ShregmapTech
 
 		// Only map if $shiftx exclusively covers the shift register
 		if (shiftx->type == "$shiftx") {
-			if (GetSize(taps) != shiftx->getParam("\\A_WIDTH").as_int())
+			if (GetSize(taps) > shiftx->getParam("\\A_WIDTH").as_int())
+				return false;
+			// Due to padding the most significant bits of A may be 1'bx,
+			//   and if so, discount them
+			if (GetSize(taps) < shiftx->getParam("\\A_WIDTH").as_int()) {
+				const SigSpec A = shiftx->getPort("\\A");
+				const int A_width = shiftx->getParam("\\A_WIDTH").as_int();
+				for (int i = GetSize(taps); i < A_width; ++i)
+					if (A[i] != RTLIL::Sx) return false;
+			}
+			else if (GetSize(taps) != shiftx->getParam("\\A_WIDTH").as_int())
 				return false;
 		}
 		else if (shiftx->type == "$mux") {
diff --git a/techlibs/common/synth.cc b/techlibs/common/synth.cc
index ccfa76e02..e41c0fe97 100644
--- a/techlibs/common/synth.cc
+++ b/techlibs/common/synth.cc
@@ -201,6 +201,8 @@ struct SynthPass : public ScriptPass
 			run("check");
 			run("opt");
 			run("wreduce");
+			run("peepopt");
+			run("opt_clean");
 			if (help_mode)
 				run("techmap -map +/cmp2lut.v", " (if -lut)");
 			else
diff --git a/techlibs/ecp5/synth_ecp5.cc b/techlibs/ecp5/synth_ecp5.cc
index 4b889d672..c6e12248e 100644
--- a/techlibs/ecp5/synth_ecp5.cc
+++ b/techlibs/ecp5/synth_ecp5.cc
@@ -253,7 +253,7 @@ struct SynthEcp5Pass : public ScriptPass
 			if (!nodffe)
 				run("dff2dffe -direct-match $_DFF_* -direct-match $__DFFS_*");
 			run("techmap -D NO_LUT -map +/ecp5/cells_map.v");
-			run("opt_expr -mux_undef");
+			run("opt_expr -undriven -mux_undef");
 			run("simplemap");
 			run("ecp5_ffinit");
 		}
diff --git a/techlibs/ice40/synth_ice40.cc b/techlibs/ice40/synth_ice40.cc
index 8899bfcc4..bb96d66d1 100644
--- a/techlibs/ice40/synth_ice40.cc
+++ b/techlibs/ice40/synth_ice40.cc
@@ -225,11 +225,13 @@ struct SynthIce40Pass : public ScriptPass
 			run("proc");
 		}
 
-		if (flatten && check_label("flatten", "(unless -noflatten)"))
+		if (check_label("flatten", "(unless -noflatten)"))
 		{
-			run("flatten");
-			run("tribuf -logic");
-			run("deminout");
+			if (flatten) {
+				run("flatten");
+				run("tribuf -logic");
+				run("deminout");
+			}
 		}
 
 		if (check_label("coarse"))
@@ -239,6 +241,8 @@ struct SynthIce40Pass : public ScriptPass
 			run("check");
 			run("opt");
 			run("wreduce");
+			run("peepopt");
+			run("opt_clean");
 			run("share");
 			run("techmap -map +/cmp2lut.v -D LUT_WIDTH=4");
 			run("opt_expr");
diff --git a/techlibs/xilinx/cells_map.v b/techlibs/xilinx/cells_map.v
index 704ab21b1..40789ddbe 100644
--- a/techlibs/xilinx/cells_map.v
+++ b/techlibs/xilinx/cells_map.v
@@ -17,6 +17,16 @@
  *
  */
 
+// Convert negative-polarity reset to positive-polarity
+(* techmap_celltype = "$_DFF_NN0_" *)
+module _90_dff_nn0_to_np0 (input D, C, R, output Q); \$_DFF_NP0_  _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .R(~R)); endmodule
+(* techmap_celltype = "$_DFF_PN0_" *)
+module _90_dff_pn0_to_pp0 (input D, C, R, output Q); \$_DFF_PP0_  _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .R(~R)); endmodule
+(* techmap_celltype = "$_DFF_NN1_" *)
+module _90_dff_nn1_to_np1 (input D, C, R, output Q); \$_DFF_NP1   _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .R(~R)); endmodule
+(* techmap_celltype = "$_DFF_PN1_" *)
+module _90_dff_pn1_to_pp1 (input D, C, R, output Q); \$_DFF_PP1   _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .R(~R)); endmodule
+
 module \$__SHREG_ (input C, input D, input E, output Q);
   parameter DEPTH = 0;
   parameter [DEPTH-1:0] INIT = 0;
diff --git a/techlibs/xilinx/ff_map.v b/techlibs/xilinx/ff_map.v
index c61fd7070..13beaa6ae 100644
--- a/techlibs/xilinx/ff_map.v
+++ b/techlibs/xilinx/ff_map.v
@@ -22,26 +22,21 @@
 
 `ifndef _NO_FFS
 
-`ifndef _NO_POS_SR
 module  \$_DFF_N_   (input D, C, output Q);    FDRE_1 #(.INIT(|0)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(1'b1), .R(1'b0)); endmodule
 module  \$_DFF_P_   (input D, C, output Q);    FDRE   #(.INIT(|0)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(1'b1), .R(1'b0)); endmodule
 
 module  \$_DFFE_NP_ (input D, C, E, output Q); FDRE_1 #(.INIT(|0)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(E),    .R(1'b0)); endmodule
 module  \$_DFFE_PP_ (input D, C, E, output Q); FDRE   #(.INIT(|0)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(E),    .R(1'b0)); endmodule
 
+module  \$_DFF_NN0_ (input D, C, R, output Q); FDCE_1 #(.INIT(|0)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(1'b1), .CLR(!R)); endmodule
 module  \$_DFF_NP0_ (input D, C, R, output Q); FDCE_1 #(.INIT(|0)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(1'b1), .CLR( R)); endmodule
+module  \$_DFF_PN0_ (input D, C, R, output Q); FDCE   #(.INIT(|0)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(1'b1), .CLR(!R)); endmodule
 module  \$_DFF_PP0_ (input D, C, R, output Q); FDCE   #(.INIT(|0)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(1'b1), .CLR( R)); endmodule
 
+module  \$_DFF_NN1_ (input D, C, R, output Q); FDPE_1 #(.INIT(|0)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(1'b1), .PRE(!R)); endmodule
 module  \$_DFF_NP1_ (input D, C, R, output Q); FDPE_1 #(.INIT(|0)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(1'b1), .PRE( R)); endmodule
+module  \$_DFF_PN1_ (input D, C, R, output Q); FDPE   #(.INIT(|0)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(1'b1), .PRE(!R)); endmodule
 module  \$_DFF_PP1_ (input D, C, R, output Q); FDPE   #(.INIT(|0)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(1'b1), .PRE( R)); endmodule
-`endif
-
-module  \$_DFF_NN0_ (input D, C, R, output Q); \$_DFF_NP0_         _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C),              .R(~R)); endmodule
-module  \$_DFF_PN0_ (input D, C, R, output Q); \$_DFF_PP0_         _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C),              .R(~R)); endmodule
-
-module  \$_DFF_NN1_ (input D, C, R, output Q); \$_DFF_NP1          _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C),              .R(~R)); endmodule
-module  \$_DFF_PN1_ (input D, C, R, output Q); \$_DFF_PP1          _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C),              .R(~R)); endmodule
-`endif
 
 `endif
 
diff --git a/techlibs/xilinx/synth_xilinx.cc b/techlibs/xilinx/synth_xilinx.cc
index 53eee7962..8aa7b508e 100644
--- a/techlibs/xilinx/synth_xilinx.cc
+++ b/techlibs/xilinx/synth_xilinx.cc
@@ -25,18 +25,9 @@
 USING_YOSYS_NAMESPACE
 PRIVATE_NAMESPACE_BEGIN
 
-bool check_label(bool &active, std::string run_from, std::string run_to, std::string label)
+struct SynthXilinxPass : public ScriptPass
 {
-	if (label == run_from)
-		active = true;
-	if (label == run_to)
-		active = false;
-	return active;
-}
-
-struct SynthXilinxPass : public Pass
-{
-	SynthXilinxPass() : Pass("synth_xilinx", "synthesis for Xilinx FPGAs") { }
+	SynthXilinxPass() : ScriptPass("synth_xilinx", "synthesis for Xilinx FPGAs") { }
 
 	void help() YS_OVERRIDE
 	{
@@ -85,79 +76,30 @@ struct SynthXilinxPass : public Pass
 		log("\n");
 		log("\n");
 		log("The following commands are executed by this synthesis command:\n");
-		log("\n");
-		log("    begin:\n");
-		log("        read_verilog -lib +/xilinx/cells_sim.v\n");
-		log("        read_verilog -lib +/xilinx/cells_xtra.v\n");
-		log("        read_verilog -lib +/xilinx/brams_bb.v\n");
-		log("        hierarchy -check -top <top>\n");
-		log("\n");
-		log("    flatten:     (only if -flatten)\n");
-		log("        proc\n");
-		log("        flatten\n");
-		log("\n");
-		log("    coarse:\n");
-		log("        synth -run coarse\n");
-		log("\n");
-		log("    bram: (only executed when '-nobram' is not given)\n");
-		log("        memory_bram -rules +/xilinx/brams.txt\n");
-		log("        techmap -map +/xilinx/brams_map.v\n");
-		log("\n");
-		log("    dram: (only executed when '-nodram' is not given)\n");
-		log("        memory_bram -rules +/xilinx/drams.txt\n");
-		log("        techmap -map +/xilinx/drams_map.v\n");
-		log("\n");
-		log("    fine:\n");
-		log("        opt -fast\n");
-		log("        memory_map\n");
-		log("        dffsr2dff\n");
-		log("        dff2dffe\n");
-		log("        techmap -map +/xilinx/arith_map.v\n");
-		log("        opt -fast\n");
-		log("\n");
-		log("    map_cells:\n");
-		log("        simplemap t:$dff t:$dffe (without '-nosrl' only)\n");
-		log("        pmux2shiftx (without '-nosrl' only)\n");
-		log("        opt_expr -mux_undef (without '-nosrl' only)\n");
-		log("        shregmap -tech xilinx -minlen 3 (without '-nosrl' only)\n");
-		log("        techmap -map +/xilinx/cells_map.v\n");
-		log("        clean\n");
-		log("\n");
-		log("    map_luts:\n");
-		log("        opt -full\n");
-		log("        techmap -map +/techmap.v -D _NO_POS_SR -map +/xilinx/ff_map.v\n");
-		log("        abc -luts 2:2,3,6:5,10,20 [-dff]\n");
-		log("        clean\n");
-		log("        shregmap -minlen 3 -init -params -enpol any_or_none (without '-nosrl' only)\n");
-		log("        techmap -map +/xilinx/lut_map.v -map +/xilinx/ff_map.v -map +/xilinx/cells_map.v");
-		log("        dffinit -ff FDRE   Q INIT -ff FDCE   Q INIT -ff FDPE   Q INIT -ff FDSE   Q INIT \\\n");
-		log("                -ff FDRE_1 Q INIT -ff FDCE_1 Q INIT -ff FDPE_1 Q INIT -ff FDSE_1 Q INIT\n");
-		log("        clean\n");
-		log("\n");
-		log("    check:\n");
-		log("        hierarchy -check\n");
-		log("        stat\n");
-		log("        check -noinit\n");
-		log("\n");
-		log("    edif:     (only if -edif)\n");
-		log("        write_edif <file-name>\n");
-		log("\n");
-		log("    blif:     (only if -blif)\n");
-		log("        write_blif <file-name>\n");
+		help_script();
 		log("\n");
 	}
+
+	std::string top_opt, edif_file, blif_file;
+	bool flatten, retime, vpr, nobram, nodram, nosrl;
+
+	void clear_flags() YS_OVERRIDE
+	{
+		top_opt = "-auto-top";
+		edif_file.clear();
+		blif_file.clear();
+		flatten = false;
+		retime = false;
+		vpr = false;
+		nobram = false;
+		nodram = false;
+		nosrl = false;
+	}
+
 	void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
 	{
-		std::string top_opt = "-auto-top";
-		std::string edif_file;
-		std::string blif_file;
 		std::string run_from, run_to;
-		bool flatten = false;
-		bool retime = false;
-		bool vpr = false;
-		bool nobram = false;
-		bool nodram = false;
-		bool nosrl = false;
+		clear_flags();
 
 		size_t argidx;
 		for (argidx = 1; argidx < args.size(); argidx++)
@@ -213,129 +155,122 @@ struct SynthXilinxPass : public Pass
 		if (!design->full_selection())
 			log_cmd_error("This command only operates on fully selected designs!\n");
 
-		bool active = run_from.empty();
-
 		log_header(design, "Executing SYNTH_XILINX pass.\n");
 		log_push();
 
-		if (check_label(active, run_from, run_to, "begin"))
-		{
-			if (vpr) {
-				Pass::call(design, "read_verilog -lib -D_EXPLICIT_CARRY +/xilinx/cells_sim.v");
-			} else {
-				Pass::call(design, "read_verilog -lib +/xilinx/cells_sim.v");
-			}
-
-			Pass::call(design, "read_verilog -lib +/xilinx/cells_xtra.v");
-
-			if (!nobram) {
-				Pass::call(design, "read_verilog -lib +/xilinx/brams_bb.v");
-			}
-
-			Pass::call(design, stringf("hierarchy -check %s", top_opt.c_str()));
-		}
-
-		if (flatten && check_label(active, run_from, run_to, "flatten"))
-		{
-			Pass::call(design, "proc");
-			Pass::call(design, "flatten");
-		}
-
-		if (check_label(active, run_from, run_to, "coarse"))
-		{
-			Pass::call(design, "synth -run coarse");
-		}
-
-		if (check_label(active, run_from, run_to, "bram"))
-		{
-			if (!nobram) {
-				Pass::call(design, "memory_bram -rules +/xilinx/brams.txt");
-				Pass::call(design, "techmap -map +/xilinx/brams_map.v");
-			}
-		}
-
-		if (check_label(active, run_from, run_to, "dram"))
-		{
-			if (!nodram) {
-				Pass::call(design, "memory_bram -rules +/xilinx/drams.txt");
-				Pass::call(design, "techmap -map +/xilinx/drams_map.v");
-			}
-		}
-
-		if (check_label(active, run_from, run_to, "fine"))
-		{
-			Pass::call(design, "opt -fast");
-			Pass::call(design, "memory_map");
-			Pass::call(design, "dffsr2dff");
-			Pass::call(design, "dff2dffe");
-
-			if (vpr) {
-				Pass::call(design, "techmap -map +/xilinx/arith_map.v -D _EXPLICIT_CARRY");
-			} else {
-				Pass::call(design, "techmap -map +/xilinx/arith_map.v");
-			}
-
-			Pass::call(design, "hierarchy -check");
-			Pass::call(design, "opt -fast");
-		}
-
-		if (check_label(active, run_from, run_to, "map_cells"))
-		{
-			if (!nosrl) {
-				// shregmap operates on bit-level flops, not word-level,
-				//   so break those down here
-				Pass::call(design, "simplemap t:$dff t:$dffe");
-				// shregmap -tech xilinx can cope with $shiftx and $mux
-				//   cells for identifiying variable-length shift registers,
-				//   so attempt to convert $pmux-es to the former
-				Pass::call(design, "pmux2shiftx");
-				// pmux2shiftx can leave behind a $pmux with a single entry
-				//   -- need this to clean that up before shregmap
-				Pass::call(design, "opt_expr -mux_undef");
-				// shregmap with '-tech xilinx' infers variable length shift regs
-				Pass::call(design, "shregmap -tech xilinx -minlen 3");
-			}
-
-			Pass::call(design, "techmap -map +/xilinx/cells_map.v");
-			Pass::call(design, "clean");
-		}
-
-		if (check_label(active, run_from, run_to, "map_luts"))
-		{
-			Pass::call(design, "opt -full");
-			Pass::call(design, "techmap -map +/techmap.v -D _NO_POS_SR -map +/xilinx/ff_map.v");
-			Pass::call(design, "abc -luts 2:2,3,6:5,10,20" + string(retime ? " -dff" : ""));
-			Pass::call(design, "clean");
-			// This shregmap call infers fixed length shift registers after abc
-			//   has performed any necessary retiming
-			if (!nosrl)
-				Pass::call(design, "shregmap -minlen 3 -init -params -enpol any_or_none");
-			Pass::call(design, "techmap -map +/xilinx/lut_map.v -map +/xilinx/ff_map.v -map +/xilinx/cells_map.v");
-			Pass::call(design, "dffinit -ff FDRE Q INIT -ff FDCE Q INIT -ff FDPE Q INIT -ff FDSE Q INIT "
-					"-ff FDRE_1 Q INIT -ff FDCE_1 Q INIT -ff FDPE_1 Q INIT -ff FDSE_1 Q INIT");
-			Pass::call(design, "clean");
-		}
-
-		if (check_label(active, run_from, run_to, "check"))
-		{
-			Pass::call(design, "hierarchy -check");
-			Pass::call(design, "stat");
-			Pass::call(design, "check -noinit");
-		}
-
-		if (check_label(active, run_from, run_to, "edif"))
-		{
-			if (!edif_file.empty())
-				Pass::call(design, stringf("write_edif -pvector bra %s", edif_file.c_str()));
-		}
-		if (check_label(active, run_from, run_to, "blif"))
-		{
-			if (!blif_file.empty())
-				Pass::call(design, stringf("write_blif %s", edif_file.c_str()));
-		}
+		run_script(design, run_from, run_to);
 
 		log_pop();
 	}
+
+	void script() YS_OVERRIDE
+	{
+		if (check_label("begin")) {
+			if (vpr)
+				run("read_verilog -lib -D_EXPLICIT_CARRY +/xilinx/cells_sim.v");
+			else
+				run("read_verilog -lib +/xilinx/cells_sim.v");
+
+			run("read_verilog -lib +/xilinx/cells_xtra.v");
+
+			if (!nobram || help_mode)
+				run("read_verilog -lib +/xilinx/brams_bb.v", "(skip if '-nobram')");
+
+			run(stringf("hierarchy -check %s", top_opt.c_str()));
+		}
+
+		if (check_label("flatten", "(with '-flatten' only)")) {
+			if (flatten || help_mode) {
+				run("proc");
+				run("flatten");
+			}
+		}
+
+		if (check_label("coarse")) {
+			run("synth -run coarse");
+		}
+
+		if (check_label("bram", "(skip if '-nobram')")) {
+			if (!nobram || help_mode) {
+				run("memory_bram -rules +/xilinx/brams.txt");
+				run("techmap -map +/xilinx/brams_map.v");
+			}
+		}
+
+		if (check_label("dram", "(skip if '-nodram')")) {
+			if (!nodram || help_mode) {
+				run("memory_bram -rules +/xilinx/drams.txt");
+				run("techmap -map +/xilinx/drams_map.v");
+			}
+		}
+
+		if (check_label("fine")) {
+			// shregmap -tech xilinx can cope with $shiftx and $mux
+			//   cells for identifiying variable-length shift registers,
+			//   so attempt to convert $pmux-es to the former
+			if (!nosrl || help_mode)
+				run("pmux2shiftx", "(skip if '-nosrl')");
+
+			run("opt -fast -full");
+			run("memory_map");
+			run("dffsr2dff");
+			run("dff2dffe");
+			run("opt -full");
+
+			if (!vpr || help_mode)
+				run("techmap -map +/xilinx/arith_map.v");
+			else
+				run("techmap -map +/xilinx/arith_map.v -D _EXPLICIT_CARRY");
+
+			if (!nosrl || help_mode) {
+				// shregmap operates on bit-level flops, not word-level,
+				//   so break those down here
+				run("simplemap t:$dff t:$dffe", "(skip if '-nosrl')");
+				// shregmap with '-tech xilinx' infers variable length shift regs
+				run("shregmap -tech xilinx -minlen 3", "(skip if '-nosrl')");
+			}
+
+			run("techmap");
+			run("opt -fast");
+		}
+
+		if (check_label("map_cells")) {
+			run("techmap -map +/techmap.v -map +/xilinx/cells_map.v");
+			run("clean");
+		}
+
+		if (check_label("map_luts")) {
+			if (help_mode)
+				run("abc -luts 2:2,3,6:5,10,20 [-dff]");
+			else
+				run("abc -luts 2:2,3,6:5,10,20" + string(retime ? " -dff" : ""));
+			run("clean");
+			// This shregmap call infers fixed length shift registers after abc
+			//   has performed any necessary retiming
+			if (!nosrl || help_mode)
+				run("shregmap -minlen 3 -init -params -enpol any_or_none", "(skip if '-nosrl')");
+			run("techmap -map +/xilinx/lut_map.v -map +/xilinx/ff_map.v -map +/xilinx/cells_map.v");
+			run("dffinit -ff FDRE Q INIT -ff FDCE Q INIT -ff FDPE Q INIT -ff FDSE Q INIT "
+					"-ff FDRE_1 Q INIT -ff FDCE_1 Q INIT -ff FDPE_1 Q INIT -ff FDSE_1 Q INIT");
+			run("clean");
+		}
+
+		if (check_label("check")) {
+			run("hierarchy -check");
+			run("stat");
+			run("check -noinit");
+		}
+
+		if (check_label("edif")) {
+			if (!edif_file.empty() || help_mode)
+				run(stringf("write_edif -pvector bra %s", edif_file.c_str()));
+		}
+
+		if (check_label("blif")) {
+			if (!blif_file.empty() || help_mode)
+				run(stringf("write_blif %s", edif_file.c_str()));
+		}
+	}
 } SynthXilinxPass;
 
 PRIVATE_NAMESPACE_END
diff --git a/tests/memories/firrtl_938.v b/tests/memories/firrtl_938.v
new file mode 100644
index 000000000..af5efcd25
--- /dev/null
+++ b/tests/memories/firrtl_938.v
@@ -0,0 +1,22 @@
+module top
+(
+	input [7:0] data_a,
+	input [6:1] addr_a,
+	input we_a, clk,
+	output reg [7:0] q_a
+);
+	// Declare the RAM variable
+	reg [7:0] ram[63:0];
+
+	// Port A
+	always @ (posedge clk)
+	begin
+		if (we_a)
+		begin
+			ram[addr_a] <= data_a;
+			q_a <= data_a;
+		end
+			q_a <= ram[addr_a];
+	end
+
+endmodule
diff --git a/tests/simple/mem2reg.v b/tests/simple/mem2reg.v
index 9839fd4a8..100426785 100644
--- a/tests/simple/mem2reg.v
+++ b/tests/simple/mem2reg.v
@@ -92,3 +92,25 @@ module mem2reg_test5(input ctrl, output out);
 	assign out = bar[foo[0]];
 endmodule
 
+// ------------------------------------------------------
+
+module mem2reg_test6 (din, dout);
+        input   wire    [3:0] din;
+        output  reg     [3:0] dout;
+
+        reg [1:0] din_array  [1:0];
+        reg [1:0] dout_array [1:0];
+
+        always @* begin
+		din_array[0] = din[0 +: 2];
+		din_array[1] = din[2 +: 2];
+
+		dout_array[0] = din_array[0];
+		dout_array[1] = din_array[1];
+
+		{dout_array[0][1], dout_array[0][0]} = dout_array[0][0] + dout_array[1][0];
+
+		dout[0 +: 2] = dout_array[0];
+		dout[2 +: 2] = dout_array[1];
+        end
+endmodule
diff --git a/tests/simple/peepopt.v b/tests/simple/peepopt.v
new file mode 100644
index 000000000..b27b9fe57
--- /dev/null
+++ b/tests/simple/peepopt.v
@@ -0,0 +1,9 @@
+module peepopt_shiftmul_0 #(parameter N=3, parameter W=3) (input [N*W-1:0] i, input [$clog2(N)-1:0] s, output [W-1:0] o);
+assign o = i[s*W+:W];
+endmodule
+
+module peepopt_muldiv_0(input [1:0] i, output [1:0] o);
+wire [3:0] t;
+assign t = i * 3;
+assign o = t / 3;
+endmodule
diff --git a/tests/simple/xfirrtl b/tests/simple/xfirrtl
index 50d693513..ba61a4476 100644
--- a/tests/simple/xfirrtl
+++ b/tests/simple/xfirrtl
@@ -16,6 +16,7 @@ operators.v	$pow
 partsel.v	drops modules
 process.v	drops modules
 realexpr.v	drops modules
+retime.v	Initial value (11110101) for (retime_test.ff) not supported
 scopes.v	original verilog issues ( -x where x isn't declared signed)
 sincos.v	$adff
 specify.v	no code (empty module generates error
diff --git a/tests/svinterfaces/runone.sh b/tests/svinterfaces/runone.sh
index 0adecc797..54cf5f2ec 100755
--- a/tests/svinterfaces/runone.sh
+++ b/tests/svinterfaces/runone.sh
@@ -11,13 +11,13 @@ echo "" > $STDERRFILE
 
 echo -n "Test: ${TESTNAME} -> "
 
-$PWD/../../yosys -p "read_verilog -sv ${TESTNAME}.sv ; hierarchy -check -top TopModule ; synth ; write_verilog ${TESTNAME}_syn.v" >> $STDOUTFILE >> $STDERRFILE
-$PWD/../../yosys -p "read_verilog -sv ${TESTNAME}_ref.v ; hierarchy -check -top TopModule ; synth ; write_verilog ${TESTNAME}_ref_syn.v" >> $STDOUTFILE >> $STDERRFILE
+set -e
+
+$PWD/../../yosys -p "read_verilog -sv ${TESTNAME}.sv ; hierarchy -check -top TopModule ; synth ; write_verilog ${TESTNAME}_syn.v" >> $STDOUTFILE 2>> $STDERRFILE
+$PWD/../../yosys -p "read_verilog -sv ${TESTNAME}_ref.v ; hierarchy -check -top TopModule ; synth ; write_verilog ${TESTNAME}_ref_syn.v" >> $STDOUTFILE 2>> $STDERRFILE
 
 rm -f a.out reference_result.txt dut_result.txt
 
-set -e
-
 iverilog -g2012 ${TESTNAME}_syn.v
 iverilog -g2012 ${TESTNAME}_ref_syn.v
 
diff --git a/tests/tools/autotest.sh b/tests/tools/autotest.sh
index bb9c3bfb5..920474a84 100755
--- a/tests/tools/autotest.sh
+++ b/tests/tools/autotest.sh
@@ -147,7 +147,8 @@ do
 		fi
 		if $genvcd; then sed -i 's,// \$dump,$dump,g' ${bn}_tb.v; fi
 		compile_and_run ${bn}_tb_ref ${bn}_out_ref ${bn}_tb.v ${bn}_ref.v $libs \
-					"$toolsdir"/../../techlibs/common/simlib.v
+					"$toolsdir"/../../techlibs/common/simlib.v \
+					"$toolsdir"/../../techlibs/common/simcells.v
 		if $genvcd; then mv testbench.vcd ${bn}_ref.vcd; fi
 
 		test_count=0