Files
revng-revng/tests/Unit/ValueManipulationAnalysis.cpp
T
Giacomo Vercesi ab125b35b0 Fix License headers
Change company name to "rev.ng Labs Srl" in all license headers
to reflect changed company name and legal status
Add missing license headers to files that didn't have one
2022-04-19 12:17:59 +02:00

725 lines
21 KiB
C++

/// \file ValueManipulationAnalysis.cpp
/// Test the ValueManipulationAnalysis analysis
//
// Copyright rev.ng Labs Srl. See LICENSE.md for details.
//
#define BOOST_TEST_MODULE ValueManipulationAnalysis
bool init_unit_test();
#include "boost/test/unit_test.hpp"
#include "llvm/ADT/STLExtras.h"
#include "llvm/IR/InstIterator.h"
#include "llvm/IR/Verifier.h"
#include "revng/Support/Debug.h"
#include "revng/Support/IRHelpers.h"
#include "revng/UnitTestHelpers/LLVMTestHelpers.h"
#include "revng/UnitTestHelpers/UnitTestHelpers.h"
#include "revng-c/ValueManipulationAnalysis/TypeColors.h"
#include "revng-c/ValueManipulationAnalysis/ValueManipulationAnalysis.h"
#include "lib/ValueManipulationAnalysis/Mincut.h"
#include "lib/ValueManipulationAnalysis/TypeFlowGraph.h"
#include "lib/ValueManipulationAnalysis/TypeFlowNode.h"
using namespace llvm;
using namespace vma;
// Node types
enum NodeType : unsigned { VALUE, USE, MAX_TYPES };
const inline std::string NodeTypeName[MAX_TYPES] = { "values", "uses " };
// Counters
using ColorCounter = std::array<unsigned, MAX_COLORS>;
using TypeCounter = std::array<unsigned, MAX_TYPES>;
struct ExpectedShape {
TypeCounter Types;
ColorCounter Colors;
unsigned NCasts;
unsigned NUndecided;
ExpectedShape(TypeCounter T,
ColorCounter C,
unsigned NCasts,
unsigned NUndecided) :
Types(T), Colors(C), NCasts(NCasts), NUndecided(NUndecided) {}
};
/// Keep a count of how many nodes are colored with each color
static ColorCounter countColors(const TypeFlowGraph &TG) {
ColorCounter CC = { 0 };
for (const TypeFlowNode *const N : nodes(&TG))
for (size_t I = 0; I < MAX_COLORS; I++)
CC[I] += N->Candidates.Bits.test(I);
return CC;
}
/// Keep a count of how many nodes of each type there are in \a TG
static TypeCounter countTypes(const TypeFlowGraph &TG) {
TypeCounter TC = { 0 };
for (const TypeFlowNode *const N : nodes(&TG)) {
if (N->isUse())
TC[USE] += 1;
else if (N->isValue())
TC[VALUE] += 1;
else
revng_abort();
}
return TC;
}
/// Check that the TG nodes have the expected types and colors
static void checkShape(const TypeFlowGraph &TG, const ExpectedShape &Expected) {
const TypeCounter &ExpectedTypes = Expected.Types;
const ColorCounter &ExpectedColors = Expected.Colors;
const TypeCounter ActualType = countTypes(TG);
const ColorCounter ActualColor = countColors(TG);
dbgs() << " What Expected Actual\n";
// Check that there are the expected number of nodes of each type
for (size_t I = 0; I < MAX_TYPES; I++) {
dbgs() << NodeTypeName[I] << "\t" << ExpectedTypes[I] << "\t"
<< ActualType[I] << "\n";
revng_check(ExpectedTypes[I] == ActualType[I]);
}
// Check that there are the expected number of nodes of each color
for (size_t I = 0; I < MAX_COLORS; I++) {
dbgs() << vma::TypeColorName[I] << " \t" << ExpectedColors[I] << "\t"
<< ActualColor[I] << "\n";
revng_check(ActualColor[I] == ExpectedColors[I]);
}
// Check number of casts in the graph
unsigned ActualCasts = countCasts(TG);
dbgs() << "casts \t" << Expected.NCasts << "\t" << ActualCasts << "\n";
revng_check(Expected.NCasts == ActualCasts);
// Check number of undecided nodes
auto IsUndecided = [](const TypeFlowNode *N) { return N->isUndecided(); };
unsigned NUndecided = llvm::count_if(TG.nodes(), IsUndecided);
dbgs() << "undecided\t" << Expected.NUndecided << "\t" << NUndecided << "\n";
revng_check(NUndecided == Expected.NUndecided);
}
/// Check that the information in the graph are consistent
static void checkTGCorrectness(TypeFlowGraph &TG) {
// Check consistency between the graph and the reverse map
for (TypeFlowNode *N : TG.nodes()) {
auto MapIter = TG.ContentToNodeMap.find(N->Content);
revng_check(MapIter != TG.ContentToNodeMap.end());
revng_check(MapIter->second == N);
}
for (auto &Elem : TG.ContentToNodeMap) {
revng_check(Elem.second != nullptr);
auto NodeIter = llvm::find(TG.nodes(), Elem.second);
revng_check(NodeIter != TG.nodes().end());
revng_check((*NodeIter)->Content == Elem.first);
}
for (const TypeFlowNode *N : TG.nodes()) {
// Candidates should be a subset of accepted colors
revng_check(N->Accepted.contains(N->Candidates));
// Nodes can contain either uses or values
revng_check(N->isUse() xor N->isValue());
// If a Use node is in the TG, there must also be a node associated to the
// user and one for the used value
if (N->isUse()) {
revng_check(TG.ContentToNodeMap.contains(N->getUse()->get()));
revng_check(TG.ContentToNodeMap.contains(N->getUse()->getUser()));
}
// No double edges between nodes
for (const auto *Succ : N->successors())
revng_check(llvm::count(N->successors(), Succ));
}
}
/// Check that a TypeFlowGraph is initialized correctly from a function
static void checkInit(const char *Body,
const ExpectedShape ExpectedInit,
const ExpectedShape ExpectedAfterProp,
const ExpectedShape ExpectedFinal) {
// Read the LLVM IR
LLVMContext C;
std::unique_ptr<llvm::Module> M = loadModule(C, Body);
revng_check(not verifyModule(*M, &dbgs()));
Function *F = M->getFunction("main");
// Build the TG
TypeFlowGraph TG = makeTypeFlowGraphFromFunction(F);
checkTGCorrectness(TG);
checkShape(TG, ExpectedInit);
// Propagate
propagateColors(TG);
checkTGCorrectness(TG);
checkShape(TG, ExpectedAfterProp);
// Numberness
propagateNumberness(TG);
for (auto *N : TG.nodes())
revng_check(not N->Candidates.Bits.test(NUMBERNESS_INDEX));
// Undirected graph
makeBidirectional(TG);
for (auto *N : TG.nodes())
revng_check(llvm::size(N->successors()) == llvm::size(N->predecessors()));
// Mincut
minCut(TG);
checkShape(TG, ExpectedFinal);
}
// Test cases
BOOST_AUTO_TEST_CASE(TestTGInit) {
VerifyLog.enable();
// Alloca
checkInit(R"LLVM(
%0 = alloca i32, align 4
%1 = alloca i32, align 4
unreachable
)LLVM",
/*Init=*/
{ { /*values=*/3,
/*uses=*/0 },
{ /*pointers=*/2,
/*unsigned=*/0,
/*bools=*/0,
/*signed=*/0,
/*floats=*/0,
/*numbers=*/0 },
/*casts=*/0,
/*undecided=*/0 },
/*After propagation=*/
{ { /*values=*/3,
/*uses=*/0 },
{ /*pointers=*/2,
/*unsigned=*/0,
/*bools=*/0,
/*signed=*/0,
/*floats=*/0,
/*numbers=*/0 },
/*casts=*/0,
/*undecided=*/0 },
/*Final=*/
{ { /*values=*/3,
/*uses=*/0 },
{ /*pointers=*/2,
/*unsigned=*/0,
/*bools=*/0,
/*signed=*/0,
/*floats=*/0,
/*numbers=*/0 },
/*casts=*/0,
/*undecided=*/0 });
// Alloca + Load
checkInit(R"LLVM(
%0 = alloca i32, align 4
%1 = alloca i32, align 4
%2 = load i32, i32* %0, align 4
%3 = load i32, i32* %1, align 4
unreachable
)LLVM",
/*Init=*/
{ { /*values=*/5,
/*uses=*/2 },
{ /*pointers=*/4,
/*unsigned=*/0,
/*bools=*/0,
/*signed=*/0,
/*floats=*/0,
/*numbers=*/0 },
/*casts=*/0,
/*undecided=*/0 },
/*After propagation=*/
{ { /*values=*/5,
/*uses=*/2 },
{ /*pointers=*/4,
/*unsigned=*/0,
/*bools=*/0,
/*signed=*/0,
/*floats=*/0,
/*numbers=*/0 },
/*casts=*/0,
/*undecided=*/0 },
/*Final=*/
{ { /*values=*/5,
/*uses=*/2 },
{ /*pointers=*/4,
/*unsigned=*/0,
/*bools=*/0,
/*signed=*/0,
/*floats=*/0,
/*numbers=*/0 },
/*casts=*/0,
/*undecided=*/0 });
// Ptr forward (with add)
checkInit(R"LLVM(
%0 = alloca i32, align 4
%1 = alloca i32, align 4
%2 = load i32, i32* %0, align 4
%3 = load i32, i32* %1, align 4
%4 = add i32 %2, %3
%5 = sdiv i32 %2, 2
%6 = inttoptr i32 %3 to i32*
store i32 10, i32* %6, align 4
unreachable
)LLVM",
/*Init=*/
{ { /*values=*/9,
/*uses=*/7 },
{ /*pointers=*/5,
/*unsigned=*/0,
/*bools=*/0,
/*signed=*/2,
/*floats=*/0,
/*numbers=*/2 },
/*casts=*/2,
/*undecided=*/0 },
/*After propagation=*/
{ { /*values=*/9,
/*uses=*/7 },
{ /*pointers=*/10, //< pointerness doesn't go past add
/*unsigned=*/0,
/*bools=*/0,
/*signed=*/9,
/*floats=*/0,
/*numbers=*/2 },
/*casts=*/0,
/*undecided=*/5 },
/*Final=*/
{ { /*values=*/9,
/*uses=*/7 },
{ /*pointers=*/10,
/*unsigned=*/0,
/*bools=*/0,
/*signed=*/4,
/*floats=*/0,
/*numbers=*/0 },
/*casts=*/1,
/*undecided=*/0 });
// Ptr backward (with add)
checkInit(R"LLVM(
%0 = alloca i32, align 4
%1 = alloca i32, align 4
%2 = load i32, i32* %0, align 4
%3 = load i32, i32* %1, align 4
%4 = add i32 %2, %3
%5 = inttoptr i32 %4 to i32*
store i32 10, i32* %5, align 4
unreachable
)LLVM",
/*Init=*/
{ { /*value=*/8,
/*uses=*/6 },
{ /*pointers=*/5,
/*unsigned=*/0,
/*bools=*/0,
/*signed=*/0,
/*floats=*/0,
/*numbers=*/0 },
/*casts=*/1,
/*undecided=*/0 },
/*After propagation=*/
{ { /*value=*/8,
/*uses=*/6 },
{ /*pointers=*/8,
/*unsigned=*/0,
/*bools=*/0,
/*signed=*/0,
/*floats=*/0,
/*numbers=*/0 },
/*casts=*/2,
/*undecided=*/0 },
/*Final=*/
{ { /*value=*/8,
/*uses=*/6 },
{ /*pointers=*/8,
/*unsigned=*/0,
/*bools=*/0,
/*signed=*/0,
/*floats=*/0,
/*numbers=*/0 },
/*casts=*/2,
/*undecided=*/0 });
// Side propagation (with add)
checkInit(R"LLVM(
%0 = alloca i32, align 4
%1 = alloca i32, align 4
%2 = load i32, i32* %0, align 4
%3 = load i32, i32* %1, align 4
%4 = add i32 %2, %3
%5 = sdiv i32 %2, 2
%6 = udiv i32 %3, 3
unreachable
)LLVM",
/*Init=*/
{ { /*values=*/8,
/*uses=*/6 },
{ /*pointers=*/4,
/*unsigned=*/2,
/*bools=*/0,
/*signed=*/2,
/*floats=*/0,
/*numbers=*/4 },
/*casts=*/2,
/*undecided=*/0 },
/*After propagation=*/
{ { /*values=*/8,
/*uses=*/6 },
{ /*pointers=*/4,
/*unsigned=*/7,
/*bools=*/0,
/*signed=*/7,
/*floats=*/0,
/*numbers=*/4 },
/*casts=*/0,
/*undecided=*/5 },
/*Final=*/
{ { /*values=*/8,
/*uses=*/6 },
{ /*pointers=*/4,
/*unsigned=*/7,
/*bools=*/0,
/*signed=*/2,
/*floats=*/0,
/*numbers=*/0 },
/*casts=*/1,
/*undecided=*/0 });
// Side propagation (with icmp)
checkInit(R"LLVM(
%0 = alloca i32, align 4
%1 = alloca i32, align 4
%2 = load i32, i32* %0, align 4
%3 = load i32, i32* %1, align 4
%4 = icmp eq i32 %2, %3
%5 = icmp sgt i32 %2, 2
%6 = icmp ugt i32 %3, 3
unreachable
)LLVM",
/*Init=*/
{ { /*values=*/8,
/*uses=*/6 },
{ /*pointers=*/4,
/*unsigned=*/1,
/*bools=*/3,
/*signed=*/1,
/*floats=*/0,
/*numbers=*/0 },
/*casts=*/2,
/*undecided=*/0 },
/*After propagation=*/
{ { /*values=*/8,
/*uses=*/6 },
{ /*pointers=*/4,
/*unsigned=*/5,
/*bools=*/3,
/*signed=*/5,
/*floats=*/0,
/*numbers=*/0 },
/*casts=*/0,
/*undecided=*/4 },
/*Final=*/
{ { /*values=*/8,
/*uses=*/6 },
{ /*pointers=*/4,
/*unsigned=*/5,
/*bools=*/3,
/*signed=*/1,
/*floats=*/0,
/*numbers=*/0 },
/*casts=*/1,
/*undecided=*/0 });
// Mul + ptr (backward)
checkInit(R"LLVM(
%0 = alloca i32, align 4
%1 = alloca i32, align 4
%2 = alloca i32, align 4
%3 = load i32, i32* %0, align 4
%4 = load i32, i32* %1, align 4
%5 = load i32, i32* %2, align 4
%6 = mul i32 %3, %4
%7 = add i32 %5, %6
%8 = inttoptr i32 %7 to i32*
store i32 10, i32* %8
unreachable
)LLVM",
/*Init=*/
{ { /*values=*/11,
/*uses=*/9 },
{ /*pointers=*/7,
/*unsigned=*/0,
/*bools=*/0,
/*signed=*/0,
/*floats=*/0,
/*numbers=*/3 },
/*casts=*/1,
/*undecided=*/0 },
/*After propagation=*/
{ { /*values=*/11,
/*uses=*/9 },
{ /*pointers=*/10,
/*unsigned=*/0,
/*bools=*/0,
/*signed=*/0,
/*floats=*/0,
/*numbers=*/3 },
/*casts=*/2,
/*undecided=*/0 },
/*Final=*/
{ { /*values=*/11,
/*uses=*/9 },
{ /*pointers=*/10,
/*unsigned=*/0,
/*bools=*/0,
/*signed=*/0,
/*floats=*/0,
/*numbers=*/0 },
/*casts=*/2,
/*undecided=*/0 });
// Mul + ptr (forward)
checkInit(R"LLVM(
%0 = alloca i32, align 4
%1 = alloca i32, align 4
%2 = alloca i32, align 4
%3 = load i32, i32* %0, align 4
%4 = load i32, i32* %1, align 4
%5 = load i32, i32* %2, align 4
%6 = mul i32 %3, %4
%7 = add i32 %5, %6
%8 = inttoptr i32 %5 to i32*
store i32 10, i32* %8
unreachable
)LLVM",
/*Init=*/
{ { /*values=*/11,
/*uses=*/9 },
{ /*pointers=*/7,
/*unsigned=*/0,
/*bools=*/0,
/*signed=*/0,
/*floats=*/0,
/*numbers=*/3 },
/*casts=*/1,
/*undecided=*/0 },
/*After propagation=*/
{ { /*values=*/11,
/*uses=*/9 },
{ /*pointers=*/12,
/*unsigned=*/0,
/*bools=*/0,
/*signed=*/0,
/*floats=*/0,
/*numbers=*/3 },
/*casts=*/1,
/*undecided=*/0 },
/*Final=*/
{ { /*values=*/11,
/*uses=*/9 },
{ /*pointers=*/12,
/*unsigned=*/0,
/*bools=*/0,
/*signed=*/0,
/*floats=*/0,
/*numbers=*/0 },
/*casts=*/1,
/*undecided=*/0 });
// And with bools
checkInit(R"LLVM(
%0 = alloca i32, align 4
%1 = alloca i32, align 4
%2 = load i32, i32* %0, align 4
%3 = load i32, i32* %1, align 4
%4 = icmp eq i32 %2, 2
%5 = icmp eq i32 %3, 3
%6 = and i1 %5, %4
unreachable
)LLVM",
/*Init=*/
{ { /*values=*/8,
/*uses=*/6 },
{ /*pointers=*/4,
/*unsigned=*/0,
/*bools=*/2,
/*signed=*/0,
/*floats=*/0,
/*numbers=*/0 },
/*casts=*/2,
/*undecided=*/0 },
/*After propagation=*/
{ { /*values=*/8,
/*uses=*/6 },
{ /*pointers=*/4,
/*unsigned=*/0,
/*bools=*/5,
/*signed=*/0,
/*floats=*/0,
/*numbers=*/0 },
/*casts=*/0,
/*undecided=*/0 },
/*Final=*/
{ { /*values=*/8,
/*uses=*/6 },
{ /*pointers=*/4,
/*unsigned=*/0,
/*bools=*/5,
/*signed=*/0,
/*floats=*/0,
/*numbers=*/0 },
/*casts=*/0,
/*undecided=*/0 });
// Shift ptr
checkInit(R"LLVM(
%0 = alloca i32, align 4
%1 = alloca i32, align 4
%2 = load i32, i32* %0, align 4
%3 = load i32, i32* %1, align 4
%4 = inttoptr i32 %2 to i32*
store i32 10, i32* %4
%5 = shl i32 %2, %3
unreachable
)LLVM",
/*Init=*/
{ { /*values=*/8,
/*uses=*/6 },
{ /*pointers=*/5,
/*unsigned=*/1,
/*bools=*/0,
/*signed=*/0,
/*floats=*/0,
/*numbers=*/0 },
/*casts=*/2,
/*undecided=*/0 },
/*After propagation=*/
{ { /*values=*/8,
/*uses=*/6 },
{ /*pointers=*/8,
/*unsigned=*/2,
/*bools=*/0,
/*signed=*/0,
/*floats=*/0,
/*numbers=*/0 },
/*casts=*/1,
/*undecided=*/0 },
/*Final=*/
{ { /*values=*/8,
/*uses=*/6 },
{ /*pointers=*/8,
/*unsigned=*/2,
/*bools=*/0,
/*signed=*/0,
/*floats=*/0,
/*numbers=*/0 },
/*casts=*/1,
/*undecided=*/0 });
// Set last bit to 0
checkInit(R"LLVM(
%0 = alloca i32, align 4
%1 = load i32, i32* %0, align 4
%2 = inttoptr i32 %1 to i32*
store i32 10, i32* %2
%3 = and i32 %1, 254
unreachable
)LLVM",
/*Init=*/
{ { /*values=*/6,
/*uses=*/4 },
{ /*pointers=*/3,
/*unsigned=*/0,
/*bools=*/0,
/*signed=*/0,
/*floats=*/0,
/*numbers=*/0 },
/*casts=*/1,
/*undecided=*/0 },
/*Final=*/
{ { /*values=*/6,
/*uses=*/4 },
{ /*pointers=*/7,
/*unsigned=*/0,
/*bools=*/0,
/*signed=*/0,
/*floats=*/0,
/*numbers=*/0 },
/*casts=*/1,
/*undecided=*/0 },
/*After propagation=*/
{ { /*values=*/6,
/*uses=*/4 },
{ /*pointers=*/7,
/*unsigned=*/0,
/*bools=*/0,
/*signed=*/0,
/*floats=*/0,
/*numbers=*/0 },
/*casts=*/1,
/*undecided=*/0 });
}
BOOST_AUTO_TEST_CASE(TestMajorityVoting) {
const auto AddNode = [](TypeFlowGraph &G, unsigned Color) {
UseOrValue DummyContent;
ColorSet DecidedColor(Color);
NodeColorProperty InitColors(DecidedColor, DecidedColor);
return G.addNode(DummyContent, InitColors);
};
TypeFlowGraph G;
TypeFlowNode *Undecided1 = AddNode(G, (POINTERNESS | UNSIGNEDNESS));
TypeFlowNode *Undecided2 = AddNode(G, (POINTERNESS | UNSIGNEDNESS));
Undecided1->addSuccessor(Undecided2);
TypeFlowNode *Decided1 = AddNode(G, POINTERNESS);
TypeFlowNode *Decided2 = AddNode(G, POINTERNESS);
TypeFlowNode *Decided3 = AddNode(G, UNSIGNEDNESS);
TypeFlowNode *Decided4 = AddNode(G, SIGNEDNESS);
Undecided1->addSuccessor(Decided1);
Undecided1->addSuccessor(Decided2);
Undecided1->addSuccessor(Decided3);
Undecided1->addSuccessor(Decided4);
makeBidirectional(G);
applyMajorityVoting(G);
revng_check(Undecided1->Candidates == (POINTERNESS | UNSIGNEDNESS));
revng_check(Undecided2->Candidates == (POINTERNESS | UNSIGNEDNESS));
TypeFlowNode *Decided5 = AddNode(G, POINTERNESS);
Undecided1->addSuccessor(Decided5);
makeBidirectional(G);
applyMajorityVoting(G);
revng_check(Undecided1->Candidates == POINTERNESS);
revng_check(Undecided2->Candidates == POINTERNESS);
}