proc_prune: new pass.
authorwhitequark <whitequark@whitequark.org>
Mon, 8 Jul 2019 15:19:01 +0000 (15:19 +0000)
committerwhitequark <whitequark@whitequark.org>
Tue, 9 Jul 2019 09:30:58 +0000 (09:30 +0000)
The proc_prune pass is similar in nature to proc_rmdead pass: while
proc_rmdead removes branches that never become active because another
branch preempts it, proc_prune removes assignments that never become
active because another assignment preempts them.

Genrtlil contains logic similar to the proc_prune pass, but their
purpose is different: genrtlil has to prune assignments to adapt
the semantics of blocking assignments in HDLs (latest assignment
wins) to semantics of assignments in RTLIL processes (assignment in
the most specific case wins). On the other hand proc_prune is
a general purpose RTLIL simplification that benefits all frontends,
even those not using the Yosys AST library.

The proc_prune pass is added to the proc script after proc_rmdead,
since it gives better results with fewer branches.

passes/proc/Makefile.inc
passes/proc/proc.cc
passes/proc/proc_prune.cc [new file with mode: 0644]

index 397fe46a19d3b0c4cc1de3d7c3b00cd87532ad63..4b56979f8340e29b51db2bc56edef35d76718fee 100644 (file)
@@ -1,5 +1,6 @@
 
 OBJS += passes/proc/proc.o
+OBJS += passes/proc/proc_prune.o
 OBJS += passes/proc/proc_clean.o
 OBJS += passes/proc/proc_rmdead.o
 OBJS += passes/proc/proc_init.o
@@ -7,4 +8,3 @@ OBJS += passes/proc/proc_arst.o
 OBJS += passes/proc/proc_mux.o
 OBJS += passes/proc/proc_dlatch.o
 OBJS += passes/proc/proc_dff.o
-
index ef7cb0f714bfe31ea46f53991fc5f59ffe2f364a..a5b4a3112c1d01d260a7a15106302ea686394e7a 100644 (file)
@@ -37,6 +37,7 @@ struct ProcPass : public Pass {
                log("\n");
                log("    proc_clean\n");
                log("    proc_rmdead\n");
+               log("    proc_prune\n");
                log("    proc_init\n");
                log("    proc_arst\n");
                log("    proc_mux\n");
@@ -83,6 +84,7 @@ struct ProcPass : public Pass {
                Pass::call(design, "proc_clean");
                if (!ifxmode)
                        Pass::call(design, "proc_rmdead");
+               Pass::call(design, "proc_prune");
                Pass::call(design, "proc_init");
                if (global_arst.empty())
                        Pass::call(design, "proc_arst");
diff --git a/passes/proc/proc_prune.cc b/passes/proc/proc_prune.cc
new file mode 100644 (file)
index 0000000..7222d41
--- /dev/null
@@ -0,0 +1,135 @@
+/*
+ *  yosys -- Yosys Open SYnthesis Suite
+ *
+ *  Copyright (C) 2019  whitequark <whitequark@whitequark.org>
+ *
+ *  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/register.h"
+#include "kernel/sigtools.h"
+#include "kernel/log.h"
+#include <stdlib.h>
+#include <stdio.h>
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+struct PruneWorker
+{
+       RTLIL::Module *module;
+       SigMap sigmap;
+
+       int count = 0;
+
+       PruneWorker(RTLIL::Module *mod) : module(mod), sigmap(mod) {}
+
+       pool<RTLIL::SigBit> do_switch(RTLIL::SwitchRule *sw, pool<RTLIL::SigBit> assigned)
+       {
+               pool<RTLIL::SigBit> all_assigned;
+               bool full_case = sw->get_bool_attribute("\\full_case");
+               bool first = true;
+               for (auto it : sw->cases) {
+                       if (it->compare.empty())
+                               full_case = true;
+                       pool<RTLIL::SigBit> case_assigned = do_case(it, assigned);
+                       if (first) {
+                               first = false;
+                               all_assigned = case_assigned;
+                       } else {
+                               for (auto &bit : all_assigned)
+                                       if (!case_assigned[bit])
+                                               all_assigned.erase(bit);
+                       }
+               }
+               if (full_case)
+                       assigned.insert(all_assigned.begin(), all_assigned.end());
+               return assigned;
+       }
+
+       pool<RTLIL::SigBit> do_case(RTLIL::CaseRule *cs, pool<RTLIL::SigBit> assigned)
+       {
+               for (auto it = cs->switches.rbegin(); it != cs->switches.rend(); ++it) {
+                       pool<RTLIL::SigBit> sw_assigned = do_switch((*it), assigned);
+                       assigned.insert(sw_assigned.begin(), sw_assigned.end());
+               }
+               pool<RTLIL::SigSig> remove;
+               for (auto it = cs->actions.rbegin(); it != cs->actions.rend(); ++it) {
+                       RTLIL::SigSpec lhs = sigmap(it->first);
+                       bool redundant = true;
+                       for (auto &bit : lhs) {
+                               if (bit.wire && !assigned[bit]) {
+                                       redundant = false;
+                                       break;
+                               }
+                       }
+                       if (redundant)
+                               remove.insert(*it);
+                       else {
+                               for (auto &bit : lhs)
+                                       if (bit.wire)
+                                               assigned.insert(bit);
+                       }
+               }
+               for (auto it = cs->actions.begin(); it != cs->actions.end(); ) {
+                       if (remove[*it]) {
+                               it = cs->actions.erase(it);
+                               count++;
+                       } else it++;
+               }
+               return assigned;
+       }
+
+       void do_process(RTLIL::Process *pr)
+       {
+               do_case(&pr->root_case, {});
+       }
+};
+
+struct ProcPrunePass : public Pass {
+       ProcPrunePass() : Pass("proc_prune", "remove redundant assignments") { }
+       void help() YS_OVERRIDE
+       {
+               //   |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+               log("\n");
+               log("    proc_prune [selection]\n");
+               log("\n");
+               log("This pass identifies assignments in processes that are always overwritten by\n");
+               log("a later assignment to the same signal and removes them.\n");
+               log("\n");
+       }
+       void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
+       {
+               int total_count = 0;
+               log_header(design, "Executing PROC_PRUNE pass (remove redundant assignments in processes).\n");
+
+               extra_args(args, 1, design);
+
+               for (auto mod : design->modules()) {
+                       if (!design->selected(mod))
+                               continue;
+                       PruneWorker worker(mod);
+                       for (auto &proc_it : mod->processes) {
+                               if (!design->selected(mod, proc_it.second))
+                                       continue;
+                               worker.do_process(proc_it.second);
+                       }
+                       total_count += worker.count;
+               }
+
+               log("Removed %d redundant assignment%s.\n", total_count, total_count == 1 ? "" : "s");
+       }
+} ProcPrunePass;
+
+PRIVATE_NAMESPACE_END