Files
revng-revng/lib/DataLayoutAnalysis/Middleend/DLAComputeUpperMemberAccess.cpp
T
Alvise de Faveri 4c8215fc47 DLA: Add pointers
* Changes to the `LayoutTypeSystem` graph

Pointers are identified in the TypeSystem graph as leaf nodes which
have a new type of edge (PointerEdge) that connects them to another
node of the graph. The destination of the edge represents the layout of
the pointed type.

* Changes to the Front-end

Pointer edges, and their destination nodes, are created by the DLA
front-end (`DLACreateIntraProceduralTypes`) whenever an access node has
a size that is compatible with the size of a pointer in the current
Architecture.
Successors might then be added to the newly generated node, if any,
by looking up the llvm::Value it is attached to.

* Changes to the Middle-end

Most of the DLA passes should ignore Pointer Edges, so they are modified
accordingly. Most notably, nodes that represent pointed layouts should
never be merged/pruned-off.

* Changes to the Back-end

The `TypeDeclCreationAction` of the decompiler and the `DLAMakeLayouts`
step of the DLA back-end are modified to take into account the new
information about pointers.

⚠️ There is a known issue with this version of the decompiler,
namely the fact that type loops are not detected and can cause the
emitter to enter an infinite loop.
2021-11-16 17:00:34 +01:00

130 lines
4.2 KiB
C++

//
// Copyright (c) rev.ng Srls. See LICENSE.md for details.
//
#include <memory>
#include <type_traits>
#include "llvm/ADT/PostOrderIterator.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/iterator_range.h"
#include "llvm/Support/Debug.h"
#include "revng/ADT/FilteredGraphTraits.h"
#include "revng/Support/Assert.h"
#include "revng/Support/Debug.h"
#include "revng-c/DataLayoutAnalysis/DLATypeSystem.h"
#include "../DLAHelpers.h"
#include "DLAStep.h"
using namespace llvm;
static Logger<> Log("dla-compute-upper-member-access");
namespace dla {
using LTSN = LayoutTypeSystemNode;
using GraphNodeT = LTSN *;
using NonPointerFilterT = EdgeFilteredGraph<GraphNodeT, isNotPointerEdge>;
using ConstNonPointerFilterT = EdgeFilteredGraph<const LTSN *,
isNotPointerEdge>;
bool ComputeUpperMemberAccesses::runOnTypeSystem(LayoutTypeSystem &TS) {
if (VerifyLog.isEnabled())
revng_assert(TS.verifyDAG() and TS.verifyInheritanceTree());
bool Changed = false;
using LTSN = LayoutTypeSystemNode;
std::set<const LTSN *> Visited;
for (LTSN *Root : llvm::nodes(&TS)) {
revng_assert(Root != nullptr);
// Leaves need to have ValidLayouts, otherwise they should have been trimmed
// by PruneLayoutNodesWithoutLayout
revng_assert(not isLeaf(Root) or Root->Size);
if (not isRoot(Root))
continue;
revng_assert(isInheritanceRoot(Root));
for (LTSN *N : post_order_ext(NonPointerFilterT(Root), Visited)) {
revng_assert(not isLeaf(N) or N->Size);
uint64_t FinalSize = N->Size;
// Look at all the instance-of edges and inheritance edges all together.
bool HasBaseClass = false;
for (auto &[Child, EdgeTag] : children_edges<ConstNonPointerFilterT>(N)) {
auto ChildSize = Child->Size;
revng_assert(ChildSize > 0LL);
switch (EdgeTag->getKind()) {
case TypeLinkTag::LK_Inheritance: {
// Treated as instance at offset 0, but can only have one.
// Should only have one parent in inheritance hierarchy.
revng_assert(not HasBaseClass);
HasBaseClass = true;
FinalSize = std::max(FinalSize, ChildSize);
} break;
case TypeLinkTag::LK_Instance: {
const OffsetExpression &OE = EdgeTag->getOffsetExpr();
revng_assert(OE.Strides.size() == OE.TripCounts.size());
// Ignore stuff at negative offsets.
revng_assert(OE.Offset >= 0LL);
// If we have an array, we have to compute its size, taking into
// account the strides and the trip counts.
for (const auto &[TripCount, Stride] :
llvm::reverse(llvm::zip(OE.TripCounts, OE.Strides))) {
revng_assert(Stride > 0LL);
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);
ChildSize = ((NumElems - 1) * StrideSize) + ChildSize;
}
revng_assert(ChildSize);
int64_t ChildOffset = std::max<int64_t>(OE.Offset, 0LL);
uint64_t ChildUpperOffset = ChildOffset + ChildSize;
FinalSize = std::max(FinalSize, ChildUpperOffset);
} break;
default:
revng_unreachable("unexpected edge");
}
}
if (FinalSize != N->Size)
Changed = true;
N->Size = FinalSize;
revng_assert(FinalSize);
}
}
if (Log.isEnabled())
TS.dumpDotOnFile("after-compute-upper-member-access.dot");
return Changed;
}
} // end namespace dla