Merge pull request #1309 from whitequark/proc_clean-fix-1268
[yosys.git] / passes / proc / proc_prune.cc
1 /*
2 * yosys -- Yosys Open SYnthesis Suite
3 *
4 * Copyright (C) 2019 whitequark <whitequark@whitequark.org>
5 *
6 * Permission to use, copy, modify, and/or distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 *
18 */
19
20 #include "kernel/register.h"
21 #include "kernel/sigtools.h"
22 #include "kernel/log.h"
23 #include <stdlib.h>
24 #include <stdio.h>
25
26 USING_YOSYS_NAMESPACE
27 PRIVATE_NAMESPACE_BEGIN
28
29 struct PruneWorker
30 {
31 RTLIL::Module *module;
32 SigMap sigmap;
33
34 int removed_count = 0, promoted_count = 0;
35
36 PruneWorker(RTLIL::Module *mod) : module(mod), sigmap(mod) {}
37
38 pool<RTLIL::SigBit> do_switch(RTLIL::SwitchRule *sw, pool<RTLIL::SigBit> assigned, pool<RTLIL::SigBit> &affected)
39 {
40 pool<RTLIL::SigBit> all_assigned;
41 bool full_case = sw->get_bool_attribute("\\full_case");
42 bool first = true;
43 for (auto it : sw->cases) {
44 if (it->compare.empty())
45 full_case = true;
46 pool<RTLIL::SigBit> case_assigned = do_case(it, assigned, affected);
47 if (first) {
48 first = false;
49 all_assigned = case_assigned;
50 } else {
51 for (auto &bit : all_assigned)
52 if (!case_assigned[bit])
53 all_assigned.erase(bit);
54 }
55 }
56 if (full_case)
57 assigned.insert(all_assigned.begin(), all_assigned.end());
58 return assigned;
59 }
60
61 pool<RTLIL::SigBit> do_case(RTLIL::CaseRule *cs, pool<RTLIL::SigBit> assigned, pool<RTLIL::SigBit> &affected,
62 bool root = false)
63 {
64 for (auto it = cs->switches.rbegin(); it != cs->switches.rend(); ++it) {
65 pool<RTLIL::SigBit> sw_assigned = do_switch((*it), assigned, affected);
66 assigned.insert(sw_assigned.begin(), sw_assigned.end());
67 }
68 for (auto it = cs->actions.rbegin(); it != cs->actions.rend(); ) {
69 RTLIL::SigSpec lhs = sigmap(it->first);
70 bool redundant = true;
71 for (auto &bit : lhs) {
72 if (bit.wire && !assigned[bit]) {
73 redundant = false;
74 break;
75 }
76 }
77 bool remove = false;
78 if (redundant) {
79 removed_count++;
80 remove = true;
81 } else {
82 if (root) {
83 bool promotable = true;
84 for (auto &bit : lhs) {
85 if (bit.wire && affected[bit] && !assigned[bit]) {
86 promotable = false;
87 break;
88 }
89 }
90 if (promotable) {
91 RTLIL::SigSpec rhs = sigmap(it->second);
92 RTLIL::SigSig conn;
93 for (int i = 0; i < GetSize(lhs); i++) {
94 RTLIL::SigBit lhs_bit = lhs[i];
95 if (lhs_bit.wire && !assigned[lhs_bit]) {
96 conn.first.append_bit(lhs_bit);
97 conn.second.append(rhs.extract(i));
98 }
99 }
100 promoted_count++;
101 module->connect(conn);
102 remove = true;
103 }
104 }
105 for (auto &bit : lhs)
106 if (bit.wire)
107 assigned.insert(bit);
108 for (auto &bit : lhs)
109 if (bit.wire)
110 affected.insert(bit);
111 }
112 if (remove)
113 cs->actions.erase((it++).base() - 1);
114 else it++;
115 }
116 return assigned;
117 }
118
119 void do_process(RTLIL::Process *pr)
120 {
121 pool<RTLIL::SigBit> affected;
122 do_case(&pr->root_case, {}, affected, /*root=*/true);
123 }
124 };
125
126 struct ProcPrunePass : public Pass {
127 ProcPrunePass() : Pass("proc_prune", "remove redundant assignments") { }
128 void help() YS_OVERRIDE
129 {
130 // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
131 log("\n");
132 log(" proc_prune [selection]\n");
133 log("\n");
134 log("This pass identifies assignments in processes that are always overwritten by\n");
135 log("a later assignment to the same signal and removes them.\n");
136 log("\n");
137 }
138 void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
139 {
140 int total_removed_count = 0, total_promoted_count = 0;
141 log_header(design, "Executing PROC_PRUNE pass (remove redundant assignments in processes).\n");
142
143 extra_args(args, 1, design);
144
145 for (auto mod : design->modules()) {
146 if (!design->selected(mod))
147 continue;
148 PruneWorker worker(mod);
149 for (auto &proc_it : mod->processes) {
150 if (!design->selected(mod, proc_it.second))
151 continue;
152 worker.do_process(proc_it.second);
153 }
154 total_removed_count += worker.removed_count;
155 total_promoted_count += worker.promoted_count;
156 }
157
158 log("Removed %d redundant assignment%s.\n",
159 total_removed_count, total_removed_count == 1 ? "" : "s");
160 log("Promoted %d assignment%s to connection%s.\n",
161 total_promoted_count, total_promoted_count == 1 ? "" : "s", total_promoted_count == 1 ? "" : "s");
162 }
163 } ProcPrunePass;
164
165 PRIVATE_NAMESPACE_END