mirror of
https://github.com/revng/revng
synced 2026-06-21 14:07:57 +00:00
43ae7a91cb
Add a step that recognizes if two subtrees of a union node are topologically equivalent and merges them. This corresponds to removing duplicate fields in unions. This deduplication was prevously done while emitting layouts. A check is inserted into DLAMakeLayouts to assert that, after constructing unions, no union has only one child, which could be the case if we didn't deduplicate union fields in the graph.
414 lines
13 KiB
C++
414 lines
13 KiB
C++
//
|
|
// Copyright (c) rev.ng Srls. See LICENSE.md for details.
|
|
//
|
|
|
|
#include <algorithm>
|
|
#include <compare>
|
|
#include <iterator>
|
|
#include <memory>
|
|
#include <set>
|
|
#include <string>
|
|
#include <type_traits>
|
|
|
|
#include "llvm/ADT/PostOrderIterator.h"
|
|
#include "llvm/ADT/STLExtras.h"
|
|
#include "llvm/ADT/SmallString.h"
|
|
#include "llvm/ADT/SmallVector.h"
|
|
#include "llvm/ADT/iterator_range.h"
|
|
#include "llvm/Support/Debug.h"
|
|
#include "llvm/Support/raw_ostream.h"
|
|
|
|
#include "revng/ADT/FilteredGraphTraits.h"
|
|
#include "revng/Support/Assert.h"
|
|
#include "revng/Support/Debug.h"
|
|
|
|
#include "revng-c/DataLayoutAnalysis/DLALayouts.h"
|
|
#include "revng-c/DataLayoutAnalysis/DLATypeSystem.h"
|
|
|
|
#include "../DLAHelpers.h"
|
|
#include "DLAMakeLayouts.h"
|
|
|
|
using namespace llvm;
|
|
|
|
static Logger<> Log("dla-make-layouts");
|
|
|
|
namespace dla {
|
|
|
|
using LTSN = LayoutTypeSystemNode;
|
|
|
|
static Layout *makeInstanceChildLayout(Layout *ChildType,
|
|
const OffsetExpression &OE,
|
|
LayoutVector &Layouts) {
|
|
revng_assert(OE.Offset >= 0LL);
|
|
|
|
// If we have trip counts we have an array of children of type ChildType,
|
|
// otherwise ChildType already points to the right child type.
|
|
revng_assert(OE.Strides.size() == OE.TripCounts.size());
|
|
if (not OE.TripCounts.empty()) {
|
|
Layout *Inner = ChildType;
|
|
for (const auto &[TC, S] : llvm::zip(OE.TripCounts, OE.Strides)) {
|
|
revng_assert(S > 0LL);
|
|
Layout::layout_size_t StrideSize = (Layout::layout_size_t) (S);
|
|
|
|
// For now, we don't handle stuff that for which the size of the element
|
|
// is larger than the stride size
|
|
if (StrideSize < Inner->size())
|
|
return nullptr;
|
|
|
|
// If the stride (StrideSize) is larger than the size of the inner
|
|
// element, we need to reserve space after each element, using
|
|
// padding.
|
|
if (StrideSize > Inner->size()) {
|
|
StructLayout::fields_container_t StructFields;
|
|
StructFields.push_back(Inner);
|
|
Layout::layout_size_t PadSize = StrideSize - Inner->size();
|
|
Layout *Padding = createLayout<PaddingLayout>(Layouts, PadSize);
|
|
StructFields.push_back(Padding);
|
|
Inner = createLayout<StructLayout>(Layouts, std::move(StructFields));
|
|
}
|
|
|
|
// Create the real array of Inner elements.
|
|
Inner = createLayout<ArrayLayout>(Layouts, Inner, S, TC);
|
|
}
|
|
ChildType = Inner;
|
|
}
|
|
revng_assert(nullptr != ChildType);
|
|
|
|
if (OE.Offset > 0LL) {
|
|
// Create padding to insert before the field, according to the
|
|
// offset.
|
|
ArrayLayout::length_t Len = OE.Offset;
|
|
// Create the struct with the padding prepended to the field.
|
|
StructLayout::fields_container_t StructFields;
|
|
StructFields.push_back(createLayout<PaddingLayout>(Layouts, Len));
|
|
StructFields.push_back(ChildType);
|
|
ChildType = createLayout<StructLayout>(Layouts, std::move(StructFields));
|
|
}
|
|
revng_assert(nullptr != ChildType);
|
|
|
|
return ChildType;
|
|
}
|
|
|
|
static Layout *getLayout(const LayoutTypeSystem &TS,
|
|
LayoutPtrVector &OrderedLayouts,
|
|
const LTSN *N) {
|
|
// First, find the node's equivalence class ID
|
|
auto EqClassID = TS.getEqClasses().getEqClassID(N->ID);
|
|
if (not EqClassID)
|
|
return nullptr;
|
|
|
|
revng_assert(*EqClassID < OrderedLayouts.size());
|
|
// Get the layout at that position
|
|
Layout *L = OrderedLayouts[*EqClassID];
|
|
return L;
|
|
}
|
|
|
|
static Layout *makeLayout(const LayoutTypeSystem &TS,
|
|
const LTSN *N,
|
|
LayoutVector &Layouts,
|
|
LayoutPtrVector &OrderedLayouts) {
|
|
switch (N->InterferingInfo) {
|
|
|
|
case AllChildrenAreNonInterfering: {
|
|
|
|
StructLayout::fields_container_t SFlds;
|
|
|
|
// Create BaseLayout for leaf nodes
|
|
revng_assert(not isLeaf(N) or N->Size);
|
|
if (isLeaf(N)) {
|
|
Layout *AccessLayout = createLayout<BaseLayout>(Layouts, N->Size);
|
|
|
|
// If the leaf has an inheritance parent, wrap the BaseLayout into a
|
|
// struct
|
|
if (llvm::any_of(N->Predecessors, isInheritanceEdge)) {
|
|
SFlds.push_back(AccessLayout);
|
|
AccessLayout = createLayout<StructLayout>(Layouts, SFlds);
|
|
}
|
|
return AccessLayout;
|
|
}
|
|
|
|
struct OrderedChild {
|
|
int64_t Offset;
|
|
decltype(N->Size) Size;
|
|
LTSN *Child;
|
|
// Make it sortable
|
|
std::strong_ordering operator<=>(const OrderedChild &) const = default;
|
|
};
|
|
using ChildrenVec = llvm::SmallVector<OrderedChild, 8>;
|
|
|
|
// Collect the children in a vector. Here we use the OrderedChild struct,
|
|
// that embeds info on the size and offset of the children, so that we can
|
|
// later sort the vector according to it.
|
|
bool InheritsFromOther = false;
|
|
ChildrenVec Children;
|
|
for (auto &[Child, EdgeTag] : llvm::children_edges<const LTSN *>(N)) {
|
|
|
|
auto OrdChild = OrderedChild{
|
|
/* .Offset */ 0LL,
|
|
/* .Size */ Child->Size,
|
|
/* .Child */ Child,
|
|
};
|
|
|
|
switch (EdgeTag->getKind()) {
|
|
|
|
case TypeLinkTag::LK_Instance: {
|
|
const OffsetExpression &OE = EdgeTag->getOffsetExpr();
|
|
revng_assert(OE.Strides.size() == OE.TripCounts.size());
|
|
|
|
// Ignore stuff at negative offsets.
|
|
if (OE.Offset < 0LL)
|
|
continue;
|
|
|
|
OrdChild.Offset = OE.Offset;
|
|
for (const auto &[TripCount, Stride] :
|
|
llvm::reverse(llvm::zip(OE.TripCounts, OE.Strides))) {
|
|
|
|
// Strides should be positive. If they are not, we don't know
|
|
// anything about how the children is layed out, so we assume the
|
|
// children doesn't even exist.
|
|
if (Stride <= 0LL) {
|
|
OrdChild.Size = 0ULL;
|
|
break;
|
|
}
|
|
|
|
auto StrideSize = static_cast<uint64_t>(Stride);
|
|
|
|
// If we have a TripCount, we expect it to be strictly positive.
|
|
revng_assert(not TripCount.has_value() or TripCount.value() > 0LL);
|
|
|
|
// Arrays with unknown numbers of elements are considered as if
|
|
// they had a single element
|
|
auto NumElems = TripCount.has_value() ? TripCount.value() : 1;
|
|
revng_assert(NumElems);
|
|
|
|
// Here we are computing the larger size that is known to be
|
|
// accessed. So if we have an array, we consider it to be one
|
|
// element shorter than expected, and we add ChildSize only once
|
|
// at the end.
|
|
// This is equivalent to:
|
|
// ChildSize = (NumElems * StrideSize) - (StrideSize - ChildSize);
|
|
OrdChild.Size = ((NumElems - 1) * StrideSize) + OrdChild.Size;
|
|
}
|
|
} break;
|
|
|
|
case TypeLinkTag::LK_Inheritance: {
|
|
revng_assert(not InheritsFromOther);
|
|
InheritsFromOther = true;
|
|
} break;
|
|
|
|
default:
|
|
revng_unreachable("unexpected edge tag");
|
|
}
|
|
|
|
if (OrdChild.Offset >= 0LL and OrdChild.Size > 0ULL) {
|
|
Children.push_back(std::move(OrdChild));
|
|
}
|
|
}
|
|
|
|
std::sort(Children.begin(), Children.end());
|
|
|
|
if (VerifyLog.isEnabled()) {
|
|
auto It = Children.begin();
|
|
for (; It != Children.end() and std::next(It) != Children.end(); ++It) {
|
|
int64_t ThisEndByte = It->Offset + static_cast<int64_t>(It->Size);
|
|
revng_assert(ThisEndByte <= std::next(It)->Offset);
|
|
}
|
|
}
|
|
|
|
// For each member of the struct
|
|
uint64_t CurSize = 0U;
|
|
for (const auto &OrdChild : Children) {
|
|
const auto &[StartByte, Size, Child] = OrdChild;
|
|
revng_assert(StartByte >= 0LL and Size > 0ULL);
|
|
uint64_t Start = static_cast<uint64_t>(StartByte);
|
|
revng_assert(Start >= CurSize);
|
|
auto PadSize = Start - CurSize;
|
|
revng_assert(PadSize >= 0);
|
|
|
|
// If there is a "hole" between accesses, add it as padding
|
|
if (PadSize) {
|
|
Layout *Padding = createLayout<PaddingLayout>(Layouts, PadSize);
|
|
SFlds.push_back(Padding);
|
|
}
|
|
CurSize = Start + Size;
|
|
|
|
Layout *ChildType = getLayout(TS, OrderedLayouts, Child);
|
|
|
|
// Bail out if we have not constructed a union field, because it means
|
|
// that this is not a supported case yet.
|
|
if (not ChildType)
|
|
return nullptr;
|
|
|
|
SFlds.push_back(ChildType);
|
|
}
|
|
|
|
// This layout has no useful access or outgoing edges that can build the
|
|
// type. Just skip it for now until we support handling richer edges and
|
|
// emitting richer types
|
|
if (SFlds.empty())
|
|
return nullptr;
|
|
|
|
Layout *CreatedLayout = createLayout<StructLayout>(Layouts, SFlds);
|
|
|
|
return CreatedLayout;
|
|
} break;
|
|
|
|
case AllChildrenAreInterfering: {
|
|
revng_assert(N->Successors.size() > 1);
|
|
UnionLayout::elements_container_t UFlds;
|
|
revng_assert(not isLeaf(N));
|
|
|
|
// Look at all the instance-of edges and inheritance edges all together
|
|
bool InheritsFromOther = false;
|
|
bool HasNullChild = false;
|
|
for (auto &[Child, EdgeTag] : children_edges<const LTSN *>(N)) {
|
|
|
|
revng_log(Log, "Child ID: " << Child->ID);
|
|
revng_assert(Child->Size);
|
|
|
|
Layout *ChildType = getLayout(TS, OrderedLayouts, Child);
|
|
|
|
// Ignore children for which we haven't created a layout, because they
|
|
// only have children from which it was not possible to create valid
|
|
// layouts.
|
|
if (not ChildType) {
|
|
revng_log(Log, "No corresponding layout for " << Child->ID);
|
|
return nullptr;
|
|
}
|
|
|
|
switch (EdgeTag->getKind()) {
|
|
|
|
case TypeLinkTag::LK_Instance: {
|
|
revng_log(Log, "Instance");
|
|
const OffsetExpression &OE = EdgeTag->getOffsetExpr();
|
|
revng_log(Log, "Has Offset: " << OE.Offset);
|
|
ChildType = makeInstanceChildLayout(ChildType, OE, Layouts);
|
|
} break;
|
|
|
|
case TypeLinkTag::LK_Inheritance: {
|
|
revng_log(Log, "Inheritance");
|
|
// Treated as instance at offset 0, but can only have one
|
|
revng_assert(not InheritsFromOther);
|
|
InheritsFromOther = true;
|
|
ChildType = makeInstanceChildLayout(ChildType,
|
|
OffsetExpression{ 0ULL },
|
|
Layouts);
|
|
} break;
|
|
|
|
default:
|
|
revng_unreachable("unexpected edge");
|
|
}
|
|
|
|
// Bail out if we have not constructed a union field, because it means
|
|
// that this is not a supported case yet.
|
|
|
|
if (nullptr != ChildType) {
|
|
bool New = UFlds.insert(ChildType).second;
|
|
|
|
if (not New)
|
|
revng_log(Log, "Duplicate layout found, size: " << UFlds.size());
|
|
} else {
|
|
HasNullChild = true;
|
|
revng_log(Log, "No type created for " << Child->ID);
|
|
}
|
|
}
|
|
|
|
// This layout has no useful access or outgoing edges that can build the
|
|
// type. Just skip it for now until we support handling richer edges and
|
|
// emitting richer types
|
|
if (UFlds.empty())
|
|
return nullptr;
|
|
|
|
// If a null layout was generated for one of the children, or the node has
|
|
// more than one parent, the union might contain only one field. In this
|
|
// case, there is no point in emitting a union, so a struct must be emitted.
|
|
if (UFlds.size() == 1) {
|
|
revng_assert(HasNullChild);
|
|
StructLayout::fields_container_t Fields;
|
|
Fields.push_back(*UFlds.begin());
|
|
return createLayout<StructLayout>(Layouts, Fields);
|
|
}
|
|
|
|
return createLayout<UnionLayout>(Layouts, UFlds);
|
|
} break;
|
|
|
|
case Unknown:
|
|
default:
|
|
revng_unreachable();
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
LayoutPtrVector makeLayouts(const LayoutTypeSystem &TS, LayoutVector &Layouts) {
|
|
if (Log.isEnabled())
|
|
TS.dumpDotOnFile("final.dot");
|
|
|
|
if (VerifyLog.isEnabled())
|
|
revng_assert(TS.verifyDAG() and TS.verifyInheritanceTree()
|
|
and TS.verifyUnions());
|
|
|
|
// Prepare the vector of layouts that correspond to actual LayoutTypePtrs
|
|
LayoutPtrVector OrderedLayouts;
|
|
const auto EqClasses = TS.getEqClasses();
|
|
OrderedLayouts.resize(EqClasses.getNumClasses());
|
|
|
|
std::set<const LTSN *> Visited;
|
|
|
|
// Create Layouts
|
|
for (LTSN *Root : llvm::nodes(&TS)) {
|
|
revng_assert(Root != nullptr);
|
|
if (not isRoot(Root))
|
|
continue;
|
|
|
|
for (const LTSN *N : post_order_ext(Root, Visited)) {
|
|
// Leaves need to have ValidLayouts, otherwise they should have been
|
|
// trimmed by PruneLayoutNodesWithoutLayout
|
|
revng_assert(not isLeaf(N) or N->Size);
|
|
Layout *LN = makeLayout(TS, N, Layouts, OrderedLayouts);
|
|
if (not LN) {
|
|
revng_log(Log, "Node ID: " << N->ID << " Type: Empty");
|
|
continue;
|
|
}
|
|
|
|
// Insert the layout at the index corresponding to the node's eq. class
|
|
auto LayoutIdx = TS.getEqClasses().getEqClassID(N->ID);
|
|
revng_assert(LayoutIdx);
|
|
OrderedLayouts[*LayoutIdx] = LN;
|
|
|
|
if (Log.isEnabled()) {
|
|
llvm::dbgs() << "\nNode ID: " << N->ID << " Type: ";
|
|
Layout::printText(llvm::dbgs(), LN);
|
|
llvm::dbgs() << ";\n";
|
|
Layout::printGraphic(llvm::dbgs(), LN);
|
|
llvm::dbgs() << '\n';
|
|
}
|
|
}
|
|
}
|
|
|
|
return OrderedLayouts;
|
|
};
|
|
|
|
ValueLayoutMap makeLayoutMap(const LayoutTypePtrVect &Values,
|
|
const LayoutPtrVector &Layouts,
|
|
const VectEqClasses &EqClasses) {
|
|
ValueLayoutMap ValMap;
|
|
|
|
for (size_t I = 0; I < Values.size(); I++) {
|
|
// The layout of the I-th Value is stored at the EqClass(I) index
|
|
auto LayoutIdx = EqClasses.getEqClassID(I);
|
|
if (LayoutIdx and not Values[I].isEmpty()) {
|
|
|
|
auto *L = Layouts[*LayoutIdx];
|
|
if (not L)
|
|
continue;
|
|
|
|
auto NewPair = std::make_pair(Values[I], L);
|
|
bool New = ValMap.insert(NewPair).second;
|
|
revng_assert(New);
|
|
}
|
|
}
|
|
|
|
return ValMap;
|
|
}
|
|
} // end namespace dla
|