/// \file ReachingDefinitionsPass.cpp /// \brief Tests for ReachingDefinitionsPass // // This file is distributed under the MIT License. See LICENSE.md for details. // // Boost includes #define BOOST_TEST_MODULE ReachingDefinitionsPass bool init_unit_test(); #include // LLVM includes #include "llvm/IR/Dominators.h" #include "llvm/IR/LegacyPassManager.h" // Local libraries includes #include "revng/BasicAnalyses/ReachingDefinitionsAnalysisImpl.h" // Local includes #include "LLVMTestHelpers.h" using namespace llvm; template static void assertReachers(Function *F, const RDA::Analysis &A, const char *InstructionName, std::vector ExpectedNames) { auto *I = cast(instructionByName(F, InstructionName)); std::set Expected; for (const char *Name : ExpectedNames) Expected.insert(instructionByName(F, Name)); std::set Actual; for (Instruction *Reacher : A.getReachers(I)) Actual.insert(Reacher); if (Expected != Actual) { dbg << "Unexpected result:\n"; dbg << "Expected:\n"; for (Instruction *I : Expected) I->dump(); dbg << "Actual:\n"; for (Instruction *I : Actual) I->dump(); revng_abort(); } } using ColorMap = std::map; namespace RDA { template<> struct ColorsProviderTraits { static ColorsList &Empty; static const ColorsList &getBlockColors(const ColorMap &CP, BasicBlock *BB) { auto It = CP.find(BB); if (It == CP.end()) return EmptyColorsList; else return It->second; } static int32_t getEdgeColor(const ColorMap &CP, BasicBlock *Source, BasicBlock *Destination) { if (auto *Branch = dyn_cast(Source->getTerminator())) { if (Branch->isUnconditional()) return 0; bool First = Source->getTerminator()->getSuccessor(0) == Destination; int32_t Pointer = reinterpret_cast(Branch->getCondition()); return Pointer * (First ? 1 : -1); } else { return 0; } } static const llvm::SmallVector & getResetColors(const ColorMap &CNP, llvm::BasicBlock *BB) { static llvm::SmallVector ResultVector; std::set Result; // Find all instructions used as a condition in a conditional branch for (Instruction &I : *BB) for (Use &U : I.uses()) if (auto *B = dyn_cast(U.getUser())) if (B->isConditional() and U.getOperandNo() == 0) Result.insert(reinterpret_cast(&I)); ResultVector.clear(); std::copy(Result.begin(), Result.end(), std::back_inserter(ResultVector)); return ResultVector; } }; } // namespace RDA enum TestType { Regular, Conditional, Both }; static void runTest(const char *Body, std::vector>> Checks, std::vector BlackList = {}, TestType T = Both) { LLVMContext TestContext; std::unique_ptr M = loadModule(TestContext, Body); Function *F = M->getFunction("main"); std::set BasicBlockBlackList; for (const char *Name : BlackList) BasicBlockBlackList.insert(basicBlockByName(F, Name)); if (T == Regular || T == Both) { using Analysis = RDA::Analysis>; Analysis A(F, RDA::NullColorsProvider(), BasicBlockBlackList, nullptr, nullptr); A.registerExtremal(&F->getEntryBlock()); A.initialize(); A.run(); for (auto &P : Checks) assertReachers(F, A, P.first, P.second); } if (T == Conditional || T == Both) { highlightConditionEdges(*F); // Compute the dominator tree // TODO: in more recent LLVM versions we don't need to recompute the // dominator tree but we'll be able to update it DominatorTree DT(*F); ColorMap Colors; // Perform a light version of the ConditionNumberingPass std::map ConditionsMap; for (BasicBlock &BB : *F) { auto *T = dyn_cast(BB.getTerminator()); if (T == nullptr or T->isUnconditional()) continue; int32_t ConditionIndex = reinterpret_cast(T->getCondition()); // ConditionIndex at the first iteration will be positive, at the second // negative std::array Successors{ T->getSuccessor(0), T->getSuccessor(1) }; for (BasicBlock *Successor : Successors) { revng_assert(Successor->getSinglePredecessor() == &BB); SmallVector Descendants; DT.getDescendants(Successor, Descendants); for (BasicBlock *Descendant : Descendants) Colors[Descendant].push_back(ConditionIndex); ConditionIndex = -ConditionIndex; } } using Analysis = RDA::Analysis>; Analysis CA(F, Colors, BasicBlockBlackList, nullptr, nullptr); CA.registerExtremal(&F->getEntryBlock()); CA.initialize(); CA.run(); for (auto &P : Checks) assertReachers(F, CA, P.first, P.second); } } BOOST_AUTO_TEST_CASE(OneStoreOneLoad) { // // One store, one load // const char *Body = R"LLVM( %zero = add i64 0, 0 store i64 %zero, i64* @rax %load_rax = load i64, i64* @rax ret void )LLVM"; runTest(Body, { { "load_rax", { "s:zero" } } }); } BOOST_AUTO_TEST_CASE(StoreToDifferentCSV) { // // Store to a different CSV // const char *Body = R"LLVM( %zero = add i64 0, 0 store i64 %zero, i64* @rax %one = add i64 0, 0 store i64 %one, i64* @rbx %load_rax = load i64, i64* @rax ret void )LLVM"; runTest(Body, { { "load_rax", { "s:zero" } } }); } BOOST_AUTO_TEST_CASE(ClobberingStore) { // // Store clobbering a previous store // const char *Body = R"LLVM( %zero = add i64 0, 0 store i64 %zero, i64* @rax %one = add i64 1, 0 store i64 %one, i64* @rax %load_rax = load i64, i64* @rax ret void )LLVM"; runTest(Body, { { "load_rax", { "s:one" } } }); } BOOST_AUTO_TEST_CASE(LoadReachingAnotherLoad) { // // Load reaching another load // const char *Body = R"LLVM( %load_rax1 = load i64, i64* @rax %load_rax2 = load i64, i64* @rax ret void )LLVM"; runTest(Body, { { "load_rax2", { "load_rax1" } } }); } BOOST_AUTO_TEST_CASE(MultipleLoadsReachingAnotherLoad) { // // Multiple loads reaching another load // const char *Body = R"LLVM( %load_rax1 = load i64, i64* @rax %load_rax2 = load i64, i64* @rax %load_rax3 = load i64, i64* @rax ret void )LLVM"; runTest(Body, { { "load_rax3", { "load_rax1" } } }); } BOOST_AUTO_TEST_CASE(IfStatement) { // // If statement // const char *If = R"LLVM( %storezero = add i64 0, 0 store i64 %storezero, i64* @rax br i1 0, label %one, label %two one: %storeone = add i64 0, 0 store i64 %storeone, i64* @rax br label %end two: %storetwo = add i64 0, 0 store i64 %storetwo, i64* @rax br label %end end: %load_rax = load i64, i64* @rax ret void )LLVM"; runTest(If, { { "load_rax", { "s:storeone", "s:storetwo" } } }); // Now try again but inhibiting propgation to the end basic block runTest(If, { { "load_rax", {} } }, { "end" }); } BOOST_AUTO_TEST_CASE(Loop) { // // Loop // const char *Body = R"LLVM( %storeone = add i64 0, 0 store i64 %storeone, i64* @rax br label %head head: %load_rax = load i64, i64* @rax %storetwo = add i64 0, 0 store i64 %storetwo, i64* @rax br i1 0, label %end, label %head end: ret void )LLVM"; runTest(Body, { { "load_rax", { "s:storeone", "s:storetwo" } } }); } BOOST_AUTO_TEST_CASE(SelfReachingLoad) { // // Self-reaching load // const char *Body = R"LLVM( br label %head head: %load_rax = load i64, i64* @rax br i1 0, label %end, label %head end: ret void )LLVM"; runTest(Body, { { "load_rax", {} } }); } BOOST_AUTO_TEST_CASE(RepeatedIfStatement) { // // Repeated if statement // const char *RepeatedIf = R"LLVM( %storezero = add i64 0, 0 store i64 %storezero, i64* @rax br i1 0, label %one, label %two one: %storeone = add i64 0, 0 store i64 %storeone, i64* @rax br label %secondif two: %storetwo = add i64 0, 0 store i64 %storetwo, i64* @rax br label %secondif secondif: br i1 0, label %three, label %four three: %load_three = load i64, i64* @rax br label %end four: %load_four = load i64, i64* @rax br label %end end: ret void )LLVM"; runTest(RepeatedIf, { { "load_three", { "s:storeone", "s:storetwo" } }, { "load_four", { "s:storeone", "s:storetwo" } } }, {}, Regular); runTest(RepeatedIf, { { "load_three", { "s:storeone" } }, { "load_four", { "s:storetwo" } } }, {}, Conditional); } BOOST_AUTO_TEST_CASE(ConditionalDefinition) { // // Conditional definition // const char *ConditionalDefinition = R"LLVM( %storezero = add i64 0, 0 store i64 %storezero, i64* @rax br i1 0, label %one, label %secondif one: %storeone = add i64 0, 0 store i64 %storeone, i64* @rax br label %secondif secondif: br i1 0, label %three, label %four three: %load_one = load i64, i64* @rax br label %end four: %load_two = load i64, i64* @rax br label %end end: ret void )LLVM"; runTest(ConditionalDefinition, { { "load_one", { "s:storeone" } }, { "load_two", { "s:storezero" } } }, {}, Conditional); } BOOST_AUTO_TEST_CASE(LoopClobbering) { // // Conditional definition // const char *ConditionalDefinition = R"LLVM( %variable = alloca i1 br label %head head: %variable_read = load i1, i1 *%variable br i1 %variable_read, label %one, label %two one: %storezero = add i64 0, 0 store i64 %storezero, i64* @rax br label %head two: %load_one = load i64, i64 *@rax br label %end end: ret void )LLVM"; runTest(ConditionalDefinition, { { "load_one", { "s:storezero" } } }, {}, Conditional); }