diff --git a/lib/Decompiler/CMakeLists.txt b/lib/Decompiler/CMakeLists.txt index ae565149c..8d75ed8c1 100644 --- a/lib/Decompiler/CMakeLists.txt +++ b/lib/Decompiler/CMakeLists.txt @@ -4,7 +4,9 @@ revng_add_analyses_library(Decompiler revngc ASTBuildAnalysis.cpp + DLAHelpers.cpp DLAStep.cpp + DLATypeSystem.cpp CDecompiler.cpp CDecompilerAction.cpp CDecompilerBeautify.cpp diff --git a/lib/Decompiler/DLAHelpers.cpp b/lib/Decompiler/DLAHelpers.cpp new file mode 100644 index 000000000..e859359a2 --- /dev/null +++ b/lib/Decompiler/DLAHelpers.cpp @@ -0,0 +1,121 @@ +// +// Copyright rev.ng Srls. See LICENSE.md for details. +// + +#include +#include +#include +#include + +#include "llvm/ADT/DepthFirstIterator.h" +#include "llvm/ADT/SCCIterator.h" +#include "llvm/ADT/SmallPtrSet.h" +#include "llvm/ADT/SmallSet.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/IR/Instructions.h" +#include "llvm/IR/Module.h" +#include "llvm/IR/Value.h" + +#include "revng/Support/Assert.h" +#include "revng/Support/Debug.h" + +#include "DLAHelpers.h" + +#include "DLATypeSystem.h" + +using std::conditional_t; + +template +using LLVMValueT = conditional_t, + const llvm::Value, + llvm::Value>; + +template +std::enable_if_t, llvm::InsertValueInst>, + llvm::SmallVector *, 2>> +getConstQualifiedInsertValueLeafOperands(T *Ins) { + using ValueT = LLVMValueT; + llvm::SmallVector Results; + llvm::SmallSet FoundIds; + auto *StructTy = llvm::cast(Ins->getType()); + unsigned NumFields = StructTy->getNumElements(); + Results.resize(NumFields, nullptr); + revng_assert(Ins->getNumUses() == 1 + and (isa(Ins->use_begin()->getUser()) + or isa(Ins->use_begin()->getUser()))); + while (1) { + revng_assert(Ins->getNumIndices() == 1); + unsigned FieldId = Ins->getIndices()[0]; + revng_assert(FieldId < NumFields); + revng_assert(FoundIds.count(FieldId) == 0); + FoundIds.insert(FieldId); + ValueT *Op = Ins->getInsertedValueOperand(); + revng_assert(isa(Op->getType()) + or isa(Op->getType())); + revng_assert(Results[FieldId] == nullptr); + Results[FieldId] = Op; + ValueT *Tmp = Ins->getAggregateOperand(); + Ins = llvm::dyn_cast(Tmp); + if (not Ins) { + revng_assert(llvm::isa(Tmp) + or llvm::isa(Tmp)); + break; + } + } + return Results; +}; + +llvm::SmallVector +getInsertValueLeafOperands(llvm::InsertValueInst *Ins) { + return getConstQualifiedInsertValueLeafOperands(Ins); +} + +llvm::SmallVector +getInsertValueLeafOperands(const llvm::InsertValueInst *Ins) { + return getConstQualifiedInsertValueLeafOperands(Ins); +} + +template +std::enable_if_t, llvm::CallInst>, + llvm::SmallVector *, 2>> +getConstQualifiedExtractedValuesFromCall(T *Call) { + using ValueT = LLVMValueT; + llvm::SmallVector Results; + llvm::SmallSet FoundIds; + auto *StructTy = llvm::cast(Call->getType()); + unsigned NumFields = StructTy->getNumElements(); + Results.resize(NumFields, nullptr); + revng_assert(Call->getNumUses() <= NumFields); + for (auto *Extract : Call->users()) { + auto *E = cast(Extract); + revng_assert(E->getNumIndices() == 1); + unsigned FieldId = E->getIndices()[0]; + revng_assert(FieldId < NumFields); + revng_assert(FoundIds.count(FieldId) == 0); + FoundIds.insert(FieldId); + revng_assert(isa(E->getType()) + or isa(E->getType())); + revng_assert(Results[FieldId] == nullptr); + Results[FieldId] = E; + } + return Results; +}; + +llvm::SmallVector +getExtractedValuesFromCall(llvm::CallInst *Call) { + return getConstQualifiedExtractedValuesFromCall(Call); +} + +llvm::SmallVector +getExtractedValuesFromCall(const llvm::CallInst *Call) { + return getConstQualifiedExtractedValuesFromCall(Call); +} + +uint64_t getLoadStoreSizeFromPtrOpUse(const dla::LayoutTypeSystem &TS, + const llvm::Use *U) { + llvm::Value *AddrOperand = U->get(); + auto *PtrTy = cast(AddrOperand->getType()); + llvm::Type *AccessedT = PtrTy->getElementType(); + const llvm::DataLayout &DL = TS.getModule().getDataLayout(); + return DL.getTypeAllocSize(AccessedT); +}; diff --git a/lib/Decompiler/DLAHelpers.h b/lib/Decompiler/DLAHelpers.h new file mode 100644 index 000000000..460eb9f56 --- /dev/null +++ b/lib/Decompiler/DLAHelpers.h @@ -0,0 +1,37 @@ +#pragma once + +// +// Copyright (c) rev.ng Srls. See LICENSE.md for details. +// + +#include "llvm/ADT/SmallVector.h" + +namespace llvm { + +class InsertValueInst; +class CallInst; +class Use; +class Value; + +} // end namespace llvm + +namespace dla { + +class LayoutTypeSystem; + +} // end namespace dla + +extern llvm::SmallVector +getInsertValueLeafOperands(llvm::InsertValueInst *); + +extern llvm::SmallVector +getInsertValueLeafOperands(const llvm::InsertValueInst *); + +extern llvm::SmallVector +getExtractedValuesFromCall(llvm::CallInst *); + +extern llvm::SmallVector +getExtractedValuesFromCall(const llvm::CallInst *); + +uint64_t getLoadStoreSizeFromPtrOpUse(const dla::LayoutTypeSystem &TS, + const llvm::Use *U); diff --git a/lib/Decompiler/DLATypeSystem.cpp b/lib/Decompiler/DLATypeSystem.cpp new file mode 100644 index 000000000..44cce5312 --- /dev/null +++ b/lib/Decompiler/DLATypeSystem.cpp @@ -0,0 +1,702 @@ +// +// Copyright (c) rev.ng Srls. See LICENSE.md for details. +// + +#include +#include + +#include "llvm/ADT/SCCIterator.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/Analysis/ScalarEvolutionExpressions.h" +#include "llvm/IR/Argument.h" +#include "llvm/IR/Instruction.h" +#include "llvm/Support/FormattedStream.h" +#include "llvm/Support/raw_ostream.h" + +#include "revng/ADT/FilteredGraphTraits.h" +#include "revng/Support/Debug.h" +#include "revng/Support/DebugHelper.h" +#include "revng/Support/IRHelpers.h" + +#include "DLATypeSystem.h" + +#include "DLAHelpers.h" + +using namespace llvm; + +std::string dumpToString(const dla::LayoutTypeSystemNode *N) { + std::string Result = "LTSN ID: " + std::to_string(N->ID); + return Result; +} + +std::string dumpToString(const dla::OffsetExpression &OE) { + std::string Result; + Result += "Off: " + std::to_string(OE.Offset); + auto NStrides = OE.Strides.size(); + revng_assert(NStrides == OE.TripCounts.size()); + if (not OE.Strides.empty()) { + for (decltype(NStrides) N = 0; N < NStrides; ++N) { + Result += ", {" + std::to_string(OE.Strides[N]) + ','; + if (OE.TripCounts[N].has_value()) + Result += std::to_string(OE.TripCounts[N].value()); + else + Result += "none"; + Result += '}'; + } + } + return Result; +} + +namespace dla { + +void LayoutTypePtr::print(raw_ostream &Out) const { + Out << '{'; + Out << "0x"; + Out.write_hex(reinterpret_cast(V)); + Out << " ["; + if (isa(V)) { + Out << "fname: " << V->getName(); + } else { + if (auto *I = dyn_cast(V)) + Out << "In Func: " << I->getFunction()->getName() << " Instr: "; + else if (auto *A = dyn_cast(V)) + Out << "In Func: " << A->getParent()->getName() << " Arg: "; + + Out.write_escaped(getName(V)); + } + Out << "], 0x"; + Out.write_hex(FieldIdx); + Out << '}'; +} + +void LayoutTypeSystemNode::printAsOperand(llvm::raw_ostream &OS, + bool /* unused */) { + OS << ID; +} + +namespace { + +static constexpr size_t str_len(const char *S) { + return S ? (*S ? (1 + str_len(S + 1)) : 0UL) : 0UL; +} + +// We use \l here instead of \n, because graphviz has this sick way of saying +// that the text in the node labels should be left-justified +static constexpr const char DoRet[] = "\\l"; +static constexpr const char NoRet[] = ""; +static_assert(sizeof(DoRet) == (str_len(DoRet) + 1)); +static_assert(sizeof(NoRet) == (str_len(NoRet) + 1)); + +static constexpr const char Equal[] = "Equal"; +static constexpr const char Inherits[] = "Inherits from"; +static constexpr const char Instance[] = "Has Instance of: "; +static constexpr const char Unexpected[] = "Unexpected!"; +static_assert(sizeof(Equal) == (str_len(Equal) + 1)); +static_assert(sizeof(Inherits) == (str_len(Inherits) + 1)); +static_assert(sizeof(Instance) == (str_len(Instance) + 1)); +static_assert(sizeof(Unexpected) == (str_len(Unexpected) + 1)); +} // end unnamed namespace + +void LayoutTypeSystem::dumpDotOnFile(const char *FName) const { + std::error_code EC; + raw_fd_ostream DotFile(FName, EC); + revng_check(not EC, "Could not open file for printing LayoutTypeSystem dot"); + + DotFile << "digraph LayoutTypeSystem {\n"; + DotFile << " // List of nodes\n"; + + unsigned AccessID = 0; + for (const LayoutTypeSystemNode *L : getLayoutsRange()) { + + DotFile << " node_" << L->ID << " [shape=rect,label=\"NODE ID: " << L->ID + << " Size: " << L->L.Size << ' '; + + const auto LayoutToTypePtrsIt = LayoutToTypePtrsMap.find(L); + if (LayoutToTypePtrsIt != LayoutToTypePtrsMap.end()) { + DotFile << DoRet; + const auto &TypePtrSet = LayoutToTypePtrsIt->second; + revng_assert(not TypePtrSet.empty()); + StringRef Ret = (TypePtrSet.size() > 1) ? + StringRef(DoRet, sizeof(DoRet) - 1) : + StringRef(NoRet, sizeof(NoRet) - 1); + + for (const dla::LayoutTypePtr &P : TypePtrSet) { + P.print(DotFile); + DotFile << Ret; + } + } + + DotFile << "\"];\n"; + + for (const llvm::Use *U : L->L.Accesses) { + const auto *I = cast(U->getUser()); + const llvm::Function *F = I->getFunction(); + DotFile << " access_" << AccessID << " [label=\"In: " << F->getName() + << " : "; + DotFile.write_escaped(dumpToString(U->getUser())); + DotFile << "\"];\n" + << " node_" << L->ID << " -> access_" << AccessID << ";\n"; + ++AccessID; + } + } + + DotFile << " // List of edges\n"; + + for (LayoutTypeSystemNode *L : getLayoutsRange()) { + + uint64_t SrcNodeId = L->ID; + + for (const auto &PredP : L->Predecessors) { + const TypeLinkTag *PredTag = PredP.second; + const auto SameLink = [&](auto &OtherPair) { + return SrcNodeId == OtherPair.first->ID and PredTag == OtherPair.second; + }; + revng_assert(std::any_of(PredP.first->Successors.begin(), + PredP.first->Successors.end(), + SameLink)); + } + + std::string Extra; + for (const auto &SuccP : L->Successors) { + const TypeLinkTag *EdgeTag = SuccP.second; + const auto SameLink = [&](auto &OtherPair) { + return SrcNodeId == OtherPair.first->ID and EdgeTag == OtherPair.second; + }; + revng_assert(std::any_of(SuccP.first->Predecessors.begin(), + SuccP.first->Predecessors.end(), + SameLink)); + const auto *TgtNode = SuccP.first; + const char *EdgeLabel = nullptr; + size_t LabelSize = 0; + Extra.clear(); + switch (EdgeTag->getKind()) { + case TypeLinkTag::LK_Equality: { + EdgeLabel = Equal; + LabelSize = sizeof(Equal) - 1; + } break; + case TypeLinkTag::LK_Instance: { + EdgeLabel = Instance; + LabelSize = sizeof(Instance) - 1; + Extra = dumpToString(EdgeTag->getOffsetExpr()); + } break; + case TypeLinkTag::LK_Inheritance: { + EdgeLabel = Inherits; + LabelSize = sizeof(Inherits) - 1; + } break; + default: { + EdgeLabel = Unexpected; + LabelSize = sizeof(Unexpected) - 1; + } break; + } + DotFile << " node_" << SrcNodeId << " -> node_" << TgtNode->ID + << " [label=\"" << StringRef(EdgeLabel, LabelSize) << Extra + << "\"];\n"; + } + } + + DotFile << "}\n"; +} + +static void assertGetLayoutTypePreConditions(const Value *V, unsigned Id) { + // We accept only integers, pointer, and function types (which are actually + // used for representing return types of functions) + const Type *VT = V->getType(); + revng_assert(isa(VT) or isa(VT) + or isa(VT)); + // The only case where we accept Id != max are Functions that return structs + revng_assert(Id == std::numeric_limits::max() + or cast(V)->getReturnType()->isStructTy()); +} + +LayoutTypeSystemNode *LayoutTypeSystem::getLayoutType(const llvm::SCEV *S) { + if (S == nullptr) + return nullptr; + if (auto *U = dyn_cast(S)) { + llvm::Value *V = U->getValue(); + return getLayoutType(V); + } + + // LayoutTypePtr Key(S, Id); + return nullptr; // TypePtrToLayoutMap.at(Key); +} + +LayoutTypeSystemNode * +LayoutTypeSystem::getLayoutType(const Value *V, unsigned Id) { + + if (V == nullptr) + return nullptr; + + // Check pre-conditions + assertGetLayoutTypePreConditions(V, Id); + + LayoutTypePtr Key(V, Id); + return TypePtrToLayoutMap.at(Key); +} + +std::pair +LayoutTypeSystem::getOrCreateLayoutType(const Value *V, unsigned Id) { + using LTSN = LayoutTypeSystemNode; + + if (V == nullptr) + return std::make_pair(nullptr, false); + + // Check pre-conditions + assertGetLayoutTypePreConditions(V, Id); + + LayoutTypePtr Key(V, Id); + auto HintIt = TypePtrToLayoutMap.lower_bound(Key); + if (HintIt != TypePtrToLayoutMap.end() + and not TypePtrToLayoutMap.key_comp()(Key, HintIt->first)) { + return std::make_pair(HintIt->second, false); + } + + // Create a new layout + const auto &[LayoutIt, Success] = Layouts.insert(std::make_unique(NID)); + revng_assert(Success); + if (Success) + ++NID; + LayoutTypeSystemNode *Res = LayoutIt->get(); + // Add the mapping between the new LayoutTypeSystemNode and the LayoutTypePtr + // that is associated to V. + const auto &[_, Ok] = LayoutToTypePtrsMap[Res].insert(Key); + TypePtrToLayoutMap.emplace_hint(HintIt, Key, Res); + revng_assert(Ok); + return std::make_pair(Res, true); +} + +static void assertGetLayoutTypePreConditions(const Value &V) { + const Type *VTy = V.getType(); + // We accept only integers, pointer, structs and and function types (which + // are actually used for representing return types of functions) + revng_assert(isa(VTy) or isa(VTy) + or isa(VTy) or isa(VTy)); +} + +SmallVector +LayoutTypeSystem::getLayoutTypes(const Value &V) { + assertGetLayoutTypePreConditions(V); + SmallVector Results; + const Type *VTy = V.getType(); + if (const auto *F = dyn_cast(&V)) { + auto *RetTy = F->getReturnType(); + if (auto *StructTy = dyn_cast(RetTy)) { + unsigned FieldId = 0; + unsigned FieldNum = StructTy->getNumElements(); + for (; FieldId < FieldNum; ++FieldId) { + auto FieldTy = StructTy->getElementType(FieldId); + revng_assert(isa(FieldTy) or isa(FieldTy)); + Results.push_back(getLayoutType(&V, FieldId)); + } + } else { + revng_assert(isa(VTy) or isa(VTy)); + Results.push_back(getLayoutType(&V)); + } + } else if (auto *StructTy = dyn_cast(VTy)) { + revng_assert(not isa(V)); + SmallVector LeafVals; + + if (auto *Ins = dyn_cast(&V)) + LeafVals = getInsertValueLeafOperands(Ins); + else if (auto *Call = dyn_cast(&V)) + LeafVals = getExtractedValuesFromCall(Call); + else + LeafVals.resize(StructTy->getNumElements(), nullptr); + + for (const Value *LeafVal : LeafVals) + Results.push_back(getLayoutType(LeafVal)); + } else { + // For non-struct and non-function types we only add a LayoutTypeSystemNode + Results.push_back(getLayoutType(&V)); + } + return Results; +} + +SmallVector, 2> +LayoutTypeSystem::getOrCreateLayoutTypes(const Value &V) { + assertGetLayoutTypePreConditions(V); + SmallVector, 2> Results; + const Type *VTy = V.getType(); + if (const auto *F = dyn_cast(&V)) { + auto *RetTy = F->getReturnType(); + if (auto *StructTy = dyn_cast(RetTy)) { + unsigned FieldId = 0; + unsigned FieldNum = StructTy->getNumElements(); + for (; FieldId < FieldNum; ++FieldId) { + auto FieldTy = StructTy->getElementType(FieldId); + revng_assert(isa(FieldTy) or isa(FieldTy)); + Results.push_back(getOrCreateLayoutType(&V, FieldId)); + } + } else { + revng_assert(isa(VTy) or isa(VTy)); + Results.push_back(getOrCreateLayoutType(&V)); + } + } else if (auto *StructTy = dyn_cast(VTy)) { + revng_assert(not isa(V)); + SmallVector LeafVals; + + if (auto *Ins = dyn_cast(&V)) + LeafVals = getInsertValueLeafOperands(Ins); + else if (auto *Call = dyn_cast(&V)) + LeafVals = getExtractedValuesFromCall(Call); + else + LeafVals.resize(StructTy->getNumElements(), nullptr); + + for (const Value *LeafVal : LeafVals) + Results.push_back(getOrCreateLayoutType(LeafVal)); + } else { + // For non-struct and non-function types we only add a LayoutTypeSystemNode + Results.push_back(getOrCreateLayoutType(&V)); + } + return Results; +} + +static void +fixPredSucc(LayoutTypeSystemNode *From, LayoutTypeSystemNode *Into) { + + // Helper lambdas + const auto IsFrom = [From](const LayoutTypeSystemNode::Link &L) { + return L.first == From; + }; + const auto IsInto = [Into](const LayoutTypeSystemNode::Link &L) { + return L.first == Into; + }; + + // All the predecessors of all the successors of From are updated so that they + // point to Into + for (auto &[Neighbor, Tag] : From->Successors) { + auto PredBegin = Neighbor->Predecessors.begin(); + auto PredEnd = Neighbor->Predecessors.end(); + auto It = std::find_if(PredBegin, PredEnd, IsFrom); + auto End = std::find_if_not(It, PredEnd, IsFrom); + while (It != End) { + auto Next = std::next(It); + auto Extracted = Neighbor->Predecessors.extract(It); + revng_assert(Extracted); + Neighbor->Predecessors.insert({ Into, Extracted.value().second }); + It = Next; + } + } + + // All the successors of all the predecessors of From are updated so that they + // point to Into + for (auto &[Neighbor, Tag] : From->Predecessors) { + auto SuccBegin = Neighbor->Successors.begin(); + auto SuccEnd = Neighbor->Successors.end(); + auto It = std::find_if(SuccBegin, SuccEnd, IsFrom); + auto End = std::find_if_not(It, SuccEnd, IsFrom); + while (It != End) { + auto Next = std::next(It); + auto Extracted = Neighbor->Successors.extract(It); + revng_assert(Extracted); + Neighbor->Successors.insert({ Into, Extracted.value().second }); + It = Next; + } + } + + // Merge all the predecessors and successors. + { + Into->Predecessors.insert(From->Predecessors.begin(), + From->Predecessors.end()); + Into->Successors.insert(From->Successors.begin(), From->Successors.end()); + } + + // Remove self-references from predecessors and successors. + { + const auto RemoveSelfEdges = [IsFrom, IsInto](auto &NeighborsSet) { + auto It = NeighborsSet.begin(); + while (It != NeighborsSet.end()) { + auto Next = std::next(It); + if (IsInto(*It) or IsFrom(*It)) + NeighborsSet.erase(It); + It = Next; + } + }; + RemoveSelfEdges(Into->Predecessors); + RemoveSelfEdges(Into->Successors); + } +} + +static Logger<> MergeLog("dla-merge-nodes"); + +inline void +LayoutTypeSystem::mergeNodes(LayoutTypeSystemNode *From, + LayoutTypeSystemNode *Into, + llvm::SmallSet *IntoTypePtrs) { + revng_log(MergeLog, "Merging: " << From << " Into: " << Into); + auto LayoutIt = Layouts.find(From); + revng_assert(LayoutIt != Layouts.end()); + + auto ToMergeLayoutToTypePtrsIt = LayoutToTypePtrsMap.find(From); + revng_assert(ToMergeLayoutToTypePtrsIt != LayoutToTypePtrsMap.end()); + + if (IntoTypePtrs == nullptr) + IntoTypePtrs = &LayoutToTypePtrsMap.at(Into); + else + revng_assert(IntoTypePtrs == &LayoutToTypePtrsMap.at(Into)); + + Into->L.Accesses.insert(From->L.Accesses.begin(), From->L.Accesses.end()); + + // Update LayoutToTypePtrsMap, the map that maps each LayoutTypeSystemNode * + // to the set of LayoutTypePtrs that are associated to it. + auto &MergedTypePtrs = ToMergeLayoutToTypePtrsIt->second; + IntoTypePtrs->insert(MergedTypePtrs.begin(), MergedTypePtrs.end()); + + // Update TypePtrToLayoutMap, the inverse map of LayoutToTypePtrsMap + for (auto P : MergedTypePtrs) { + revng_assert(TypePtrToLayoutMap.at(P) == From); + TypePtrToLayoutMap.at(P) = Into; + } + + fixPredSucc(From, Into); + + // Clear stuff in LayoutTypeToPtrsMap, because now From must be removed. + LayoutToTypePtrsMap.erase(ToMergeLayoutToTypePtrsIt); + + // Remove From from Layouts + Layouts.erase(LayoutIt); +} + +using LayoutTypeSystemNodePtrVec = std::vector; + +void LayoutTypeSystem::mergeNodes(const LayoutTypeSystemNodePtrVec &ToMerge) { + revng_assert(ToMerge.size() > 1ULL); + LayoutTypeSystemNode *Candidate = ToMerge[0]; + auto &IntoTypePtrs = LayoutToTypePtrsMap.at(Candidate); + for (size_t I = 1ULL; I < ToMerge.size(); ++I) + mergeNodes(ToMerge[I], Candidate, &IntoTypePtrs); +} + +void LayoutTypeSystem::removeNode(LayoutTypeSystemNode *N) { + auto It = LayoutToTypePtrsMap.find(N); + revng_assert(It != LayoutToTypePtrsMap.end()); + for (auto P : It->second) + TypePtrToLayoutMap.erase(P); + LayoutToTypePtrsMap.erase(It); + auto LayoutIt = Layouts.find(N); + revng_assert(LayoutIt != Layouts.end()); + + const auto IsN = [N](const LayoutTypeSystemNode::Link &L) { + return L.first == N; + }; + + for (auto &[Neighbor, Tag] : LayoutIt->get()->Successors) { + auto PredBegin = Neighbor->Predecessors.begin(); + auto PredEnd = Neighbor->Predecessors.end(); + auto It = std::find_if(PredBegin, PredEnd, IsN); + auto End = std::find_if_not(It, PredEnd, IsN); + Neighbor->Predecessors.erase(It, End); + } + + for (auto &[Neighbor, Tag] : LayoutIt->get()->Predecessors) { + auto SuccBegin = Neighbor->Successors.begin(); + auto SuccEnd = Neighbor->Successors.end(); + auto It = std::find_if(SuccBegin, SuccEnd, IsN); + auto End = std::find_if_not(It, SuccEnd, IsN); + Neighbor->Successors.erase(It, End); + } + + Layouts.erase(LayoutIt); +} + +static Logger<> VerifyDLALog("dla-verify"); + +bool LayoutTypeSystem::verifyConsistency() const { + for (auto &NodeUPtr : Layouts) { + if (NodeUPtr.get() == nullptr) { + if (VerifyDLALog.isEnabled()) + revng_check(false); + return false; + } + // Check that predecessors and successors are consistent + for (auto &P : NodeUPtr->Predecessors) { + if (P.first == nullptr) { + if (VerifyDLALog.isEnabled()) + revng_check(false); + return false; + } + + // same edge with same tag + auto It = P.first->Successors.find({ NodeUPtr.get(), P.second }); + if (It == P.first->Successors.end()) { + if (VerifyDLALog.isEnabled()) + revng_check(false); + return false; + } + } + for (auto &P : NodeUPtr->Successors) { + if (P.first == nullptr) { + if (VerifyDLALog.isEnabled()) + revng_check(false); + return false; + } + + // same edge with same tag + auto It = P.first->Predecessors.find({ NodeUPtr.get(), P.second }); + if (It == P.first->Predecessors.end()) { + if (VerifyDLALog.isEnabled()) + revng_check(false); + return false; + } + } + + // Check that there are no self-edges + for (auto &P : NodeUPtr->Predecessors) { + LayoutTypeSystemNode *Pred = P.first; + if (Pred == NodeUPtr.get()) { + if (VerifyDLALog.isEnabled()) + revng_check(false); + return false; + } + } + + for (auto &P : NodeUPtr->Successors) { + LayoutTypeSystemNode *Succ = P.first; + if (Succ == NodeUPtr.get()) { + if (VerifyDLALog.isEnabled()) + revng_check(false); + return false; + } + } + } + return true; +} + +bool LayoutTypeSystem::verifyDAG() const { + if (not verifyConsistency()) + return false; + + if (not verifyInheritanceDAG()) + return false; + + if (not verifyInstanceDAG()) + return false; + + std::set SCCHeads; + + // A graph is a DAG if and only if all its strongly connected components have + // size 1 + std::set Visited; + for (const auto &Node : llvm::nodes(this)) { + revng_assert(Node != nullptr); + if (Visited.count(Node)) + continue; + + auto I = scc_begin(Node); + auto E = scc_end(Node); + for (; I != E; ++I) { + Visited.insert(I->begin(), I->end()); + if (I.hasLoop()) { + if (VerifyDLALog.isEnabled()) + revng_check(false); + return false; + } + } + } + + return true; +} + +bool LayoutTypeSystem::verifyInheritanceDAG() const { + if (not verifyConsistency()) + return false; + + // A graph is a DAG if and only if all its strongly connected components have + // size 1 + std::set Visited; + for (const auto &Node : llvm::nodes(this)) { + revng_assert(Node != nullptr); + if (Visited.count(Node)) + continue; + + using GraphNodeT = const LayoutTypeSystemNode *; + using InheritanceNodeT = EdgeFilteredGraph; + auto I = scc_begin(InheritanceNodeT(Node)); + auto E = scc_end(InheritanceNodeT(Node)); + for (; I != E; ++I) { + Visited.insert(I->begin(), I->end()); + if (I.hasLoop()) { + if (VerifyDLALog.isEnabled()) + revng_check(false); + return false; + } + } + } + + return true; +} + +bool LayoutTypeSystem::verifyInstanceDAG() const { + if (not verifyConsistency()) + return false; + + // A graph is a DAG if and only if all its strongly connected components have + // size 1 + std::set Visited; + for (const auto &Node : llvm::nodes(this)) { + revng_assert(Node != nullptr); + if (Visited.count(Node)) + continue; + + using GraphNodeT = const LayoutTypeSystemNode *; + using InstanceNodeT = EdgeFilteredGraph; + auto I = scc_begin(InstanceNodeT(Node)); + auto E = scc_end(InstanceNodeT(Node)); + for (; I != E; ++I) { + Visited.insert(I->begin(), I->end()); + if (I.hasLoop()) { + if (VerifyDLALog.isEnabled()) + revng_check(false); + return false; + } + } + } + + return true; +} + +bool LayoutTypeSystem::verifyNoEquality() const { + if (not verifyConsistency()) + return false; + for (const auto &Node : llvm::nodes(this)) { + using LTSN = LayoutTypeSystemNode; + for (const auto &Edge : llvm::children_edges(Node)) { + if (isEqualityEdge(Edge)) { + if (VerifyDLALog.isEnabled()) + revng_check(false); + return false; + } + } + } + return true; +} + +bool LayoutTypeSystem::verifyLeafs() const { + for (const auto &Node : llvm::nodes(this)) { + if (isLeaf(Node)) { + if (not hasValidLayout(Node)) { + if (VerifyDLALog.isEnabled()) + revng_check(false); + return false; + } + } + } + return true; +} + +bool LayoutTypeSystem::verifyInheritanceTree() const { + using GraphNodeT = const LayoutTypeSystemNode *; + using InheritanceNodeT = EdgeFilteredGraph; + using GT = GraphTraits; + for (GraphNodeT Node : llvm::nodes(this)) { + auto Beg = GT::child_begin(Node); + auto End = GT::child_end(Node); + if ((Beg != End) and (std::next(Beg) != End)) { + if (VerifyDLALog.isEnabled()) + revng_check(false); + return false; + } + } + return true; +} + +} // end namespace dla diff --git a/lib/Decompiler/DLATypeSystem.h b/lib/Decompiler/DLATypeSystem.h new file mode 100644 index 000000000..d06638dfe --- /dev/null +++ b/lib/Decompiler/DLATypeSystem.h @@ -0,0 +1,658 @@ +#pragma once + +// +// Copyright (c) rev.ng Srls. See LICENSE.md for details. +// + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "llvm/ADT/GraphTraits.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallSet.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/IR/DerivedTypes.h" +#include "llvm/IR/Function.h" +#include "llvm/IR/Type.h" +#include "llvm/IR/Value.h" +#include "llvm/Support/Casting.h" + +#include "revng/ADT/FilteredGraphTraits.h" +#include "revng/Support/Assert.h" + +namespace llvm { + +class SCEV; + +} // end namespace llvm + +namespace dla { + +/// A representation of a pointer to a type. +class LayoutTypePtr { + const llvm::Value *V; + unsigned FieldIdx; + +public: + explicit LayoutTypePtr(const llvm::Value *Val, + unsigned Idx = std::numeric_limits::max()) : + V(Val), FieldIdx(Idx) { + revng_assert(Val != nullptr); + using llvm::cast; + using llvm::dyn_cast; + using llvm::isa; + [[maybe_unused]] const llvm::Type *Ty = V->getType(); + // We only accept Functions or Values with integer or pointer type. + revng_assert(isa(V) or isa(Ty) + or isa(Ty)); + + // FieldIdx != std::numeric_limits::max() if and only if V is a + // Function that returns a struct. + const auto *F = dyn_cast(V); + const auto *StructTy = (F == nullptr) ? + nullptr : + dyn_cast(F->getReturnType()); + [[maybe_unused]] bool VIsFunctionAndReturnsStruct = StructTy != nullptr; + revng_assert(VIsFunctionAndReturnsStruct + xor (FieldIdx == std::numeric_limits::max())); + + // If V is a Function that returns a struct then FieldIdx < number of + // elements of the returned struct. + revng_assert(not VIsFunctionAndReturnsStruct + or FieldIdx < StructTy->getNumElements()); + } + + LayoutTypePtr() = delete; + ~LayoutTypePtr() = default; + LayoutTypePtr(const LayoutTypePtr &) = default; + LayoutTypePtr(LayoutTypePtr &&) = default; + LayoutTypePtr &operator=(const LayoutTypePtr &) = default; + LayoutTypePtr &operator=(LayoutTypePtr &&) = default; + + std::strong_ordering operator<=>(const LayoutTypePtr &Other) const { + if (auto Cmp = V <=> Other.V; Cmp != 0) + return Cmp; + return FieldIdx <=> Other.FieldIdx; + } + + bool operator<(const LayoutTypePtr &Other) const { + return (*this <=> Other) < 0; + } + + bool operator==(const LayoutTypePtr &Other) const { + return operator<=>(Other) == 0; + } + + void print(llvm::raw_ostream &Out) const; + friend struct std::less; +}; // end class LayoutTypePtr + +/// Class used to mark InstanceLinkTags between LayoutTypes +struct OffsetExpression { + llvm::SmallVector, 4> TripCounts; + llvm::SmallVector Strides; + int64_t Offset; + + explicit OffsetExpression(int64_t Off) : + TripCounts(), Strides(), Offset(Off) {} + explicit OffsetExpression() : OffsetExpression(0LL){}; + + std::strong_ordering operator<=>(const OffsetExpression &Other) const { + auto OffsetCompare = Offset <=> Other.Offset; + if (OffsetCompare != 0) + return OffsetCompare; + + if (Strides < Other.Strides) + return std::strong_ordering::less; + else if (Other.Strides < Strides) + return std::strong_ordering::greater; + + if (TripCounts < Other.TripCounts) + return std::strong_ordering::less; + else if (Other.TripCounts < TripCounts) + return std::strong_ordering::greater; + + return std::strong_ordering::equal; + } + + bool operator<(const OffsetExpression &Other) const { + return (*this <=> Other) < 0; + } +}; // end class OffsetExpression + +class TypeLinkTag { +public: + enum LinkKind { + LK_Inheritance, + LK_Equality, + LK_Instance, + LK_All, + }; + + static const char *toString(enum LinkKind K) { + switch (K) { + case LK_Inheritance: + return "Inheritance"; + case LK_Equality: + return "Equality"; + case LK_Instance: + return "Instance"; + case LK_All: + return "None"; + } + revng_unreachable(); + } + +protected: + OffsetExpression OE; + const LinkKind Kind; + + explicit TypeLinkTag(LinkKind K, OffsetExpression &&O) : OE(O), Kind(K) {} + + // TODO: potentially we are interested in marking TypeLinkTags with some info + // that allows us to track which step on the type system has created them. + // However, this is not necessary now, so I'll leave it for when we have + // identified more clearly if we really need it and why. + +public: + TypeLinkTag() = delete; + + LinkKind getKind() const { return Kind; } + + const OffsetExpression &getOffsetExpr() const { + revng_assert(getKind() == LK_Instance); + return OE; + } + + static TypeLinkTag equalityTag() { + return TypeLinkTag(LK_Equality, OffsetExpression{}); + } + + static TypeLinkTag inheritanceTag() { + return TypeLinkTag(LK_Inheritance, OffsetExpression{}); + } + + // This method is templated just to enable perfect forwarding. + template + static TypeLinkTag instanceTag(OffsetExpressionT &&O) { + return TypeLinkTag(LK_Instance, std::forward(O)); + } + + std::strong_ordering operator<=>(const TypeLinkTag &Other) const { + if (auto Cmp = (Kind <=> Other.Kind); Cmp != 0) + return Cmp; + return OE <=> Other.OE; + } + + bool operator<(const TypeLinkTag &Other) const { + return (*this <=> Other) < 0; + } +}; // end class TypeLinkTag + +struct LayoutType { + // TODO: do we really need the accesses? + llvm::SmallPtrSet Accesses{}; + uint64_t Size{}; +}; // end class LayoutType + +class LayoutTypeSystem; + +struct LayoutTypeSystemNode { + const uint64_t ID = 0ULL; + using Link = std::pair; + using NeighborsSet = std::set; + NeighborsSet Successors{}; + NeighborsSet Predecessors{}; + LayoutType L{}; + LayoutTypeSystemNode(uint64_t I) : ID(I) {} + +public: + // This method should never be called, but it's necessary to be able to use + // some llvm::GraphTraits algorithms, otherwise they wouldn't compile. + LayoutTypeSystem *getParent() { + revng_unreachable(); + return nullptr; + } + + void printAsOperand(llvm::raw_ostream &OS, bool /* unused */); +}; + +inline bool hasValidLayout(const LayoutTypeSystemNode *N) { + if (N == nullptr) + return false; + return not N->L.Accesses.empty(); +} + +struct LayoutTypeSystemNodePtrCompare { + using is_transparent = std::true_type; + +private: + struct Helper { + const LayoutTypeSystemNode *P; + Helper() = default; + ~Helper() = default; + Helper(const Helper &) = default; + Helper(Helper &&) = default; + Helper &operator=(const Helper &) = default; + Helper &operator=(Helper &&) = default; + Helper(const LayoutTypeSystemNode *Ptr) : P(Ptr) {} + Helper(const std::unique_ptr &Ptr) : P(Ptr.get()) {} + }; + +public: + bool operator()(const Helper A, const Helper B) const { return A.P < B.P; } +}; + +class LayoutTypeSystem { +public: + using Node = LayoutTypeSystemNode; + using NodePtr = LayoutTypeSystemNode *; + using NodeUniquePtr = std::unique_ptr; + + static dla::LayoutTypeSystem::NodePtr + getNodePtr(const dla::LayoutTypeSystem::NodeUniquePtr &P) { + return P.get(); + } + + LayoutTypeSystem(llvm::Module &Mod) : M(Mod) {} + + llvm::Module &getModule() const { return M; } + +public: + LayoutTypeSystemNode *getLayoutType(const llvm::Value *V, unsigned Id); + + LayoutTypeSystemNode *getLayoutType(const llvm::Value *V) { + return getLayoutType(V, std::numeric_limits::max()); + }; + + std::pair + getOrCreateLayoutType(const llvm::Value *V, unsigned Id); + + std::pair + getOrCreateLayoutType(const llvm::Value *V) { + return getOrCreateLayoutType(V, std::numeric_limits::max()); + } + + llvm::SmallVector + getLayoutTypes(const llvm::Value &V); + + llvm::SmallVector, 2> + getOrCreateLayoutTypes(const llvm::Value &V); + + LayoutTypeSystemNode *getLayoutType(const llvm::SCEV *S); + +protected: + // This method is templated only to enable perfect forwarding. + template + std::pair + addLink(LayoutTypeSystemNode *Src, LayoutTypeSystemNode *Tgt, TagT &&Tag) { + if (Src == nullptr or Tgt == nullptr or Src == Tgt) + return std::make_pair(nullptr, false); + revng_assert(Layouts.count(Src)); + revng_assert(Layouts.count(Tgt)); + auto It = LinkTags.insert(std::forward(Tag)).first; + revng_assert(It != LinkTags.end()); + const TypeLinkTag *T = &*It; + bool New = Src->Successors.insert(std::make_pair(Tgt, T)).second; + New |= Tgt->Predecessors.insert(std::make_pair(Src, T)).second; + return std::make_pair(T, New); + } + +public: + std::pair + addEqualityLink(LayoutTypeSystemNode *Src, LayoutTypeSystemNode *Tgt) { + auto ForwardLinkTag = addLink(Src, Tgt, dla::TypeLinkTag::equalityTag()); + auto BackwardLinkTag = addLink(Tgt, Src, dla::TypeLinkTag::equalityTag()); + revng_assert(ForwardLinkTag == BackwardLinkTag); + return ForwardLinkTag; + } + + std::pair + addInheritanceLink(LayoutTypeSystemNode *Src, LayoutTypeSystemNode *Tgt) { + return addLink(Src, Tgt, dla::TypeLinkTag::inheritanceTag()); + } + + // This method is templated just to enable perfect forwarding. + template + std::pair + addInstanceLink(LayoutTypeSystemNode *Src, + LayoutTypeSystemNode *Tgt, + OffsetExpressionT &&OE) { + using OET = OffsetExpressionT; + return addLink(Src, + Tgt, + dla::TypeLinkTag::instanceTag(std::forward(OE))); + } + + void dumpDotOnFile(const char *FName) const; + + void dumpDotOnFile(const std::string &FName) const { + dumpDotOnFile(FName.c_str()); + } + + auto getNumLayouts() const { return Layouts.size(); } + + auto getLayoutsRange() const { + return llvm::make_range(llvm::map_iterator(Layouts.begin(), getNodePtr), + llvm::map_iterator(Layouts.end(), getNodePtr)); + } + +protected: + void mergeNodes(LayoutTypeSystemNode *From, + LayoutTypeSystemNode *Into, + llvm::SmallSet *IntoTypePtrs); + +public: + void mergeNodes(LayoutTypeSystemNode *From, LayoutTypeSystemNode *Into) { + return mergeNodes(From, Into, nullptr); + } + + void mergeNodes(const std::vector &ToMerge); + + const llvm::SmallSet & + getLayoutTypePtrs(const LayoutTypeSystemNode *N) const { + return LayoutToTypePtrsMap.at(N); + } + + bool hasLayoutTypePtrs(const LayoutTypeSystemNode *N) const { + return LayoutToTypePtrsMap.count(N); + } + + void removeNode(LayoutTypeSystemNode *N); + +private: + // A reference to the associated Module + llvm::Module &M; + + uint64_t NID = 0ULL; + + // Holds all the LayoutTypeSystemNode + std::set, + LayoutTypeSystemNodePtrCompare> + Layouts; + + // Maps llvm::Value to layout types. + // This map is updated along the way when the DLA algorithm merges + // LayoutTypeSystemNodes that are considered to represent the same type. + std::map TypePtrToLayoutMap; + + // Maps layout types to the set of LayoutTypePtr representing the llvm::Value + // that generated them. + std::map> + LayoutToTypePtrsMap; + + // Holds the link tags, so that they can be deduplicated and referred to using + // TypeLinkTag * in the links inside LayoutTypeSystemNode + std::set LinkTags; + +public: + // Checks that is valid, and returns true if it is, false otherwise + bool verifyConsistency() const; + // Checks that is valid and a DAG, and returns true if it is, false otherwise + bool verifyDAG() const; + // Checks that is valid and a DAG, and returns true if it is, false otherwise + bool verifyInheritanceDAG() const; + // Checks that is valid and a DAG, and returns true if it is, false otherwise + bool verifyInstanceDAG() const; + // Checks that the type system, filtered looking only at inheritance edges, is + // a tree, meaning that a give LayoutTypeSystemNode cannot inherit from two + // different LayoutTypeSystemNodes. + bool verifyInheritanceTree() const; + // Checks that there are no leaf nodes without valid layout information + bool verifyLeafs() const; + // Checks that there are no equality edges. + bool verifyNoEquality() const; +}; // end class LayoutTypeSystem + +} // end namespace dla + +template<> +struct llvm::GraphTraits { +protected: + using NodeT = dla::LayoutTypeSystemNode; + +public: + using NodeRef = NodeT *; + using EdgeRef = const NodeT::NeighborsSet::value_type; + + static NodeRef edge_dest(EdgeRef E) { return E.first; } + using EdgeDestT = NodeRef (*)(EdgeRef); + + using ChildEdgeIteratorType = NodeT::NeighborsSet::iterator; + using ChildIteratorType = llvm::mapped_iterator; + + static NodeRef getEntryNode(const NodeRef &N) { return N; } + + static ChildIteratorType child_begin(NodeRef N) { + return llvm::map_iterator(N->Successors.begin(), edge_dest); + } + static ChildIteratorType child_end(NodeRef N) { + return llvm::map_iterator(N->Successors.end(), edge_dest); + } + + static ChildEdgeIteratorType child_edge_begin(NodeRef N) { + return N->Successors.begin(); + } + static ChildEdgeIteratorType child_edge_end(NodeRef N) { + return N->Successors.end(); + } +}; // end struct llvm::GraphTraits + +template<> +struct llvm::GraphTraits { +protected: + using NodeT = const dla::LayoutTypeSystemNode; + +public: + using NodeRef = NodeT *; + using EdgeRef = const NodeT::NeighborsSet::value_type; + + static NodeRef edge_dest(EdgeRef E) { return E.first; } + using EdgeDestT = NodeRef (*)(EdgeRef); + + using ChildEdgeIteratorType = NodeT::NeighborsSet::iterator; + using ChildIteratorType = llvm::mapped_iterator; + + static NodeRef getEntryNode(const NodeRef &N) { return N; } + + static ChildIteratorType child_begin(NodeRef N) { + return llvm::map_iterator(N->Successors.begin(), edge_dest); + } + static ChildIteratorType child_end(NodeRef N) { + return llvm::map_iterator(N->Successors.end(), edge_dest); + } + + static ChildEdgeIteratorType child_edge_begin(NodeRef N) { + return N->Successors.begin(); + } + static ChildEdgeIteratorType child_edge_end(NodeRef N) { + return N->Successors.end(); + } +}; // end struct llvm::GraphTraits + +template<> +struct llvm::GraphTraits> { +protected: + using NodeT = dla::LayoutTypeSystemNode; + +public: + using NodeRef = NodeT *; + using EdgeRef = const NodeT::NeighborsSet::value_type; + + static NodeRef edge_dest(EdgeRef E) { return E.first; } + using EdgeDestT = NodeRef (*)(EdgeRef); + + using ChildEdgeIteratorType = NodeT::NeighborsSet::iterator; + using ChildIteratorType = llvm::mapped_iterator; + + static NodeRef getEntryNode(const NodeRef &N) { return N; } + + static ChildIteratorType child_begin(NodeRef N) { + return llvm::map_iterator(N->Predecessors.begin(), edge_dest); + } + static ChildIteratorType child_end(NodeRef N) { + return llvm::map_iterator(N->Predecessors.end(), edge_dest); + } + + static ChildEdgeIteratorType child_edge_begin(NodeRef N) { + return N->Predecessors.begin(); + } + static ChildEdgeIteratorType child_edge_end(NodeRef N) { + return N->Predecessors.end(); + } +}; // end struct llvm::GraphTraits + +template<> +struct llvm::GraphTraits> { +protected: + using NodeT = const dla::LayoutTypeSystemNode; + +public: + using NodeRef = NodeT *; + using EdgeRef = const NodeT::NeighborsSet::value_type; + + static NodeRef edge_dest(EdgeRef E) { return E.first; } + using EdgeDestT = NodeRef (*)(EdgeRef); + + using ChildEdgeIteratorType = NodeT::NeighborsSet::iterator; + using ChildIteratorType = llvm::mapped_iterator; + + static NodeRef getEntryNode(const NodeRef &N) { return N; } + + static ChildIteratorType child_begin(NodeRef N) { + return llvm::map_iterator(N->Predecessors.begin(), edge_dest); + } + static ChildIteratorType child_end(NodeRef N) { + return llvm::map_iterator(N->Predecessors.end(), edge_dest); + } + + static ChildEdgeIteratorType child_edge_begin(NodeRef N) { + return N->Predecessors.begin(); + } + static ChildEdgeIteratorType child_edge_end(NodeRef N) { + return N->Predecessors.end(); + } +}; // end struct llvm::GraphTraits + +template<> +struct llvm::GraphTraits + : public llvm::GraphTraits { +protected: + using NodeSetItT = std::set::iterator; + using NodeUniquePtr = dla::LayoutTypeSystem::NodeUniquePtr; + using GetPtrT = dla::LayoutTypeSystem::NodePtr (*)(const NodeUniquePtr &); + +public: + using nodes_iterator = llvm::mapped_iterator; + + static NodeRef getEntryNode(const dla::LayoutTypeSystem *) { return nullptr; } + + static nodes_iterator nodes_begin(const dla::LayoutTypeSystem *G) { + return G->getLayoutsRange().begin(); + } + + static nodes_iterator nodes_end(const dla::LayoutTypeSystem *G) { + return G->getLayoutsRange().end(); + } + + static unsigned size(const dla::LayoutTypeSystem *G) { + return G->getNumLayouts(); + } +}; // struct llvm::GraphTraits + +template<> +struct llvm::GraphTraits + : public llvm::GraphTraits { +protected: + using NodeSetItT = std::set::iterator; + using NodeUniquePtr = dla::LayoutTypeSystem::NodeUniquePtr; + using GetPtrT = dla::LayoutTypeSystem::NodePtr (*)(const NodeUniquePtr &); + +public: + using nodes_iterator = llvm::mapped_iterator; + + static NodeRef getEntryNode(const dla::LayoutTypeSystem *) { return nullptr; } + + static nodes_iterator nodes_begin(const dla::LayoutTypeSystem *G) { + return G->getLayoutsRange().begin(); + } + + static nodes_iterator nodes_end(const dla::LayoutTypeSystem *G) { + return G->getLayoutsRange().end(); + } + + static unsigned size(dla::LayoutTypeSystem *G) { return G->getNumLayouts(); } +}; // struct llvm::GraphTraits + +namespace dla { + +template +inline bool hasLinkKind(const dla::LayoutTypeSystemNode::Link &L) { + if constexpr (K == dla::TypeLinkTag::LinkKind::LK_All) + return true; + else + return L.second->getKind() == K; +} + +inline bool +isEqualityEdge(const llvm::GraphTraits::EdgeRef &E) { + return hasLinkKind(E); +} + +inline bool +isInheritanceEdge(const llvm::GraphTraits::EdgeRef &E) { + return hasLinkKind(E); +} + +inline bool +isInstanceEdge(const llvm::GraphTraits::EdgeRef &E) { + return hasLinkKind(E); +} + +template +inline bool isLeaf(const LayoutTypeSystemNode *N) { + using LTSN = const LayoutTypeSystemNode; + using GraphNodeT = LTSN *; + using FilteredNodeT = EdgeFilteredGraph>; + using GT = llvm::GraphTraits; + return GT::child_begin(N) == GT::child_end(N); +} + +inline bool isInheritanceLeaf(const LayoutTypeSystemNode *N) { + return isLeaf(N); +} + +inline bool isInstanceLeaf(const LayoutTypeSystemNode *N) { + return isLeaf(N); +} + +template +inline bool isRoot(const LayoutTypeSystemNode *N) { + using LTSN = const LayoutTypeSystemNode; + using GraphNodeT = LTSN *; + using FilteredNodeT = EdgeFilteredGraph>; + using IGT = llvm::GraphTraits>; + return IGT::child_begin(N) == IGT::child_end(N); +} + +inline bool isInheritanceRoot(const LayoutTypeSystemNode *N) { + return isRoot(N); +} + +inline bool isInstanceRoot(const LayoutTypeSystemNode *N) { + return isRoot(N); +} +} // end namespace dla + +std::string dumpToString(const dla::OffsetExpression &OE); +std::string dumpToString(const dla::LayoutTypeSystemNode *N);