| /* | |
| * 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. | |
| * | |
| */ | |
| // 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<typename Key, typename T, typename OPS = hash_ops<Key>> | |
| struct stackmap | |
| { | |
| private: | |
| std::vector<dict<Key, T*, OPS>> backup_state; | |
| dict<Key, T, OPS> current_state; | |
| static T empty_tuple; | |
| public: | |
| stackmap() { } | |
| stackmap(const dict<Key, T, OPS> &other) : current_state(other) { } | |
| template<typename Other> | |
| 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<Key, T, OPS> &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 T, typename C = std::less<T>> | |
| struct TopoSort | |
| { | |
| bool analyze_loops, found_loops; | |
| std::map<T, std::set<T, C>, C> database; | |
| std::set<std::set<T, C>> loops; | |
| std::vector<T> sorted; | |
| TopoSort() | |
| { | |
| analyze_loops = true; | |
| found_loops = false; | |
| } | |
| void node(T n) | |
| { | |
| if (database.count(n) == 0) | |
| database[n] = std::set<T, C>(); | |
| } | |
| void edge(T left, T right) | |
| { | |
| node(left); | |
| database[right].insert(left); | |
| } | |
| void sort_worker(const T &n, std::set<T, C> &marked_cells, std::set<T, C> &active_cells, std::vector<T> &active_stack) | |
| { | |
| if (active_cells.count(n)) { | |
| found_loops = true; | |
| if (analyze_loops) { | |
| std::set<T, C> loop; | |
| for (int i = GetSize(active_stack)-1; i >= 0; i--) { | |
| loop.insert(active_stack[i]); | |
| if (active_stack[i] == n) | |
| break; | |
| } | |
| loops.insert(loop); | |
| } | |
| return; | |
| } | |
| if (marked_cells.count(n)) | |
| return; | |
| if (!database.at(n).empty()) | |
| { | |
| if (analyze_loops) | |
| active_stack.push_back(n); | |
| active_cells.insert(n); | |
| for (auto &left_n : database.at(n)) | |
| sort_worker(left_n, marked_cells, active_cells, active_stack); | |
| if (analyze_loops) | |
| active_stack.pop_back(); | |
| active_cells.erase(n); | |
| } | |
| marked_cells.insert(n); | |
| sorted.push_back(n); | |
| } | |
| bool sort() | |
| { | |
| loops.clear(); | |
| sorted.clear(); | |
| found_loops = false; | |
| std::set<T, C> marked_cells; | |
| std::set<T, C> active_cells; | |
| std::vector<T> active_stack; | |
| for (auto &it : database) | |
| sort_worker(it.first, marked_cells, active_cells, active_stack); | |
| log_assert(GetSize(sorted) == GetSize(database)); | |
| return !found_loops; | |
| } | |
| }; | |
| YOSYS_NAMESPACE_END | |
| #endif |