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.
341 lines
9.3 KiB
C++
341 lines
9.3 KiB
C++
#pragma once
|
|
|
|
//
|
|
// Copyright (c) rev.ng Srls. See LICENSE.md for details.
|
|
//
|
|
|
|
#include <algorithm>
|
|
#include <memory>
|
|
#include <type_traits>
|
|
|
|
#include "llvm/ADT/ArrayRef.h"
|
|
#include "llvm/ADT/SmallPtrSet.h"
|
|
#include "llvm/ADT/SmallVector.h"
|
|
|
|
#include "revng-c/DataLayoutAnalysis/DLALayouts.h"
|
|
#include "revng-c/DataLayoutAnalysis/DLATypeSystem.h"
|
|
|
|
namespace dla {
|
|
|
|
// Forward declaration for dla::LayoutTypeSystem, that will be defined later.
|
|
// For this preliminary implementation, dla::Step does not really need the
|
|
// implementation of the dla::LayoutTypeSystem class, because for now we are
|
|
// only concerned about the schedule of the Steps, and the Step implementations
|
|
// are really just empty.
|
|
class LayoutTypeSystem;
|
|
|
|
class Step {
|
|
public:
|
|
using IDSet = llvm::SmallPtrSet<const void *, 2>;
|
|
using IDSetRef = llvm::SmallPtrSetImpl<const void *> &;
|
|
using IDSetConstRef = const llvm::SmallPtrSetImpl<const void *> &;
|
|
|
|
protected:
|
|
const void *StepID;
|
|
|
|
IDSet Dependencies;
|
|
IDSet Invalidated;
|
|
|
|
Step(const char &C,
|
|
std::initializer_list<const void *> D,
|
|
std::initializer_list<const void *> I) :
|
|
StepID(&C), Dependencies(D), Invalidated(I) {}
|
|
|
|
Step(const char &C) : Step(C, {}, {}) {}
|
|
|
|
public:
|
|
Step() = delete;
|
|
virtual ~Step() = default;
|
|
|
|
/// Runs the Step on TS, returns true if it has applied changes to TS.
|
|
virtual bool runOnTypeSystem(LayoutTypeSystem &TS) = 0;
|
|
|
|
IDSetConstRef getDependencies() const { return Dependencies; }
|
|
IDSetConstRef getInvalidated() const { return Invalidated; }
|
|
|
|
const void *getStepID() const { return StepID; };
|
|
};
|
|
|
|
/// dla::Step that collapses loops in the type system with equality or
|
|
/// inheritange edges
|
|
//
|
|
// After the execution of this step, the LayoutTypeSystem graph should contain
|
|
// only inheritance and instance-of edges, and should be a DAG.
|
|
class CollapseIdentityAndInheritanceCC : public Step {
|
|
static const char ID;
|
|
|
|
public:
|
|
static const constexpr void *getID() { return &ID; }
|
|
|
|
CollapseIdentityAndInheritanceCC() : Step(ID){};
|
|
|
|
virtual ~CollapseIdentityAndInheritanceCC() override = default;
|
|
|
|
virtual bool runOnTypeSystem(LayoutTypeSystem &TS) override;
|
|
};
|
|
|
|
/// dla::Step that removes transitive inheritance edges
|
|
//
|
|
// Here we use the notation notation A --> B to mean an inheritance edge.
|
|
// After the execution of this step, the LayoutTypeSystem graph should have the
|
|
// following property: For each types A, B, C (all different from each other),
|
|
// if A --> B and B --> C, then we should not have A --> C.
|
|
class RemoveTransitiveInheritanceEdges : public Step {
|
|
static const char ID;
|
|
|
|
public:
|
|
static const constexpr void *getID() { return &ID; }
|
|
|
|
RemoveTransitiveInheritanceEdges() : Step(ID){};
|
|
|
|
virtual ~RemoveTransitiveInheritanceEdges() override = default;
|
|
|
|
virtual bool runOnTypeSystem(LayoutTypeSystem &TS) override;
|
|
};
|
|
|
|
/// dla::Step that enforces that inheritance edges form a tree-shaped graph
|
|
//
|
|
// After the execution of this step, the LayoutTypeSystem graph, filtered on
|
|
// inheritance edges, should be a tree.
|
|
class MakeInheritanceTree : public Step {
|
|
static const char ID;
|
|
|
|
public:
|
|
static const constexpr void *getID() { return &ID; }
|
|
|
|
MakeInheritanceTree() : Step(ID){};
|
|
|
|
virtual ~MakeInheritanceTree() override = default;
|
|
|
|
virtual bool runOnTypeSystem(LayoutTypeSystem &TS) override;
|
|
};
|
|
|
|
/// dla::Step that removes leaf nodes without valid layout information
|
|
//
|
|
// Initially valid layout information is simply represented by accesses, but we
|
|
// expect this to be possibly user provded for leafs that otherwise had no valid
|
|
// layout information, such as calls to external library functions.
|
|
class PruneLayoutNodesWithoutLayout : public Step {
|
|
static const char ID;
|
|
|
|
public:
|
|
static const constexpr void *getID() { return &ID; }
|
|
|
|
PruneLayoutNodesWithoutLayout() : Step(ID){};
|
|
|
|
virtual ~PruneLayoutNodesWithoutLayout() override = default;
|
|
|
|
virtual bool runOnTypeSystem(LayoutTypeSystem &TS) override;
|
|
};
|
|
|
|
/// dla::Step that computes and propagates informations on accesses and type
|
|
/// sizes.
|
|
class ComputeUpperMemberAccesses : public Step {
|
|
static const char ID;
|
|
|
|
public:
|
|
static const constexpr void *getID() { return &ID; }
|
|
|
|
ComputeUpperMemberAccesses() :
|
|
Step(ID,
|
|
// Dependencies
|
|
{ CollapseIdentityAndInheritanceCC::getID() },
|
|
// Invalidated
|
|
{}) {}
|
|
|
|
virtual ~ComputeUpperMemberAccesses() override = default;
|
|
|
|
virtual bool runOnTypeSystem(LayoutTypeSystem &TS) override;
|
|
};
|
|
|
|
/// dla::Step that tries to aggregate compatible arrays into a single array
|
|
class CollapseCompatibleArrays : public Step {
|
|
static const char ID;
|
|
|
|
public:
|
|
static const constexpr void *getID() { return &ID; }
|
|
|
|
CollapseCompatibleArrays() :
|
|
Step(ID,
|
|
// Dependencies
|
|
{ ComputeUpperMemberAccesses::getID() },
|
|
// Invalidated
|
|
{}) {}
|
|
|
|
virtual ~CollapseCompatibleArrays() override = default;
|
|
|
|
virtual bool runOnTypeSystem(LayoutTypeSystem &TS) override { return true; }
|
|
};
|
|
|
|
/// dla::Step that propagates inheritance relationships through accessor methods
|
|
class PropagateInheritanceToAccessors : public Step {
|
|
static const char ID;
|
|
|
|
public:
|
|
static const constexpr void *getID() { return &ID; }
|
|
|
|
PropagateInheritanceToAccessors() :
|
|
Step(ID,
|
|
// Dependencies
|
|
{},
|
|
// Invalidated
|
|
{ RemoveTransitiveInheritanceEdges::getID() }) {}
|
|
|
|
virtual ~PropagateInheritanceToAccessors() override = default;
|
|
|
|
virtual bool runOnTypeSystem(LayoutTypeSystem &TS) override { return true; }
|
|
};
|
|
|
|
/// Removes instance-of edges that connect nodes with an inheritance edge
|
|
class RemoveConflictingEdges : public Step {
|
|
static const char ID;
|
|
|
|
public:
|
|
static const constexpr void *getID() { return &ID; }
|
|
static bool removeConflicts(LayoutTypeSystem &TS, LayoutTypeSystemNode *Node);
|
|
|
|
RemoveConflictingEdges() :
|
|
Step(ID,
|
|
// Dependencies
|
|
{},
|
|
// Invalidated
|
|
{}) {}
|
|
|
|
virtual ~RemoveConflictingEdges() override = default;
|
|
|
|
virtual bool runOnTypeSystem(LayoutTypeSystem &TS) override;
|
|
};
|
|
|
|
/// dla::Step that collapses nodes that have a single child at offset 0
|
|
class CollapseSingleChild : public Step {
|
|
static const char ID;
|
|
|
|
public:
|
|
static const constexpr void *getID() { return &ID; }
|
|
static bool collapseSingle(LayoutTypeSystem &TS, LayoutTypeSystemNode *Node);
|
|
|
|
CollapseSingleChild() :
|
|
Step(ID,
|
|
// Dependencies
|
|
{},
|
|
// Invalidated
|
|
{}) {}
|
|
|
|
virtual ~CollapseSingleChild() override = default;
|
|
|
|
virtual bool runOnTypeSystem(LayoutTypeSystem &TS) override;
|
|
};
|
|
|
|
/// dla::Step that decompose the LayoutTypeSystem into components, each of which
|
|
/// cannot overlap with others
|
|
class ComputeNonInterferingComponents : public Step {
|
|
static const char ID;
|
|
|
|
public:
|
|
static const constexpr void *getID() { return &ID; }
|
|
|
|
ComputeNonInterferingComponents() :
|
|
Step(ID,
|
|
// Dependencies
|
|
{ ComputeUpperMemberAccesses::getID() },
|
|
// Invalidated
|
|
{}) {}
|
|
|
|
virtual ~ComputeNonInterferingComponents() override = default;
|
|
|
|
virtual bool runOnTypeSystem(LayoutTypeSystem &TS) override;
|
|
};
|
|
|
|
/// dla::Step that merges structurally identical subtrees of an interfering
|
|
/// node.
|
|
class DeduplicateUnionFields : public Step {
|
|
static const char ID;
|
|
|
|
public:
|
|
static const constexpr void *getID() { return &ID; }
|
|
|
|
DeduplicateUnionFields() :
|
|
Step(ID,
|
|
// Dependencies
|
|
{ ComputeNonInterferingComponents::getID() },
|
|
// Invalidated
|
|
{ ComputeNonInterferingComponents::getID() }) {}
|
|
|
|
virtual ~DeduplicateUnionFields() override = default;
|
|
|
|
virtual bool runOnTypeSystem(LayoutTypeSystem &TS) override;
|
|
};
|
|
|
|
template<typename IterT>
|
|
bool intersect(IterT I1, IterT E1, IterT I2, IterT E2) {
|
|
while ((I1 != E1) and (I2 != E2)) {
|
|
if (*I1 < *I2)
|
|
++I1;
|
|
else if (*I2 < *I1)
|
|
++I2;
|
|
else
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
template<typename RangeT>
|
|
bool intersect(const RangeT &R1, const RangeT &R2) {
|
|
return intersect(R1.begin(), R1.end(), R2.begin(), R2.end());
|
|
}
|
|
|
|
class StepManager {
|
|
|
|
public:
|
|
enum StepState {
|
|
NewlyAdded,
|
|
Invalidated,
|
|
Done,
|
|
};
|
|
|
|
public:
|
|
llvm::SmallVector<std::unique_ptr<Step>, 16> Schedule;
|
|
llvm::SmallPtrSet<const void *, 16> InsertedSteps;
|
|
llvm::SmallPtrSet<const void *, 16> InvalidatedSteps;
|
|
|
|
using sched_const_iterator = decltype(Schedule)::const_iterator;
|
|
using sched_const_range = llvm::iterator_range<sched_const_iterator>;
|
|
|
|
public:
|
|
StepManager() : Schedule(), InsertedSteps(), InvalidatedSteps() {}
|
|
|
|
/// Adds a Step to the StepManager, moving ownership into it.
|
|
[[nodiscard]] bool addStep(std::unique_ptr<Step> S);
|
|
|
|
template<typename StepT, typename... ArgsT>
|
|
[[nodiscard]] bool addStep(ArgsT &&...Args) {
|
|
return addStep(std::make_unique<StepT>(std::forward<ArgsT &&>(Args)...));
|
|
}
|
|
|
|
/// Runs the added steps
|
|
void run(LayoutTypeSystem &TS);
|
|
|
|
/// Drops all the scheduled steps
|
|
void reset() {
|
|
Schedule.clear();
|
|
InsertedSteps.clear();
|
|
InvalidatedSteps.clear();
|
|
}
|
|
|
|
bool hasValidSchedule() const {
|
|
return not intersect(InsertedSteps, InvalidatedSteps);
|
|
}
|
|
|
|
/// Get the number of scheduled steps
|
|
auto getNumSteps() const { return Schedule.size(); }
|
|
|
|
// Methods for const iteration on Schedule
|
|
sched_const_iterator sched_begin() const { return Schedule.begin(); }
|
|
sched_const_iterator sched_end() const { return Schedule.end(); }
|
|
sched_const_range sched() const {
|
|
return llvm::make_range(sched_begin(), sched_end());
|
|
}
|
|
};
|
|
|
|
} // end namespace dla
|