mirror of
https://github.com/revng/revng
synced 2026-06-21 14:07:57 +00:00
93852f92d4
This is more uniform with the directory structure we have in revng
725 lines
21 KiB
C++
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);
|
|
}
|