blob: e80d4ae889857d60a0b0b0524582fc0be1e5e680 [file] [log] [blame]
// 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