/* * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2012 Claire Xenia Wolf * * 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. * */ // This file contains various c++ utility routines and helper classes that // do not depend on any other components of yosys (except stuff like log_*). #include "kernel/yosys.h" #ifndef UTILS_H #define UTILS_H YOSYS_NAMESPACE_BEGIN // ------------------------------------------------ // A map-like container, but you can save and restore the state // ------------------------------------------------ template> struct stackmap { private: std::vector> backup_state; dict current_state; static T empty_tuple; public: stackmap() { } stackmap(const dict &other) : current_state(other) { } template void operator=(const Other &other) { for (auto &it : current_state) if (!backup_state.empty() && backup_state.back().count(it.first) == 0) backup_state.back()[it.first] = new T(it.second); current_state.clear(); for (auto &it : other) set(it.first, it.second); } bool has(const Key &k) { return current_state.count(k) != 0; } void set(const Key &k, const T &v) { if (!backup_state.empty() && backup_state.back().count(k) == 0) backup_state.back()[k] = current_state.count(k) ? new T(current_state.at(k)) : nullptr; current_state[k] = v; } void unset(const Key &k) { if (!backup_state.empty() && backup_state.back().count(k) == 0) backup_state.back()[k] = current_state.count(k) ? new T(current_state.at(k)) : nullptr; current_state.erase(k); } const T &get(const Key &k) { if (current_state.count(k) == 0) return empty_tuple; return current_state.at(k); } void reset(const Key &k) { for (int i = GetSize(backup_state)-1; i >= 0; i--) if (backup_state[i].count(k) != 0) { if (backup_state[i].at(k) == nullptr) current_state.erase(k); else current_state[k] = *backup_state[i].at(k); return; } current_state.erase(k); } const dict &stdmap() { return current_state; } void save() { backup_state.resize(backup_state.size()+1); } void restore() { log_assert(!backup_state.empty()); for (auto &it : backup_state.back()) if (it.second != nullptr) { current_state[it.first] = *it.second; delete it.second; } else current_state.erase(it.first); backup_state.pop_back(); } ~stackmap() { while (!backup_state.empty()) restore(); } }; // ------------------------------------------------ // A simple class for topological sorting // ------------------------------------------------ template , typename OPS = hash_ops> class TopoSort { public: // We use this ordering of the edges in the adjacency matrix for // exact compatibility with an older implementation. struct IndirectCmp { IndirectCmp(const std::vector &nodes) : node_cmp_(), nodes_(nodes) {} bool operator()(int a, int b) const { log_assert(static_cast(a) < nodes_.size()); log_assert(static_cast(b) < nodes_.size()); return node_cmp_(nodes_[a], nodes_[b]); } const C node_cmp_; const std::vector &nodes_; }; bool analyze_loops; std::map node_to_index; std::vector> edges; std::vector sorted; std::set> loops; TopoSort() : indirect_cmp(nodes) { analyze_loops = true; found_loops = false; } int node(T n) { auto rv = node_to_index.emplace(n, static_cast(nodes.size())); if (rv.second) { nodes.push_back(n); edges.push_back(std::set(indirect_cmp)); } return rv.first->second; } void edge(int l_index, int r_index) { edges[r_index].insert(l_index); } void edge(T left, T right) { edge(node(left), node(right)); } bool has_node(const T &node) { return node_to_index.find(node) != node_to_index.end(); } bool sort() { log_assert(GetSize(node_to_index) == GetSize(edges)); log_assert(GetSize(nodes) == GetSize(edges)); loops.clear(); sorted.clear(); found_loops = false; std::vector marked_cells(edges.size(), false); std::vector active_cells(edges.size(), false); std::vector active_stack; sorted.reserve(edges.size()); for (const auto &it : node_to_index) sort_worker(it.second, marked_cells, active_cells, active_stack); log_assert(GetSize(sorted) == GetSize(nodes)); return !found_loops; } // Build the more expensive representation of edges for // a few passes that use it directly. std::map, C> get_database() { std::map, C> database; for (size_t i = 0; i < nodes.size(); ++i) { std::set converted_edge_set; for (int other_node : edges[i]) { converted_edge_set.insert(nodes[other_node]); } database.emplace(nodes[i], converted_edge_set); } return database; } private: bool found_loops; std::vector nodes; const IndirectCmp indirect_cmp; void sort_worker(const int root_index, std::vector &marked_cells, std::vector &active_cells, std::vector &active_stack) { if (active_cells[root_index]) { found_loops = true; if (analyze_loops) { std::set loop; for (int i = GetSize(active_stack) - 1; i >= 0; i--) { const int index = active_stack[i]; loop.insert(nodes[index]); if (index == root_index) break; } loops.insert(loop); } return; } if (marked_cells[root_index]) return; if (!edges[root_index].empty()) { if (analyze_loops) active_stack.push_back(root_index); active_cells[root_index] = true; for (int left_n : edges[root_index]) sort_worker(left_n, marked_cells, active_cells, active_stack); if (analyze_loops) active_stack.pop_back(); active_cells[root_index] = false; } marked_cells[root_index] = true; sorted.push_back(nodes[root_index]); } }; YOSYS_NAMESPACE_END #endif