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.
900 lines
30 KiB
C++
900 lines
30 KiB
C++
//
|
|
// Copyright rev.ng Srls. See LICENSE.md for details.
|
|
//
|
|
|
|
#define BOOST_TEST_MODULE DLASteps
|
|
bool init_unit_test();
|
|
|
|
#include "boost/test/unit_test.hpp"
|
|
|
|
#include "revng-c/DataLayoutAnalysis/DLATypeSystem.h"
|
|
#include "revng-c/Utils/Utils.h"
|
|
|
|
#include "lib/DataLayoutAnalysis/Middleend/DLAStep.h"
|
|
|
|
using LTSN = dla::LayoutTypeSystemNode;
|
|
|
|
using namespace llvm;
|
|
using namespace dla;
|
|
|
|
template<typename StepT>
|
|
static void runStep(LayoutTypeSystem &TS) {
|
|
// Enable expensive checks
|
|
VerifyLog.enable();
|
|
|
|
// Run Step
|
|
dla::StepManager SM;
|
|
revng_check(SM.addStep<StepT>());
|
|
SM.run(TS);
|
|
|
|
// Compress the equivalence classes
|
|
dla::VectEqClasses &Eq = TS.getEqClasses();
|
|
Eq.compress();
|
|
}
|
|
|
|
static LTSN *createRoot(LayoutTypeSystem &TS, unsigned Size = 0U) {
|
|
LTSN *Root = TS.createArtificialLayoutType();
|
|
Root->Size = Size;
|
|
|
|
return Root;
|
|
}
|
|
|
|
static LTSN *addInstanceAtOffset(LayoutTypeSystem &TS,
|
|
LTSN *Parent,
|
|
unsigned ChildOffset,
|
|
unsigned ChildSize) {
|
|
LTSN *Child = TS.createArtificialLayoutType();
|
|
Child->Size = ChildSize;
|
|
OffsetExpression OE{};
|
|
OE.Offset = ChildOffset;
|
|
TS.addInstanceLink(Parent, Child, std::move(OE));
|
|
|
|
return Child;
|
|
}
|
|
|
|
static LTSN *
|
|
addInheritance(LayoutTypeSystem &TS, LTSN *Parent, unsigned ChildSize = 0U) {
|
|
LTSN *Child = TS.createArtificialLayoutType();
|
|
Child->Size = ChildSize;
|
|
TS.addInheritanceLink(Parent, Child);
|
|
|
|
return Child;
|
|
}
|
|
|
|
static LTSN *
|
|
addEquality(LayoutTypeSystem &TS, LTSN *Parent, unsigned ChildSize = 0U) {
|
|
LTSN *Child = TS.createArtificialLayoutType();
|
|
Child->Size = ChildSize;
|
|
TS.addEqualityLink(Parent, Child);
|
|
|
|
return Child;
|
|
}
|
|
|
|
void checkNode(const LayoutTypeSystem &TS,
|
|
const LayoutTypeSystemNode *N,
|
|
const unsigned ExpectedSize,
|
|
const InterferingChildrenInfo ExpectedInfo,
|
|
const std::set<unsigned> &ExpectedEqClass) {
|
|
const dla::VectEqClasses &Eq = TS.getEqClasses();
|
|
revng_check(N);
|
|
revng_check(not Eq.isRemoved(N->ID));
|
|
revng_check(ExpectedSize == N->Size);
|
|
revng_check(ExpectedInfo == N->InterferingInfo);
|
|
|
|
const auto &EqClass = Eq.computeEqClass(N->ID);
|
|
revng_check(EqClass.size() == ExpectedEqClass.size());
|
|
for (auto &Collapsed : EqClass)
|
|
revng_check(ExpectedEqClass.contains(Collapsed));
|
|
}
|
|
|
|
// Test cases
|
|
|
|
// ----------------- CollapseIdentityAndInheritanceCC ---
|
|
|
|
///\brief Test an equality CC
|
|
BOOST_AUTO_TEST_CASE(CollapseIdentityAndInheritanceCC_equality) {
|
|
dla::LayoutTypeSystem TS;
|
|
|
|
// Build TS
|
|
LTSN *Root = createRoot(TS);
|
|
LTSN *Node1 = addEquality(TS, Root);
|
|
LTSN *Node2 = addEquality(TS, Root);
|
|
LTSN *Node3 = addEquality(TS, Node2);
|
|
TS.addEqualityLink(Node1, Node3);
|
|
LTSN *PtrNode = createRoot(TS);
|
|
|
|
// Run step
|
|
runStep<dla::CollapseIdentityAndInheritanceCC>(TS);
|
|
|
|
// Check that equality nodes have been collapsed
|
|
revng_check(TS.getNumLayouts() == 2);
|
|
revng_check(llvm::is_contained(TS.getLayoutsRange(), Node2));
|
|
revng_check(llvm::is_contained(TS.getLayoutsRange(), PtrNode));
|
|
revng_check(Node2->Successors.size() == 0);
|
|
|
|
// Check Eq Classes
|
|
dla::VectEqClasses &Eq = TS.getEqClasses();
|
|
revng_check(Eq.getNumElements() == 5);
|
|
revng_check(Eq.getNumClasses() == 2);
|
|
revng_check(not Eq.isRemoved(Node2->ID) and not Eq.isRemoved(PtrNode->ID));
|
|
revng_check(Eq.haveSameEqClass(Node2->ID, Node1->ID)
|
|
and Eq.haveSameEqClass(Node3->ID, Node2->ID)
|
|
and Eq.haveSameEqClass(Node2->ID, Root->ID));
|
|
}
|
|
|
|
///\brief Test an inheritance loop
|
|
BOOST_AUTO_TEST_CASE(CollapseIdentityAndInheritanceCC_inheritance) {
|
|
dla::LayoutTypeSystem TS;
|
|
|
|
// Build TS
|
|
LTSN *Root = createRoot(TS);
|
|
LTSN *Node1 = addInheritance(TS, Root);
|
|
LTSN *Node2 = addInheritance(TS, Root);
|
|
LTSN *Node3 = addInheritance(TS, Node2);
|
|
TS.addInheritanceLink(Node3, Root);
|
|
LTSN *Node4 = addInheritance(TS, Node1);
|
|
|
|
LTSN *PtrNode = createRoot(TS);
|
|
|
|
// Run step
|
|
runStep<dla::CollapseIdentityAndInheritanceCC>(TS);
|
|
|
|
// Check that inheritance loops have been collapsed
|
|
revng_check(TS.getNumLayouts() == 4);
|
|
revng_check(llvm::is_contained(TS.getLayoutsRange(), Node3));
|
|
revng_check(llvm::is_contained(TS.getLayoutsRange(), Node1));
|
|
revng_check(llvm::is_contained(TS.getLayoutsRange(), Node4));
|
|
revng_check(llvm::is_contained(TS.getLayoutsRange(), PtrNode));
|
|
revng_check(Node2->Successors.size() == 1);
|
|
|
|
// Check Eq Classes
|
|
dla::VectEqClasses &Eq = TS.getEqClasses();
|
|
revng_check(Eq.getNumElements() == 6);
|
|
revng_check(Eq.getNumClasses() == 4);
|
|
revng_check(not Eq.isRemoved(Node3->ID) and not Eq.isRemoved(PtrNode->ID));
|
|
revng_check(Eq.haveSameEqClass(Root->ID, Node2->ID)
|
|
and Eq.haveSameEqClass(Root->ID, Node3->ID));
|
|
}
|
|
|
|
///\brief Test an inheritance CC with a backward instance edge (loop)
|
|
BOOST_AUTO_TEST_CASE(CollapseIdentityAndInheritanceCC_instance) {
|
|
dla::LayoutTypeSystem TS;
|
|
|
|
// Build TS
|
|
LTSN *Root = createRoot(TS);
|
|
LTSN *Node1 = addInheritance(TS, Root);
|
|
LTSN *Node2 = addInheritance(TS, Root);
|
|
LTSN *Node3 = addInheritance(TS, Node2);
|
|
TS.addInstanceLink(Node3, Root, OffsetExpression{});
|
|
addInstanceAtOffset(TS, Node1, 0, 8);
|
|
|
|
createRoot(TS);
|
|
|
|
// Run step
|
|
runStep<dla::CollapseIdentityAndInheritanceCC>(TS);
|
|
|
|
// Check that no node has been collapsed
|
|
revng_check(TS.getNumLayouts() == 6);
|
|
|
|
// Check that the instance back-edge has been removed
|
|
revng_check(Node3->Successors.size() == 0);
|
|
|
|
// Check Eq Classes
|
|
dla::VectEqClasses &Eq = TS.getEqClasses();
|
|
revng_check(Eq.getNumElements() == 6);
|
|
revng_check(Eq.getNumClasses() == 6);
|
|
}
|
|
|
|
// ----------------- RemoveTransitiveInheritanceEdges ---
|
|
|
|
///\brief Test that transitive edges are removed
|
|
BOOST_AUTO_TEST_CASE(RemoveTransitiveInheritanceEdges_basic) {
|
|
dla::LayoutTypeSystem TS;
|
|
|
|
// Build TS
|
|
LTSN *Root = createRoot(TS);
|
|
LTSN *Node1 = addInheritance(TS, Root);
|
|
LTSN *Node2 = addInheritance(TS, Node1);
|
|
LTSN *Node3 = addInheritance(TS, Node2);
|
|
// Add transitive edges
|
|
TS.addInheritanceLink(Root, Node2);
|
|
TS.addInheritanceLink(Root, Node3);
|
|
|
|
LTSN *PtrNode = createRoot(TS);
|
|
TS.addInheritanceLink(Root, PtrNode);
|
|
|
|
// Run step
|
|
runStep<dla::RemoveTransitiveInheritanceEdges>(TS);
|
|
|
|
// Check that no node has been collapsed
|
|
revng_check(TS.getNumLayouts() == 5);
|
|
|
|
// Check that transitive edges have been removed
|
|
revng_check(Root->Successors.size() == 2);
|
|
revng_check(llvm::is_contained(llvm::children<LTSN *>(Root), Node1));
|
|
|
|
// Check Eq Classes
|
|
dla::VectEqClasses &Eq = TS.getEqClasses();
|
|
revng_check(Eq.getNumElements() == 5);
|
|
revng_check(Eq.getNumClasses() == 5);
|
|
revng_check(not Eq.isRemoved(Node3->ID) and not Eq.isRemoved(PtrNode->ID));
|
|
}
|
|
|
|
// ----------------- MakeInheritanceTree ----------------
|
|
|
|
///\brief Test that inheritance "diamonds" are removed
|
|
BOOST_AUTO_TEST_CASE(MakeInheritanceTree_diamond) {
|
|
dla::LayoutTypeSystem TS;
|
|
|
|
// Build TS
|
|
LTSN *Root = createRoot(TS);
|
|
LTSN *Node1 = addInheritance(TS, Root);
|
|
LTSN *Node2 = addInheritance(TS, Root);
|
|
LTSN *Node3 = addInheritance(TS, Node1);
|
|
TS.addInheritanceLink(Node2, Node3);
|
|
|
|
// Run step
|
|
runStep<dla::MakeInheritanceTree>(TS);
|
|
|
|
// Check that nodes have been collapsed
|
|
revng_check(TS.getNumLayouts() == 3);
|
|
// Check diamonds have been collapsed
|
|
revng_check(Root->Successors.size() == 1);
|
|
revng_check(llvm::is_contained(llvm::children<LTSN *>(Root), Node1));
|
|
revng_check(not llvm::is_contained(llvm::children<LTSN *>(Root), Node2));
|
|
// Check Eq Classes
|
|
dla::VectEqClasses &Eq = TS.getEqClasses();
|
|
revng_check(Eq.getNumElements() == 4);
|
|
revng_check(Eq.getNumClasses() == 3);
|
|
revng_check(not Eq.isRemoved(Node2->ID) and not Eq.isRemoved(Node1->ID));
|
|
revng_check(Eq.haveSameEqClass(Node2->ID, Node1->ID));
|
|
}
|
|
|
|
///\brief Test that multiple inheritance is collapsed
|
|
BOOST_AUTO_TEST_CASE(MakeInheritanceTree_virtualPostDom) {
|
|
dla::LayoutTypeSystem TS;
|
|
|
|
// Build TS
|
|
LTSN *Root = createRoot(TS);
|
|
LTSN *Node1 = addInheritance(TS, Root);
|
|
LTSN *Node2 = addInheritance(TS, Root);
|
|
LTSN *Node3 = addInheritance(TS, Node1);
|
|
TS.addInheritanceLink(Node2, Node3);
|
|
LTSN *Node4 = addInheritance(TS, Root);
|
|
createRoot(TS);
|
|
|
|
// Run step
|
|
runStep<dla::MakeInheritanceTree>(TS);
|
|
|
|
// Check that nodes have been collapsed
|
|
revng_check(TS.getNumLayouts() == 3);
|
|
|
|
// Check that all inheritance children have been collapsed
|
|
revng_check(Root->Successors.size() == 1);
|
|
revng_check(llvm::is_contained(llvm::children<LTSN *>(Root), Node3));
|
|
revng_check(not llvm::is_contained(llvm::children<LTSN *>(Root), Node1));
|
|
revng_check(not llvm::is_contained(llvm::children<LTSN *>(Root), Node2));
|
|
revng_check(not llvm::is_contained(llvm::children<LTSN *>(Root), Node4));
|
|
// Check Eq Classes
|
|
dla::VectEqClasses &Eq = TS.getEqClasses();
|
|
revng_check(Eq.getNumElements() == 6);
|
|
revng_check(Eq.getNumClasses() == 3);
|
|
revng_check(not Eq.isRemoved(Root->ID) and not Eq.isRemoved(Node3->ID));
|
|
revng_check(Eq.haveSameEqClass(Node1->ID, Node2->ID)
|
|
and Eq.haveSameEqClass(Node1->ID, Node3->ID)
|
|
and Eq.haveSameEqClass(Node1->ID, Node4->ID));
|
|
}
|
|
|
|
// ----------------- PruneLayoutNodesWithoutLayout ------
|
|
|
|
///\brief Test that branches without sized leaves are pruned
|
|
BOOST_AUTO_TEST_CASE(PruneLayoutNodesWithoutLayout_basic) {
|
|
dla::LayoutTypeSystem TS;
|
|
|
|
// Build TS
|
|
LTSN *Root = createRoot(TS);
|
|
std::vector<LTSN *> Level0 = {
|
|
addInstanceAtOffset(TS, Root, /*offset=*/0, /*size=*/0),
|
|
addInstanceAtOffset(TS, Root, /*offset=*/0, /*size=*/0)
|
|
};
|
|
std::vector<LTSN *> Level1 = {
|
|
addInstanceAtOffset(TS, Level0[0], /*offset=*/0, /*size=*/0),
|
|
addInstanceAtOffset(TS, Level0[0], /*offset=*/0, /*size=*/0),
|
|
addInstanceAtOffset(TS, Level0[1], /*offset=*/0, /*size=*/0),
|
|
addInstanceAtOffset(TS, Level0[1], /*offset=*/0, /*size=*/0)
|
|
};
|
|
std::vector<LTSN *> Leaves = {
|
|
addInstanceAtOffset(TS, Level1[0], /*offset=*/0, /*size=*/8),
|
|
addInstanceAtOffset(TS, Level1[1], /*offset=*/0, /*size=*/8),
|
|
addInstanceAtOffset(TS, Level1[2], /*offset=*/0, /*size=*/0),
|
|
addInstanceAtOffset(TS, Level1[3], /*offset=*/0, /*size=*/0)
|
|
};
|
|
|
|
// Run step
|
|
runStep<dla::PruneLayoutNodesWithoutLayout>(TS);
|
|
|
|
// Check final tree
|
|
revng_check(TS.getNumLayouts() == 6);
|
|
revng_check(Root->Successors.size() == 1);
|
|
revng_check(llvm::is_contained(llvm::children<LTSN *>(Root), Level0[0]));
|
|
revng_check(not llvm::is_contained(llvm::children<LTSN *>(Root), Level0[1]));
|
|
|
|
revng_check(Level0[0]->Successors.size() == 2);
|
|
revng_check(llvm::is_contained(llvm::children<LTSN *>(Level0[0]), Level1[0]));
|
|
revng_check(llvm::is_contained(llvm::children<LTSN *>(Level0[0]), Level1[1]));
|
|
|
|
revng_check(Level1[0]->Successors.size() == 1);
|
|
revng_check(llvm::is_contained(llvm::children<LTSN *>(Level1[0]), Leaves[0]));
|
|
revng_check(Level1[1]->Successors.size() == 1);
|
|
revng_check(llvm::is_contained(llvm::children<LTSN *>(Level1[1]), Leaves[1]));
|
|
|
|
revng_check(isLeaf(Leaves[0]) and isLeaf(Leaves[1]));
|
|
|
|
// Check Eq Classes
|
|
dla::VectEqClasses &Eq = TS.getEqClasses();
|
|
revng_check(Eq.getNumElements() == 11);
|
|
revng_check(Eq.getNumClasses() == 7);
|
|
revng_check(Eq.isRemoved(Level0[1]->ID) and Eq.isRemoved(Level1[2]->ID)
|
|
and Eq.isRemoved(Level1[3]->ID) and Eq.isRemoved(Leaves[2]->ID)
|
|
and Eq.isRemoved(Leaves[3]->ID));
|
|
}
|
|
|
|
// ----------------- CollapseSingleChild ----------------
|
|
|
|
///\brief Test nominal case with one parent and one instance child at offset 0
|
|
BOOST_AUTO_TEST_CASE(CollapseSingleChild_offsetZero) {
|
|
dla::LayoutTypeSystem TS;
|
|
|
|
// Build TS
|
|
LTSN *Parent = createRoot(TS, 0U);
|
|
addInstanceAtOffset(TS,
|
|
Parent,
|
|
/*offset=*/0U,
|
|
/*size=*/8U);
|
|
|
|
// Run step
|
|
runStep<dla::CollapseSingleChild>(TS);
|
|
|
|
// Check graph
|
|
revng_check(TS.getNumLayouts() == 1);
|
|
checkNode(TS, Parent, 8, InterferingChildrenInfo::Unknown, { 0, 1 });
|
|
}
|
|
|
|
///\brief Test the case where there is only one child but with offset > 0
|
|
BOOST_AUTO_TEST_CASE(CollapseSingleChild_offsetNonZero) {
|
|
dla::LayoutTypeSystem TS;
|
|
|
|
// Build TS
|
|
LTSN *Parent = createRoot(TS, 0U);
|
|
LTSN *Child = addInstanceAtOffset(TS,
|
|
Parent,
|
|
/*offset=*/8U,
|
|
/*size=*/8U);
|
|
|
|
// Run step
|
|
runStep<dla::CollapseSingleChild>(TS);
|
|
|
|
// Check graph
|
|
revng_check(TS.getNumLayouts() == 2);
|
|
checkNode(TS, Parent, 0, InterferingChildrenInfo::Unknown, { 0 });
|
|
checkNode(TS, Child, 8, InterferingChildrenInfo::Unknown, { 1 });
|
|
}
|
|
|
|
///\brief Test the case in which a node has many children (should not collapse)
|
|
BOOST_AUTO_TEST_CASE(CollapseSingleChild_multiChild) {
|
|
dla::LayoutTypeSystem TS;
|
|
|
|
// Build TS
|
|
LTSN *Parent = createRoot(TS, 0U);
|
|
LTSN *Child1 = addInstanceAtOffset(TS,
|
|
Parent,
|
|
/*offset=*/0U,
|
|
/*size=*/0U);
|
|
addInstanceAtOffset(TS,
|
|
Child1,
|
|
/*offset=*/0U,
|
|
/*size=*/8U);
|
|
LTSN *Child2 = addInstanceAtOffset(TS,
|
|
Parent,
|
|
/*offset=*/8U,
|
|
/*size=*/0U);
|
|
addInstanceAtOffset(TS,
|
|
Child2,
|
|
/*offset=*/0U,
|
|
/*size=*/8U);
|
|
|
|
// Run step
|
|
runStep<dla::CollapseSingleChild>(TS);
|
|
|
|
// Check graph
|
|
revng_check(TS.getNumLayouts() == 3);
|
|
checkNode(TS, Parent, 0, InterferingChildrenInfo::Unknown, { 0 });
|
|
checkNode(TS, Child1, 8, InterferingChildrenInfo::Unknown, { 1, 2 });
|
|
checkNode(TS, Child2, 8, InterferingChildrenInfo::Unknown, { 3, 4 });
|
|
}
|
|
|
|
///\brief Test the case in which a node has many parents (should not collapse)
|
|
BOOST_AUTO_TEST_CASE(CollapseSingleChild_multiParent) {
|
|
dla::LayoutTypeSystem TS;
|
|
|
|
// Build TS
|
|
LTSN *Parent1 = createRoot(TS, 0U);
|
|
LTSN *Parent2 = createRoot(TS, 0U);
|
|
LTSN *Child1 = addInstanceAtOffset(TS,
|
|
Parent1,
|
|
/*offset=*/8U,
|
|
/*size=*/0U);
|
|
TS.addInheritanceLink(Parent2, Child1);
|
|
/*LTSN *Child2 =*/addInstanceAtOffset(TS,
|
|
Child1,
|
|
/*offset=*/0U,
|
|
/*size=*/8U);
|
|
|
|
// Run step
|
|
TS.dumpDotOnFile("bef.dot", true);
|
|
runStep<dla::CollapseSingleChild>(TS);
|
|
TS.dumpDotOnFile("after.dot", true);
|
|
|
|
// Check graph
|
|
revng_check(TS.getNumLayouts() == 3);
|
|
checkNode(TS, Parent1, 0, InterferingChildrenInfo::Unknown, { 0 });
|
|
checkNode(TS, Parent2, 0, InterferingChildrenInfo::Unknown, { 1 });
|
|
checkNode(TS, Child1, 8, InterferingChildrenInfo::Unknown, { 2, 3 });
|
|
}
|
|
|
|
///\brief Test the case in which there are multiple levels of single-childs to
|
|
/// be collapsed
|
|
BOOST_AUTO_TEST_CASE(CollapseSingleChild_multiLevel) {
|
|
dla::LayoutTypeSystem TS;
|
|
|
|
// Build TS
|
|
LTSN *Level0 = createRoot(TS, 0U);
|
|
LTSN *Level1 = addInstanceAtOffset(TS,
|
|
Level0,
|
|
/*offset=*/0U,
|
|
/*size=*/0U);
|
|
LTSN *Level2 = addInstanceAtOffset(TS,
|
|
Level1,
|
|
/*offset=*/0U,
|
|
/*size=*/0U);
|
|
LTSN *Level3 = addInstanceAtOffset(TS,
|
|
Level2,
|
|
/*offset=*/0U,
|
|
/*size=*/0U);
|
|
/*LTSN *Level4 = */ addInstanceAtOffset(TS,
|
|
Level3,
|
|
/*offset=*/0U,
|
|
/*size=*/8U);
|
|
|
|
// Run step
|
|
runStep<dla::CollapseSingleChild>(TS);
|
|
|
|
// Check graph
|
|
revng_check(TS.getNumLayouts() == 1);
|
|
checkNode(TS, Level0, 8, InterferingChildrenInfo::Unknown, { 0, 1, 2, 3, 4 });
|
|
}
|
|
|
|
// ----------------- ComputeUpperMemberAccesses ---------
|
|
|
|
///\brief Test member access computation
|
|
BOOST_AUTO_TEST_CASE(ComputeUpperMemberAccesses_basic) {
|
|
dla::LayoutTypeSystem TS;
|
|
|
|
// Build TS
|
|
LTSN *Root = createRoot(TS);
|
|
Root->Size = 0U;
|
|
|
|
LTSN *Child1 = createRoot(TS);
|
|
Child1->Size = 8U;
|
|
TS.addInstanceLink(Root, Child1, OffsetExpression{});
|
|
|
|
LTSN *Child2 = createRoot(TS);
|
|
Child2->Size = 8U;
|
|
OffsetExpression MultiOE;
|
|
MultiOE.Offset = 10U;
|
|
MultiOE.Strides = { 10U };
|
|
MultiOE.TripCounts = { 3U };
|
|
TS.addInstanceLink(Root, Child2, std::move(MultiOE));
|
|
|
|
LTSN *Child3 = createRoot(TS);
|
|
Child3->Size = 16U;
|
|
OffsetExpression SingleOE;
|
|
SingleOE.Offset = 30U;
|
|
TS.addInstanceLink(Root, Child3, std::move(SingleOE));
|
|
|
|
LTSN *Root2 = createRoot(TS);
|
|
LTSN *PtrNode = createRoot(TS);
|
|
PtrNode->Size = 8U;
|
|
TS.addInheritanceLink(Root2, PtrNode);
|
|
|
|
// Run step
|
|
VerifyLog.enable();
|
|
dla::StepManager SM;
|
|
revng_check(SM.addStep<CollapseIdentityAndInheritanceCC>());
|
|
revng_check(SM.addStep<ComputeUpperMemberAccesses>());
|
|
SM.run(TS);
|
|
// Compress the equivalence classes
|
|
dla::VectEqClasses &Eq = TS.getEqClasses();
|
|
Eq.compress();
|
|
|
|
// Check remaining nodes
|
|
revng_check(TS.getNumLayouts() == 6);
|
|
// Check edges
|
|
unsigned NInstance = 0U;
|
|
for (auto &[Child, Tag] : llvm::children_edges<LTSN *>(Root)) {
|
|
switch (Tag->getKind()) {
|
|
case TypeLinkTag::LK_Instance:
|
|
NInstance++;
|
|
break;
|
|
default:
|
|
revng_check(false);
|
|
}
|
|
}
|
|
revng_check(NInstance == 3);
|
|
// Check size
|
|
revng_check(Root->Size == 46);
|
|
revng_check(Root2->Size == 8);
|
|
|
|
// Check Eq Classes
|
|
revng_check(Eq.getNumElements() == 6);
|
|
revng_check(Eq.getNumClasses() == 6);
|
|
revng_check(not Eq.isRemoved(PtrNode->ID));
|
|
}
|
|
|
|
// ----------------- ComputeNonInterferingComponents ----
|
|
|
|
///\brief Test union nested inside a struct
|
|
BOOST_AUTO_TEST_CASE(ComputeNonInterferingComponents_basic) {
|
|
dla::LayoutTypeSystem TS;
|
|
|
|
// Build TS
|
|
LTSN *Root = createRoot(TS);
|
|
Root->Size = 0U;
|
|
|
|
LTSN *Child1 = createRoot(TS);
|
|
Child1->Size = 8U;
|
|
TS.addInstanceLink(Root, Child1, OffsetExpression{});
|
|
|
|
LTSN *Child2 = createRoot(TS);
|
|
Child2->Size = 8U;
|
|
OffsetExpression MultiOE;
|
|
MultiOE.Offset = 10U;
|
|
MultiOE.Strides = { 10U };
|
|
MultiOE.TripCounts = { 3U };
|
|
TS.addInstanceLink(Root, Child2, std::move(MultiOE));
|
|
|
|
LTSN *Child3 = createRoot(TS);
|
|
Child3->Size = 16U;
|
|
OffsetExpression SingleOE;
|
|
SingleOE.Offset = 30U;
|
|
TS.addInstanceLink(Root, Child3, std::move(SingleOE));
|
|
|
|
LTSN *Root2 = createRoot(TS);
|
|
LTSN *PtrNode = createRoot(TS);
|
|
PtrNode->Size = 8U;
|
|
TS.addInheritanceLink(Root2, PtrNode);
|
|
LTSN *UnionNode = addInstanceAtOffset(TS, Root2, 0U, 8U);
|
|
|
|
// Run step
|
|
VerifyLog.enable();
|
|
dla::StepManager SM;
|
|
revng_check(SM.addStep<CollapseIdentityAndInheritanceCC>());
|
|
revng_check(SM.addStep<ComputeUpperMemberAccesses>());
|
|
revng_check(SM.addStep<ComputeNonInterferingComponents>());
|
|
SM.run(TS);
|
|
// Compress the equivalence classes
|
|
dla::VectEqClasses &Eq = TS.getEqClasses();
|
|
Eq.compress();
|
|
|
|
// Check remaining nodes
|
|
revng_check(TS.getNumLayouts() == 8);
|
|
// Check edges
|
|
unsigned NInstance = 0U;
|
|
for (auto &[Child, Tag] : llvm::children_edges<LTSN *>(Root)) {
|
|
switch (Tag->getKind()) {
|
|
case TypeLinkTag::LK_Instance:
|
|
NInstance++;
|
|
break;
|
|
default:
|
|
revng_check(false);
|
|
}
|
|
}
|
|
revng_check(NInstance == 2);
|
|
// Check size
|
|
revng_check(Root->Size == 46);
|
|
revng_check(Root2->Size == 8);
|
|
revng_check(Root->InterferingInfo = AllChildrenAreNonInterfering);
|
|
revng_check(Child1->InterferingInfo = AllChildrenAreNonInterfering);
|
|
revng_check(Child2->InterferingInfo = AllChildrenAreNonInterfering);
|
|
revng_check(Root2->InterferingInfo = AllChildrenAreInterfering);
|
|
revng_check(PtrNode->InterferingInfo = AllChildrenAreNonInterfering);
|
|
revng_check(UnionNode->InterferingInfo = AllChildrenAreNonInterfering);
|
|
// Check new node
|
|
const auto &[FinalChild1, Tag1] = *(Child2->Predecessors.begin());
|
|
const auto &[FinalChild2, Tag2] = *(Child3->Predecessors.begin());
|
|
revng_check(Tag1->getOffsetExpr().Offset == 0U);
|
|
revng_check(Tag2->getOffsetExpr().Offset == 20U);
|
|
|
|
revng_check(FinalChild1 == FinalChild2);
|
|
|
|
// Check Eq Classes
|
|
revng_check(Eq.getNumElements() == 8);
|
|
revng_check(Eq.getNumClasses() == 8);
|
|
revng_check(not Eq.isRemoved(PtrNode->ID));
|
|
}
|
|
|
|
// ----------------- Propagate to accessors --------------
|
|
|
|
BOOST_AUTO_TEST_CASE(PropagateToAccessors) {
|
|
dla::LayoutTypeSystem TS;
|
|
|
|
// Build TS
|
|
LTSN *NodeA = createRoot(TS);
|
|
LTSN *NodeB = addInheritance(TS, NodeA);
|
|
LTSN *NodeC = addInheritance(TS, NodeA);
|
|
LTSN *NodeD = addInstanceAtOffset(TS, NodeB, /*offset=*/0, /*size=*/0);
|
|
/*LTSN *NodeE = */ addInstanceAtOffset(TS, NodeD, /*offset=*/0, /*size=*/8);
|
|
LTSN *NodeF = addInstanceAtOffset(TS, NodeC, /*offset=*/8, /*size=*/0);
|
|
/*LTSN *NodeG = */ addInstanceAtOffset(TS, NodeF, /*offset=*/0, /*size=*/8);
|
|
|
|
// Run steps
|
|
VerifyLog.enable();
|
|
dla::StepManager SM;
|
|
revng_check(SM.addStep<CollapseIdentityAndInheritanceCC>());
|
|
revng_check(SM.addStep<MakeInheritanceTree>());
|
|
revng_check(SM.addStep<CollapseSingleChild>());
|
|
revng_check(SM.addStep<ComputeUpperMemberAccesses>());
|
|
|
|
SM.run(TS);
|
|
|
|
// Compress the equivalence classes
|
|
dla::VectEqClasses &Eq = TS.getEqClasses();
|
|
Eq.compress();
|
|
|
|
// Check TS
|
|
revng_check(TS.getNumLayouts() == 3);
|
|
checkNode(TS, NodeA, 16, InterferingChildrenInfo::Unknown, { 0, 1, 2 });
|
|
checkNode(TS, NodeD, 8, InterferingChildrenInfo::Unknown, { 3, 4 });
|
|
checkNode(TS, NodeF, 8, InterferingChildrenInfo::Unknown, { 5, 6 });
|
|
}
|
|
|
|
// ----------------- Deduplicate Union Fields --------------
|
|
|
|
BOOST_AUTO_TEST_CASE(DeduplicateUnionFields_basic) {
|
|
dla::LayoutTypeSystem TS;
|
|
|
|
// Build TS
|
|
LTSN *NodeA = createRoot(TS);
|
|
LTSN *NodeB = addInheritance(TS, NodeA);
|
|
LTSN *NodeD = addInstanceAtOffset(TS, NodeB, /*offset=*/0, /*size=*/8);
|
|
LTSN *NodeE = addInstanceAtOffset(TS, NodeB, /*offset=*/8, /*size=*/8);
|
|
|
|
LTSN *NodeC = addInstanceAtOffset(TS, NodeA, /*offset=*/0, /*size=*/16);
|
|
/*LTSN *NodeF*/ addInstanceAtOffset(TS, NodeC, /*offset=*/0, /*size=*/8);
|
|
/*LTSN *NodeG*/ addInstanceAtOffset(TS, NodeC, /*offset=*/8, /*size=*/8);
|
|
|
|
LTSN *Node1 = addInstanceAtOffset(TS, NodeA, /*offset=*/0, /*size=*/12);
|
|
LTSN *Node2 = addInstanceAtOffset(TS, Node1, /*offset=*/4, /*size=*/8);
|
|
LTSN *Node3 = addInstanceAtOffset(TS, Node2, /*offset=*/0, /*size=*/4);
|
|
LTSN *Node4 = addInstanceAtOffset(TS, Node2, /*offset=*/4, /*size=*/4);
|
|
|
|
// Run steps
|
|
VerifyLog.enable();
|
|
dla::StepManager SM;
|
|
revng_check(SM.addStep<CollapseIdentityAndInheritanceCC>());
|
|
revng_check(SM.addStep<ComputeUpperMemberAccesses>());
|
|
revng_check(SM.addStep<ComputeNonInterferingComponents>());
|
|
revng_check(SM.addStep<DeduplicateUnionFields>());
|
|
|
|
SM.run(TS);
|
|
|
|
// Compress the equivalence classes
|
|
dla::VectEqClasses &Eq = TS.getEqClasses();
|
|
Eq.compress();
|
|
|
|
// Check TS
|
|
revng_check(TS.getNumLayouts() == 8);
|
|
checkNode(TS, NodeA, 16, AllChildrenAreInterfering, { 0 });
|
|
checkNode(TS, NodeB, 16, AllChildrenAreNonInterfering, { 1, 4 });
|
|
checkNode(TS, NodeD, 8, AllChildrenAreNonInterfering, { 2, 5 });
|
|
checkNode(TS, NodeE, 8, AllChildrenAreNonInterfering, { 3, 6 });
|
|
checkNode(TS, Node1, 12, AllChildrenAreNonInterfering, { 7 });
|
|
checkNode(TS, Node2, 8, AllChildrenAreNonInterfering, { 8 });
|
|
checkNode(TS, Node3, 4, AllChildrenAreNonInterfering, { 9 });
|
|
checkNode(TS, Node4, 4, AllChildrenAreNonInterfering, { 10 });
|
|
}
|
|
|
|
BOOST_AUTO_TEST_CASE(DeduplicateUnionFields_diamond) {
|
|
dla::LayoutTypeSystem TS;
|
|
|
|
// Build TS
|
|
LTSN *NodeA = createRoot(TS);
|
|
LTSN *NodeB = addInheritance(TS, NodeA);
|
|
NodeB->Size = 8;
|
|
/*LTSN *NodeC =*/addInstanceAtOffset(TS, NodeA, /*offset=*/0, /*size=*/8);
|
|
LTSN *NodeD = addInstanceAtOffset(TS, NodeA, /*offset=*/0, /*size=*/0);
|
|
LTSN *NodeE = addInstanceAtOffset(TS, NodeA, /*offset=*/0, /*size=*/0);
|
|
LTSN *NodeF = addInstanceAtOffset(TS, NodeA, /*offset=*/0, /*size=*/0);
|
|
LTSN *NodeG = addInstanceAtOffset(TS, NodeA, /*offset=*/0, /*size=*/0);
|
|
|
|
LTSN *NodeH = addInstanceAtOffset(TS, NodeD, /*offset=*/0, /*size=*/8);
|
|
OffsetExpression OE{};
|
|
OE.Offset = 0;
|
|
TS.addInstanceLink(NodeE, NodeH, std::move(OE));
|
|
|
|
LTSN *NodeI = addInstanceAtOffset(TS, NodeF, /*offset=*/0, /*size=*/8);
|
|
OffsetExpression OE2{};
|
|
OE2.Offset = 0;
|
|
TS.addInstanceLink(NodeG, NodeI, std::move(OE2));
|
|
|
|
// Run steps
|
|
VerifyLog.enable();
|
|
dla::StepManager SM;
|
|
revng_check(SM.addStep<CollapseIdentityAndInheritanceCC>());
|
|
revng_check(SM.addStep<ComputeUpperMemberAccesses>());
|
|
revng_check(SM.addStep<ComputeNonInterferingComponents>());
|
|
revng_check(SM.addStep<DeduplicateUnionFields>());
|
|
revng_check(SM.addStep<ComputeNonInterferingComponents>());
|
|
|
|
SM.run(TS);
|
|
|
|
// Compress the equivalence classes
|
|
dla::VectEqClasses &Eq = TS.getEqClasses();
|
|
Eq.compress();
|
|
|
|
// Check TS
|
|
revng_check(TS.getNumLayouts() == 1);
|
|
checkNode(TS,
|
|
NodeA,
|
|
8,
|
|
AllChildrenAreNonInterfering,
|
|
{ 0, 1, 2, 3, 4, 5, 6, 7, 8 });
|
|
}
|
|
|
|
BOOST_AUTO_TEST_CASE(DeduplicateUnionFields_commonNodeSymmetric) {
|
|
dla::LayoutTypeSystem TS;
|
|
|
|
// Build TS
|
|
LTSN *NodeUnion = createRoot(TS);
|
|
LTSN *NodeA = addInstanceAtOffset(TS, NodeUnion, /*offset=*/0, /*size=*/0);
|
|
LTSN *NodeB = addInstanceAtOffset(TS, NodeA, /*offset=*/0, /*size=*/8);
|
|
LTSN *NodeC = addInstanceAtOffset(TS, NodeA, /*offset=*/0, /*size=*/0);
|
|
LTSN *NodeD = addInstanceAtOffset(TS, NodeC, /*offset=*/8, /*size=*/8);
|
|
|
|
LTSN *NodeA1 = addInstanceAtOffset(TS, NodeUnion, /*offset=*/0, /*size=*/0);
|
|
LTSN *NodeC1 = addInstanceAtOffset(TS, NodeA1, /*offset=*/0, /*size=*/0);
|
|
/*LTSN *NodeD1 =*/addInstanceAtOffset(TS, NodeC1, /*offset=*/8, /*size=*/8);
|
|
OffsetExpression OE{};
|
|
OE.Offset = 0;
|
|
TS.addInstanceLink(NodeA1, NodeB, std::move(OE));
|
|
|
|
// Run steps
|
|
VerifyLog.enable();
|
|
dla::StepManager SM;
|
|
revng_check(SM.addStep<CollapseIdentityAndInheritanceCC>());
|
|
revng_check(SM.addStep<ComputeUpperMemberAccesses>());
|
|
revng_check(SM.addStep<ComputeNonInterferingComponents>());
|
|
revng_check(SM.addStep<DeduplicateUnionFields>());
|
|
revng_check(SM.addStep<ComputeNonInterferingComponents>());
|
|
SM.run(TS);
|
|
|
|
// Compress the equivalence classes
|
|
dla::VectEqClasses &Eq = TS.getEqClasses();
|
|
Eq.compress();
|
|
|
|
// Check TS
|
|
revng_check(TS.getNumLayouts() == 4);
|
|
checkNode(TS, NodeUnion, 16, AllChildrenAreInterfering, { 0, 1, 5 });
|
|
checkNode(TS, NodeB, 8, AllChildrenAreNonInterfering, { 2 });
|
|
checkNode(TS, NodeC, 16, AllChildrenAreNonInterfering, { 3, 6 });
|
|
checkNode(TS, NodeD, 8, AllChildrenAreNonInterfering, { 4, 7 });
|
|
}
|
|
|
|
BOOST_AUTO_TEST_CASE(DeduplicateUnionFields_commonNodeAsymmetric) {
|
|
dla::LayoutTypeSystem TS;
|
|
|
|
// Build TS
|
|
LTSN *NodeUnion = createRoot(TS);
|
|
LTSN *NodeA = addInstanceAtOffset(TS, NodeUnion, /*offset=*/0, /*size=*/0);
|
|
/*LTSN *NodeB =*/addInstanceAtOffset(TS, NodeA, /*offset=*/0, /*size=*/8);
|
|
LTSN *NodeC = addInstanceAtOffset(TS, NodeA, /*offset=*/0, /*size=*/0);
|
|
LTSN *NodeD = addInstanceAtOffset(TS, NodeC, /*offset=*/4, /*size=*/8);
|
|
|
|
LTSN *NodeA1 = addInstanceAtOffset(TS, NodeUnion, /*offset=*/0, /*size=*/0);
|
|
LTSN *NodeC1 = addInstanceAtOffset(TS, NodeA1, /*offset=*/0, /*size=*/0);
|
|
/*LTSN *NodeD1 =*/addInstanceAtOffset(TS, NodeC1, /*offset=*/4, /*size=*/8);
|
|
OffsetExpression OE{};
|
|
OE.Offset = 0;
|
|
TS.addInstanceLink(NodeA1, NodeD, std::move(OE));
|
|
|
|
// Run steps
|
|
VerifyLog.enable();
|
|
dla::StepManager SM;
|
|
revng_check(SM.addStep<CollapseIdentityAndInheritanceCC>());
|
|
revng_check(SM.addStep<ComputeUpperMemberAccesses>());
|
|
revng_check(SM.addStep<ComputeNonInterferingComponents>());
|
|
revng_check(SM.addStep<DeduplicateUnionFields>());
|
|
revng_check(SM.addStep<ComputeNonInterferingComponents>());
|
|
|
|
SM.run(TS);
|
|
|
|
// Compress the equivalence classes
|
|
dla::VectEqClasses &Eq = TS.getEqClasses();
|
|
Eq.compress();
|
|
|
|
// Check TS
|
|
revng_check(TS.getNumLayouts() == 3);
|
|
checkNode(TS, NodeUnion, 12, AllChildrenAreInterfering, { 0, 1, 5 });
|
|
checkNode(TS, NodeC, 12, AllChildrenAreNonInterfering, { 3, 6 });
|
|
checkNode(TS, NodeD, 8, AllChildrenAreNonInterfering, { 2, 4, 7 });
|
|
}
|
|
|
|
BOOST_AUTO_TEST_CASE(DeduplicateUnionFields_commonNodeAsymmetricCollapse) {
|
|
dla::LayoutTypeSystem TS;
|
|
|
|
// Build TS
|
|
LTSN *NodeUnion = createRoot(TS);
|
|
LTSN *NodeA = addInstanceAtOffset(TS, NodeUnion, /*offset=*/0, /*size=*/0);
|
|
/*LTSN *NodeB =*/addInstanceAtOffset(TS, NodeA, /*offset=*/0, /*size=*/8);
|
|
LTSN *NodeC = addInstanceAtOffset(TS, NodeA, /*offset=*/0, /*size=*/0);
|
|
LTSN *NodeD = addInstanceAtOffset(TS, NodeC, /*offset=*/0, /*size=*/8);
|
|
|
|
LTSN *NodeA1 = addInstanceAtOffset(TS, NodeUnion, /*offset=*/0, /*size=*/0);
|
|
LTSN *NodeC1 = addInstanceAtOffset(TS, NodeA1, /*offset=*/0, /*size=*/0);
|
|
/*LTSN *NodeD1 =*/addInstanceAtOffset(TS, NodeC1, /*offset=*/0, /*size=*/8);
|
|
OffsetExpression OE{};
|
|
OE.Offset = 0;
|
|
TS.addInstanceLink(NodeA1, NodeD, std::move(OE));
|
|
|
|
// Run steps
|
|
VerifyLog.enable();
|
|
dla::StepManager SM;
|
|
revng_check(SM.addStep<CollapseIdentityAndInheritanceCC>());
|
|
revng_check(SM.addStep<ComputeUpperMemberAccesses>());
|
|
revng_check(SM.addStep<ComputeNonInterferingComponents>());
|
|
revng_check(SM.addStep<DeduplicateUnionFields>());
|
|
revng_check(SM.addStep<ComputeNonInterferingComponents>());
|
|
|
|
SM.run(TS);
|
|
|
|
// Compress the equivalence classes
|
|
dla::VectEqClasses &Eq = TS.getEqClasses();
|
|
Eq.compress();
|
|
|
|
// Check TS
|
|
revng_check(TS.getNumLayouts() == 3);
|
|
checkNode(TS, NodeUnion, 8, AllChildrenAreInterfering, { 0, 1, 5 });
|
|
checkNode(TS, NodeC, 8, AllChildrenAreNonInterfering, { 3, 6 });
|
|
checkNode(TS, NodeD, 8, AllChildrenAreNonInterfering, { 2, 4, 7 });
|
|
}
|
|
|
|
// ----------------- Remove Conflicting edges --------------
|
|
|
|
BOOST_AUTO_TEST_CASE(RemoveConflictingEdges_basic) {
|
|
dla::LayoutTypeSystem TS;
|
|
|
|
// Build TS
|
|
LTSN *NodeA = createRoot(TS);
|
|
LTSN *NodeB = addInstanceAtOffset(TS, NodeA, /*offset=*/0, /*size=*/8);
|
|
TS.addInheritanceLink(NodeA, NodeB);
|
|
|
|
LTSN *Node1 = createRoot(TS);
|
|
LTSN *Node2 = addInstanceAtOffset(TS, Node1, /*offset=*/4, /*size=*/8);
|
|
TS.addInheritanceLink(Node1, Node2);
|
|
|
|
// Run steps
|
|
VerifyLog.enable();
|
|
dla::StepManager SM;
|
|
revng_check(SM.addStep<RemoveConflictingEdges>());
|
|
|
|
SM.run(TS);
|
|
|
|
// Compress the equivalence classes
|
|
dla::VectEqClasses &Eq = TS.getEqClasses();
|
|
Eq.compress();
|
|
|
|
// Check TS
|
|
revng_check(TS.getNumLayouts() == 4);
|
|
revng_check(NodeA->Successors.size() == 1);
|
|
revng_check(Node1->Successors.size() == 2);
|
|
} |