cxxrtl: make alias analysis outlining-aware.
authorwhitequark <whitequark@whitequark.org>
Sun, 13 Dec 2020 15:33:47 +0000 (15:33 +0000)
committerwhitequark <whitequark@whitequark.org>
Tue, 15 Dec 2020 11:02:38 +0000 (11:02 +0000)
Before this commit, if a sequence of wires assigned in a chain would
terminate on a cell, none of the wires would get marked as aliases,
and typically all of the public wires would get outlined. The reason
for this behavior is that alias analysis predates outlining and in
fact runs before it.

After this commit, alias analysis runs after outlining and considers
outlined wires valid aliasees. More importantly, if the chained wires
contain any valid aliasees, then all of the wires are aliased to
the one that is topologically deepest.

Aliased wires incur virtually no overhead for the VCD writer, unlike
outlined wires that would otherwise take their place. On Minerva SoC
SRAM, size of the full VCD dump is reduced by ~65%, and throughput
is increased by ~55%.

backends/cxxrtl/cxxrtl_backend.cc

index 588cca12e0ea486772fbef528c3cffaf61ed66ac..fa19a8dd612d7734813c76832f59bddf20bcc63b 100644 (file)
@@ -2426,43 +2426,6 @@ struct CxxrtlWorker {
                        for (auto item : flow.bit_has_state)
                                bit_has_state.insert(item);
 
-                       if (debug_info && debug_alias) {
-                               // Find wires that alias other wires or are tied to a constant; debug information can be enriched with these
-                               // at essentially zero additional cost.
-                               //
-                               // Note that the information collected here can't be used for optimizing the netlist: debug information queries
-                               // are pure and run on a design in a stable state, which allows assumptions that do not otherwise hold.
-                               for (auto wire : module->wires()) {
-                                       if (!wire->name.isPublic())
-                                               continue;
-                                       if (!unbuffered_wires[wire])
-                                               continue;
-                                       const RTLIL::Wire *wire_it = wire;
-                                       while (1) {
-                                               if (!(flow.wire_def_inlinable.count(wire_it) && flow.wire_def_inlinable[wire_it]))
-                                                       break; // not an alias: complex def
-                                               log_assert(flow.wire_comb_defs[wire_it].size() == 1);
-                                               FlowGraph::Node *node = *flow.wire_comb_defs[wire_it].begin();
-                                               if (node->type != FlowGraph::Node::Type::CONNECT)
-                                                       break; // not an alias: def by cell
-                                               RTLIL::SigSpec rhs_sig = node->connect.second;
-                                               if (rhs_sig.is_wire()) {
-                                                       RTLIL::Wire *rhs_wire = rhs_sig.as_wire();
-                                                       if (unbuffered_wires[rhs_wire]) {
-                                                               wire_it = rhs_wire; // maybe an alias
-                                                       } else {
-                                                               debug_alias_wires[wire] = rhs_wire; // is an alias
-                                                               break;
-                                                       }
-                                               } else if (rhs_sig.is_fully_const()) {
-                                                       debug_const_wires[wire] = rhs_sig.as_const(); // is a const
-                                                       break;
-                                               } else {
-                                                       break; // not an alias: complex rhs
-                                               }
-                                       }
-                               }
-                       }
                        if (debug_info && debug_eval) {
                                // Find wires that can be be outlined, i.e. whose values can be always recovered from
                                // the values of other wires. (This is the inverse of inlining--any wire that can be
@@ -2471,7 +2434,7 @@ struct CxxrtlWorker {
                                pool<const RTLIL::Wire*> worklist, visited;
                                for (auto wire : module->wires()) {
                                        if (!wire->name.isPublic())
-                                               continue; // only outline public wires
+                                               continue;
                                        worklist.insert(wire);
                                }
                                while (!worklist.empty()) {
@@ -2487,6 +2450,53 @@ struct CxxrtlWorker {
                                                                worklist.insert(node_use);
                                }
                        }
+                       if (debug_info && debug_alias) {
+                               // Find wires that alias other wires or are tied to a constant. Both of these cases are
+                               // directly expressible in the debug information, improving coverage at zero cost.
+                               for (auto wire : module->wires()) {
+                                       if (!wire->name.isPublic())
+                                               continue;
+                                       const RTLIL::Wire *cursor = wire;
+                                       RTLIL::SigSpec alias_of;
+                                       while (1) {
+                                               if (!(flow.wire_def_inlinable.count(cursor) && flow.wire_def_inlinable[cursor]))
+                                                       break; // not an alias: complex def
+                                               log_assert(flow.wire_comb_defs[cursor].size() == 1);
+                                               FlowGraph::Node *node = *flow.wire_comb_defs[cursor].begin();
+                                               if (node->type != FlowGraph::Node::Type::CONNECT)
+                                                       break; // not an alias: def by cell
+                                               RTLIL::SigSpec rhs_sig = node->connect.second;
+                                               if (rhs_sig.is_fully_const()) {
+                                                       alias_of = rhs_sig; // alias of const
+                                                       break;
+                                               } else if (rhs_sig.is_wire()) {
+                                                       RTLIL::Wire *rhs_wire = rhs_sig.as_wire(); // possible alias of wire
+                                                       if (rhs_wire->port_input && !rhs_wire->port_output) {
+                                                               alias_of = rhs_wire; // alias of input
+                                                               break;
+                                                       } else if (!localized_wires.count(rhs_wire) && !inlined_wires.count(rhs_wire)) {
+                                                               alias_of = rhs_wire; // alias of member
+                                                               break;
+                                                       } else {
+                                                               if (rhs_wire->name.isPublic() && debug_outlined_wires.count(rhs_wire))
+                                                                       alias_of = rhs_wire; // alias of either outline or another alias
+                                                               cursor = rhs_wire; // keep looking
+                                                       }
+                                               } else {
+                                                       break; // not an alias: complex rhs
+                                               }
+                                       }
+                                       if (alias_of.empty()) {
+                                               continue;
+                                       } else if (alias_of.is_fully_const()) {
+                                               debug_const_wires[wire] = alias_of.as_const();
+                                       } else if (alias_of.is_wire()) {
+                                               debug_alias_wires[wire] = alias_of.as_wire();
+                                       } else log_abort();
+                                       if (inlined_wires.count(wire))
+                                               debug_outlined_wires.erase(wire);
+                               }
+                       }
                }
                if (has_feedback_arcs || has_buffered_comb_wires) {
                        // Although both non-feedback buffered combinatorial wires and apparent feedback wires may be eliminated