mirror of
https://github.com/revng/revng
synced 2026-06-21 14:07:57 +00:00
5009074e9e
* Introduce `ShrinkInstructionOperandsPass`: a transformation shrinking operands and the results of instructions if they are zero/sign-extended immediately before and after the instruction. * Introduce `ConstantRangeSet`: similar to `ConstantRange` but allows disjoint ranges. * Introduce `MaterializedValue`: a class that can represent a constant value or a symbol plus offset pair. * Introduce `DropHelperCallsPass`: a transformation removing calls to helpers and replacing them with a function call reading the CSVs that the helper reads and writing the CSVs that the helper writes (according to CSAA). * Introduce `DropRangeMetadataPass`: a transformation dropping the `range` metadata, which, in certain situations, lowers the quality of the results provided by `LazyValueInfo`. * Introduce `AdvancedValueInfo`: an analysis exploiting results of `LazyValueInfo` but collecting them as `ConstantRangeSet` with a monotone framework. It produces `MaterializedValue`. * Anticipate linking of helpers: `AVI` requires `CSAA`, which requires helper functions to be linked in. * Drop `--no-link`. * Force x86-64 `DataLayout`. * Reorganize harvesting to either collect simple literals or go with (incremental) `AVI`. * Drop `SET`, `OSRA`, the reaching definition analysis, the `SimplifyComparisonsPass` and all the sumjump-related code: e now clone `root`, optimize it and analyze it with `AVI`. * Temporarily drop the `NoReturnAnalysis`. * Link `libLLVMInstCombine`, `libLLVMCodeGen` and `libLLVMPasses`. * Introduce tests for `AdvancedValueInfo`, `ShrinkInstructionOperandsPass` and `ConstantRangeSet`. * Fix test results. * Add `llvm.bswap.i64` and `@pc` to the LLVM template module for unit tests.
428 lines
10 KiB
C++
428 lines
10 KiB
C++
/// \file AdvancedValueInfo.cpp
|
|
/// \brief Test the AdvancedValueInfo analysis
|
|
|
|
//
|
|
// This file is distributed under the MIT License. See LICENSE.md for details.
|
|
//
|
|
|
|
// Boost includes
|
|
#define BOOST_TEST_MODULE AdvancedValueInfo
|
|
bool init_unit_test();
|
|
#include <boost/test/unit_test.hpp>
|
|
|
|
// LLVM includes
|
|
#include "llvm/Analysis/Passes.h"
|
|
#include "llvm/IR/LegacyPassManager.h"
|
|
#include "llvm/IR/Verifier.h"
|
|
#include "llvm/Transforms/Scalar.h"
|
|
|
|
// Local libraries includes
|
|
#include "revng/BasicAnalyses/AdvancedValueInfo.h"
|
|
#include "revng/Support/Debug.h"
|
|
#include "revng/Support/IRHelpers.h"
|
|
#include "revng/UnitTestHelpers/LLVMTestHelpers.h"
|
|
#include "revng/UnitTestHelpers/UnitTestHelpers.h"
|
|
|
|
using namespace llvm;
|
|
|
|
class MockupMemoryOracle {
|
|
private:
|
|
const llvm::DataLayout &DL;
|
|
|
|
public:
|
|
MockupMemoryOracle(const llvm::DataLayout &DL) : DL(DL) {}
|
|
|
|
const llvm::DataLayout &getDataLayout() const { return DL; }
|
|
|
|
MaterializedValue load(Constant *Address) {
|
|
if (auto *CI = dyn_cast<ConstantInt>(skipCasts(Address)))
|
|
if (getLimitedValue(CI) == 1000)
|
|
return { "symbol", 0 };
|
|
return { 42 };
|
|
}
|
|
};
|
|
|
|
class TestAdvancedValueInfoPass : public ModulePass {
|
|
public:
|
|
using ResultsMap = std::map<Value *, MaterializedValues>;
|
|
|
|
public:
|
|
static char ID;
|
|
|
|
public:
|
|
TestAdvancedValueInfoPass() : ModulePass(ID), Results(nullptr) {}
|
|
TestAdvancedValueInfoPass(ResultsMap &Results) :
|
|
ModulePass(ID),
|
|
Results(&Results) {}
|
|
|
|
void getAnalysisUsage(llvm::AnalysisUsage &AU) const override {
|
|
AU.setPreservesAll();
|
|
AU.addRequired<DominatorTreeWrapperPass>();
|
|
AU.addRequired<LazyValueInfoWrapperPass>();
|
|
}
|
|
|
|
bool runOnModule(llvm::Module &M) override;
|
|
|
|
private:
|
|
ResultsMap *Results;
|
|
};
|
|
|
|
char TestAdvancedValueInfoPass::ID = 0;
|
|
|
|
#define PASS_FLAG "test-advanced-value-info"
|
|
#define PASS_DESCRIPTION "Test Pass for the Advanced Value Info analysis"
|
|
|
|
using Register = RegisterPass<TestAdvancedValueInfoPass>;
|
|
static Register Y(PASS_FLAG, PASS_DESCRIPTION, false, false);
|
|
|
|
#undef PASS_FLAG
|
|
#undef PASS_DESCRIPTION
|
|
|
|
bool TestAdvancedValueInfoPass::runOnModule(llvm::Module &M) {
|
|
Function &Root = *M.getFunction("main");
|
|
auto &LVI = getAnalysis<LazyValueInfoWrapperPass>(Root).getLVI();
|
|
auto &DT = getAnalysis<DominatorTreeWrapperPass>(Root).getDomTree();
|
|
ReversePostOrderTraversal<Function *> RPOT(&Root);
|
|
std::vector<BasicBlock *> RPOTVector;
|
|
std::copy(RPOT.begin(), RPOT.end(), std::back_inserter(RPOTVector));
|
|
|
|
MockupMemoryOracle MO(M.getDataLayout());
|
|
AdvancedValueInfo<MockupMemoryOracle> AVI(LVI, DT, MO, RPOTVector);
|
|
|
|
for (User *U : M.getGlobalVariable("pc", true)->users()) {
|
|
if (auto *Store = dyn_cast<StoreInst>(U)) {
|
|
Value *V = Store->getValueOperand();
|
|
(*Results)[V] = AVI.explore(Store->getParent(), V);
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
using CheckMap = std::map<const char *, MaterializedValues>;
|
|
|
|
static void checkAdvancedValueInfo(const char *Body, const CheckMap &Map) {
|
|
auto &Registry = *PassRegistry::getPassRegistry();
|
|
initializeDominatorTreeWrapperPassPass(Registry);
|
|
initializeLazyValueInfoWrapperPassPass(Registry);
|
|
|
|
LLVMContext C;
|
|
std::unique_ptr<llvm::Module> M = loadModule(C, Body);
|
|
revng_assert(not verifyModule(*M, &dbgs()));
|
|
|
|
Function *F = M->getFunction("main");
|
|
|
|
TestAdvancedValueInfoPass::ResultsMap Results;
|
|
|
|
legacy::PassManager PM;
|
|
PM.add(createLazyValueInfoPass());
|
|
PM.add(new TestAdvancedValueInfoPass(Results));
|
|
PM.run(*M);
|
|
|
|
TestAdvancedValueInfoPass::ResultsMap Reference;
|
|
for (auto &P : Map)
|
|
Reference[instructionByName(F, P.first)] = P.second;
|
|
|
|
revng_check(Results == Reference);
|
|
}
|
|
|
|
BOOST_AUTO_TEST_CASE(TestConstant) {
|
|
|
|
checkAdvancedValueInfo(R"LLVM(
|
|
%constant = add i64 4194424, 0
|
|
store i64 %constant, i64* @pc
|
|
unreachable
|
|
)LLVM",
|
|
{ { "constant", { 4194424 } } });
|
|
|
|
checkAdvancedValueInfo(R"LLVM(
|
|
%other = add i64 4194424, 0
|
|
%constant = add i64 %other, 0
|
|
store i64 %constant, i64* @pc
|
|
unreachable
|
|
)LLVM",
|
|
{ { "constant", { 4194424 } } });
|
|
}
|
|
|
|
BOOST_AUTO_TEST_CASE(TestRange) {
|
|
checkAdvancedValueInfo(R"LLVM(
|
|
%to_store = load i64, i64 *@pc
|
|
%cmp = icmp ult i64 %to_store, 5
|
|
br i1 %cmp, label %smaller, label %end
|
|
|
|
smaller:
|
|
store i64 %to_store, i64* @pc
|
|
br label %end
|
|
|
|
end:
|
|
unreachable
|
|
|
|
)LLVM",
|
|
{ { "to_store", { 0, 1, 2, 3, 4 } } });
|
|
|
|
checkAdvancedValueInfo(R"LLVM(
|
|
%original = load i64, i64 *@pc
|
|
%cmp = icmp ult i64 %original, 5
|
|
br i1 %cmp, label %smaller, label %end
|
|
|
|
smaller:
|
|
%shifted = shl i64 %original, 1
|
|
store i64 %shifted, i64* @pc
|
|
br label %end
|
|
|
|
end:
|
|
unreachable
|
|
|
|
)LLVM",
|
|
{ { "shifted", { 0, 2, 4, 6, 8 } } });
|
|
}
|
|
|
|
BOOST_AUTO_TEST_CASE(TestPhi) {
|
|
checkAdvancedValueInfo(R"LLVM(
|
|
br label %start
|
|
|
|
start:
|
|
br label %use_phi
|
|
|
|
use_phi:
|
|
%to_store = phi i64 [ 5, %start ]
|
|
store i64 %to_store, i64* @pc
|
|
unreachable
|
|
|
|
)LLVM",
|
|
{ { "to_store", { 5 } } });
|
|
|
|
checkAdvancedValueInfo(R"LLVM(
|
|
br label %start
|
|
|
|
start:
|
|
%pre_phi = add i64 5, 0
|
|
br label %use_phi
|
|
|
|
use_phi:
|
|
%to_store = phi i64 [ %pre_phi, %start ]
|
|
store i64 %to_store, i64* @pc
|
|
unreachable
|
|
|
|
)LLVM",
|
|
{ { "to_store", { 5 } } });
|
|
|
|
// Two distinct constants
|
|
checkAdvancedValueInfo(R"LLVM(
|
|
br label %start
|
|
|
|
start:
|
|
br i1 true, label %true, label %false
|
|
|
|
true:
|
|
%pre_phi1 = zext i32 5 to i64
|
|
br label %use_phi
|
|
|
|
false:
|
|
%pre_phi2 = zext i32 10 to i64
|
|
br label %use_phi
|
|
|
|
use_phi:
|
|
; Here LVI will tell us that to_store is in [5, 10], but we want to be more
|
|
; accurate than that
|
|
%to_store = phi i64 [ %pre_phi1, %true ], [ %pre_phi2, %false ]
|
|
store i64 %to_store, i64* @pc
|
|
unreachable
|
|
|
|
)LLVM",
|
|
{ { "to_store", { 5, 10 } } });
|
|
|
|
// Multi-level phi
|
|
checkAdvancedValueInfo(R"LLVM(
|
|
br i1 true, label %true, label %false
|
|
|
|
true:
|
|
br i1 true, label %true_true, label %true_false
|
|
|
|
false:
|
|
br i1 true, label %false_true, label %false_false
|
|
|
|
true_true:
|
|
%five = add i64 0, 5
|
|
br label %true_merge
|
|
|
|
true_false:
|
|
%ten = add i64 0, 10
|
|
br label %true_merge
|
|
|
|
true_merge:
|
|
%true_phi = phi i64 [ %five, %true_true ], [ %ten, %true_false ]
|
|
br label %use_phi
|
|
|
|
false_true:
|
|
%nineteen = add i64 0, 19
|
|
br label %false_merge
|
|
|
|
false_false:
|
|
%twentytwo = add i64 0, 22
|
|
br label %false_merge
|
|
|
|
false_merge:
|
|
%false_phi = phi i64 [ %nineteen, %false_true ], [ %twentytwo, %false_false ]
|
|
br label %use_phi
|
|
|
|
use_phi:
|
|
%to_store = phi i64 [ %true_phi, %true_merge ], [ %false_phi, %false_merge ]
|
|
store i64 %to_store, i64* @pc
|
|
ret void
|
|
|
|
)LLVM",
|
|
{ { "to_store", { 5, 10, 19, 22 } } });
|
|
|
|
// Two disjoint ranges and a constant
|
|
checkAdvancedValueInfo(R"LLVM(
|
|
br label %entry
|
|
|
|
entry:
|
|
%x = load i64, i64* @rax
|
|
%equal100 = icmp ne i64 %x, 100
|
|
br i1 %equal100, label %true, label %final
|
|
|
|
true:
|
|
%zerofive_condition = icmp ult i64 %x, 5
|
|
br i1 %zerofive_condition, label %true_merge, label %continue
|
|
|
|
continue:
|
|
%gt10 = icmp ugt i64 %x, 10
|
|
%lt15 = icmp ult i64 %x, 15
|
|
%in10_15 = and i1 %gt10, %lt15
|
|
br i1 %in10_15, label %true_merge, label %dead
|
|
|
|
dead:
|
|
unreachable
|
|
|
|
true_merge:
|
|
%true_result = phi i64 [ %x, %true ], [ %x, %continue ]
|
|
br label %final
|
|
|
|
final:
|
|
%to_store = phi i64 [ %x, %entry ], [ %true_result, %true_merge ]
|
|
store i64 %to_store, i64* @pc
|
|
ret void
|
|
|
|
)LLVM",
|
|
{ { "to_store",
|
|
{ 0, 1, 2, 3, 4, 11, 12, 13, 14, 100 } } });
|
|
}
|
|
|
|
BOOST_AUTO_TEST_CASE(TestLoops) {
|
|
checkAdvancedValueInfo(R"LLVM(
|
|
br label %entry
|
|
|
|
entry:
|
|
%initial = add i64 5, 0
|
|
br label %start
|
|
|
|
start:
|
|
%to_store = phi i64 [ %initial, %entry ], [ %in_loop, %start ]
|
|
store i64 %to_store, i64* @pc
|
|
%in_loop = add i64 8, 0
|
|
br i1 false, label %end, label %start
|
|
|
|
end:
|
|
unreachable
|
|
|
|
)LLVM",
|
|
{ { "to_store", { 5, 8 } } });
|
|
|
|
// Note: LazyValueInfo is not expressive enough to track the fact that the add
|
|
// is monotone. This is test is to ensure we don't end in an infinite
|
|
// loop in presence of a phi in a loop.
|
|
checkAdvancedValueInfo(R"LLVM(
|
|
br label %entry
|
|
|
|
entry:
|
|
%initial = add i64 5, 0
|
|
br label %start
|
|
|
|
start:
|
|
%to_store = phi i64 [ %initial, %entry ], [ %in_loop, %start ]
|
|
store i64 %to_store, i64* @pc
|
|
%in_loop = add i64 %to_store, 1
|
|
%condition = icmp ugt i64 %in_loop, 10
|
|
br i1 %condition, label %end, label %start
|
|
|
|
end:
|
|
unreachable
|
|
|
|
)LLVM",
|
|
{ { "to_store", { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 } } });
|
|
}
|
|
|
|
BOOST_AUTO_TEST_CASE(TestMemory) {
|
|
checkAdvancedValueInfo(R"LLVM(
|
|
%fortytwo = load i64, i64* inttoptr (i64 4294967296 to i64*)
|
|
%to_store = add i64 %fortytwo, 1
|
|
store i64 %to_store, i64* @pc
|
|
unreachable
|
|
|
|
)LLVM",
|
|
{ { "to_store", { 43 } } });
|
|
}
|
|
|
|
BOOST_AUTO_TEST_CASE(TestBswap) {
|
|
checkAdvancedValueInfo(R"LLVM(
|
|
%to_store = call i64 @llvm.bswap.i64(i64 255)
|
|
store i64 %to_store, i64* @pc
|
|
unreachable
|
|
|
|
)LLVM",
|
|
{ { "to_store", { 0xFFULL << (7 * 8) } } });
|
|
}
|
|
|
|
BOOST_AUTO_TEST_CASE(TestSymbol) {
|
|
checkAdvancedValueInfo(R"LLVM(
|
|
%symbol = load i64, i64* inttoptr (i64 1000 to i64*)
|
|
%to_store = add i64 %symbol, 10
|
|
store i64 %to_store, i64* @pc
|
|
unreachable
|
|
|
|
)LLVM",
|
|
{ { "to_store", { { "symbol", 10 } } } });
|
|
|
|
// We don't handle multiplication of symbol values
|
|
checkAdvancedValueInfo(R"LLVM(
|
|
%symbol = load i64, i64* inttoptr (i64 1000 to i64*)
|
|
%to_store = mul i64 %symbol, 10
|
|
store i64 %to_store, i64* @pc
|
|
unreachable
|
|
|
|
)LLVM",
|
|
{ { "to_store", {} } });
|
|
}
|
|
|
|
BOOST_AUTO_TEST_CASE(TestDisjoint) {
|
|
// Two disjoint intervals without phis
|
|
checkAdvancedValueInfo(R"LLVM(
|
|
br label %start
|
|
|
|
start:
|
|
%to_store = load i64, i64* @rax
|
|
%gt10 = icmp ugt i64 %to_store, 10
|
|
%lt15 = icmp ult i64 %to_store, 15
|
|
%in10_15 = and i1 %gt10, %lt15
|
|
br i1 %in10_15, label %end, label %false
|
|
|
|
false:
|
|
%gt30 = icmp ugt i64 %to_store, 30
|
|
%lt35 = icmp ult i64 %to_store, 35
|
|
%in30_35 = and i1 %gt30, %lt35
|
|
br i1 %in30_35, label %end, label %exit
|
|
|
|
exit:
|
|
unreachable
|
|
|
|
end:
|
|
store i64 %to_store, i64* @pc
|
|
unreachable
|
|
|
|
)LLVM",
|
|
{ { "to_store",
|
|
{ 11, 12, 13, 14, 31, 32, 33, 34 } } });
|
|
}
|