// // Copyright rev.ng Labs Srl. See LICENSE.md for details. // #include "llvm/ADT/Twine.h" #include "llvm/Support/Casting.h" #include "llvm/Support/DOTGraphTraits.h" #include "llvm/Support/GraphWriter.h" #include "revng/ADT/FilteredGraphTraits.h" #include "revng/ADT/GenericGraph.h" #include "revng/Model/Binary.h" #include "revng/Model/Type.h" #include "revng/Support/Assert.h" #include "revng/Support/Debug.h" #include "DependencyGraph.h" static Logger<> Log{ "model-to-header-dependencies" }; using namespace llvm; static llvm::StringRef toString(TypeNode::Kind K) { switch (K) { case TypeNode::Kind::TypeName: return "TypeName"; case TypeNode::Kind::FullType: return "FullType"; } return "Invalid"; } void DependencyGraph::addNode(const model::Type *T) { constexpr auto TypeName = TypeNode::Kind::TypeName; auto *NameNode = GenericGraph::addNode(TypeNode{ T, TypeName }); TypeToNode[TypeKindPair{ T, TypeName }] = NameNode; constexpr auto FullType = TypeNode::Kind::FullType; auto *FullNode = GenericGraph::addNode(TypeNode{ T, FullType }); TypeToNode[TypeKindPair{ T, FullType }] = FullNode; } std::string getNodeLabel(const TypeDependencyNode *N) { return (Twine(getNameFromYAMLScalar(N->T->key())) + Twine("-") + Twine(toString(N->K))) .str(); } using DepNode = TypeDependencyNode; using DepGraph = DependencyGraph; std::string llvm::DOTGraphTraits::getNodeLabel(const DepNode *N, const DepGraph *G) { return ::getNodeLabel(N); } static TypeDependencyNode * getDependencyForTypeName(const model::QualifiedType &QT, const TypeToDependencyNodeMap &TypeToNode) { const auto *Unqualified = QT.UnqualifiedType().get(); // If we find at least a pointer qualifier, then we only need the name of // the unqualified type, not its full definition. bool ArrayFound = false; for (const auto &Qualifier : QT.Qualifiers()) { if (model::Qualifier::isPointer(Qualifier)) return TypeToNode.at({ Unqualified, TypeNode::Kind::TypeName }); if (model::Qualifier::isArray(Qualifier)) ArrayFound = true; } // If we reach this point we haven't found not even a single pointer // qualifier. // If we did find an array qualifier, we need the full type of the // unqualified type. if (ArrayFound) return TypeToNode.at({ Unqualified, TypeNode::Kind::FullType }); // Otherwise we can get away with just the name of the unqualified type. return TypeToNode.at({ Unqualified, TypeNode::Kind::TypeName }); } static TypeDependencyNode * getDependencyForFullType(const model::QualifiedType &QT, const TypeToDependencyNodeMap &TypeToNode) { const auto *Unqualified = QT.UnqualifiedType().get(); // If we find at least a pointer qualifier, then we only need the name of // the unqualified type, not its full definition. for (const auto &Qualifier : QT.Qualifiers()) if (model::Qualifier::isPointer(Qualifier)) return TypeToNode.at({ Unqualified, TypeNode::TypeName }); // If we reach this point we haven't found not even a single pointer // qualifier. Given that we need the full definition, we need the full // type of of the unqualified type. return TypeToNode.at({ Unqualified, TypeNode::FullType }); } static void registerDependencies(const model::Type *T, const TypeToDependencyNodeMap &TypeToNode) { using Edge = std::pair; llvm::SmallVector Deps; switch (T->Kind()) { case model::TypeKind::Invalid: { revng_abort("Primitive or Invalid type should never depend on others"); } break; case model::TypeKind::PrimitiveType: { // Nothing to do here. Primitive types names and full definitions can // always be defined without dependencies, because they are either not // necessary (for primitive types that are already present in stdint.h) // or they boil down to a simple typedef of a type in stdint.h. In both // cases, the definition provide visibility on both the name and on the // full definition. } break; case model::TypeKind::EnumType: { // Enum names and full definitions could always be conjured out of thin // air. However, given that we have enums with underlying primitive // types, for consistency we enforce that enums names and full // definitions always depend on full definition of the underlying // primitive type. This adds a little unnessary edges, but makes the // overall structure of the graph easier to reason about. Moreover, full // definitions of primitive types can also always be conjured out of // thin air, so we're always sure that this does not generates infinite // loops. const auto *E = cast(T); const model::QualifiedType &UnderlyingQT = E->UnderlyingType(); revng_assert(T->edges().size() == 1 and UnderlyingQT == *T->edges().begin() and UnderlyingQT.Qualifiers().empty()); auto *U = cast(UnderlyingQT.UnqualifiedType().get()); auto *EnumName = TypeToNode.at({ E, TypeNode::Kind::TypeName }); auto *EnumFull = TypeToNode.at({ E, TypeNode::Kind::FullType }); auto *UnderFull = TypeToNode.at({ U, TypeNode::Kind::FullType }); Deps.push_back({ EnumName, UnderFull }); Deps.push_back({ EnumFull, UnderFull }); revng_log(Log, getNodeLabel(EnumName) << " depends on " << getNodeLabel(UnderFull)); revng_log(Log, getNodeLabel(EnumFull) << " depends on " << getNodeLabel(UnderFull)); } break; case model::TypeKind::StructType: case model::TypeKind::UnionType: { // Struct and Union names can always be conjured out of thin air thanks to // typedefs. So we only need to add dependencies between their full // definition and the full definition of their fields. auto *Full = TypeToNode.at({ T, TypeNode::Kind::FullType }); for (const model::QualifiedType &QT : T->edges()) { TypeDependencyNode *Dep = getDependencyForFullType(QT, TypeToNode); Deps.push_back({ Full, Dep }); revng_log(Log, getNodeLabel(Full) << " depends on " << getNodeLabel(Dep)); } } break; case model::TypeKind::TypedefType: { // Typedefs are nasty. auto *TD = cast(T); const model::QualifiedType &Underlying = TD->UnderlyingType(); auto *TDName = TypeToNode.at({ TD, TypeNode::Kind::TypeName }); TypeDependencyNode *NameDep = getDependencyForTypeName(Underlying, TypeToNode); Deps.push_back({ TDName, NameDep }); revng_log(Log, getNodeLabel(TDName) << " depends on " << getNodeLabel(NameDep)); auto *TDFull = TypeToNode.at({ TD, TypeNode::Kind::FullType }); TypeDependencyNode *FullDep = getDependencyForFullType(Underlying, TypeToNode); Deps.push_back({ TDFull, FullDep }); revng_log(Log, getNodeLabel(TDFull) << " depends on " << getNodeLabel(FullDep)); } break; case model::TypeKind::CABIFunctionType: case model::TypeKind::RawFunctionType: { // For function types we can print a valid typedef definition as long as // we have visibility on all the names of all the argument types and all // return types. auto *FullNode = TypeToNode.at({ T, TypeNode::Kind::FullType }); auto *NameNode = TypeToNode.at({ T, TypeNode::Kind::TypeName }); for (const model::QualifiedType &QT : T->edges()) { // The two dependencies added here below are actually stricter than // necessary for e.g. stack arguments. // The reason is that, on the model, stack arguments are represented by // value, but in some cases they are actually passed by pointer in C. // Given that with the edges() accessor here we cannot discriminate, we // decided to err on the strict side. // This could potentially create graphs with loops of dependencies, or // make some instances not solvable, that would have otherwise been valid. // This should only happen in nasty cases involving loops of function // pointers, but possibly other cases we haven't considered. // Overall, these remote cases have never showed up until now. // If this ever happen, we'll need to fix this properly, either relaxing // this dependencies, or pre-processing the model so that what reaches // this point is always guaranteed to be in a form that can be emitted. TypeDependencyNode *FullDep = getDependencyForFullType(QT, TypeToNode); Deps.push_back({ FullNode, FullDep }); TypeDependencyNode *NameDep = getDependencyForTypeName(QT, TypeToNode); Deps.push_back({ NameNode, NameDep }); revng_log(Log, getNodeLabel(FullNode) << " depends on " << getNodeLabel(FullDep)); revng_log(Log, getNodeLabel(NameNode) << " depends on " << getNodeLabel(NameDep)); } } break; default: revng_abort(); } for (const auto &[From, To] : Deps) { revng_log(Log, "Adding edge " << getNodeLabel(From) << " --> " << getNodeLabel(To)); From->addSuccessor(To); } } DependencyGraph buildDependencyGraph(const TypeVector &Types) { DependencyGraph Dependencies; // Create nodes for (const UpcastablePointer &MT : Types) Dependencies.addNode(MT.get()); // Compute dependencies and add them to the graph for (const UpcastablePointer &MT : Types) registerDependencies(MT.get(), Dependencies.TypeNodes()); if (Log.isEnabled()) llvm::ViewGraph(&Dependencies, "type-deps.dot"); return Dependencies; }