| // Copyright 2017-2020 The Verible Authors. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| #include "verilog/analysis/dependencies.h" |
| |
| #include <iostream> |
| |
| #include "common/strings/compare.h" |
| #include "common/strings/display_utils.h" |
| #include "common/util/logging.h" |
| #include "verilog/analysis/symbol_table.h" |
| #include "verilog/analysis/verilog_project.h" |
| |
| namespace verilog { |
| |
| static FileDependencies::symbol_index_type CreateSymbolMapFromSymbolTable( |
| const SymbolTableNode& root, const VerilogProject* project) { |
| VLOG(1) << __FUNCTION__ << ": collecting definitions"; |
| CHECK(project != nullptr) |
| << "VerilogProject* is required for dependency analysis."; |
| FileDependencies::symbol_index_type symbols_index; |
| |
| using SymbolData = FileDependencies::SymbolData; |
| |
| // Collect definers of root-level symbols. |
| for (const SymbolTableNode::key_value_type& child : root) { |
| const absl::string_view symbol_name(child.first); |
| const VerilogSourceFile* file_origin = child.second.Value().file_origin; |
| if (file_origin == nullptr) continue; |
| |
| SymbolData& symbol_data(symbols_index[symbol_name]); |
| if (symbol_data.definer == nullptr) { |
| // Take the first definition, arbitrarily. |
| symbol_data.definer = file_origin; |
| } |
| } |
| |
| // Collect all unqualified and unresolved references from all scopes. |
| VLOG(1) << __FUNCTION__ << ": collecting references"; |
| root.ApplyPreOrder([&symbols_index, project](const SymbolTableNode& node) { |
| const SymbolInfo& symbol_info(node.Value()); |
| for (const DependentReferences& ref : |
| symbol_info.local_references_to_bind) { |
| // Only look at the root reference node, which is unqualified. |
| const ReferenceComponent& ref_comp(ref.components->Value()); |
| const absl::string_view ref_id(ref_comp.identifier); |
| VLOG(2) << " referenced id: " << ref_id; |
| |
| const VerilogSourceFile* ref_file_origin = |
| project->LookupFileOrigin(ref_id); |
| if (ref_file_origin == nullptr) continue; // unknown file |
| |
| if (ref_comp.resolved_symbol != nullptr) { |
| const VerilogSourceFile* def_file_origin = |
| ref_comp.resolved_symbol->Value().file_origin; |
| if (ref_file_origin == def_file_origin) { |
| continue; // skip already resolved symbols in same file |
| } |
| } |
| |
| VLOG(2) << " registering reference edge"; |
| SymbolData& symbol_data(symbols_index[ref_id]); |
| symbol_data.referencers.insert(ref_file_origin); |
| } |
| }); |
| VLOG(1) << "end of " << __FUNCTION__; |
| return symbols_index; // move |
| } |
| |
| // Struct for printing readable dependency edge. |
| struct DepEdge { |
| const VerilogSourceFile* const ref; |
| const VerilogSourceFile* const def; |
| const FileDependencies::SymbolNameSet& symbols; |
| }; |
| |
| static std::ostream& operator<<(std::ostream& stream, const DepEdge& dep) { |
| return stream << '"' << dep.ref->ReferencedPath() << "\" depends on \"" |
| << dep.def->ReferencedPath() << "\" for symbols " |
| << verible::SequenceFormatter(dep.symbols, ", ", "{ ", " }"); |
| } |
| |
| static FileDependencies::file_deps_graph_type |
| CreateFileDependenciesFromSymbolMap( |
| const FileDependencies::symbol_index_type& symbol_map) { |
| VLOG(1) << __FUNCTION__; |
| FileDependencies::file_deps_graph_type file_deps; |
| for (const auto& symbol_entry : symbol_map) { |
| const absl::string_view symbol_name(symbol_entry.first); |
| const FileDependencies::SymbolData& symbol_info(symbol_entry.second); |
| const VerilogSourceFile* def = symbol_info.definer; |
| // If no definition is found, then do not create any edges for it. |
| if (def == nullptr) continue; |
| |
| for (const VerilogSourceFile* ref : symbol_info.referencers) { |
| // Skip self-edges. |
| if (ref == def) continue; |
| VLOG(2) << DepEdge{.ref = ref, .def = def, .symbols = {symbol_name}}; |
| file_deps[ref][def].insert(symbol_name); |
| } |
| } |
| VLOG(1) << "end of " << __FUNCTION__; |
| return file_deps; // move |
| } |
| |
| FileDependencies::FileDependencies(const SymbolTable& symbol_table) |
| : root_symbols_index(CreateSymbolMapFromSymbolTable( |
| symbol_table.Root(), symbol_table.Project())), |
| file_deps(CreateFileDependenciesFromSymbolMap(root_symbols_index)) { |
| // All the work is done by the initializers. |
| } |
| |
| bool FileDependencies::Empty() const { |
| for (const auto& ref : file_deps) { |
| for (const auto& def : ref.second) { |
| if (!def.second.empty()) return false; |
| } |
| } |
| return true; |
| } |
| |
| void FileDependencies::TraverseDependencyEdges( |
| const std::function<void(const node_type&, const node_type&, |
| const SymbolNameSet&)>& edge_func) const { |
| for (const auto& tail : file_deps) { |
| for (const auto& head : tail.second) { |
| edge_func(tail.first, head.first, head.second); |
| } |
| } |
| } |
| |
| std::ostream& FileDependencies::PrintGraph(std::ostream& stream) const { |
| TraverseDependencyEdges([&stream](const node_type& ref, const node_type& def, |
| const SymbolNameSet& symbols) { |
| stream << DepEdge{.ref = ref, .def = def, .symbols = symbols} << std::endl; |
| }); |
| return stream; |
| } |
| |
| std::ostream& operator<<(std::ostream& stream, const FileDependencies& deps) { |
| return deps.PrintGraph(stream); |
| } |
| |
| } // namespace verilog |